Skip to content

caravana-studio/jokers-of-neon-mods

Repository files navigation

Jokers of Neon Modding Documentation

1. Getting Started with Modding

Welcome to the Jokers of Neon Modding Docs! This guide will help you create and customize mods to enhance gameplay, tweak game mechanics, and introduce new visuals.

With the mods, you can:

  • Customize initial game configurations.
  • Set up a personalized shop.
  • Create unique deck setups.
  • Design new special cards, rage cards, and loot boxes with distinct effects.
  • Replace visual elements such as card designs, borders, and backgrounds.

Prerequisites

If you have a different version of Dojo, you can update it by running:

dojoup --version 1.1.2

1.1. Understanding the Mod Configuration Files

The mods rely on a robust configuration system that allows you to modify various aspects of the game. These configuration files are located in: mods/<mod_name>/src/configs/

This folder contains the following key configuration files:

  • Game Configuration: Defines gameplay rules and mechanics.
  • Shop Configuration: Sets up the initial shop inventory.

Tip

Start by cloning this repo and navigate to mods/jokers_of_neon_template to get familiar with the folder structure.


1.1.1. Game Configuration

The Game Configuration file allows you to modify the foundational rules of the game. It’s located at: mods/<mod_name>/src/configs/game.cairo.

Here’s an example configuration:

GameConfig {
    plays: 5,                    // Number of plays per round
    discards: 5,                 // Number of discards allowed
    specials_slots: 2,           // Starting special card slots
    max_special_slots: 7,        // Maximum special card slots
    power_up_slots: 4,           // Starting power-up slots
    max_power_up_slots: 4,       // Maximum power-up slots
    hand_len: 8,                 // Starting hand size
    start_cash: 99999,           // Starting cash amount
    start_special_slots: 1,      // Initial special slots available
}

1.1.2. Shop Configuration

The Shop Configuration file customizes the initial inventory available to players. It’s located at: mods/mod_name/src/configs/shop/config.cairo.

Here’s an example configuration:

ShopConfig {
    traditional_cards_quantity: 5,    // Regular cards in shop
    modifiers_cards_quantity: 3,      // Modifier cards in shop
    specials_cards_quantity: 3,       // Special cards in shop
    loot_boxes_quantity: 2,           // Loot boxes in shop
    power_ups_quantity: 2,            // Power-ups in shop
    poker_hands_quantity: 3,          // Poker hand cards in shop
}

1.2. Modding special cards

Special cards in Jokers of Neon introduce unique gameplay dynamics. They can grant or subtract points, multi, cash, and more. To streamline their behavior, we’ve implemented an abstract layer with predefined categories. Each category encapsulates fundamental methods to define and execute specific behaviors:

  1. CardType::Hit
  2. CardType::Discard
  3. CardType::PowerUp
  4. CardType::Round
  5. CardType::PokerHand
  6. CardType::Game

Below, we’ll explore each type, its use case, and implementation.


1.2.1. CardType::Hit

Description:

This card type is executed for every card in a player’s play. It uses the card’s Suit and Value properties to trigger effects.

Example:

Grant 100 points and 1 multi for every Joker in the play.

fn condition(self: @ContractState, context: GameContext, raw_data: felt252) -> bool {
    let card: Card = raw_data.into();
    card.suit == Suit::Joker
}

fn execute(ref self: ContractState, context: GameContext, raw_data: felt252) -> (i32, i32, i32) {
    let joker_card: Card = raw_data.into();
    ((joker_card.points * 2).try_into().unwrap(), (joker_card.multi_add * 2).try_into().unwrap(), 0)
}

Tip

Suggested Use: Use this card type for effects tied to specific cards, such as rewarding certain suits or values.


1.2.2. CardType::Discard

Description:

This type of card is executed for each discarded card of a player. It uses the card’s Suit and Value properties to trigger effects.

Example:

Grant 500 cash for every discarded card.

fn condition(self: @ContractState, context: GameContext, raw_data: felt252) -> bool {
    let card: Card = raw_data.into();
    card.suit == Suit::Joker
}

fn execute(ref self: ContractState, context: GameContext, raw_data: felt252) -> (i32, i32, i32) {
    (0, 0, 500)
}

1.2.3. CardType::PowerUp

Description:

This card type interacts with activated PowerUp cards, leveraging their points and multi properties.

Example:

Double the points and multipliers of all activated PowerUps.

fn execute(ref self: ContractState, context: GameContext, raw_data: felt252) -> (i32, i32, i32) {
    let power_up: PowerUp = raw_data.into();
    ((power_up.points * 2).try_into().unwrap(), (power_up.points * 2).try_into().unwrap(), 0)
}

Tip

Suggested Use: Amplify PowerUp effects or create synergy with specific PowerUp types.


1.2.4. CardType::Round

Description:

This card type is triggered once per round and evaluates the state of the game, including player_score, level_score, remaining_plays, and remaining_discards.

Example:

Grant 100 points and 10 multiplier during the first play of the round.

fn execute(ref self: ContractState, context: GameContext, raw_data: felt252) -> (i32, i32, i32) {
    if context.round.remaining_plays.into() == context.game.plays {
        (100, 10, 0)
    } else {
        (0, 0, 0)
    }
}

Tip

Suggested Use: Introduce strategic advantages based on the player’s progress within a round.


1.2.5. CardType::PokerHand

Description:

This card type triggers once per round and evaluates the poker hand formed during the play.

Example:

Grant 20 points and 4 multiplier for achieving a Two Pairs poker hand.

fn execute(ref self: ContractState, context: GameContext, raw_data: felt252) -> (i32, i32, i32) {
    if context.hand == PokerHand::TwoPair {
        (20, 4, 0)
    } else {
        (0, 0, 0)
    }
}

Tip

Suggested Use: Reward specific poker hands to add depth to hand-building strategies.


1.2.6. CardType::Game

Description:

This card type modifies global game properties such as hand_len, plays, and discards when equipped.

Example:

Increase the hand size by 2 cards when the card is equipped.

fn equip(ref self: ContractState, context: GameContext) -> GameContext {
    let mut context = context;
    context.game.hand_len += 2;
    context
}

fn unequip(ref self: ContractState, context: GameContext) -> GameContext {
    let mut context = context;
    context.game.hand_len -= 2;
    context
}

Tip

Suggested Use: Design cards that alter game rules for broader, long-term effects.


Tips for Designing Special Cards

Tip

  1. Balance: Ensure the cards are neither too powerful nor too weak.
  2. Theme: Design cards that align with the Jokers of Neon universe.
  3. Testing: Verify that the conditions and executions work correctly in various scenarios.
  4. Documentation: Provide clear descriptions of each card’s effects for players.

1.3. Visual Customization

Customize the appearance of special, traditional, neon, and modifier cards, along with game backgrounds and borders. You can also add new designs for newly implemented special cards.

Important

All modding resources must be placed in the directory: /public/mods/MOD_NAME/resources/.

Replace MOD_NAME with your mod's name.


1.3.1. Card Designs

You can modify existing card designs or add new ones. To replace a card, locate its specific ID and upload the image file to: /public/mods/(MOD_NAME)/resources/Cards/{cardID}.png

Recommended size for cards is: 276 x 420 px

Category Range Details
Traditional Cards 0-12 Clubs (2 to Ace)
13-25 Diamonds (2 to Ace)
26-38 Hearts (2 to Ace)
39-51 Spades (2 to Ace)
52 Joker
53 Wildcard
Neon Cards 200-212 Clubs (2 to Ace)
213-225 Diamonds (2 to Ace)
226-238 Hearts (2 to Ace)
239-251 Spades (2 to Ace)
252 Joker
253 Wildcard
Other Cards 300-400 Special Cards
401-600 Rage Cards
601-700 Modifier Cards

1.3.2. Backgrounds

Customize the game’s backgrounds by replacing the corresponding files in the /public/mods/(MOD_NAME)/resources/bg folder:

  • game-bg.jpg: Game background
  • home-bg.jpg: Home screen background
  • store-bg.jpg: Store screen background

1.3.3. Borders

To change the borders in your game, replace the following files in the /public/mods/(MOD_NAME)/resources/borders folder :

  • bottom.png
  • bottom-rage.png
  • top.png
  • top-rage.png

1.3.4. Deck Background

You can also personalize the deck background by replacing the file located at: /public/mods/(MOD_NAME)/resources/Cards/Backs/back.png

2. Step-by-Step Tutorial: Creating Your First Mod

2.1. Initial Setup

  1. Clone this repo

  2. Navigate to the mods folder in your game directory

  3. Copy the jokers_of_neon_template folder:

    cp -r jokers_of_neon_template your_mod_name

Important

Use underscores to separate words in the folder name

Every mod follows this basic structure:

└── mods/
    └── MOD_NAME/
        └── src/
            ├── configs/
            │   ├── game/
            │   └── shop/
            ├── specials/
            │   ├── special_game_type/
            │   ├── special_individual/
            │   ├── special_poker_hand/
            │   ├── special_power_up/
            │   ├── special_round_state/
            │   └── specials/
            ├── rages/
            │   ├── game/
            │   ├── round/
            │   ├── silence/
            │   └── rages/
            ├── lib.cairo
            └── loot_box.cairo
└── public/mods/MOD_NAME/resources

2.2. Adding Basic Mod Details

This information is used to display your mod on the game's page.

Important

All files must be uploaded to your mod's resources folder: /public/mods/MOD_NAME/resources/


2.2.1. Create a Mod Configuration File

In the mod's resources folder, create a config.json file containing essential details about your mod. Use the following structure:

{
  "name": "My awesome JN Mod",
  "description": "Brief description of your mod"
}

2.2.2. Add Preview Image

To customize the mod's preview image, upload an image named thumbnail.png to the same directory as config.json.


2.3. Modifying the Default Configuration

2.3.1. Game Configuration

You can modify the initial game settings to create a more challenging experience while offering certain advantages. These parameters are defined in: mods/(MOD_NAME)/src/configs/game.cairo.

Below is an example configuration with adjusted values for plays, discards, hand size, and starting cash:

GameConfig {
    plays: 2,                    // Number of plays per round
    discards: 7,                 // Number of discards allowed
    max_special_slots: 5,        // Maximum number of special slots
    power_up_slots: 4,           // Number of active power up slots
    max_power_up_slots: 4,       // Maximum number of power up slots
    hand_len: 10,                 // Starting hand size
    start_cash: 10000,           // Starting cash amount
    start_special_slots: 1,      // Number of special card slots active at the start of the game
}

2.3.2. Shop Configuration

You can customize the shop to show more special cards and fewer traditional cards or loot boxes, tailoring the experience to your mod. The shop configuration is located in: mods/mod_name/src/configs/shop.cairo.

Example of a customized shop configuration:

ShopConfig {
    traditional_cards_quantity: 2,    // Regular cards in shop
    modifiers_cards_quantity: 3,      // Modifier cards in shop
    specials_cards_quantity: 5,       // Special cards in shop
    loot_boxes_quantity: 1,           // Loot boxes in shop
    power_ups_quantity: 2,            // Power ups in shop
    poker_hands_quantity: 3           // Poker hands available to level up in shop
}

2.4. Implementing your first Special Card

In this section, we’ll create a special card that rewards points, multiplier, and cash when the player has a HighCard hand. All special cards must be defined in mods/mod_name/src/specials/specials.cairo, with each card assigned a unique ID between 300 and 400.

2.4.1. Define the Card ID

Open mods/mod_name/src/specials/specials.cairo and add the unique ID for your new Special Card:

const SPECIAL_HIGH_CARD_BOOSTER_ID: u32 = 309;

2.4.2. Register the Card in the Game

Add the new card id into the *specials_ids_all* function :

  fn specials_ids_all() -> Array<u32> {
  array![
      ....,
      SPECIAL_HIGH_CARD_BOOSTER_ID
  ]}

Assign the card to a group to make it purchasable in the shop. For example, add it to the SS group: Groups define the probability the card has for appearing in the shop and also its cost. You can also adjust the probabilities and costs for each group. Ensure that the probabilities of all defined groups add up to 100.

let SS_SPECIALS = array![..., SPECIAL_HIGH_CARD_BOOSTER_ID].span();

2.4.3. Create the Implementation File

Since this card is specific to PokerHand functionality, its type will be CardType::PokerHand. Navigate to the mods/mod_name/src/specials/poker_hand/ directory and create a new file named high_card_booster.cairo. Add the implementation for your new card in this file.

After that whe should go to src/lib.cairo and add this line to include our new module:

  mod high_card_booster;

Example Implementation

Below is an example of how to implement the HighCard Booster special card:

#[dojo::contract]
pub mod special_high_card_booster {
    use jokers_of_neon_classic::specials::specials::SPECIAL_HIGH_CARD_BOOSTER_ID;
    use jokers_of_neon_lib::interfaces::{base::ICardBase, cards::executable::ICardExecutable};
    use jokers_of_neon_lib::models::{card_type::CardType, data::poker_hand::PokerHand, tracker::GameContext};

    #[abi(embed_v0)]
    impl HighCardBoostExecutable of ICardExecutable<ContractState> {
        fn execute(ref self: ContractState, context: GameContext, raw_data: felt252) -> (i32, i32, i32) {
            if context.hand == PokerHand::HighCard {
                (100, 20, 500)
            } else {
                (0, 0, 0)
            }
        }
    }

    #[abi(embed_v0)]
    impl HighCardBoostBase of ICardBase<ContractState> {
        fn get_id(self: @ContractState) -> u32 {
            SPECIAL_HIGH_CARD_BOOSTER_ID
        }

        fn get_types(self: @ContractState) -> Span<CardType> {
            array![CardType::PokerHand].span()
        }
    }
}
Key Methods Explained
  • get_id(): Returns the unique ID of the card.
  • get_type(): Defines the card as a PokerHand type.
  • execute(): Implements the card’s effect. In this case:
    • HighCard Hand: Rewards 100 points, 20 multiplier, and 500 cash.
    • Other Hands: Rewards 0 points, 0 multiplier, and 0 cash.

2.4.4. Making Special Cards Available to the Frontend

To ensure your special cards are accessible in the frontend, follow these steps:

2.4.4.1. Update the Card Metadata

Navigate to /public/mods/(MOD_NAME)/resources/ and update the specials.json file to include the name and description of your special cards.

For example:

{
  "CardID": {
    "name": "Card Name",
    "description": "Card Description"
  },
  "349": {
    "name": "Random Diamond Joker",
    "description": "Adds a number between -2 and 6 to the multiplier for each Diamonds-suited card played."
  },
  "355": {
    "name": "Extra",
    "description": "Demo"
  }
}

Replace CardID with the unique ID of your card. For the card we implemented earlier, use 309.


2.4.4.2. Add the Card Image

Upload an image for your special card to the following directory: /public/mods/(MOD_NAME)/resources/Cards/{cardID}.png

Replace {cardID} with the unique ID of your card. For the example card, the image file should be named 309.png

2.5. Create a PR on this repository

Once you have completed the implementation of your mod including frontend assets and cairo code, create a pull request to this repository.

2.6. Deploy Your Mod

Important

If you do not have Cairo installed, skip this step and comment your PR saying that you have skipped the deployment step, so that we can take care of it.

Rename the .env_example file to .env

Run the deploy command:

make deploy-mod mod_name=your_mod_name

2.7. Play your mod

Wait until your PR is merged. Once it is, you will be able to play your mod at modding.jokersofneon.com.