Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
249 changes: 147 additions & 102 deletions client/pages/getting-started/basics/constants.md
Original file line number Diff line number Diff line change
@@ -1,147 +1,192 @@
# Constants Usage in Dojo Engine 🎮

## What are Constants? 📝
Constants are like the **configuration file** of your game - they define the fixed values that control how your game behaves. Think of them as the "rules" that determine damage amounts, health points, cooldown times, and other gameplay mechanics in your Dojo game.

Constants are fixed values used in game code to configure essential parameters such as damage, health, time durations, and other gameplay aspects. In Dojo Engine, constants are defined in Cairo files like `constants.cairo`, enabling consistent game balance and easy configuration.
## Why Constants Matter in Game Development 🤔

## Why Use Constants? 🤔
Imagine you're balancing a fighting game. Without constants, you'd have random numbers scattered throughout your code like:

- **Maintainability:** Changing a constant updates all parts of the code that use it.
- **Game Balance:** Allows tweaking gameplay parameters without modifying logic.
- **Clarity:** Descriptive names are easier to understand than "magic numbers."
```cairo
// ❌ Bad: Magic numbers everywhere
let damage = attack_power * 150; // What does 150 mean?
let health = base_health + level * 20; // Why 20?
```

## Real Example: constants.cairo 💻
With constants, your code becomes self-documenting and easier to balance:

```cairo
// Game Balance Constants
// These constants control core gameplay mechanics and balance

// Time-based constants for game progression
pub const SECONDS_PER_DAY: u64 = 86400; // Used for daily rewards and time-based events
pub const GAME_TICK_RATE: u64 = 60; // How often the game state updates

// Combat system constants
pub const BASE_LEVEL_BONUS: u8 = 10; // Base bonus per level for character progression
pub const SUPER_EFFECTIVE: u8 = 150; // 150% damage for type advantages
pub const NORMAL_EFFECTIVENESS: u8 = 100; // 100% damage for neutral matchups
pub const NOT_VERY_EFFECTIVE: u8 = 50; // 50% damage for type disadvantages

// Attack multipliers for strategic depth
pub const FAVORED_ATTACK_MULTIPLIER: u8 = 120; // 120% damage for favored attacks
pub const NORMAL_ATTACK_MULTIPLIER: u8 = 100; // 100% damage for normal attacks

// Energy system constants
pub const MAX_ENERGY: u8 = 100; // Maximum energy a player can have
pub const ENERGY_REGEN_RATE: u8 = 5; // Energy points regenerated per tick
pub const ENERGY_COST_PER_ACTION: u8 = 10; // Cost of basic actions

// Health system constants
pub const BASE_HEALTH: u8 = 100; // Starting health for new players
pub const HEALTH_REGEN_RATE: u8 = 2; // Health points regenerated per tick
pub const MAX_HEALTH_BONUS_PER_LEVEL: u8 = 20; // Health increase per level

// Utility constants
pub fn ZERO_ADDRESS() -> ContractAddress {
contract_address_const::<0x0>() // Used for null checks and initialization
}
// ✅ Good: Clear, meaningful constants
let damage = attack_power * SUPER_EFFECTIVE;
let health = base_health + level * HEALTH_BONUS_PER_LEVEL;
```

## Organizing Constants 📊
**Key Benefits:**
- **Easy Game Balancing**: Change one number to affect the entire game
- **Clear Code**: Anyone can understand what `MAX_ENERGY` means
- **Consistent Values**: No more accidentally using 100 in one place and 99 in another
- **Team Collaboration**: Designers can suggest balance changes without touching code logic

- Group constants by categories: combat, health, energy, time, etc.
- Use clear comments to explain each constant's purpose.
- Use uppercase snake_case naming for constants (e.g., MAX_SEARCH).
## Real-World Example: Building a Combat System 💻

## Best Practices ⭐
Here's how you might structure constants for a turn-based RPG in Dojo:

- Avoid using "magic numbers" directly in code; always reference constants.
- Keep constants immutable.
- Use clear and descriptive names to avoid confusion.
- Document each constant with a brief comment.
```cairo
// constants.cairo
// Combat Balance - The heart of your game's feel

## How Constants Help in Dojo 🚀
// Type Effectiveness System (Rock-Paper-Scissors mechanics)
pub const SUPER_EFFECTIVE: u8 = 150; // 1.5x damage - strong advantage
pub const NORMAL_EFFECTIVENESS: u8 = 100; // 1.0x damage - neutral matchup
pub const NOT_VERY_EFFECTIVE: u8 = 50; // 0.5x damage - poor matchup

### Game Balance Tuning
Constants make it easy to adjust game balance without changing core logic. For example:
- Adjusting `ENERGY_REGEN_RATE` and `ENERGY_COST_PER_ACTION` to control gameplay pace
- Modifying `MAX_HEALTH_BONUS_PER_LEVEL` to scale difficulty with player progression
- Tweaking `SUPER_EFFECTIVE` and `NOT_VERY_EFFECTIVE` to balance combat mechanics
// Character Progression
pub const BASE_LEVEL_BONUS: u8 = 10; // Stats gained per level
pub const MAX_HEALTH_BONUS_PER_LEVEL: u8 = 20; // HP increase per level

### Code Organization
Constants help organize game parameters into logical categories:
- Combat system constants
- Energy management constants
- Health system constants
- Time-based constants
- Utility constants
// Energy Management (Prevents spam, adds strategy)
pub const MAX_ENERGY: u8 = 100; // Energy cap
pub const ENERGY_REGEN_RATE: u8 = 5; // Energy gained per turn
pub const ENERGY_COST_PER_ACTION: u8 = 10; // Energy spent per basic attack

### Maintainability
Using constants improves code maintainability by:
- Centralizing configuration values
- Making balance changes easier to track
- Reducing the risk of inconsistent values
- Simplifying testing and debugging
// Time-Based Mechanics
pub const SECONDS_PER_DAY: u64 = 86400; // For daily rewards/events
pub const GAME_TICK_RATE: u64 = 60; // How often game updates

## How to Use Constants in Your Cairo Code 👨‍💻
// Special Attack Modifiers
pub const FAVORED_ATTACK_MULTIPLIER: u8 = 120; // 1.2x for specialized attacks
pub const NORMAL_ATTACK_MULTIPLIER: u8 = 100; // 1.0x for basic attacks

Once you define constants in `constants.cairo`, you can import and use them throughout your Dojo game modules for clear and consistent logic.
// Utility Constants
pub fn ZERO_ADDRESS() -> ContractAddress {
contract_address_const::<0x0>() // Null address for empty/invalid states
}
```

### Importing Constants
## How to Organize Your Constants 📊

**Group by Game System:**
```cairo
// Import constants module
use constants::*;
// ⚡ Energy System
pub const MAX_ENERGY: u8 = 100;
pub const ENERGY_REGEN_RATE: u8 = 5;

// ❤️ Health System
pub const BASE_HEALTH: u8 = 100;
pub const HEALTH_REGEN_RATE: u8 = 2;

// ⚔️ Combat System
pub const SUPER_EFFECTIVE: u8 = 150;
pub const NORMAL_EFFECTIVENESS: u8 = 100;
```

### Example 1: Using Constants in Functions
**Naming Convention:** Use `SCREAMING_SNAKE_CASE` for constants - it's the Cairo standard and makes them easy to spot in your code.

## Putting Constants to Work 👨‍💻

Once you've defined your constants, here's how to use them effectively in your game logic:

### Import Your Constants
```cairo
// Function to calculate attack effectiveness based on type matchups
fn calculate_effectiveness(attacker_type: BeastType, defender_type: BeastType) -> u8 {
// At the top of your Cairo files
use dojo_examples::constants::*;
```

### Example 1: Combat Effectiveness Calculator
```cairo
// Calculate how effective an attack is based on elemental types
fn calculate_damage_multiplier(attacker_type: ElementType, defender_type: ElementType) -> u8 {
match (attacker_type, defender_type) {
(BeastType::Light, BeastType::Shadow) |
(BeastType::Magic, BeastType::Light) |
(BeastType::Shadow, BeastType::Magic) => SUPER_EFFECTIVE,
(BeastType::Light, BeastType::Magic) |
(BeastType::Magic, BeastType::Shadow) |
(BeastType::Shadow, BeastType::Light) => NOT_VERY_EFFECTIVE,
// Fire beats Ice, Water beats Fire, Ice beats Water
(ElementType::Fire, ElementType::Ice) |
(ElementType::Water, ElementType::Fire) |
(ElementType::Ice, ElementType::Water) => SUPER_EFFECTIVE,

// Reverse matchups are weak
(ElementType::Ice, ElementType::Fire) |
(ElementType::Fire, ElementType::Water) |
(ElementType::Water, ElementType::Ice) => NOT_VERY_EFFECTIVE,

// Same types are neutral
_ => NORMAL_EFFECTIVENESS,
}
}
```

### Example 2: Using Constants for Calculations
### Example 2: Level-Up Rewards
```cairo
// Calculate how much health a player gains when leveling up
fn calculate_level_up_bonus(current_level: u8) -> u8 {
current_level * BASE_LEVEL_BONUS + MAX_HEALTH_BONUS_PER_LEVEL
}
```

### Example 3: Energy Management
```cairo
// Calculate daily bonus based on player level
fn calculate_daily_bonus(level: u8) -> u8 {
level * BASE_LEVEL_BONUS
}
// Check if player has enough energy for an action
fn can_perform_action(current_energy: u8, action_cost: u8) -> bool {
current_energy >= action_cost
}

// Regenerate energy each turn (with cap)
fn regenerate_energy(current_energy: u8) -> u8 {
let new_energy = current_energy + ENERGY_REGEN_RATE;
if new_energy > MAX_ENERGY {
MAX_ENERGY
} else {
new_energy
}
}
```

### Example 3: Using Constants in Conditionals
## Game Designer's Perspective 🎯

Constants make your game **designer-friendly**. Want to make combat faster? Increase `ENERGY_REGEN_RATE`. Want to reduce grinding? Boost `BASE_LEVEL_BONUS`.

**Before Constants:**
"The game feels too slow, but I'd need to find every energy-related calculation in the code..."

**With Constants:**
"Change `ENERGY_REGEN_RATE` from 5 to 8 and test it!"

## Best Practices for Dojo Games ⭐

1. **Document Your Intent**: Add comments explaining why you chose specific values
2. **Group Related Constants**: Keep all combat constants together, all time constants together
3. **Use Meaningful Names**: `CRITICAL_HIT_CHANCE` is better than `CRIT_CHANCE`
4. **Start Conservative**: It's easier to buff than nerf in live games
5. **Version Your Balance**: Keep track of constant changes for rollback purposes

## Testing Your Constants 🧪

```cairo
// Determine if an attack is favored based on beast type
fn is_favored_attack(beast_type: BeastType, skill_type: SkillType) -> bool {
match skill_type {
SkillType::Beam | SkillType::Slash | SkillType::Pierce |
SkillType::Wave => beast_type == BeastType::Light,
SkillType::Blast | SkillType::Freeze | SkillType::Burn |
SkillType::Punch => beast_type == BeastType::Magic,
SkillType::Smash | SkillType::Crush | SkillType::Shock |
SkillType::Kick => beast_type == BeastType::Shadow,
_ => false,
#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_type_effectiveness() {
// Fire should be super effective against Ice
let multiplier = calculate_damage_multiplier(ElementType::Fire, ElementType::Ice);
assert(multiplier == SUPER_EFFECTIVE, 'Fire should beat Ice');
}

#[test]
fn test_energy_cap() {
// Energy should never exceed maximum
let capped_energy = regenerate_energy(MAX_ENERGY);
assert(capped_energy == MAX_ENERGY, 'Energy should be capped');
}
}
```

## Benefits ✨
## Summary 📚

- Centralized configuration for easy tuning
- Avoid hardcoding values throughout the codebase
- Improves readability and maintainability
Constants are the foundation of maintainable game development in Dojo. They transform scattered magic numbers into a centralized, editable configuration system that makes balancing your game a breeze.

## Summary 📚
**Remember:**
- Constants = Game Balance Control Center
- Good naming = Self-documenting code
- Organized constants = Happy team
- Testable constants = Reliable game mechanics

Proper use of constants is essential for maintaining organized, scalable, and balanced code in Dojo Engine. Learn to define, organize, and use constants effectively to enhance your on-chain games.
Start simple, stay organized, and let constants handle the heavy lifting of game balance while you focus on creating amazing gameplay experiences! 🚀
Loading