Skip to main content

Getting Started

Elemental Battlecards is an open-source multiplayer card game. We welcome contributions from developers of all skill levels!

Prerequisites

Node.js

Version 16+ required for both backend and frontend

npm

Package manager (comes with Node.js)

Git

Version control for cloning and submitting changes

Code Editor

VS Code recommended with Phaser extensions

Development Setup

Clone the Repository

git clone https://github.com/yourusername/elemental-battlecards.git
cd elemental-battlecards

Backend Setup

1

Navigate to Backend

cd Backend
2

Install Dependencies

npm install
Key dependencies:
  • express - Web server
  • socket.io - Real-time communication
  • sequelize - Database ORM
  • bcryptjs - Password hashing
  • jsonwebtoken - Authentication
3

Configure Environment

Create a .env file:
.env
PORT=3001
DB_ENABLED=true
DB_HOST=localhost
DB_PORT=5432
DB_NAME=battlecards_dev
DB_USER=postgres
DB_PASSWORD=your_password
JWT_SECRET=your_secret_key
4

Run Development Server

npm run dev
The server will start on http://localhost:3001 with auto-reload via nodemon.

Frontend Setup

1

Navigate to Frontend

cd Frontend
2

Install Dependencies

npm install
Key dependencies:
  • phaser - Game framework
  • vite - Build tool
  • socket.io-client - Real-time client
3

Run Development Server

npm run dev
Vite will start the dev server with HMR at http://localhost:5173

Running Both Servers

For full development, run both servers simultaneously:
cd Backend
npm run dev
Open http://localhost:5173 in your browser to play.
The frontend automatically connects to http://localhost:3001 for the backend API and Socket.IO.

Project Structure

Backend Structure

Backend/
├── config/
   ├── config.js          # Database configuration
   └── db.js              # Database connection
├── controllers/
   └── authController.js  # Authentication logic
├── models/
   ├── index.js           # Sequelize models export
   └── user.js            # User model
├── routes/
   └── authRoutes.js      # API route definitions
├── migrations/            # Database migrations
├── server.js              # Entry point
├── socketManager.js       # Real-time game logic
└── package.json

Frontend Structure

Frontend/
├── public/
   └── assets/
       └── images/        # All game assets
├── src/
   ├── game_objects/      # Card, Player classes
   ├── card.js
   ├── card-definitions.js
   └── player.js
   ├── helpers/           # Utility functions
   ├── combat.js      # Battle resolution
   ├── constants.js   # Game constants
   └── zone.js        # Position helpers
   ├── scenes/            # Phaser scenes
   ├── LoginScene.js
   ├── RegisterScene.updated.js
   ├── Preloader.js
   ├── HomeScenes.js
   ├── CreateRoomScene.js
   ├── GameSceneLAN.js
   ├── GameScene.js
   └── uiScene.js
   └── main.js            # Phaser initialization
├── index.html
├── vite.config.js
└── package.json

Code Style Guide

JavaScript Conventions

// Use PascalCase for classes
export default class CardController {
    constructor(type, level) {
        this.type = type;
        this.level = level;
    }
}

File Naming

  • Scenes: PascalCase.js (e.g., GameScene.js)
  • Utilities: camelCase.js (e.g., combat.js)
  • Constants: camelCase.js (e.g., constants.js)
  • Models: camelCase.js (e.g., user.js)

Code Comments

/**
 * Resolves combat between two cards based on type advantages and levels.
 * 
 * @param {Card} attacker - The attacking card
 * @param {Card} defender - The defending card
 * @returns {string} Combat result: 'attacker_wins', 'defender_wins', or 'neutral'
 */
export function resolveCombat(attacker, defender) {
    // Implementation
}

Adding New Features

Adding a New Card Type

1

Update Constants

Frontend/src/helpers/constants.js
export const CARD_TYPES = {
    FUEGO: 'fuego',
    AGUA: 'agua',
    PLANTA: 'planta',
    LUZ: 'luz',
    SOMBRA: 'sombra',
    ESPIRITU: 'espiritu',
    TRUENO: 'trueno'  // New type
};
2

Add Card Definition

Frontend/src/game_objects/card-definitions.js
export const CardDefinitions = {
    // ...
    trueno: {
        1: { texture: 'card-trueno-1' },
        2: { texture: 'card-trueno-2' },
        3: { texture: 'card-trueno-3' }
    }
};
3

Add Combat Logic

Frontend/src/helpers/combat.js
const advantages = {
    // ...
    trueno: ['agua', 'espiritu']
};
4

Add Assets

Place card images in Frontend/public/assets/images/cartas/:
  • carta-trueno-1.png
  • carta-trueno-2.png
  • carta-trueno-3.png
5

Preload Textures

Frontend/src/scenes/GameScene.js
preload() {
    this.load.image('card-trueno-1', '/assets/images/cartas/carta-trueno-1.png');
    this.load.image('card-trueno-2', '/assets/images/cartas/carta-trueno-2.png');
    this.load.image('card-trueno-3', '/assets/images/cartas/carta-trueno-3.png');
}

Adding a New Socket Event

1

Define Event in Backend

Backend/socketManager.js
socket.on('custom_event', (data) => {
    // Validate data
    if (!validateCustomEvent(data)) {
        return;
    }
    
    // Process event
    const result = processCustomEvent(data);
    
    // Broadcast to room
    io.to(socket.roomCode).emit('custom_event_result', result);
});
2

Emit from Frontend

Frontend/src/scenes/GameScene.js
performCustomAction(data) {
    if (this.socket && this.isLAN) {
        this.socket.emit('custom_event', data);
    }
}
3

Handle Response

Frontend/src/scenes/GameScene.js
create() {
    if (this.socket) {
        this.socket.on('custom_event_result', (result) => {
            this.handleCustomEventResult(result);
        });
    }
}
4

Document the Event

Add documentation to the Socket Events page.

Adding a New Scene

1

Create Scene File

Frontend/src/scenes/TutorialScene.js
import Phaser from 'phaser';

export default class TutorialScene extends Phaser.Scene {
    constructor() {
        super('TutorialScene');
    }
    
    init(data) {
        this.playerData = data.playerData;
    }
    
    preload() {
        // Load tutorial assets
    }
    
    create() {
        // Setup tutorial UI
    }
}
2

Register Scene

Frontend/src/main.js
import TutorialScene from './scenes/TutorialScene.js';

const config = {
    scene: [
        LoginScene,
        RegisterScene,
        PreloaderScene,
        HomeScenes,
        TutorialScene,  // Add here
        CreateRoomScene,
        GameSceneLAN,
        GameScene,
        UIScene
    ]
};
3

Add Navigation

Frontend/src/scenes/HomeScenes.js
const tutorialBtn = document.querySelector('#tutorial-btn');
tutorialBtn.addEventListener('click', () => {
    this.scene.start('TutorialScene', { 
        playerData: this.playerData 
    });
});

Testing

Manual Testing

  1. Start backend and frontend servers
  2. Open http://localhost:5173
  3. Click “Jugar con bot”
  4. Test game mechanics against AI

Debugging

// Enable socket debug logs
localStorage.debug = 'socket.io-client:socket';

// View game scene
const gameScene = game.scene.getScene('GameScene');
console.log(gameScene.player);
console.log(gameScene.opponent);

Git Workflow

Branching Strategy

# Create feature branch
git checkout -b feature/add-new-card-type

# Create bugfix branch
git checkout -b fix/combat-calculation

# Create documentation branch
git checkout -b docs/update-api-reference

Commit Messages

Follow conventional commit format:
# Feature
git commit -m "feat: add thunder card type with combat advantages"

# Bug fix
git commit -m "fix: resolve combat calculation for level 3 cards"

# Documentation
git commit -m "docs: add socket events reference"

# Refactor
git commit -m "refactor: extract combat logic to helper function"

# Style
git commit -m "style: format code according to style guide"

Pull Request Process

1

Push Your Branch

git push origin feature/add-new-card-type
2

Create Pull Request

  • Go to GitHub repository
  • Click “New Pull Request”
  • Select your branch
  • Fill out PR template
3

PR Checklist

  • Code follows style guide
  • All tests pass
  • Documentation updated
  • No console errors
  • Tested in multiple browsers
4

Address Review Comments

Make requested changes and push updates:
git add .
git commit -m "fix: address PR review comments"
git push

Common Tasks

Adding a Database Model

Backend/models/gameStats.js
module.exports = (sequelize, DataTypes) => {
    const GameStats = sequelize.define('GameStats', {
        userId: {
            type: DataTypes.INTEGER,
            allowNull: false,
            references: { model: 'users', key: 'id' }
        },
        gamesPlayed: {
            type: DataTypes.INTEGER,
            defaultValue: 0
        },
        gamesWon: {
            type: DataTypes.INTEGER,
            defaultValue: 0
        }
    }, {
        tableName: 'game_stats',
        timestamps: true
    });
    
    return GameStats;
};
Create migration:
npx sequelize-cli migration:generate --name create-game-stats

Updating Card Assets

  1. Place new images in Frontend/public/assets/images/cartas/
  2. Use naming convention: carta-{type}-{level}.png
  3. Recommended size: 200x287px (aspect ratio 0.7)
  4. Format: PNG with transparency

Modifying Combat Logic

Edit Frontend/src/helpers/combat.js:
export function resolveCombat(attacker, defender) {
    // 1. Check level difference
    const levelDiff = attacker.level - defender.level;
    
    if (levelDiff >= 2) return 'attacker_wins';
    if (levelDiff <= -2) return 'defender_wins';
    
    // 2. Check type advantages
    const advantage = getTypeAdvantage(attacker.type, defender.type);
    
    if (advantage === 'strong') return 'attacker_wins';
    if (advantage === 'weak') return 'defender_wins';
    
    // 3. Neutral
    return 'neutral';
}

Troubleshooting

Common Issues

Problem: Frontend cannot connect to backendSolution:
# Verify backend is running
curl http://localhost:3001/ping

# Check firewall settings
# Ensure port 3001 is open
Problem: Images or videos show as brokenSolution:
// Check asset paths in preload
preload() {
    // Paths are relative to public/
    this.load.image('card', '/assets/images/cartas/card.png');
    //                       ^ Note the leading slash
}
Problem: Scene stays blank or doesn’t transitionSolution:
// Check scene key matches
constructor() {
    super('GameScene'); // Must match key used in scene.start()
}

// Ensure scene is registered in main.js
scene: [GameScene, ...]
Problem: Backend fails to connect to databaseSolution:
# Check .env configuration
DB_HOST=localhost
DB_PORT=5432
DB_NAME=battlecards_dev
DB_USER=postgres
DB_PASSWORD=your_password

# Test database connection
psql -h localhost -U postgres -d battlecards_dev

Resources

Phaser 3 Docs

Official Phaser 3 API documentation

Socket.IO Docs

Real-time communication guide

Sequelize Docs

ORM documentation for database

Vite Docs

Build tool and dev server

Getting Help

If you need assistance:
  1. Check existing GitHub Issues
  2. Review this documentation
  3. Ask in project discussions
  4. Create a new issue with:
    • Clear description of the problem
    • Steps to reproduce
    • Expected vs actual behavior
    • Screenshots if applicable

Code of Conduct

We are committed to providing a welcoming and inclusive experience for everyone. Please:
  • Be respectful and constructive
  • Assume good intentions
  • Give and receive feedback gracefully
  • Focus on what is best for the community

Next Steps

Architecture

Understand the system architecture

Game Scenes

Learn about Phaser scene structure

Socket Events

Master real-time communication
Thank you for contributing to Elemental Battlecards!

Build docs developers (and LLMs) love