GS2-Lottery

Lottery processing feature

This is a mechanism for implementing gachas. GS2-Lottery supports both normal gachas and box gachas.

graph TD
  Trigger["Lottery trigger<br/>(GS2-Showcase purchase reward / quest clear reward, etc.)"] --> LotteryModel["LotteryModel<br/>(lottery model)"]
  LotteryModel -- "mode = normal" --> Normal["Normal mode lottery"]
  LotteryModel -- "mode = box" --> Box["Box mode lottery"]
  Normal --> PrizeTable["Prize table<br/>(PrizeTable)"]
  Box --> BoxData["Player-exclusive box"]
  PrizeTable --> Prize["Prize<br/>(acquireActions)"]
  BoxData --> Prize
  Prize --> Acquire["GS2-Distributor<br/>reward distribution"]

Normal Gacha

A normal gacha performs a pure lottery with the specified probability.

Prize Table

A prize table defines the emission probability of prizes that appear when the gacha is executed. It is defined as master data.

Weights

The probability in a prize table is set by specifying a weight for each prize. Specifically, you will create a table such as the following.

Weight
Prize A 1
Prize B 2
Prize C 4

Interpreted as percentage probabilities, this can be read as follows.

Weight Probability
Prize A 1 14.285%
Prize B 2 28.571%
Prize C 4 57.143%

When setting probabilities on a percentage basis, you must ensure they all add up to 100%, but when setting them on a weight basis, there is no such burden.

Nesting Prize Tables

Prize tables can be nested up to 5 layers deep.

A specific use case is when you want to set characters to be emitted based on a basic policy such as:

  • SSR characters at 3% emission probability
  • SR characters at 7% emission probability
  • R characters at 90% emission probability

In this case, you would define 4 prize tables in 2 tiers.

Prize table for rarity lottery

Weight Prize type Lottery table name
SSR 3 Nested prize table Prize table for SSR character lottery
SR 7 Nested prize table Prize table for SR character lottery
R 90 Nested prize table Prize table for R character lottery

Prize table for SSR character lottery

Weight Prize type Lottery table name
SSR-0001 1 Acquire action Record SSR-0001 in GS2-Dictionary
SSR-0002 1 Acquire action Record SSR-0002 in GS2-Dictionary
SSR-0003 1 Acquire action Record SSR-0003 in GS2-Dictionary

Prize table for SR character lottery

Weight Prize type Lottery table name
SR-0001 1 Acquire action Record SR-0001 in GS2-Dictionary
SR-0002 1 Acquire action Record SR-0002 in GS2-Dictionary
SR-0003 1 Acquire action Record SR-0003 in GS2-Dictionary

Prize table for R character lottery

Weight Prize type Lottery table name
R-0001 1 Acquire action Record R-0001 in GS2-Dictionary
R-0002 1 Acquire action Record R-0002 in GS2-Dictionary
R-0003 1 Acquire action Record R-0003 in GS2-Dictionary

Box Gacha

In a box gacha, prizes defined in the master data are placed into a box in the specified quantities, and the lottery is performed by drawing prizes from the box.

In other words, if you prepare a box that contains one “winning” prize out of 100 prizes, the winning prize is guaranteed to be drawn within 100 draws.

Placing Prizes into the Box

The prize table is also used to configure the contents of the box. In normal lottery mode, you set the emission weight, but in box mode the weight parameter specifies the number of prizes to place into the box.

Weight
Prize A 1
Prize B 2
Prize C 4

In this case, the box initially contains 7 prizes: 1 Prize A, 2 Prize B, and 4 Prize C.

Resetting the Box

A box can be returned to its initial state using the “Reset box” action. Since each player has their own independent box, this can be used for purposes such as resetting the box after granting a completion reward, or refreshing the box at the start of each month.

Lottery Model

A lottery model specifies which of the prize tables can be used by the lottery API. It is defined as master data.

In the example above, it would be problematic if calling the lottery API could directly use the “Prize table for SSR character lottery” to run a lottery.

By defining master data for the lottery model such as:

Lottery model name Prize table name
gacha Prize table for rarity lottery

When invoking the lottery process, you specify “gacha” as the lottery model name.

Lottery Mode (mode) and Lottery Method (method)

A lottery model uses mode to specify the lottery method and method to specify how the prize table is referenced.

mode Description
normal Normal mode (performs an independent lottery with the specified probability each time)
box Box mode (draws prizes from the player’s own box)
method Description
prize_table Use a statically specified prize table
script Dynamically select a prize table using GS2-Script

Selecting Prize Tables via Script

By setting method to script and specifying choicePrizeTableScriptId, you can invoke GS2-Script when the lottery is executed to dynamically select the prize table to be used. This enables per-player probability changes (such as pity systems) and switching prize tables according to the event currently in progress.

Script Triggers

By setting lotteryTriggerScriptId on the namespace, you can execute GS2-Script synchronously before the lottery process. The script can decide whether to allow or deny the lottery, or override the lottery result.

The main event triggers and the script setting names that can be configured are as follows.

  • lotteryTriggerScriptId: Executed synchronously before the lottery process

You can implement custom processing such as verifying lottery results or replacing results under specific conditions.

Transaction Actions

GS2-Lottery provides the following transaction actions.

  • Acquire action: Execute a lottery, Reset a box

By using “Execute a lottery” as an acquire action, you can directly run a gacha (lottery) as a reward for purchasing an item at a shop or as a quest clear reward. Furthermore, by including “Reset a box” as an acquire action, you can safely control, within a transaction, automatic resetting of a box gacha so it can be drawn from the start again, for example when a specific item is acquired or at a milestone in an event.

Master Data Management

By registering master data, you can configure the data and behaviors available in the microservice.

The types of master data are as follows.

  • LotteryModel: Lottery model. Defines the lottery method and the prize table to use.
  • PrizeTable: Prize table. Defines the weight of each prize together with acquire actions, or a nested prize table.
  • Prize: The unit of a prize. Specifies the reward obtained via acquireActions, and can also configure emission limits and failover destinations via drawnLimit and limitFailOverPrizeId.

In addition to registering master data from the management console, you can also reflect data from GitHub or build a workflow that registers it from CI using GS2-Deploy.

The following is an example of master data in JSON.

{
  "version": "2019-02-21",
  "lotteryModels": [
    {
      "name": "lottery-0001",
      "metadata": "Normal gacha",
      "mode": "normal",
      "method": "prize_table",
      "prizeTableName": "rarity"
    }
  ],
  "prizeTables": [
    {
      "name": "rarity",
      "metadata": "Rarity lottery",
      "prizes": [
        {
          "prizeId": "ssr",
          "type": "prize_table",
          "prizeTableName": "ssr-prizes",
          "weight": 3
        },
        {
          "prizeId": "sr",
          "type": "prize_table",
          "prizeTableName": "sr-prizes",
          "weight": 7
        },
        {
          "prizeId": "r",
          "type": "prize_table",
          "prizeTableName": "r-prizes",
          "weight": 90
        }
      ]
    }
  ]
}

Implementation Examples

Executing a Lottery

Executing a lottery cannot be performed with game engine SDKs.

Please implement it by, for example, executing the lottery as a reward for purchasing an item via GS2-Showcase. By including the DrawByUserId of GS2-Lottery in the acquireActions of GS2-Showcase, you can run the lottery as part of the purchase transaction.

Obtaining Emission Probabilities

For displaying prizes, you can obtain the current emission probabilities for the player. If emission quantity limits (described later) apply, the returned probabilities reflect that effect.

    var items = await gs2.Lottery.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).ProbabilitiesAsync(
        lotteryName: "lottery-0001"
    ).ToListAsync();
    const auto It = Gs2->Lottery->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->Probabilities(
        "lottery-0001" // lotteryName
    );
    TArray<Gs2::UE5::Lottery::Model::FEzProbabilityPtr> Result;
    for (auto Item : *It)
    {
        if (Item.IsError())
        {
            return false;
        }
        Result.Add(Item.Current());
    }

Retrieving the Contents of a Box

    var item = await gs2.Lottery.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).BoxItems(
        prizeTableName: "prizeTable-0001"
    ).ModelAsync();
    const auto Domain = Gs2->Lottery->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->BoxItems(
        "prizeTable-0001" // prizeTableName
    );
    const auto Future = Domain->Model();
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;
    const auto Result = Future->GetTask().Result();

Resetting a Box

    var result = await gs2.Lottery.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).BoxItems(
        prizeTableName: "prizeTable-0001"
    ).ResetBoxAsync(
    );
    const auto Future = Gs2->Lottery->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->BoxItems(
        "prizeTable-0001" // prizeTableName
    )->ResetBox(
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;

Retrieving the List of Lottery Models

To display a list of gachas in the UI, you can retrieve the lottery models defined in the namespace.

    var items = await gs2.Lottery.Namespace(
        namespaceName: "namespace-0001"
    ).LotteryModelsAsync(
    ).ToListAsync();
    const auto It = Gs2->Lottery->Namespace(
        "namespace-0001" // namespaceName
    )->LotteryModels();
    TArray<Gs2::UE5::Lottery::Model::FEzLotteryModelPtr> Result;
    for (auto Item : *It)
    {
        if (Item.IsError())
        {
            return false;
        }
        Result.Add(Item.Current());
    }

Other Features

Limiting the Emission Quantity of Prizes

When operating in normal mode, you can set an upper limit on the emission quantity per prize. Using this feature causes a discrepancy between the probabilities returned by the Probabilities function and the actual probabilities. This feature should not be used when you are displaying emission probabilities using Probabilities.

The intended use case is for lottery processes that distribute physical prizes. For example, if you have a gift certificate that is emitted with 1% probability but you have only 100 gift certificates prepared, this feature is useful. You set the emission quantity limit for the gift certificate to 100, and set a failover prize such as a blank or a different winning prize.

With this setting, when 100 gift certificates have already been emitted and another gift certificate would have been drawn, the prize configured as the failover prize is emitted instead. If a quantity limit is set on the failover prize as well, the same kind of limit applies.

Detailed Reference