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); } }

Enhanced Wargame Character Tracker

Manage your characters’ status and weapons

Select a Character Captain Machine Repair ReA-G ReB-H Receiver Communicator Wall 1 Wall 2 Add Character

Tabletop Wargame Character Tracker © 2023

// 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.name}

${characterData.name}

${characterData.health.current} +
`; return card; } const nameClass = characterData.health.current === 0 ? ‘dead’ : ‘alive’; let html = `
${characterData.name}

${characterData.name}

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’); });