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
Install Dependencies
Key dependencies:
express - Web server
socket.io - Real-time communication
sequelize - Database ORM
bcryptjs - Password hashing
jsonwebtoken - Authentication
Configure Environment
Create a .env file: 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
Run Development Server
The server will start on http://localhost:3001 with auto-reload via nodemon.
Frontend Setup
Install Dependencies
Key dependencies:
phaser - Game framework
vite - Build tool
socket.io-client - Real-time client
Run Development Server
Vite will start the dev server with HMR at http://localhost:5173
Running Both Servers
For full development, run both servers simultaneously:
Terminal 1 (Backend)
Terminal 2 (Frontend)
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
Classes
Functions
Constants
Phaser Scenes
// 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)
/**
* 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
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
};
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' }
}
};
Add Combat Logic
Frontend/src/helpers/combat.js
const advantages = {
// ...
trueno: [ 'agua' , 'espiritu' ]
};
Add Assets
Place card images in Frontend/public/assets/images/cartas/:
carta-trueno-1.png
carta-trueno-2.png
carta-trueno-3.png
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
Define Event in Backend
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 );
});
Emit from Frontend
Frontend/src/scenes/GameScene.js
performCustomAction ( data ) {
if ( this . socket && this . isLAN ) {
this . socket . emit ( 'custom_event' , data );
}
}
Handle Response
Frontend/src/scenes/GameScene.js
create () {
if ( this . socket ) {
this . socket . on ( 'custom_event_result' , ( result ) => {
this . handleCustomEventResult ( result );
});
}
}
Adding a New Scene
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
}
}
Register Scene
import TutorialScene from './scenes/TutorialScene.js' ;
const config = {
scene: [
LoginScene ,
RegisterScene ,
PreloaderScene ,
HomeScenes ,
TutorialScene , // Add here
CreateRoomScene ,
GameSceneLAN ,
GameScene ,
UIScene
]
};
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
Single Player
Multiplayer (Local)
Multiplayer (LAN)
Start backend and frontend servers
Open http://localhost:5173
Click “Jugar con bot”
Test game mechanics against AI
Start backend and frontend servers
Open two browser windows
Window 1: Create room
Window 2: Join with room code
Play game between windows
Start backend with npm run dev
Note the network IP (e.g., 192.168.1.100:3001)
On another device, navigate to http://<IP>:5173?backend=http://<IP>:3001
Create/join room and play
Debugging
Browser Console
Phaser Debugging
Server Logs
// 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
Push Your Branch
git push origin feature/add-new-card-type
Create Pull Request
Go to GitHub repository
Click “New Pull Request”
Select your branch
Fill out PR template
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
Place new images in Frontend/public/assets/images/cartas/
Use naming convention: carta-{type}-{level}.png
Recommended size: 200x287px (aspect ratio 0.7)
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
Socket connection refused
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
}
Phaser scene not starting
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 , ... ]
Database connection error
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:
Check existing GitHub Issues
Review this documentation
Ask in project discussions
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!