# VIP System

The VIP System provides a premium membership purchasing interface with configurable tiers, pricing options, and bonus systems. Players can purchase VIP status using WCoins, WPCoins, or Goblin Points.

## File Location

```
GameServer/Data/Plugins/VIP/VIPSystem.xml
```

## VIP Tiers

| Tier | Description | Typical Use                                        |
| ---- | ----------- | -------------------------------------------------- |
| 0    | Free Player | Default non-VIP status (not purchasable)           |
| 1-3  | VIP Tiers   | Purchasable premium tiers with increasing benefits |

## Configuration Structure

```xml
<VIPSystem Enabled="true" MaxTiers="4">
    <Tiers>
        <!-- Tier definitions with descriptions and bonuses -->
    </Tiers>
    <Pricing>
        <!-- Price options for each tier -->
    </Pricing>
    <UpgradeRules>
        <!-- How upgrades and time conversion work -->
    </UpgradeRules>
    <Display>
        <!-- UI text customization -->
    </Display>
</VIPSystem>
```

## Sections

### Root Element

```xml
<VIPSystem Enabled="true" MaxTiers="4">
```

| Attribute  | Type | Default | Description                                        |
| ---------- | ---- | ------- | -------------------------------------------------- |
| `Enabled`  | bool | false   | Enable/disable the entire VIP system               |
| `MaxTiers` | int  | 4       | Total number of tiers (including tier 0 free tier) |

{% hint style="info" %}
**MaxTiers** includes the free tier (Index 0). For 3 purchasable VIP levels, set MaxTiers="4".
{% endhint %}

### Tiers

Each tier defines a membership level with its display properties, description, and bonuses.

```xml
<Tier Index="1" Name="Bronze VIP" Color="CD7F32" IconIndex="1">
    <Description>
        <Line>+20% Experience Rate</Line>
        <Line>+10% Item Drop Rate</Line>
    </Description>
    <Bonuses>
        <Bonus Type="EXP" Value="20" Display="+20% Experience" />
        <Bonus Type="DROP" Value="10" Display="+10% Item Drop" />
    </Bonuses>
</Tier>
```

#### Tier Attributes

| Attribute   | Type   | Description                                               |
| ----------- | ------ | --------------------------------------------------------- |
| `Index`     | int    | Tier level (0=Free, 1-3=VIP tiers)                        |
| `Name`      | string | Display name (max 32 characters)                          |
| `Color`     | hex    | RGB color code without # prefix (e.g., "FFD700" for gold) |
| `IconIndex` | int    | Icon reference for client UI                              |

#### Description Lines

Add multiple `<Line>` elements to describe the tier benefits. **Maximum 8 lines** (hardcoded limit: `MAX_VIP_DESCRIPTION_LINES`), **64 characters each**.

```xml
<Description>
    <Line>+50% Experience Rate</Line>
    <Line>+25% Item Drop Rate</Line>
    <Line>Access to VIP Maps</Line>
</Description>
```

{% hint style="danger" %}
**Lines beyond the 8th will be ignored!** The server will only read the first 8 description lines. If you configure more than 8, they will not be sent to the client.
{% endhint %}

#### Bonuses

Bonuses are displayed to the player in the UI. The bonus values shown here are for **display only** - actual gameplay bonuses must be configured in `ServerInfo.txt` under the VIP rate multipliers.

**Maximum 12 bonuses per tier** (hardcoded limit: `MAX_VIP_BONUSES_PER_TIER`).

```xml
<Bonus Type="EXP" Value="50" Display="+50% Experience" />
<Bonus Type="FEATURE" Display="VIP Maps Access" />
```

| Attribute | Type   | Description                                                  |
| --------- | ------ | ------------------------------------------------------------ |
| `Type`    | enum   | Bonus category: `EXP`, `DROP`, `ZEN`, `FEATURE`, `COSMETIC`  |
| `Value`   | int    | Numeric value (used for rate bonuses, optional for features) |
| `Display` | string | Text shown to player (max 48 characters)                     |

**Bonus Types:**

| Type       | Purpose               | Example                |
| ---------- | --------------------- | ---------------------- |
| `EXP`      | Experience rate bonus | "Value=50" = 50% bonus |
| `DROP`     | Item drop rate bonus  | "Value=25" = 25% bonus |
| `ZEN`      | Zen drop rate bonus   | "Value=25" = 25% bonus |
| `FEATURE`  | Gameplay feature      | "VIP Maps Access"      |
| `COSMETIC` | Visual effect         | "Gold Name Color"      |

{% hint style="warning" %}
**Bonuses are UI-only!** The actual experience/drop rates must be configured separately in ServerInfo.txt. These values tell the player what they're getting, but don't affect gameplay directly.
{% endhint %}

### Pricing

Define pricing options for each purchasable tier. Multiple duration options can be offered per tier.

```xml
<TierPricing TierIndex="1">
    <Option Days="7" WCoins="100" WPCoins="0" GoblinPoints="500" />
    <Option Days="30" WCoins="350" WPCoins="0" GoblinPoints="1800" />
    <Option Days="90" WCoins="900" WPCoins="0" GoblinPoints="4500" />
</TierPricing>
```

#### TierPricing Attributes

| Attribute   | Type | Description                              |
| ----------- | ---- | ---------------------------------------- |
| `TierIndex` | int  | Which tier this pricing applies to (1-3) |

{% hint style="info" %}
Tier 0 (Free Player) should not have pricing defined - it's not purchasable.
{% endhint %}

#### Option Attributes

| Attribute      | Type | Valid Range | Description                                                |
| -------------- | ---- | ----------- | ---------------------------------------------------------- |
| `Days`         | int  | 1-255       | Duration of VIP membership in days (sent as BYTE, max 255) |
| `WCoins`       | int  | 0+          | Cost in WCoins (set to 0 to disable this currency)         |
| `WPCoins`      | int  | 0+          | Cost in WPCoins (set to 0 to disable this currency)        |
| `GoblinPoints` | int  | 0+          | Cost in Goblin Points (set to 0 to disable this currency)  |

{% hint style="warning" %}
**Days field limitation:** While the config accepts any integer for Days, the value is sent to the client as a BYTE (0-255). Values above 255 will overflow. For durations longer than 255 days, players must purchase multiple times or you'll need to modify the protocol.
{% endhint %}

**Currency Selection Logic:**

* Any currency with a value > 0 is available for payment
* The player chooses which currency to use during purchase
* At least one currency must be > 0 for a valid option

**Example Configurations:**

{% tabs %}
{% tab title="WCoins Only" %}

```xml
<Option Days="30" WCoins="500" WPCoins="0" GoblinPoints="0" />
```

Players can only pay with WCoins for this option.
{% endtab %}

{% tab title="Multiple Currencies" %}

```xml
<Option Days="30" WCoins="500" WPCoins="250" GoblinPoints="2500" />
```

Players can choose to pay with WCoins, WPCoins, OR Goblin Points.
{% endtab %}

{% tab title="Event Currency Only" %}

```xml
<Option Days="7" WCoins="0" WPCoins="0" GoblinPoints="1000" />
```

Special promotion - only purchasable with Goblin Points.
{% endtab %}
{% endtabs %}

### Upgrade Rules

Controls how VIP tier upgrades and time conversion work.

```xml
<UpgradeRules>
    <Rule ConvertRemainingTime="true" ConversionRate="50" AllowDowngrade="false" />
</UpgradeRules>
```

| Attribute              | Type | Default | Description                                                     |
| ---------------------- | ---- | ------- | --------------------------------------------------------------- |
| `ConvertRemainingTime` | bool | true    | Convert remaining lower-tier time when upgrading to higher tier |
| `ConversionRate`       | int  | 50      | Percentage of time that carries over (0-100)                    |
| `AllowDowngrade`       | bool | false   | Allow purchasing lower tier when player has higher tier         |

#### Time Conversion Behavior

When a player upgrades from a lower tier to a higher tier, the system can convert their remaining time on the old tier.

**Formula:**

```
ConvertedTime = RemainingSeconds × (CurrentTier / NewTier) × (ConversionRate / 100)
```

**Example:**

Player has 15 days remaining on VIP1 (Bronze) and upgrades to VIP3 (Gold) with `ConversionRate="50"`:

```
ConvertedTime = 15 days × (1/3) × 0.5 = 2.5 days
```

The player gets 2.5 days bonus added to their VIP3 purchase.

{% hint style="info" %}
**Extension vs Upgrade:**

* **Extension** (same tier): Adds duration to existing expiration date, no conversion needed
* **Upgrade** (higher tier): Uses time conversion rules to calculate bonus from remaining time
  {% endhint %}

{% hint style="danger" %}
**AllowDowngrade=true** is not recommended! Players could abuse time conversion by bouncing between tiers. Only enable if you have a specific use case.
{% endhint %}

### Display Settings

Customize the UI text shown in the VIP purchase window.

```xml
<Display>
    <WindowTitle>VIP Membership</WindowTitle>
    <PurchaseButtonText>Purchase</PurchaseButtonText>
    <ExtendButtonText>Extend</ExtendButtonText>
    <UpgradeButtonText>Upgrade</UpgradeButtonText>
    <CurrentTierLabel>Current Tier:</CurrentTierLabel>
    <ExpiresLabel>Expires:</ExpiresLabel>
    <SelectDurationLabel>Select Duration:</SelectDurationLabel>
</Display>
```

| Element               | Max Length | Description                              |
| --------------------- | ---------- | ---------------------------------------- |
| `WindowTitle`         | 64 chars   | Window title bar text                    |
| `PurchaseButtonText`  | 32 chars   | Button text for new VIP purchase         |
| `ExtendButtonText`    | 32 chars   | Button text for extending current tier   |
| `UpgradeButtonText`   | 32 chars   | Button text for upgrading to higher tier |
| `CurrentTierLabel`    | 32 chars   | Label for current VIP status             |
| `ExpiresLabel`        | 32 chars   | Label for expiration date                |
| `SelectDurationLabel` | 32 chars   | Label for duration selection dropdown    |

## Important Behavior

### Purchase Types

The system automatically determines the purchase type:

| Type        | Condition                    | Behavior                                    |
| ----------- | ---------------------------- | ------------------------------------------- |
| **NEW**     | Player has no VIP (tier 0)   | Grants VIP starting now                     |
| **EXTEND**  | Player purchases same tier   | Adds duration to existing expiration        |
| **UPGRADE** | Player purchases higher tier | Converts remaining time + adds new duration |

### Currency Deduction

When a purchase succeeds:

1. Currency is deducted from the player's account via `GDSetCoinSend` to DataServer
2. Player's balance updates immediately in the client UI (via `GCSendCoin` notification)
3. Account level and expiration are updated via `GJAccountLevelSaveSend` to JoinServer

{% hint style="warning" %}
**No rollback mechanism:** If the database write fails, there is no automatic rollback. The purchase validation happens BEFORE any changes are made, so failures during execution should be rare. However, if a database connection issue occurs during the write, the player may lose currency without receiving VIP status. Database connectivity should be monitored.
{% endhint %}

### Account Level Storage

VIP tier is stored in the `AccountLevel` field:

* Tier 0 = Free player
* Tier 1-3 = VIP tiers
* Expiration date stored in `AccountExpireDate` field

{% hint style="info" %}
**Server Rates:** The bonus values shown in `<Bonus>` elements are cosmetic. Actual EXP/Drop/Zen multipliers must be configured in `ServerInfo.txt` under VIP rate settings.
{% endhint %}

### Time Handling

The system handles time zones and expiration as follows:

* All times use server local time
* Expiration format: `YYYY-MM-DD HH:MM:SS`
* Grace period: Players retain benefits until the exact expiration second
* Extensions: GameServer calculates new expiration locally AND sends duration to JoinServer for database persistence

{% hint style="info" %}
**Extension behavior:** When a player extends their current tier, the GameServer adds the purchased duration to their remaining time for immediate display. Separately, it sends the duration to JoinServer which adds it to the database expiration date. This dual update ensures instant UI feedback while maintaining database consistency.
{% endhint %}

## Examples

{% tabs %}
{% tab title="Basic 3-Tier System" %}

```xml
<VIPSystem Enabled="true" MaxTiers="4">
    <Tiers>
        <Tier Index="0" Name="Free Player" Color="808080" IconIndex="0">
            <Description>
                <Line>Standard gameplay</Line>
            </Description>
            <Bonuses />
        </Tier>

        <Tier Index="1" Name="Bronze VIP" Color="CD7F32" IconIndex="1">
            <Description>
                <Line>+20% Experience</Line>
                <Line>+10% Drop Rate</Line>
            </Description>
            <Bonuses>
                <Bonus Type="EXP" Value="20" Display="+20% Experience" />
                <Bonus Type="DROP" Value="10" Display="+10% Drop Rate" />
            </Bonuses>
        </Tier>

        <Tier Index="2" Name="Silver VIP" Color="C0C0C0" IconIndex="2">
            <Description>
                <Line>+50% Experience</Line>
                <Line>+25% Drop Rate</Line>
            </Description>
            <Bonuses>
                <Bonus Type="EXP" Value="50" Display="+50% Experience" />
                <Bonus Type="DROP" Value="25" Display="+25% Drop Rate" />
            </Bonuses>
        </Tier>

        <Tier Index="3" Name="Gold VIP" Color="FFD700" IconIndex="3">
            <Description>
                <Line>+100% Experience</Line>
                <Line>+50% Drop Rate</Line>
            </Description>
            <Bonuses>
                <Bonus Type="EXP" Value="100" Display="+100% Experience" />
                <Bonus Type="DROP" Value="50" Display="+50% Drop Rate" />
            </Bonuses>
        </Tier>
    </Tiers>

    <Pricing>
        <TierPricing TierIndex="1">
            <Option Days="7" WCoins="100" WPCoins="0" GoblinPoints="0" />
            <Option Days="30" WCoins="350" WPCoins="0" GoblinPoints="0" />
        </TierPricing>
        <TierPricing TierIndex="2">
            <Option Days="7" WCoins="200" WPCoins="0" GoblinPoints="0" />
            <Option Days="30" WCoins="700" WPCoins="0" GoblinPoints="0" />
        </TierPricing>
        <TierPricing TierIndex="3">
            <Option Days="7" WCoins="400" WPCoins="0" GoblinPoints="0" />
            <Option Days="30" WCoins="1400" WPCoins="0" GoblinPoints="0" />
        </TierPricing>
    </Pricing>

    <UpgradeRules>
        <Rule ConvertRemainingTime="true" ConversionRate="50" AllowDowngrade="false" />
    </UpgradeRules>

    <Display>
        <WindowTitle>VIP Membership</WindowTitle>
        <PurchaseButtonText>Purchase</PurchaseButtonText>
        <ExtendButtonText>Extend</ExtendButtonText>
        <UpgradeButtonText>Upgrade</UpgradeButtonText>
        <CurrentTierLabel>Current Tier:</CurrentTierLabel>
        <ExpiresLabel>Expires:</ExpiresLabel>
        <SelectDurationLabel>Select Duration:</SelectDurationLabel>
    </Display>
</VIPSystem>
```

{% endtab %}

{% tab title="Multi-Currency Event" %}

```xml
<Pricing>
    <TierPricing TierIndex="1">
        <!-- Regular pricing -->
        <Option Days="30" WCoins="350" WPCoins="0" GoblinPoints="0" />
        <!-- Special event: Buy with Goblin Points -->
        <Option Days="7" WCoins="0" WPCoins="0" GoblinPoints="500" />
    </TierPricing>
    <TierPricing TierIndex="2">
        <!-- Allow payment with either WCoins OR Goblin Points -->
        <Option Days="30" WCoins="700" WPCoins="0" GoblinPoints="3500" />
    </TierPricing>
</Pricing>
```

This configuration offers flexible payment options for special events.
{% endtab %}

{% tab title="No Time Conversion" %}

```xml
<UpgradeRules>
    <Rule ConvertRemainingTime="false" ConversionRate="0" AllowDowngrade="false" />
</UpgradeRules>
```

Players lose remaining time when upgrading. This encourages players to wait until their VIP expires before upgrading.
{% endtab %}
{% endtabs %}

## Common Issues

{% hint style="danger" %}
**Config file has invalid Gold VIP tier**

The default `VIPSystem.xml` contains a Gold VIP tier (Index 3) with **10 description lines**, which exceeds the hardcoded limit of 8. Lines 9 and 10 will be ignored by the server. Edit the config to remove excess lines or consolidate them.
{% endhint %}

{% hint style="warning" %}
**Bonus values don't affect gameplay**

The `<Bonus>` elements are purely informational. You must configure actual rate multipliers in `ServerInfo.txt` to apply the advertised bonuses.
{% endhint %}

{% hint style="warning" %}
**Currency not deducted**

If the player's currency balance doesn't decrease after purchase, check:

1. DataServer is running and connected
2. GDSetCoinSend is working (check DataServer logs)
3. Database connection is healthy
   {% endhint %}

{% hint style="warning" %}
**VIP expires immediately**

If VIP expires right after purchase, check:

1. Server time zone matches database time zone
2. `AccountExpireDate` field is set correctly in database
3. JoinServer stored procedure `WZ_SetAccountLevel` is working
   {% endhint %}

{% hint style="danger" %}
**Time conversion abuse**

If `ConvertRemainingTime="true"` and `AllowDowngrade="true"`, players could exploit time conversion by:

1. Buy VIP3 for 30 days
2. Immediately downgrade to VIP1 (getting bonus days from conversion)
3. Upgrade back to VIP3 (getting more bonus days)

Always use `AllowDowngrade="false"` unless you have anti-abuse measures.
{% endhint %}

## Protocol

The VIP System uses custom protocol handlers in the `0xF3` extended command space:

| Subcode | Direction     | Purpose                                         |
| ------- | ------------- | ----------------------------------------------- |
| `0x74`  | Client→Server | Open VIP menu request                           |
| `0x74`  | Server→Client | VIP menu data response (tiers, pricing, status) |
| `0x75`  | Client→Server | Purchase request (tier, duration, currency)     |
| `0x76`  | Server→Client | Purchase result (success/failure, new status)   |

{% hint style="info" %}
**Subcode 0x74 is bidirectional:** The client sends an empty 0x74 request to open the menu, and the server responds with 0x74 containing the full VIP configuration and player status.
{% endhint %}

## Dependencies

| Depends On     | Purpose                                              |
| -------------- | ---------------------------------------------------- |
| JoinServer     | Stores VIP tier and expiration date in account table |
| DataServer     | Deducts currency from account balance                |
| ServerInfo.txt | Actual rate multipliers for VIP bonuses              |

## Related Configuration

* **ServerInfo.txt** - VIP rate multipliers (EXP, Drop, Zen)
* **CashShop** - WCoin/WPCoin management
* **Move/Gate configurations** - VIP-only map access
