Enhanced Wargame Character Tracker
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: ‘Segoe UI’, Tahoma, Geneva, Verdana, sans-serif;
}
body {
background: linear-gradient(135deg, #1a2a3a, #2c3e50);
color: #ecf0f1;
line-height: 1.4;
padding: 15px;
min-height: 100vh;
}
.container {
max-width: 1400px;
margin: 0 auto;
}
header {
text-align: center;
margin-bottom: 20px;
padding: 15px;
background: rgba(0, 0, 0, 0.3);
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
}
h1 {
font-size: 1.8rem;
margin-bottom: 8px;
color: #f1c40f;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
}
.controls {
display: flex;
justify-content: center;
align-items: center;
gap: 12px;
margin-bottom: 15px;
flex-wrap: wrap;
}
select, button {
padding: 8px 12px;
border: none;
border-radius: 4px;
background-color: #ecf0f1;
color: #2c3e50;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
font-size: 0.9rem;
}
button {
background-color: #f1c40f;
color: #2c3e50;
}
button:hover {
background-color: #f39c12;
transform: translateY(-2px);
}
.characters-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 15px;
}
.character-card {
background: rgba(52, 73, 94, 0.8);
border-radius: 8px;
overflow: hidden;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.4);
transition: transform 0.3s ease;
backdrop-filter: blur(5px);
display: flex;
flex-direction: column;
height: 100%;
}
.character-card:hover {
transform: translateY(-3px);
}
.character-header {
display: flex;
padding: 12px;
background-color: rgba(26, 37, 48, 0.7);
border-bottom: 2px solid #f1c40f;
}
.character-image {
width: 70px;
height: 70px;
border-radius: 6px;
object-fit: cover;
border: 2px solid #f1c40f;
margin-right: 12px;
}
.character-name {
font-size: 1.3rem;
margin-bottom: 5px;
}
.alive {
color: #f1c40f;
}
.dead {
color: #e74c3c;
text-decoration: line-through;
}
.core-stats {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 8px;
padding: 10px;
background-color: rgba(0, 0, 0, 0.2);
}
.core-stat {
display: flex;
flex-direction: column;
align-items: center;
padding: 6px;
background-color: rgba(0, 0, 0, 0.3);
border-radius: 4px;
}
.stat-label {
font-size: 0.75rem;
color: #bdc3c7;
margin-bottom: 3px;
}
.stat-value {
font-weight: bold;
color: #f1c40f;
font-size: 1.1rem;
}
.adjuster {
display: flex;
align-items: center;
gap: 6px;
margin-top: 4px;
}
.adjust-btn {
width: 25px;
height: 25px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
cursor: pointer;
transition: all 0.2s ease;
font-size: 0.8rem;
}
.decrement {
background-color: #e74c3c;
color: white;
}
.increment {
background-color: #27ae60;
color: white;
}
.adjust-btn:hover {
transform: scale(1.1);
}
.weapon-section {
padding: 10px;
background-color: rgba(0, 0, 0, 0.2);
margin: 5px 10px;
border-radius: 6px;
}
.weapon-title {
color: #f1c40f;
margin-bottom: 8px;
font-size: 1rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.weapon-select {
width: 100%;
padding: 6px;
margin-bottom: 8px;
background-color: rgba(0, 0, 0, 0.3);
color: white;
border: 1px solid #7f8c8d;
border-radius: 4px;
font-size: 0.85rem;
}
.weapon-stats {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 6px;
}
.weapon-stat {
display: flex;
flex-direction: column;
align-items: center;
padding: 6px;
background-color: rgba(0, 0, 0, 0.3);
border-radius: 4px;
}
.weapon-stat-label {
font-size: 0.7rem;
color: #bdc3c7;
margin-bottom: 2px;
}
.weapon-stat-value {
font-weight: bold;
color: #f1c40f;
font-size: 0.9rem;
}
.ammo-adjuster {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
margin-top: 8px;
}
.health-only {
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
.health-value {
font-size: 2rem;
font-weight: bold;
color: #f1c40f;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
}
footer {
text-align: center;
margin-top: 30px;
padding: 15px;
color: #ecf0f1;
font-size: 0.8rem;
}
@media (max-width: 768px) {
.characters-container {
grid-template-columns: 1fr;
}
.weapon-stats {
grid-template-columns: repeat(2, 1fr);
}
}
@media (min-width: 1200px) {
.characters-container {
grid-template-columns: repeat(4, 1fr);
}
}
Select a Character
Captain
Machine
Repair
ReA-G
ReB-H
Receiver
Communicator
Wall 1
Wall 2
Add Character
// Character data with all specified attributes
const charactersData = {
captain: {
name: “Captain”,
image: “https://drive.google.com/uc?export=view&id=1y6KYbop3DMTQuJgSGzsA6t00wgPmYYvj”,
health: { current: 4, max: 4 },
move: 3,
dex: 1,
cost: 0,
shield: { current: 0, max: 0 },
hasShield: false,
rangedWeapon: {
name: “Hand Gun”,
attack: 1,
hit: 1,
rate: 2,
range: “1-2”,
ammo: { current: 10, max: 10 }
},
meleeWeapon: {
name: “Combat Knife”,
attack: 1,
hit: 4,
rate: 1,
range: 1
},
hasRanged: true,
hasMelee: true
},
machine: {
name: “Machine”,
image: “https://drive.google.com/uc?export=view&id=1kiDbcDchOll7LQcf7So__XAJHn0m2NwG”,
health: { current: 4, max: 4 },
move: 4,
dex: 2,
cost: 0,
shield: { current: 2, max: 2 },
hasShield: true,
rangedWeapon: {
name: “Machine Gun”,
attack: 1,
hit: 3,
rate: 3,
range: “1-3”,
ammo: { current: 8, max: 8 }
},
meleeWeapon: {
name: “Shield Push”,
attack: 2,
hit: 4,
rate: 1,
range: 1
},
hasRanged: true,
hasMelee: true
},
repair: {
name: “Repair”,
image: “https://drive.google.com/uc?export=view&id=1qHZNM5DjzYNS9_IBj5_zszfOPMlLZn5s”,
health: { current: 4, max: 4 },
move: 4,
dex: 1,
cost: 0,
shield: { current: 0, max: 0 },
hasShield: false,
rangedWeapon: {
name: “Electric Shock”,
attack: 0,
hit: 3,
rate: 1,
range: “1-2”,
ammo: null
},
meleeWeapon: null,
hasRanged: true,
hasMelee: false,
hasAmmo: false
},
“rea-g”: {
name: “ReA-G”,
image: “https://drive.google.com/uc?export=view&id=1t4x6-lRPM9Ll5F5ulYBB5jGTHXmJ_RJy”,
health: { current: 4, max: 4 },
move: 4,
dex: 1,
cost: 0,
shield: { current: 0, max: 0 },
hasShield: false,
rangedWeapon: {
name: “Twin Blaster”,
attack: 1,
hit: 1,
rate: 4,
range: “1-3”,
ammo: null
},
meleeWeapon: {
name: “Kick”,
attack: 1,
hit: 4,
rate: 1,
range: 1
},
hasRanged: true,
hasMelee: true,
hasAmmo: false
},
“reb-h”: {
name: “ReB-H”,
image: “https://drive.google.com/uc?export=view&id=1WDnrkAbVnCpLezu1VDeQapihOIliWroB”,
health: { current: 8, max: 8 },
move: 3,
dex: 1,
cost: 0,
shield: { current: 0, max: 0 },
hasShield: false,
rangedWeapon: null,
meleeWeapons: [
{
name: “Hammer Crush”,
attack: 6,
hit: 5,
rate: 1,
range: 1
},
{
name: “Kick”,
attack: 2,
hit: 6,
rate: 1,
range: 1
}
],
hasRanged: false,
hasMelee: true,
multipleMelee: true
},
receiver: {
name: “Receiver”,
image: “https://drive.google.com/uc?export=view&id=1vjLzu-obBhlLVt7Kvvw4SqFx2wyjXqz5”,
health: { current: 8, max: 8 },
move: 0,
dex: 0,
cost: 0,
shield: { current: 0, max: 0 },
hasShield: false,
rangedWeapon: null,
meleeWeapon: null,
hasRanged: false,
hasMelee: false,
healthOnly: true
},
communicator: {
name: “Communicator”,
image: “https://drive.google.com/uc?export=view&id=1F8JPnlypAuMw6-bJZW7YIvYVAoDN4pri”,
health: { current: 8, max: 8 },
move: 0,
dex: 0,
cost: 0,
shield: { current: 0, max: 0 },
hasShield: false,
rangedWeapon: null,
meleeWeapon: null,
hasRanged: false,
hasMelee: false,
healthOnly: true
},
wall1: {
name: “Wall 1”,
image: “https://drive.google.com/uc?export=view&id=1ID_3KztcUCNe-Zr9b0HHwnrgshFdCZMr”,
health: { current: 8, max: 8 },
move: 0,
dex: 0,
cost: 0,
shield: { current: 0, max: 0 },
hasShield: false,
rangedWeapon: null,
meleeWeapon: null,
hasRanged: false,
hasMelee: false,
healthOnly: true
},
wall2: {
name: “Wall 2”,
image: “https://drive.google.com/uc?export=view&id=1GAuQ3JgndqHb06r29YGStknh5vamTEDC”,
health: { current: 8, max: 8 },
move: 0,
dex: 0,
cost: 0,
shield: { current: 0, max: 0 },
hasShield: false,
rangedWeapon: null,
meleeWeapon: null,
hasRanged: false,
hasMelee: false,
healthOnly: true
}
};
// Function to create a character card
function createCharacterCard(characterData) {
const card = document.createElement(‘div’);
card.className = ‘character-card’;
card.dataset.character = characterData.name.toLowerCase().replace(/\s/g, ‘-‘);
// Health only characters (Receiver, Communicator, Wall 1, Wall 2)
if (characterData.healthOnly) {
const nameClass = characterData.health.current === 0 ? ‘dead’ : ‘alive’;
card.innerHTML = `
–
${characterData.health.current}
+
`;
return card;
}
const nameClass = characterData.health.current === 0 ? ‘dead’ : ‘alive’;
let html = `
Health
${characterData.health.current}
–
+
Move
${characterData.move}
Dex
${characterData.dex}
`;
// Shield point if available
if (characterData.hasShield) {
html += `
Shield
${characterData.shield.current}
–
+
`;
}
// Ranged weapon if available
if (characterData.hasRanged) {
html += `
Ranged: ${characterData.rangedWeapon.name}
Attack
${characterData.rangedWeapon.attack}
Hit
${characterData.rangedWeapon.hit}
Rate
${characterData.rangedWeapon.rate}
Range
${characterData.rangedWeapon.range}
`;
// Ammo clip if available
if (characterData.rangedWeapon.ammo) {
html += `
Ammo:
–
${characterData.rangedWeapon.ammo.current}
+
`;
}
html += `
`;
}
// Melee weapon if available
if (characterData.hasMelee) {
if (characterData.multipleMelee) {
// Handle multiple melee weapons (ReB-H)
html += `
Melee: ${characterData.meleeWeapons[0].name}
Attack
${characterData.meleeWeapons[0].attack}
Hit
${characterData.meleeWeapons[0].hit}
Rate
${characterData.meleeWeapons[0].rate}
Range
${characterData.meleeWeapons[0].range}
Melee: ${characterData.meleeWeapons[1].name}
Attack
${characterData.meleeWeapons[1].attack}
Hit
${characterData.meleeWeapons[1].hit}
Rate
${characterData.meleeWeapons[1].rate}
Range
${characterData.meleeWeapons[1].range}
`;
} else {
// Single melee weapon
html += `
Melee: ${characterData.meleeWeapon.name}
Attack
${characterData.meleeWeapon.attack}
Hit
${characterData.meleeWeapon.hit}
Rate
${characterData.meleeWeapon.rate}
Range
${characterData.meleeWeapon.range}
`;
}
}
card.innerHTML = html;
return card;
}
// Function to add a new character
function addCharacter(characterType) {
if (!characterType) return;
const characterData = charactersData[characterType];
if (!characterData) return;
const card = createCharacterCard(characterData);
document.getElementById(‘characters-container’).appendChild(card);
// Add event listeners for the new card
addCardEventListeners(card, characterData);
}
// Function to add event listeners to a card
function addCardEventListeners(card, characterData) {
// Adjust buttons
const adjustButtons = card.querySelectorAll(‘.adjust-btn’);
adjustButtons.forEach(button => {
button.addEventListener(‘click’, function() {
const stat = this.dataset.stat;
const valueElement = card.querySelector(`#${stat}-value`);
let value = parseInt(valueElement.textContent);
let maxValue = 0;
// Determine max value based on stat and character
if (stat === ‘health’) maxValue = characterData.health.max;
if (stat === ‘shield’ && characterData.hasShield) maxValue = characterData.shield.max;
if (stat === ‘ammo’ && characterData.hasRanged && characterData.rangedWeapon.ammo) {
maxValue = characterData.rangedWeapon.ammo.max;
}
if (this.classList.contains(‘decrement’)) {
value = Math.max(0, value – 1);
} else {
value = Math.min(maxValue, value + 1);
}
valueElement.textContent = value;
// Update name color if health changed
if (stat === ‘health’) {
const nameElement = card.querySelector(‘.character-name’);
if (value === 0) {
nameElement.classList.remove(‘alive’);
nameElement.classList.add(‘dead’);
} else {
nameElement.classList.remove(‘dead’);
nameElement.classList.add(‘alive’);
}
}
});
});
}
// Initialize the page
document.addEventListener(‘DOMContentLoaded’, function() {
const characterSelect = document.getElementById(‘character-select’);
const addCharacterButton = document.getElementById(‘add-character’);
// Add character button event listener
addCharacterButton.addEventListener(‘click’, function() {
const selectedCharacter = characterSelect.value;
addCharacter(selectedCharacter);
});
// Add the first four characters by default
addCharacter(‘captain’);
addCharacter(‘machine’);
addCharacter(‘repair’);
addCharacter(‘rea-g’);
});