Spaces:
Sleeping
Sleeping
๐ง Quick Fixes for Critical Gameplay Issues
Fix 1: Attack System Implementation
Backend (app.py)
Add after line 420:
# Add to handle_command method
elif cmd_type == "attack_unit":
attacker_ids = command.get("attacker_ids", [])
target_id = command.get("target_id")
if target_id in self.game_state.units:
target = self.game_state.units[target_id]
for uid in attacker_ids:
if uid in self.game_state.units:
attacker = self.game_state.units[uid]
# Set target for attack
attacker.target = Position(target.position.x, target.position.y)
attacker.target_unit_id = target_id # Add this field to Unit class
# Add combat logic to game_loop (after line 348)
def update_combat(self):
"""Handle unit combat"""
for unit in list(self.game_state.units.values()):
if hasattr(unit, 'target_unit_id') and unit.target_unit_id:
target_id = unit.target_unit_id
if target_id in self.game_state.units:
target = self.game_state.units[target_id]
# Check range
distance = unit.position.distance_to(target.position)
if distance <= unit.range:
# In range - attack!
unit.target = None # Stop moving
# Apply damage (simplified - no cooldown for now)
target.health -= unit.damage / 20 # Damage over time
if target.health <= 0:
# Target destroyed
del self.game_state.units[target_id]
unit.target_unit_id = None
else:
# Move closer
unit.target = Position(target.position.x, target.position.y)
else:
# Target no longer exists
unit.target_unit_id = None
Frontend (static/game.js)
Replace onRightClick method around line 220:
onRightClick(e) {
e.preventDefault();
if (this.selectedUnits.size === 0) return;
const rect = this.canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
// Convert to world coordinates
const worldX = (x / this.camera.zoom) + this.camera.x;
const worldY = (y / this.camera.zoom) + this.camera.y;
// Check if clicking on an enemy unit
const clickedUnit = this.getUnitAtPosition(worldX, worldY);
if (clickedUnit && clickedUnit.player_id !== 0) {
// ATTACK ENEMY UNIT
this.attackUnit(clickedUnit.id);
this.showNotification(`๐ฏ Attacking enemy ${clickedUnit.type}!`, 'warning');
} else {
// MOVE TO POSITION
this.moveSelectedUnits(worldX, worldY);
}
}
// Add new methods
getUnitAtPosition(worldX, worldY) {
if (!this.gameState || !this.gameState.units) return null;
const CLICK_TOLERANCE = CONFIG.TILE_SIZE;
for (const [id, unit] of Object.entries(this.gameState.units)) {
const dx = worldX - (unit.position.x + CONFIG.TILE_SIZE / 2);
const dy = worldY - (unit.position.y + CONFIG.TILE_SIZE / 2);
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < CLICK_TOLERANCE) {
return { id, ...unit };
}
}
return null;
}
attackUnit(targetId) {
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;
this.ws.send(JSON.stringify({
type: 'attack_unit',
attacker_ids: Array.from(this.selectedUnits),
target_id: targetId
}));
}
Fix 2: Production Requirements
Backend (app.py)
Add near top of file after imports:
# Production requirements mapping
PRODUCTION_REQUIREMENTS = {
UnitType.INFANTRY: BuildingType.BARRACKS,
UnitType.TANK: BuildingType.WAR_FACTORY,
UnitType.ARTILLERY: BuildingType.WAR_FACTORY,
UnitType.HELICOPTER: BuildingType.WAR_FACTORY,
UnitType.HARVESTER: BuildingType.HQ # โ CRITICAL: Harvester needs HQ!
}
Replace build_unit handler in handle_command:
elif cmd_type == "build_unit":
unit_type_str = command.get("unit_type")
player_id = command.get("player_id", 0)
if not unit_type_str:
return
try:
unit_type = UnitType(unit_type_str)
except ValueError:
return
# Find required building type
required_building = PRODUCTION_REQUIREMENTS.get(unit_type)
if not required_building:
return
# Find a suitable building owned by player
suitable_building = None
for building in self.game_state.buildings.values():
if (building.player_id == player_id and
building.type == required_building):
suitable_building = building
break
if suitable_building:
# Add to production queue
suitable_building.production_queue.append(unit_type_str)
# Notify client
await self.broadcast({
"type": "notification",
"message": f"Training {unit_type_str} at {required_building.value}",
"level": "success"
})
else:
# Send error
await self.broadcast({
"type": "notification",
"message": f"โ ๏ธ Requires {required_building.value} to train {unit_type_str}!",
"level": "error"
})
Frontend (static/game.js)
Update train unit methods around line 540:
trainUnit(unitType) {
// Check requirements
const requirements = {
'infantry': 'barracks',
'tank': 'war_factory',
'artillery': 'war_factory',
'helicopter': 'war_factory',
'harvester': 'hq' // โ CRITICAL!
};
const requiredBuilding = requirements[unitType];
if (!this.hasBuilding(requiredBuilding)) {
this.showNotification(
`โ ๏ธ Need ${requiredBuilding.replace('_', ' ').toUpperCase()} to train ${unitType}!`,
'error'
);
return;
}
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;
this.ws.send(JSON.stringify({
type: 'build_unit',
unit_type: unitType,
player_id: 0
}));
}
hasBuilding(buildingType) {
if (!this.gameState || !this.gameState.buildings) return false;
for (const building of Object.values(this.gameState.buildings)) {
if (building.player_id === 0 && building.type === buildingType) {
return true;
}
}
return false;
}
// Update setupBuildMenu to show requirements
setupBuildMenu() {
document.getElementById('train-infantry').addEventListener('click', () => {
this.trainUnit('infantry');
});
document.getElementById('train-tank').addEventListener('click', () => {
this.trainUnit('tank');
});
document.getElementById('train-harvester').addEventListener('click', () => {
this.trainUnit('harvester');
});
document.getElementById('train-helicopter').addEventListener('click', () => {
this.trainUnit('helicopter');
});
document.getElementById('train-artillery').addEventListener('click', () => {
this.trainUnit('artillery');
});
// Add tooltips
document.getElementById('train-infantry').title = 'Requires: Barracks';
document.getElementById('train-tank').title = 'Requires: War Factory';
document.getElementById('train-harvester').title = 'Requires: HQ (Command Center)';
document.getElementById('train-helicopter').title = 'Requires: War Factory';
document.getElementById('train-artillery').title = 'Requires: War Factory';
}
Fix 3: Add Unit Range Field
Backend (app.py)
Update Unit dataclass around line 68:
@dataclass
class Unit:
id: str
type: UnitType
player_id: int
position: Position
health: int
max_health: int
speed: float
damage: int
range: float = 100.0 # Add range field
target_unit_id: Optional[str] = None # Add target tracking
# ... rest of methods
Update create_unit method around line 185 to set range:
def create_unit(self, unit_type: UnitType, player_id: int, position: Position) -> Unit:
"""Create a new unit"""
unit_stats = {
UnitType.INFANTRY: {"health": 100, "speed": 2.0, "damage": 10, "range": 80},
UnitType.TANK: {"health": 200, "speed": 1.5, "damage": 30, "range": 120},
UnitType.HARVESTER: {"health": 150, "speed": 1.0, "damage": 0, "range": 0},
UnitType.HELICOPTER: {"health": 120, "speed": 3.0, "damage": 25, "range": 150},
UnitType.ARTILLERY: {"health": 100, "speed": 1.0, "damage": 50, "range": 200},
}
stats = unit_stats[unit_type]
unit_id = str(uuid.uuid4())
unit = Unit(
id=unit_id,
type=unit_type,
player_id=player_id,
position=position,
health=stats["health"],
max_health=stats["health"],
speed=stats["speed"],
damage=stats["damage"],
range=stats["range"], # Add range
target=None
)
self.units[unit_id] = unit
return unit
Testing Checklist
After applying fixes:
Test Attack:
- Select friendly unit
- Right-click on enemy unit
- Unit should move toward enemy and attack when in range
- Enemy health should decrease
- Enemy should be destroyed when health reaches 0
Test Production:
- Try to train Harvester WITHOUT HQ โ Should show error
- Build HQ
- Try to train Harvester WITH HQ โ Should work
- Try to train Infantry without Barracks โ Should show error
- Build Barracks
- Train Infantry โ Should work
Test Requirements:
- Hover over unit buttons โ Should show tooltip with requirements
- Click button without building โ Should show error notification
Quick Apply Commands
# Backup current files
cd /home/luigi/rts/web
cp app.py app.py.backup
cp static/game.js static/game.js.backup
# Apply fixes manually or use sed/patch
# Then rebuild Docker:
docker stop rts-game
docker rm rts-game
docker build -t rts-game-web .
docker run -d --name rts-game -p 7860:7860 rts-game-web
Summary
These fixes add:
- โ Attack System - Right-click enemies to attack
- โ Production Requirements - Harvester needs HQ (not Refinery!)
- โ Error Messages - Clear feedback when requirements not met
- โ Tooltips - Shows what building is required
Impact: Game becomes playable and faithful to original mechanics!