Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>DIGIMON: WORLDS BEYOND THE CODE</title>
- <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700&family=Orbitron:wght@500;700&display=swap">
- <style>
- :root {
- --primary-color: #f088ff; /* Light purple */
- --primary-dark: #5a4aad;
- --secondary-color: #4169e1; /* Royal blue */
- --accent-color: #87cefa; /* Light sky blue */
- --dark-blue: #191970; /* Midnight blue */
- --text-color: #333333;
- --bg-color: #f0f8ff; /* Alice blue */
- --card-bg: #ffffff;
- --border-color: #d0d0ff;
- --success-color: #2ecc71;
- --danger-color: #e74c3c;
- --warning-color: #f39c12;
- }
- * {
- box-sizing: border-box;
- font-family: 'Montserrat', sans-serif;
- }
- body {
- color: var(--text-color);
- margin: 0;
- padding: 0;
- line-height: 1.6;
- background-color: #f5f5f5;
- background-image:
- linear-gradient(45deg, #f0f0ff 25%, transparent 25%),
- linear-gradient(-45deg, #f0f0ff 25%, transparent 25%),
- linear-gradient(45deg, transparent 75%, #f0f0ff 75%),
- linear-gradient(-45deg, transparent 75%, #f0f0ff 75%);
- background-size: 20px 20px;
- background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
- min-height: 100vh;
- }
- .main-container {
- max-width: 1200px;
- margin: 0 auto;
- padding: 30px 20px;
- }
- .game-header {
- text-align: center;
- padding: 25px;
- margin-bottom: 30px;
- background: linear-gradient(135deg, var(--dark-blue), var(--secondary-color));
- border-radius: 15px;
- box-shadow: 0 10px 30px rgba(0,0,0,0.15);
- position: relative;
- overflow: hidden;
- }
- .game-header::before {
- content: '';
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MCIgaGVpZ2h0PSI2MCIgdmlld0JveD0iMCAwIDYwIDYwIj48cGF0aCBkPSJNMzAgMzBtLTI1IDAgYTI1LDI1IDAgMSwwIDUwLDAgYTI1LDI1IDAgMSwwIC01MCwwIiBmaWxsPSJub25lIiBzdHJva2U9InJnYmEoMjU1LDI1NSwyNTUsMC4xKSIgc3Ryb2tlLXdpZHRoPSIyIj48L3BhdGg+PC9zdmc+');
- background-size: 100px 100px;
- opacity: 0.1;
- }
- .game-title {
- color: white;
- margin: 0;
- font-family: 'Orbitron', sans-serif;
- font-size: 2.8rem;
- text-shadow: 0 2px 10px rgba(0,0,0,0.3);
- letter-spacing: 1px;
- }
- .game-subtitle {
- display: block;
- color: var(--primary-color);
- font-weight: 700;
- font-size: 2.2rem;
- text-shadow: 0 0 10px rgba(255,255,255,0.7);
- margin-top: 5px;
- }
- .tab-container {
- width: 100%;
- margin-bottom: 30px;
- box-shadow: 0 5px 20px rgba(0,0,0,0.1);
- border-radius: 15px;
- overflow: hidden;
- }
- .tabs {
- display: flex;
- flex-wrap: wrap;
- background-color: var(--dark-blue);
- padding: 5px 5px 0;
- position: relative;
- }
- .tab {
- padding: 12px 20px;
- cursor: pointer;
- transition: all 0.3s;
- background-color: rgba(135, 206, 250, 0.2);
- color: white;
- border-radius: 10px 10px 0 0;
- margin-right: 5px;
- margin-bottom: 0;
- font-weight: 500;
- position: relative;
- overflow: hidden;
- border-bottom: 3px solid transparent;
- }
- .tab:hover {
- background-color: rgba(135, 206, 250, 0.3);
- }
- .tab.active {
- background-color: var(--card-bg);
- color: var(--primary-color);
- font-weight: 600;
- border-bottom: 3px solid var(--primary-color);
- }
- .tab::after {
- content: '';
- position: absolute;
- bottom: 0;
- left: 0;
- width: 100%;
- height: 3px;
- background-color: var(--accent-color);
- transform: scaleX(0);
- transition: transform 0.3s;
- }
- .tab:hover::after {
- transform: scaleX(1);
- }
- .tab-content {
- display: none;
- padding: 25px;
- background-color: var(--card-bg);
- border-radius: 0 0 15px 15px;
- }
- .tab-content.active {
- display: block;
- animation: fadeIn 0.5s;
- }
- @keyframes fadeIn {
- from { opacity: 0; transform: translateY(10px); }
- to { opacity: 1; transform: translateY(0); }
- }
- h1, h2, h3, h4 {
- color: var(--secondary-color);
- font-family: 'Orbitron', sans-serif;
- margin-top: 0;
- }
- .card {
- background-color: var(--card-bg);
- border-radius: 12px;
- box-shadow: 0 5px 15px rgba(0,0,0,0.07);
- padding: 25px;
- margin-bottom: 25px;
- border-left: 5px solid var(--primary-color);
- position: relative;
- overflow: hidden;
- transition: all 0.3s;
- }
- .card:hover {
- box-shadow: 0 8px 25px rgba(0,0,0,0.1);
- transform: translateY(-2px);
- }
- .card::after {
- content: '';
- position: absolute;
- top: 0;
- right: 0;
- width: 150px;
- height: 150px;
- background-image: linear-gradient(135deg, rgba(106, 90, 205, 0.1) 0%, rgba(255, 255, 255, 0) 60%);
- border-radius: 0 0 0 100%;
- }
- .card-title {
- position: relative;
- border-bottom: 2px solid var(--secondary-color);
- padding-bottom: 12px;
- margin-bottom: 20px;
- font-weight: 700;
- color: var(--dark-blue);
- display: inline-block;
- }
- .card-title::after {
- content: '';
- position: absolute;
- bottom: -2px;
- left: 0;
- width: 100%;
- height: 2px;
- background: linear-gradient(90deg, var(--primary-color) 0%, var(--accent-color) 100%);
- }
- .flex-container {
- display: flex;
- flex-wrap: wrap;
- gap: 20px;
- }
- .grid-container {
- display: grid;
- grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
- gap: 25px;
- }
- .form-group {
- margin-bottom: 20px;
- }
- .form-row {
- display: flex;
- flex-wrap: wrap;
- gap: 15px;
- margin-bottom: 20px;
- align-items: flex-start;
- }
- .form-row .form-group {
- flex: 1 1 200px;
- margin-bottom: 0;
- }
- label {
- display: block;
- margin-bottom: 8px;
- font-weight: 600;
- color: var(--dark-blue);
- font-size: 0.9rem;
- text-transform: uppercase;
- letter-spacing: 0.5px;
- }
- input[type="text"],
- input[type="number"],
- select,
- textarea {
- width: 100%;
- padding: 12px 15px;
- border: 1px solid var(--border-color);
- border-radius: 8px;
- font-size: 1rem;
- transition: all 0.3s;
- background-color: #f9f9ff;
- }
- input[type="text"]:focus,
- input[type="number"]:focus,
- select:focus,
- textarea:focus {
- outline: none;
- border-color: var(--primary-color);
- box-shadow: 0 0 0 3px rgba(106, 90, 205, 0.1);
- }
- input[type="radio"],
- input[type="checkbox"] {
- margin-right: 8px;
- transform: scale(1.2);
- accent-color: var(--primary-color);
- }
- .radio-container,
- .checkbox-container {
- display: flex;
- gap: 20px;
- flex-wrap: wrap;
- }
- .radio-container label,
- .checkbox-container label {
- display: flex;
- align-items: center;
- text-transform: none;
- font-weight: normal;
- margin-bottom: 0;
- cursor: pointer;
- padding: 8px;
- border-radius: 8px;
- transition: all 0.2s;
- }
- .radio-container label:hover,
- .checkbox-container label:hover {
- background-color: rgba(106, 90, 205, 0.05);
- }
- .stat-container {
- display: flex;
- flex-wrap: wrap;
- gap: 15px;
- margin-bottom: 25px;
- }
- .stat-box {
- flex: 1 1 120px;
- border: 2px solid var(--primary-color);
- border-radius: 10px;
- text-align: center;
- padding: 15px 10px;
- background-color: rgba(135, 206, 250, 0.1);
- box-shadow: 0 3px 10px rgba(0,0,0,0.05);
- transition: all 0.3s;
- position: relative;
- overflow: hidden;
- }
- .stat-box:hover {
- transform: translateY(-3px);
- box-shadow: 0 5px 15px rgba(0,0,0,0.1);
- }
- .stat-box::after {
- content: '';
- position: absolute;
- top: -20px;
- right: -20px;
- width: 60px;
- height: 60px;
- background-color: rgba(106, 90, 205, 0.1);
- border-radius: 50%;
- }
- .stat-name {
- font-weight: 700;
- margin-bottom: 8px;
- color: var(--dark-blue);
- text-transform: uppercase;
- font-size: 0.85rem;
- letter-spacing: 1px;
- }
- .stat-value {
- font-size: 1.8rem;
- font-weight: 700;
- color: var(--primary-color);
- font-family: 'Orbitron', sans-serif;
- }
- input.stat-value {
- background-color: transparent;
- border: none;
- text-align: center;
- font-size: 1.8rem;
- padding: 0;
- width: 100%;
- color: var(--primary-color);
- font-family: 'Orbitron', sans-serif;
- }
- input.stat-value:focus {
- outline: none;
- box-shadow: none;
- }
- .skill-container {
- display: grid;
- grid-template-columns: 1fr auto auto;
- gap: 15px;
- align-items: center;
- padding: 12px;
- border-bottom: 1px solid rgba(208, 208, 255, 0.5);
- transition: all 0.3s;
- border-radius: 8px;
- }
- .skill-container:hover {
- background-color: rgba(106, 90, 205, 0.03);
- }
- .skill-name {
- font-weight: 600;
- color: var(--dark-blue);
- display: flex;
- flex-direction: column;
- }
- .skill-stat {
- color: var(--secondary-color);
- font-style: italic;
- font-size: 0.85rem;
- font-weight: normal;
- margin-top: 3px;
- }
- .dice-selector {
- display: flex;
- flex-wrap: wrap;
- gap: 10px;
- }
- .dice-option {
- display: inline-flex;
- align-items: center;
- margin-right: 10px;
- cursor: pointer;
- }
- button {
- background: linear-gradient(135deg, var(--secondary-color), var(--primary-color));
- color: white;
- border: none;
- padding: 10px 20px;
- border-radius: 8px;
- cursor: pointer;
- font-size: 0.9rem;
- font-weight: 600;
- transition: all 0.3s;
- text-transform: uppercase;
- letter-spacing: 0.5px;
- box-shadow: 0 3px 10px rgba(0,0,0,0.1);
- position: relative;
- overflow: hidden;
- }
- button::after {
- content: '';
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background-image: linear-gradient(rgba(255,255,255,0.2), rgba(255,255,255,0));
- opacity: 0;
- transition: opacity 0.3s;
- }
- button:hover {
- transform: translateY(-2px);
- box-shadow: 0 5px 15px rgba(0,0,0,0.15);
- }
- button:hover::after {
- opacity: 1;
- }
- button:active {
- transform: translateY(0);
- box-shadow: 0 2px 5px rgba(0,0,0,0.1);
- }
- button.secondary {
- background: linear-gradient(135deg, var(--primary-color), var(--primary-dark));
- }
- button.accent {
- background: linear-gradient(135deg, var(--accent-color), var(--secondary-color));
- }
- button.success {
- background: linear-gradient(135deg, var(--success-color), #27ae60);
- }
- button.danger {
- background: linear-gradient(135deg, var(--danger-color), #c0392b);
- }
- button.sm {
- padding: 6px 12px;
- font-size: 0.8rem;
- }
- .add-button {
- background: linear-gradient(135deg, #4caf50, #2e7d32);
- margin-top: 10px;
- }
- .remove-button {
- background: linear-gradient(135deg, #f44336, #c62828);
- margin-bottom: 10px;
- }
- .notes-container {
- display: flex;
- flex-direction: column;
- height: 300px;
- background-color: #f9f9ff;
- border-radius: 8px;
- overflow: hidden;
- box-shadow: 0 3px 10px rgba(0,0,0,0.05);
- }
- .notes-heading {
- background-color: var(--primary-color);
- color: white;
- padding: 10px 15px;
- font-weight: 600;
- font-size: 0.9rem;
- }
- .notes-content {
- flex-grow: 1;
- resize: vertical;
- border: none;
- padding: 15px;
- font-size: 0.95rem;
- line-height: 1.6;
- }
- .notes-content:focus {
- outline: none;
- }
- .image-upload {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- width: 200px;
- height: 200px;
- border: 2px dashed var(--border-color);
- margin: 0 auto 25px;
- position: relative;
- overflow: hidden;
- background-color: rgba(106, 90, 205, 0.05);
- border-radius: 10px;
- transition: all 0.3s;
- }
- .image-upload:hover {
- border-color: var(--primary-color);
- background-color: rgba(106, 90, 205, 0.1);
- }
- .image-upload img {
- max-width: 100%;
- max-height: 100%;
- object-fit: contain;
- }
- .image-upload-btn {
- position: absolute;
- bottom: 10px;
- opacity: 0.8;
- background: rgba(0,0,0,0.6);
- padding: 8px 12px;
- font-size: 0.8rem;
- border-radius: 20px;
- }
- .hide {
- display: none;
- }
- .background-info {
- margin-top: 15px;
- padding: 15px;
- background-color: rgba(106, 90, 205, 0.05);
- border-radius: 8px;
- border-left: 4px solid var(--primary-color);
- box-shadow: 0 3px 10px rgba(0,0,0,0.05);
- }
- .background-info h3 {
- margin-top: 0;
- color: var(--primary-color);
- font-size: 1.2rem;
- }
- .background-info ul {
- margin-bottom: 0;
- }
- .dice-roller {
- padding: 20px;
- background: linear-gradient(135deg, rgba(106, 90, 205, 0.1), rgba(135, 206, 250, 0.1));
- border-radius: 10px;
- margin-top: 25px;
- text-align: center;
- position: relative;
- overflow: hidden;
- box-shadow: 0 5px 15px rgba(0,0,0,0.05);
- border: 1px solid rgba(208, 208, 255, 0.5);
- }
- .dice-roller::before {
- content: '';
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MCIgaGVpZ2h0PSI2MCIgdmlld0JveD0iMCAwIDYwIDYwIj48cGF0aCBkPSJNMzAgMzBtLTI1IDAgYTI1LDI1IDAgMSwwIDUwLDAgYTI1LDI1IDAgMSwwIC01MCwwIiBmaWxsPSJub25lIiBzdHJva2U9InJnYmEoMTA2LCA5MCwgMjA1LCAwLjEpIiBzdHJva2Utd2lkdGg9IjIiPjwvcGF0aD48L3N2Zz4=');
- background-size: 100px 100px;
- opacity: 0.5;
- }
- .dice-roller h3 {
- margin-top: 0;
- color: var(--dark-blue);
- position: relative;
- }
- #rollResult {
- font-size: 1.8rem;
- font-weight: 700;
- margin: 15px 0;
- min-height: 45px;
- color: var(--primary-color);
- font-family: 'Orbitron', sans-serif;
- position: relative;
- text-shadow: 0 2px 5px rgba(0,0,0,0.1);
- }
- .evo-line {
- border: 1px solid var(--border-color);
- padding: 20px;
- margin-bottom: 20px;
- border-radius: 10px;
- background-color: rgba(106, 90, 205, 0.02);
- position: relative;
- overflow: hidden;
- box-shadow: 0 3px 10px rgba(0,0,0,0.05);
- }
- .evo-line::after {
- content: '';
- position: absolute;
- top: 0;
- right: 0;
- width: 150px;
- height: 150px;
- background-image: linear-gradient(135deg, rgba(106, 90, 205, 0.05) 0%, rgba(255, 255, 255, 0) 60%);
- border-radius: 0 0 0 100%;
- }
- .evo-stage {
- margin-bottom: 15px;
- padding: 15px;
- border-radius: 8px;
- background-color: white;
- border-left: 3px solid var(--secondary-color);
- box-shadow: 0 3px 10px rgba(0,0,0,0.05);
- transition: all 0.3s;
- }
- .evo-stage:hover {
- box-shadow: 0 5px 15px rgba(0,0,0,0.1);
- }
- .evo-stage-title {
- color: var(--dark-blue);
- font-weight: 700;
- margin-bottom: 10px;
- font-size: 1.1rem;
- display: flex;
- align-items: center;
- }
- .evo-stage-title::before {
- content: '';
- display: inline-block;
- width: 10px;
- height: 10px;
- background-color: var(--secondary-color);
- border-radius: 50%;
- margin-right: 8px;
- }
- .mech-part {
- border: 1px solid var(--border-color);
- padding: 15px;
- margin-bottom: 15px;
- border-radius: 8px;
- background-color: white;
- box-shadow: 0 3px 10px rgba(0,0,0,0.05);
- transition: all 0.3s;
- position: relative;
- overflow: hidden;
- }
- .mech-part:hover {
- box-shadow: 0 5px 15px rgba(0,0,0,0.1);
- }
- .mech-part::after {
- content: '';
- position: absolute;
- bottom: 0;
- right: 0;
- width: 80px;
- height: 80px;
- background-image: linear-gradient(135deg, rgba(65, 105, 225, 0.05) 0%, rgba(255, 255, 255, 0) 60%);
- border-radius: 100% 0 0 0;
- }
- .mech-part h4 {
- margin-top: 0;
- color: var(--secondary-color);
- font-size: 1.1rem;
- display: flex;
- align-items: center;
- }
- .mech-part h4::before {
- content: '⚙';
- margin-right: 8px;
- font-size: 1rem;
- }
- .item-entry {
- border: 1px solid var(--border-color);
- padding: 15px;
- margin-bottom: 15px;
- border-radius: 8px;
- background-color: white;
- box-shadow: 0 3px 10px rgba(0,0,0,0.05);
- transition: all 0.3s;
- position: relative;
- }
- .item-entry:hover {
- box-shadow: 0 5px 15px rgba(0,0,0,0.1);
- }
- .item-entry::before {
- content: '🎒';
- position: absolute;
- top: 15px;
- right: 15px;
- font-size: 1.5rem;
- opacity: 0.2;
- }
- .import-export {
- margin-top: 30px;
- padding: 20px;
- border: 1px solid var(--border-color);
- border-radius: 10px;
- background: linear-gradient(135deg, rgba(106, 90, 205, 0.05), rgba(135, 206, 250, 0.05));
- box-shadow: 0 5px 15px rgba(0,0,0,0.07);
- }
- .import-export h3 {
- margin-top: 0;
- color: var(--dark-blue);
- }
- /* Custom notification */
- .notification {
- position: fixed;
- top: 20px;
- right: 20px;
- padding: 15px 20px;
- background-color: var(--success-color);
- color: white;
- border-radius: 8px;
- box-shadow: 0 5px 15px rgba(0,0,0,0.2);
- transform: translateY(-100px);
- opacity: 0;
- transition: all 0.5s;
- z-index: 1000;
- font-weight: 600;
- }
- .notification.show {
- transform: translateY(0);
- opacity: 1;
- }
- .notification.error {
- background-color: var(--danger-color);
- }
- /* Toggle switch */
- .switch {
- position: relative;
- display: inline-block;
- width: 54px;
- height: 28px;
- }
- .switch input {
- opacity: 0;
- width: 0;
- height: 0;
- }
- .slider {
- position: absolute;
- cursor: pointer;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background-color: #ccc;
- transition: .4s;
- border-radius: 34px;
- }
- .slider:before {
- position: absolute;
- content: "";
- height: 20px;
- width: 20px;
- left: 4px;
- bottom: 4px;
- background-color: white;
- transition: .4s;
- border-radius: 50%;
- }
- input:checked + .slider {
- background-color: var(--success-color);
- }
- input:focus + .slider {
- box-shadow: 0 0 1px var(--success-color);
- }
- input:checked + .slider:before {
- transform: translateX(26px);
- }
- /* Responsive */
- @media (max-width: 768px) {
- .grid-container {
- grid-template-columns: 1fr;
- }
- .form-row {
- flex-direction: column;
- gap: 10px;
- }
- .form-row .form-group {
- flex: 1 1 100%;
- }
- .stat-container {
- justify-content: center;
- }
- .image-upload {
- width: 150px;
- height: 150px;
- }
- .tab {
- padding: 10px 15px;
- font-size: 0.9rem;
- }
- .skill-container {
- grid-template-columns: 1fr;
- gap: 10px;
- }
- .dice-selector {
- justify-content: center;
- }
- .game-title {
- font-size: 2.2rem;
- }
- .game-subtitle {
- font-size: 1.8rem;
- }
- }
- /* Add at the end of your existing CSS */
- /* Moves Tab Styling */
- #moves-tab .controls {
- display: flex;
- justify-content: space-between;
- margin-bottom: 20px;
- align-items: center;
- }
- .move-entry {
- background: rgba(255, 255, 255, 0.08);
- border-radius: 10px;
- padding: 15px;
- margin-bottom: 15px;
- position: relative;
- border: 1px solid var(--border-color);
- transition: all 0.3s ease;
- }
- .move-entry:hover {
- box-shadow: 0 0 10px rgba(186, 104, 200, 0.3);
- }
- .move-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 10px;
- }
- .move-title {
- font-size: 1.2em;
- font-weight: bold;
- color: var(--primary-color);
- display: flex;
- align-items: center;
- }
- .move-type {
- background-color: rgba(186, 104, 200, 0.2);
- padding: 4px 8px;
- border-radius: 12px;
- font-size: 0.8em;
- margin-left: 10px;
- }
- .move-sp-cost {
- background-color: rgba(86, 144, 255, 0.2);
- padding: 4px 8px;
- border-radius: 12px;
- font-size: 0.8em;
- margin-left: 10px;
- }
- .move-actions {
- display: flex;
- gap: 10px;
- }
- .move-details {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
- gap: 15px;
- margin-bottom: 15px;
- }
- .move-description {
- grid-column: 1 / -1;
- }
- .move-calculator {
- background: rgba(86, 144, 255, 0.1);
- border-radius: 10px;
- padding: 15px;
- margin-top: 30px;
- }
- .calc-result {
- background: rgba(0, 0, 0, 0.2);
- padding: 10px;
- border-radius: 5px;
- margin-top: 15px;
- min-height: 50px;
- display: flex;
- align-items: center;
- font-family: 'Courier New', monospace;
- }
- /* Move type colors */
- .move-type.attack {
- background-color: rgba(255, 86, 86, 0.2);
- }
- .move-type.healing {
- background-color: rgba(86, 255, 86, 0.2);
- }
- .move-type.buff {
- background-color: rgba(255, 217, 86, 0.2);
- }
- .move-type.utility {
- background-color: rgba(86, 172, 255, 0.2);
- }
- /* Move animation */
- @keyframes moveGlow {
- 0% { box-shadow: 0 0 5px rgba(186, 104, 200, 0.3); }
- 50% { box-shadow: 0 0 15px rgba(186, 104, 200, 0.5); }
- 100% { box-shadow: 0 0 5px rgba(186, 104, 200, 0.3); }
- }
- .move-entry.performing {
- animation: moveGlow 1s infinite;
- }
- </style>
- </head>
- <body>
- <div class="main-container">
- <div class="game-header">
- <h1 class="game-title">DIGIMON: <span class="game-subtitle">WORLDS BEYOND THE CODE</span></h1>
- </div>
- <div class="tab-container">
- <div class="tabs">
- <div class="tab active" data-tab="character-tab">Character</div>
- <div class="tab" data-tab="partner-tab">Partner</div>
- <div class="tab" data-tab="skills-tab">Skills</div>
- <div class="tab" data-tab="notes-tab">Notes</div>
- <div class="tab" data-tab="class-skills-tab">Class Skills</div>
- <div class="tab" data-tab="items-tab">Items</div>
- <div class="tab" data-tab="mechs-tab">Mechs</div>
- <div class="tab" data-tab="digimon-lines-tab">Digimon Lines</div>
- <div class="tab" data-tab="rules-tab">Rules</div>
- <div class="tab" data-tab="moves-tab">Moves</div>
- <div class="tab" data-tab="partner-moves-tab">Partner Moves</div>
- </div>
- <div id="character-tab" class="tab-content active">
- <div class="card">
- <h2 class="card-title">Character Type</h2>
- <div class="radio-container">
- <label>
- <input type="radio" name="characterType" value="tamer" checked> Tamer (Human)
- </label>
- <label>
- <input type="radio" name="characterType" value="digimon"> Digimon
- </label>
- </div>
- <div class="form-row">
- <div class="form-group">
- <label for="characterName">Name</label>
- <input type="text" id="characterName" placeholder="Enter name">
- </div>
- <div class="form-group">
- <label for="characterLevel">Level (1-99)</label>
- <input type="number" id="characterLevel" min="1" max="99" value="1">
- </div>
- <div class="form-group">
- <label for="characterExp">Experience Points</label>
- <input type="number" id="characterExp" min="0" value="0">
- </div>
- <div class="form-group">
- <label for="characterNextLevel">Next Level At</label>
- <input type="number" id="characterNextLevel" value="10" disabled>
- </div>
- <div class="form-group tamer-only">
- <label for="characterBackground">Background</label>
- <select id="characterBackground">
- <option value="">-- Select Background --</option>
- <option value="courtesan">Courtesan</option>
- <option value="adventurer">Adventurer</option>
- <option value="diplomat">Diplomat</option>
- <option value="farmer">Farmer</option>
- <option value="janitor">Janitor</option>
- <option value="sleuth">Sleuth</option>
- <option value="slave">Slave</option>
- <option value="wronged_hero">Wronged Hero</option>
- <option value="doctor">Doctor</option>
- <option value="carpenter">Carpenter</option>
- <option value="child">Child</option>
- <option value="explorer">Explorer</option>
- <option value="fallen_noble">Fallen Noble</option>
- <option value="student_of_magic">Student of Magic</option>
- <option value="survivor">Survivor</option>
- <option value="messenger">Messenger</option>
- <option value="from_the_past">From The Past</option>
- <option value="miner">Miner</option>
- <option value="royalty">Royalty</option>
- <option value="mute">Mute</option>
- <option value="artist">Artist</option>
- <option value="negotiator">Negotiator</option>
- <option value="innkeeper">Innkeeper</option>
- <option value="barmaid">Barmaid</option>
- <option value="bartender">Bartender</option>
- <option value="sex_slave">Sex Slave</option>
- <option value="inventor">Inventor</option>
- <option value="beggar">Beggar</option>
- <option value="shepherd">Shepherd</option>
- <option value="pilot">Pilot</option>
- <option value="bodyguard">Bodyguard</option>
- <option value="librarian">Librarian</option>
- <option value="isekai">Isekai'd</option>
- </select>
- </div>
- <div class="form-group digimon-only" style="display: none;">
- <label for="digimonBackground">Background</label>
- <select id="digimonBackground">
- <option value="">-- Select Background --</option>
- <option value="isekai">Isekai'd</option>
- <option value="partnered">Partnered</option>
- </select>
- </div>
- <div class="form-group">
- <label for="characterRank">Rank</label>
- <select id="characterRank">
- <option value="in-training">None / In-Training (Levels 1-5)</option>
- <option value="rookie">Copper / Rookie (Levels 6-20)</option>
- <option value="champion">Silver / Champion (Levels 21-64)</option>
- <option value="ultimate">Gold / Ultimate (Levels 65-99)</option>
- <option value="mega">Platinum / Mega (Narrative)</option>
- </select>
- </div>
- </div>
- <div class="background-info" id="backgroundInfo">
- <p>Select a background to see details</p>
- </div>
- <div class="form-group isekai-only" style="display: none;">
- <h3>Isekai'd Stat Bonuses</h3>
- <p>Select two stats to gain +2 bonus:</p>
- <div class="checkbox-container">
- <label><input type="checkbox" class="isekai-stat-bonus" value="baseHP"> Base HP</label>
- <label><input type="checkbox" class="isekai-stat-bonus" value="baseSP"> Base SP</label>
- <label><input type="checkbox" class="isekai-stat-bonus" value="str"> STR</label>
- <label><input type="checkbox" class="isekai-stat-bonus" value="def"> DEF</label>
- <label><input type="checkbox" class="isekai-stat-bonus" value="int"> INT</label>
- <label><input type="checkbox" class="isekai-stat-bonus" value="agi"> AGI</label>
- <label><input type="checkbox" class="isekai-stat-bonus" value="wil"> WIL</label>
- </div>
- </div>
- <div class="image-upload">
- <img id="characterImage" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzZhNWFjZCIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxyZWN0IHg9IjMiIHk9IjMiIHdpZHRoPSIxOCIgaGVpZ2h0PSIxOCIgcng9IjIiIHJ5PSIyIj48L3JlY3Q+PGNpcmNsZSBjeD0iOC41IiBjeT0iOC41IiByPSIxLjUiPjwvY2lyY2xlPjxwb2x5bGluZSBwb2ludHM9IjIxIDE1IDEwIDIxIDMgMTUiPjwvcG9seWxpbmU+PC9zdmc+" alt="Character Image">
- <button type="button" class="image-upload-btn" onclick="document.getElementById('characterImageUpload').click()">Add Image</button>
- <input type="file" id="characterImageUpload" accept="image/*" style="display: none;" onchange="previewImage(this, 'characterImage')">
- </div>
- </div>
- <div class="card">
- <h2 class="card-title">Stats</h2>
- <p>Available Points: <span id="statPointsRemaining">7</span></p>
- <div class="form-row">
- <div class="form-group">
- <label for="baseHP">Base HP</label>
- <input type="number" id="baseHP" min="1" max="20" value="1" class="stat-input">
- </div>
- <div class="form-group">
- <label for="baseSP">Base SP</label>
- <input type="number" id="baseSP" min="1" max="20" value="1" class="stat-input">
- </div>
- <div class="form-group">
- <label for="str">STR</label>
- <input type="number" id="str" min="1" max="20" value="1" class="stat-input">
- </div>
- <div class="form-group">
- <label for="def">DEF</label>
- <input type="number" id="def" min="1" max="20" value="1" class="stat-input">
- </div>
- <div class="form-group">
- <label for="int">INT</label>
- <input type="number" id="int" min="1" max="20" value="1" class="stat-input">
- </div>
- <div class="form-group">
- <label for="agi">AGI</label>
- <input type="number" id="agi" min="1" max="20" value="1" class="stat-input">
- </div>
- <div class="form-group">
- <label for="wil">WIL</label>
- <input type="number" id="wil" min="1" max="20" value="1" class="stat-input">
- </div>
- </div>
- <div class="stat-container">
- <div class="stat-box">
- <div class="stat-name">Current HP</div>
- <input type="number" id="currentHP" class="stat-value">
- </div>
- <div class="stat-box">
- <div class="stat-name">Max HP</div>
- <div class="stat-value" id="hp-value">-</div>
- </div>
- <div class="stat-box">
- <div class="stat-name">Current SP</div>
- <input type="number" id="currentSP" class="stat-value">
- </div>
- <div class="stat-box">
- <div class="stat-name">Max SP</div>
- <div class="stat-value" id="sp-value">-</div>
- </div>
- </div>
- </div>
- <div class="dice-roller">
- <h3>Dice Roller</h3>
- <div class="form-row">
- <div class="form-group">
- <select id="diceType">
- <option value="d4">d4 (In-Training)</option>
- <option value="d6">d6 (Rookie)</option>
- <option value="d8">d8 (Champion)</option>
- <option value="d12">d12 (Ultimate)</option>
- <option value="d20">d20 (Mega)</option>
- </select>
- </div>
- <button type="button" onclick="rollDice()">Roll</button>
- </div>
- <div id="rollResult"></div>
- </div>
- </div>
- <div id="partner-tab" class="tab-content">
- <div class="card">
- <h2 class="card-title">Partner</h2>
- <div class="form-row">
- <div class="form-group">
- <label for="partnerName">Name</label>
- <input type="text" id="partnerName" placeholder="Enter partner name">
- </div>
- <div class="form-group">
- <label for="partnerLevel">Level (1-99)</label>
- <input type="number" id="partnerLevel" min="1" max="99" value="1">
- </div>
- <div class="form-group">
- <label for="partnerExp">Experience Points</label>
- <input type="number" id="partnerExp" min="0" value="0">
- </div>
- <div class="form-group">
- <label for="partnerNextLevel">Next Level At</label>
- <input type="number" id="partnerNextLevel" value="10" disabled>
- </div>
- <div class="form-group">
- <label for="partnerType">Type</label>
- <select id="partnerType">
- <option value="digimon">Digimon</option>
- <option value="tamer">Human</option>
- </select>
- </div>
- <div class="form-group partner-tamer-only" style="display: none;">
- <label for="partnerHumanBackground">Background</label>
- <select id="partnerHumanBackground">
- <option value="">-- Select Background --</option>
- <option value="courtesan">Courtesan</option>
- <option value="adventurer">Adventurer</option>
- <option value="diplomat">Diplomat</option>
- <option value="farmer">Farmer</option>
- <option value="janitor">Janitor</option>
- <option value="sleuth">Sleuth</option>
- <option value="slave">Slave</option>
- <option value="wronged_hero">Wronged Hero</option>
- <option value="doctor">Doctor</option>
- <option value="carpenter">Carpenter</option>
- <option value="child">Child</option>
- <option value="explorer">Explorer</option>
- <option value="fallen_noble">Fallen Noble</option>
- <option value="student_of_magic">Student of Magic</option>
- <option value="survivor">Survivor</option>
- <option value="messenger">Messenger</option>
- <option value="from_the_past">From The Past</option>
- <option value="miner">Miner</option>
- <option value="royalty">Royalty</option>
- <option value="mute">Mute</option>
- <option value="artist">Artist</option>
- <option value="negotiator">Negotiator</option>
- <option value="innkeeper">Innkeeper</option>
- <option value="barmaid">Barmaid</option>
- <option value="bartender">Bartender</option>
- <option value="sex_slave">Sex Slave</option>
- <option value="inventor">Inventor</option>
- <option value="beggar">Beggar</option>
- <option value="shepherd">Shepherd</option>
- <option value="pilot">Pilot</option>
- <option value="bodyguard">Bodyguard</option>
- <option value="librarian">Librarian</option>
- <option value="isekai">Isekai'd</option>
- </select>
- </div>
- <div class="form-group partner-digimon-only">
- <label for="partnerDigimonBackground">Background</label>
- <select id="partnerDigimonBackground">
- <option value="">-- Select Background --</option>
- <option value="isekai">Isekai'd</option>
- <option value="partnered">Partnered</option>
- </select>
- </div>
- <div class="form-group">
- <label for="partnerRank">Rank</label>
- <select id="partnerRank">
- <option value="in-training">None / In-Training (Levels 1-5)</option>
- <option value="rookie">Copper / Rookie (Levels 6-20)</option>
- <option value="champion">Silver / Champion (Levels 21-64)</option>
- <option value="ultimate">Gold / Ultimate (Levels 65-99)</option>
- <option value="mega">Platinum / Mega (Narrative)</option>
- </select>
- </div>
- </div>
- <div class="background-info" id="partnerBackgroundInfo">
- <p>Select a background to see details</p>
- </div>
- <div class="form-group partner-isekai-only" style="display: none;">
- <h3>Partner Isekai'd Stat Bonuses</h3>
- <p>Select two stats to gain +2 bonus:</p>
- <div class="checkbox-container">
- <label><input type="checkbox" class="partner-isekai-stat-bonus" value="partnerBaseHP"> Base HP</label>
- <label><input type="checkbox" class="partner-isekai-stat-bonus" value="partnerBaseSP"> Base SP</label>
- <label><input type="checkbox" class="partner-isekai-stat-bonus" value="partner-str"> STR</label>
- <label><input type="checkbox" class="partner-isekai-stat-bonus" value="partner-def"> DEF</label>
- <label><input type="checkbox" class="partner-isekai-stat-bonus" value="partner-int"> INT</label>
- <label><input type="checkbox" class="partner-isekai-stat-bonus" value="partner-agi"> AGI</label>
- <label><input type="checkbox" class="partner-isekai-stat-bonus" value="partner-wil"> WIL</label>
- </div>
- </div>
- <div class="image-upload">
- <img id="partnerImage" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzZhNWFjZCIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxyZWN0IHg9IjMiIHk9IjMiIHdpZHRoPSIxOCIgaGVpZ2h0PSIxOCIgcng9IjIiIHJ5PSIyIj48L3JlY3Q+PGNpcmNsZSBjeD0iOC41IiBjeT0iOC41IiByPSIxLjUiPjwvY2lyY2xlPjxwb2x5bGluZSBwb2ludHM9IjIxIDE1IDEwIDIxIDMgMTUiPjwvcG9seWxpbmU+PC9zdmc+" alt="Partner Image">
- <button type="button" class="image-upload-btn" onclick="document.getElementById('partnerImageUpload').click()">Add Image</button>
- <input type="file" id="partnerImageUpload" accept="image/*" style="display: none;" onchange="previewImage(this, 'partnerImage')">
- </div>
- </div>
- <div class="card">
- <h2 class="card-title">Partner Stats</h2>
- <p>Available Points: <span id="partnerStatPointsRemaining">7</span></p>
- <div class="form-row">
- <div class="form-group">
- <label for="partnerBaseHP">Base HP</label>
- <input type="number" id="partnerBaseHP" min="1" max="20" value="1" class="partner-stat-input">
- </div>
- <div class="form-group">
- <label for="partnerBaseSP">Base SP</label>
- <input type="number" id="partnerBaseSP" min="1" max="20" value="1" class="partner-stat-input">
- </div>
- <div class="form-group">
- <label for="partner-str">STR</label>
- <input type="number" id="partner-str" min="1" max="20" value="1" class="partner-stat-input">
- </div>
- <div class="form-group">
- <label for="partner-def">DEF</label>
- <input type="number" id="partner-def" min="1" max="20" value="1" class="partner-stat-input">
- </div>
- <div class="form-group">
- <label for="partner-int">INT</label>
- <input type="number" id="partner-int" min="1" max="20" value="1" class="partner-stat-input">
- </div>
- <div class="form-group">
- <label for="partner-agi">AGI</label>
- <input type="number" id="partner-agi" min="1" max="20" value="1" class="partner-stat-input">
- </div>
- <div class="form-group">
- <label for="partner-wil">WIL</label>
- <input type="number" id="partner-wil" min="1" max="20" value="1" class="partner-stat-input">
- </div>
- </div>
- <div class="stat-container">
- <div class="stat-box">
- <div class="stat-name">Current HP</div>
- <input type="number" id="partnerCurrentHP" class="stat-value">
- </div>
- <div class="stat-box">
- <div class="stat-name">Max HP</div>
- <div class="stat-value" id="partner-hp-value">-</div>
- </div>
- <div class="stat-box">
- <div class="stat-name">Current SP</div>
- <input type="number" id="partnerCurrentSP" class="stat-value">
- </div>
- <div class="stat-box">
- <div class="stat-name">Max SP</div>
- <div class="stat-value" id="partner-sp-value">-</div>
- </div>
- </div>
- </div>
- </div>
- <div id="skills-tab" class="tab-content">
- <div class="card">
- <h2 class="card-title">Skills</h2>
- <p>Click on the appropriate dice to set your proficiency level.</p>
- <h3>Character Skills</h3>
- <div class="skill-container">
- <div class="skill-name">
- Dodge <span class="skill-stat">(AGI)</span>
- </div>
- <div class="dice-selector" data-skill="dodge">
- <label class="dice-option">
- <input type="radio" name="dodge" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="dodge" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="dodge" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="dodge" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="dodge" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollSkill('dodge')">Roll</button>
- </div>
- <div class="skill-container">
- <div class="skill-name">
- Fight <span class="skill-stat">(STR)</span>
- </div>
- <div class="dice-selector" data-skill="fight">
- <label class="dice-option">
- <input type="radio" name="fight" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="fight" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="fight" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="fight" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="fight" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollSkill('fight')">Roll</button>
- </div>
- <div class="skill-container">
- <div class="skill-name">
- Stealth <span class="skill-stat">(AGI)</span>
- </div>
- <div class="dice-selector" data-skill="stealth">
- <label class="dice-option">
- <input type="radio" name="stealth" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="stealth" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="stealth" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="stealth" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="stealth" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollSkill('stealth')">Roll</button>
- </div>
- <div class="skill-container">
- <div class="skill-name">
- Manipulate <span class="skill-stat">(WIL)</span>
- </div>
- <div class="dice-selector" data-skill="manipulate">
- <label class="dice-option">
- <input type="radio" name="manipulate" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="manipulate" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="manipulate" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="manipulate" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="manipulate" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollSkill('manipulate')">Roll</button>
- </div>
- <div class="skill-container">
- <div class="skill-name">
- Perform <span class="skill-stat">(WIL)</span>
- </div>
- <div class="dice-selector" data-skill="perform">
- <label class="dice-option">
- <input type="radio" name="perform" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="perform" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="perform" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="perform" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="perform" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollSkill('perform')">Roll</button>
- </div>
- <div class="skill-container">
- <div class="skill-name">
- Persuade <span class="skill-stat">(WIL)</span>
- </div>
- <div class="dice-selector" data-skill="persuade">
- <label class="dice-option">
- <input type="radio" name="persuade" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="persuade" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="persuade" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="persuade" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="persuade" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollSkill('persuade')">Roll</button>
- </div>
- <div class="skill-container">
- <div class="skill-name">
- Survival <span class="skill-stat">(AGI)</span>
- </div>
- <div class="dice-selector" data-skill="survival">
- <label class="dice-option">
- <input type="radio" name="survival" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="survival" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="survival" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="survival" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="survival" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollSkill('survival')">Roll</button>
- </div>
- <div class="skill-container">
- <div class="skill-name">
- Perception <span class="skill-stat">(AGI)</span>
- </div>
- <div class="dice-selector" data-skill="perception">
- <label class="dice-option">
- <input type="radio" name="perception" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="perception" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="perception" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="perception" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="perception" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollSkill('perception')">Roll</button>
- </div>
- <h3>Knowledge Skills</h3>
- <div class="skill-container">
- <div class="skill-name">
- Knowledge: Digimon <span class="skill-stat">(INT)</span>
- </div>
- <div class="dice-selector" data-skill="knowledge_digimon">
- <label class="dice-option">
- <input type="radio" name="knowledge_digimon" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_digimon" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_digimon" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_digimon" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_digimon" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollSkill('knowledge_digimon')">Roll</button>
- </div>
- <div class="skill-container">
- <div class="skill-name">
- Knowledge: Human <span class="skill-stat">(WIL)</span>
- </div>
- <div class="dice-selector" data-skill="knowledge_human">
- <label class="dice-option">
- <input type="radio" name="knowledge_human" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_human" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_human" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_human" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_human" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollSkill('knowledge_human')">Roll</button>
- </div>
- <div class="skill-container">
- <div class="skill-name">
- Knowledge: Local <span class="skill-stat">(INT)</span>
- </div>
- <div class="dice-selector" data-skill="knowledge_local">
- <label class="dice-option">
- <input type="radio" name="knowledge_local" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_local" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_local" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_local" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_local" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollSkill('knowledge_local')">Roll</button>
- </div>
- <div class="skill-container">
- <div class="skill-name">
- Knowledge: Region <span class="skill-stat">(INT)</span>
- </div>
- <div class="dice-selector" data-skill="knowledge_region">
- <label class="dice-option">
- <input type="radio" name="knowledge_region" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_region" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_region" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_region" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_region" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollSkill('knowledge_region')">Roll</button>
- </div>
- <div class="skill-container">
- <div class="skill-name">
- Knowledge: Trapper <span class="skill-stat">(AGI)</span>
- </div>
- <div class="dice-selector" data-skill="knowledge_trapper">
- <label class="dice-option">
- <input type="radio" name="knowledge_trapper" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_trapper" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_trapper" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_trapper" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_trapper" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollSkill('knowledge_trapper')">Roll</button>
- </div>
- <div class="skill-container">
- <div class="skill-name">
- Knowledge: Smith <span class="skill-stat">(WIL)</span>
- </div>
- <div class="dice-selector" data-skill="knowledge_smith">
- <label class="dice-option">
- <input type="radio" name="knowledge_smith" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_smith" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_smith" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_smith" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_smith" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollSkill('knowledge_smith')">Roll</button>
- </div>
- <div class="skill-container">
- <div class="skill-name">
- Knowledge: Carpenter <span class="skill-stat">(WIL)</span>
- </div>
- <div class="dice-selector" data-skill="knowledge_carpenter">
- <label class="dice-option">
- <input type="radio" name="knowledge_carpenter" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_carpenter" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_carpenter" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_carpenter" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_carpenter" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollSkill('knowledge_carpenter')">Roll</button>
- </div>
- <div class="skill-container">
- <div class="skill-name">
- Knowledge: Handyman <span class="skill-stat">(INT)</span>
- </div>
- <div class="dice-selector" data-skill="knowledge_handyman">
- <label class="dice-option">
- <input type="radio" name="knowledge_handyman" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_handyman" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_handyman" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_handyman" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_handyman" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollSkill('knowledge_handyman')">Roll</button>
- </div>
- <div class="skill-container">
- <div class="skill-name">
- Knowledge: Cook <span class="skill-stat">(WIL)</span>
- </div>
- <div class="dice-selector" data-skill="knowledge_cook">
- <label class="dice-option">
- <input type="radio" name="knowledge_cook" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_cook" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_cook" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_cook" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_cook" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollSkill('knowledge_cook')">Roll</button>
- </div>
- <div class="skill-container">
- <div class="skill-name">
- Knowledge: Hacking <span class="skill-stat">(INT)</span>
- </div>
- <div class="dice-selector" data-skill="knowledge_hacking">
- <label class="dice-option">
- <input type="radio" name="knowledge_hacking" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_hacking" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_hacking" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_hacking" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_hacking" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollSkill('knowledge_hacking')">Roll</button>
- </div>
- <div class="skill-container">
- <div class="skill-name">
- Knowledge: Engineering <span class="skill-stat">(INT)</span>
- </div>
- <div class="dice-selector" data-skill="knowledge_engineering">
- <label class="dice-option">
- <input type="radio" name="knowledge_engineering" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_engineering" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_engineering" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_engineering" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="knowledge_engineering" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollSkill('knowledge_engineering')">Roll</button>
- </div>
- </div>
- <div class="card">
- <h2 class="card-title">Partner Skills</h2>
- <div id="partnerSkillsContainer">
- <!-- Partner skills set up -->
- <div class="skill-container">
- <div class="skill-name">
- Partner Dodge <span class="skill-stat">(AGI)</span>
- </div>
- <div class="dice-selector" data-skill="partner_dodge">
- <label class="dice-option">
- <input type="radio" name="partner_dodge" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_dodge" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_dodge" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_dodge" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_dodge" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollPartnerSkill('partner_dodge', 'agi')">Roll</button>
- </div>
- <div class="skill-container">
- <div class="skill-name">
- Partner Fight <span class="skill-stat">(STR)</span>
- </div>
- <div class="dice-selector" data-skill="partner_fight">
- <label class="dice-option">
- <input type="radio" name="partner_fight" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_fight" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_fight" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_fight" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_fight" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollPartnerSkill('partner_fight', 'str')">Roll</button>
- </div>
- <div class="skill-container">
- <div class="skill-name">
- Partner Stealth <span class="skill-stat">(AGI)</span>
- </div>
- <div class="dice-selector" data-skill="partner_stealth">
- <label class="dice-option">
- <input type="radio" name="partner_stealth" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_stealth" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_stealth" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_stealth" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_stealth" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollPartnerSkill('partner_stealth', 'agi')">Roll</button>
- </div>
- <div class="skill-container">
- <div class="skill-name">
- Partner Manipulate <span class="skill-stat">(WIL)</span>
- </div>
- <div class="dice-selector" data-skill="partner_manipulate">
- <label class="dice-option">
- <input type="radio" name="partner_manipulate" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_manipulate" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_manipulate" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_manipulate" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_manipulate" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollPartnerSkill('partner_manipulate', 'wil')">Roll</button>
- </div>
- <div class="skill-container">
- <div class="skill-name">
- Partner Perform <span class="skill-stat">(WIL)</span>
- </div>
- <div class="dice-selector" data-skill="partner_perform">
- <label class="dice-option">
- <input type="radio" name="partner_perform" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_perform" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_perform" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_perform" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_perform" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollPartnerSkill('partner_perform', 'wil')">Roll</button>
- </div>
- <div class="skill-container">
- <div class="skill-name">
- Partner Persuade <span class="skill-stat">(WIL)</span>
- </div>
- <div class="dice-selector" data-skill="partner_persuade">
- <label class="dice-option">
- <input type="radio" name="partner_persuade" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_persuade" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_persuade" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_persuade" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_persuade" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollPartnerSkill('partner_persuade', 'wil')">Roll</button>
- </div>
- <div class="skill-container">
- <div class="skill-name">
- Partner Survival <span class="skill-stat">(AGI)</span>
- </div>
- <div class="dice-selector" data-skill="partner_survival">
- <label class="dice-option">
- <input type="radio" name="partner_survival" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_survival" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_survival" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_survival" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_survival" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollPartnerSkill('partner_survival', 'agi')">Roll</button>
- </div>
- <div class="skill-container">
- <div class="skill-name">
- Partner Perception <span class="skill-stat">(AGI)</span>
- </div>
- <div class="dice-selector" data-skill="partner_perception">
- <label class="dice-option">
- <input type="radio" name="partner_perception" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_perception" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_perception" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_perception" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_perception" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollPartnerSkill('partner_perception', 'agi')">Roll</button>
- </div>
- <h3>Partner Knowledge Skills</h3>
- <div class="skill-container">
- <div class="skill-name">
- Partner Knowledge: Digimon <span class="skill-stat">(INT)</span>
- </div>
- <div class="dice-selector" data-skill="partner_knowledge_digimon">
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_digimon" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_digimon" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_digimon" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_digimon" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_digimon" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollPartnerSkill('partner_knowledge_digimon', 'int')">Roll</button>
- </div>
- <div class="skill-container">
- <div class="skill-name">
- Partner Knowledge: Human <span class="skill-stat">(WIL)</span>
- </div>
- <div class="dice-selector" data-skill="partner_knowledge_human">
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_human" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_human" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_human" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_human" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_human" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollPartnerSkill('partner_knowledge_human', 'wil')">Roll</button>
- </div>
- <div class="skill-container">
- <div class="skill-name">
- Partner Knowledge: Local <span class="skill-stat">(INT)</span>
- </div>
- <div class="dice-selector" data-skill="partner_knowledge_local">
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_local" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_local" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_local" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_local" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_local" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollPartnerSkill('partner_knowledge_local', 'int')">Roll</button>
- </div>
- <div class="skill-container">
- <div class="skill-name">
- Partner Knowledge: Region <span class="skill-stat">(INT)</span>
- </div>
- <div class="dice-selector" data-skill="partner_knowledge_region">
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_region" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_region" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_region" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_region" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_region" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollPartnerSkill('partner_knowledge_region', 'int')">Roll</button>
- </div>
- <div class="skill-container">
- <div class="skill-name">
- Partner Knowledge: Trapper <span class="skill-stat">(AGI)</span>
- </div>
- <div class="dice-selector" data-skill="partner_knowledge_trapper">
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_trapper" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_trapper" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_trapper" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_trapper" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_trapper" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollPartnerSkill('partner_knowledge_trapper', 'agi')">Roll</button>
- </div>
- <div class="skill-container">
- <div class="skill-name">
- Partner Knowledge: Smith <span class="skill-stat">(WIL)</span>
- </div>
- <div class="dice-selector" data-skill="partner_knowledge_smith">
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_smith" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_smith" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_smith" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_smith" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_smith" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollPartnerSkill('partner_knowledge_smith', 'wil')">Roll</button>
- </div>
- <div class="skill-container">
- <div class="skill-name">
- Partner Knowledge: Carpenter <span class="skill-stat">(WIL)</span>
- </div>
- <div class="dice-selector" data-skill="partner_knowledge_carpenter">
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_carpenter" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_carpenter" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_carpenter" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_carpenter" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_carpenter" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollPartnerSkill('partner_knowledge_carpenter', 'wil')">Roll</button>
- </div>
- <div class="skill-container">
- <div class="skill-name">
- Partner Knowledge: Handyman <span class="skill-stat">(INT)</span>
- </div>
- <div class="dice-selector" data-skill="partner_knowledge_handyman">
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_handyman" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_handyman" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_handyman" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_handyman" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_handyman" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollPartnerSkill('partner_knowledge_handyman', 'int')">Roll</button>
- </div>
- <div class="skill-container">
- <div class="skill-name">
- Partner Knowledge: Cook <span class="skill-stat">(WIL)</span>
- </div>
- <div class="dice-selector" data-skill="partner_knowledge_cook">
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_cook" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_cook" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_cook" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_cook" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_cook" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollPartnerSkill('partner_knowledge_cook', 'wil')">Roll</button>
- </div>
- <div class="skill-container">
- <div class="skill-name">
- Partner Knowledge: Hacking <span class="skill-stat">(INT)</span>
- </div>
- <div class="dice-selector" data-skill="partner_knowledge_hacking">
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_hacking" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_hacking" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_hacking" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_hacking" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_hacking" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollPartnerSkill('partner_knowledge_hacking', 'int')">Roll</button>
- </div>
- <div class="skill-container">
- <div class="skill-name">
- Partner Knowledge: Engineering <span class="skill-stat">(INT)</span>
- </div>
- <div class="dice-selector" data-skill="partner_knowledge_engineering">
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_engineering" value="none" checked> None
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_engineering" value="d6"> d6
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_engineering" value="d8"> d8
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_engineering" value="d12"> d12
- </label>
- <label class="dice-option">
- <input type="radio" name="partner_knowledge_engineering" value="d20"> d20
- </label>
- </div>
- <button type="button" onclick="rollPartnerSkill('partner_knowledge_engineering', 'int')">Roll</button>
- </div>
- </div>
- </div>
- </div>
- </div>
- <!-- Add more partner skills as needed -->
- </div>
- </div>
- </div>
- <div id="notes-tab" class="tab-content">
- <div class="grid-container">
- <div class="card">
- <h2 class="card-title">Items</h2>
- <div class="notes-container">
- <div class="notes-heading">Items Inventory</div>
- <textarea id="itemsNotes" class="notes-content" placeholder="Record your items here..."></textarea>
- </div>
- </div>
- <div class="card">
- <h2 class="card-title">Character Aspects</h2>
- <div class="notes-container">
- <div class="notes-heading">Major & Minor Aspects</div>
- <textarea id="aspectsNotes" class="notes-content" placeholder="Record your major and minor aspects here..."></textarea>
- </div>
- </div>
- <div class="card">
- <h2 class="card-title">Campaign Notes</h2>
- <div class="notes-container">
- <div class="notes-heading">Campaign Journal</div>
- <textarea id="campaignNotes" class="notes-content" placeholder="Record your campaign notes here..."></textarea>
- </div>
- </div>
- </div>
- </div>
- <div id="class-skills-tab" class="tab-content">
- <div class="card">
- <h2 class="card-title">Class Skills</h2>
- <div class="notes-container">
- <div class="notes-heading">Special Abilities</div>
- <textarea id="classSkillsNotes" class="notes-content" placeholder="Record your class skills here..."></textarea>
- </div>
- </div>
- </div>
- <div id="items-tab" class="tab-content">
- <div class="card">
- <h2 class="card-title">Items</h2>
- <div id="itemsContainer">
- <!-- Item entries will go here -->
- </div>
- <button type="button" onclick="addItem()" class="add-button">Add New Item</button>
- </div>
- </div>
- <div id="mechs-tab" class="tab-content">
- <div class="card">
- <h2 class="card-title">Mech Parts Library</h2>
- <p>Add mech parts to your library that can be used to build mechs.</p>
- <div class="form-row">
- <div class="form-group">
- <label for="mechPartType">Part Type</label>
- <select id="mechPartType">
- <option value="core">Core</option>
- <option value="frame">Frame</option>
- <option value="weapon">Weapon</option>
- <option value="arm">Arm</option>
- <option value="leg">Leg</option>
- <option value="head">Head</option>
- </select>
- </div>
- <button type="button" onclick="addMechPart()" class="add-button">Add New Part</button>
- </div>
- <h3>Cores</h3>
- <div id="mechCoresContainer" class="mech-parts-container">
- <!-- Core parts will go here -->
- </div>
- <h3>Frames</h3>
- <div id="mechFramesContainer" class="mech-parts-container">
- <!-- Frame parts will go here -->
- </div>
- <h3>Weapons</h3>
- <div id="mechWeaponsContainer" class="mech-parts-container">
- <!-- Weapon parts will go here -->
- </div>
- <h3>Arms</h3>
- <div id="mechArmsContainer" class="mech-parts-container">
- <!-- Arm parts will go here -->
- </div>
- <h3>Legs</h3>
- <div id="mechLegsContainer" class="mech-parts-container">
- <!-- Leg parts will go here -->
- </div>
- <h3>Heads</h3>
- <div id="mechHeadsContainer" class="mech-parts-container">
- <!-- Head parts will go here -->
- </div>
- </div>
- <div class="card">
- <h2 class="card-title">Character Mech Configuration</h2>
- <div id="characterMechContainer">
- <!-- Character mech configuration will go here -->
- <div class="form-group">
- <label for="mechName">Mech Name</label>
- <input type="text" id="mechName">
- </div>
- <div class="form-group">
- <label for="mechDescription">Description</label>
- <textarea id="mechDescription"></textarea>
- </div>
- <div class="form-group">
- <label for="selectedCore">Core</label>
- <select id="selectedCore">
- <option value="">-- Select Core --</option>
- </select>
- </div>
- <div class="form-group">
- <label for="selectedFrame">Frame</label>
- <select id="selectedFrame">
- <option value="">-- Select Frame --</option>
- </select>
- </div>
- <div class="form-group">
- <label for="selectedWeapon">Weapon</label>
- <select id="selectedWeapon">
- <option value="">-- Select Weapon --</option>
- </select>
- </div>
- <div class="form-group">
- <label for="selectedArm">Arms</label>
- <select id="selectedArm">
- <option value="">-- Select Arms --</option>
- </select>
- </div>
- <div class="form-group">
- <label for="selectedLeg">Legs</label>
- <select id="selectedLeg">
- <option value="">-- Select Legs --</option>
- </select>
- </div>
- <div class="form-group">
- <label for="selectedHead">Head</label>
- <select id="selectedHead">
- <option value="">-- Select Head --</option>
- </select>
- </div>
- </div>
- </div>
- </div>
- <div id="digimon-lines-tab" class="tab-content">
- <div class="card">
- <h2 class="card-title">Digimon Evolution Lines</h2>
- <div id="digimonLinesContainer">
- <!-- Digimon lines will go here -->
- </div>
- <button type="button" onclick="addDigimonLine()" class="add-button">Add New Evolution Line</button>
- </div>
- </div>
- <div id="rules-tab" class="tab-content">
- <div class="card">
- <h2 class="card-title">Game Rules</h2>
- <div class="notes-container">
- <div class="notes-heading">Rules Reference</div>
- <textarea id="rulesNotes" class="notes-content" placeholder="Record important game rules here..."></textarea>
- </div>
- </div>
- </div>
- </div>
- <!-- After your other tab content divs -->
- <div id="moves-tab" class="tab-content">
- <div class="card">
- <h2 class="card-title">Move List</h2>
- <p class="card-subtitle">Create and manage your character's attacks, abilities, and techniques.</p>
- <div class="controls">
- <button type="button" onclick="addMove()" class="add-button">Add New Move</button>
- <select id="moveFilter" onchange="filterMoves()">
- <option value="all">All Moves</option>
- <option value="attack">Attacks</option>
- <option value="healing">Healing</option>
- <option value="buff">Buffs/Debuffs</option>
- <option value="utility">Utility</option>
- </select>
- </div>
- <div id="movesContainer">
- <!-- Moves will be added here -->
- </div>
- <div class="move-calculator">
- <h3>Move Calculator</h3>
- <div class="form-row">
- <div class="form-group">
- <label for="calcMoveName">Move:</label>
- <select id="calcMoveName">
- <option value="">-- Select Move --</option>
- </select>
- </div>
- <div class="form-group">
- <label for="calcTarget">Target Defense:</label>
- <input type="number" id="calcTarget" min="0" value="0">
- </div>
- <div class="form-group">
- <button type="button" onclick="calculateMoveEffect()">Calculate</button>
- </div>
- </div>
- <div id="moveCalcResult" class="calc-result">
- Result will appear here...
- </div>
- </div>
- </div>
- </div>
- <div id="partner-moves-tab" class="tab-content">
- <div class="card">
- <h2 class="card-title">Partner Move List</h2>
- <p class="card-subtitle">Create and manage your partner's attacks, abilities, and techniques.</p>
- <div class="controls">
- <button type="button" onclick="addPartnerMove()" class="add-button">Add New Partner Move</button>
- <select id="partnerMoveFilter" onchange="filterPartnerMoves()">
- <option value="all">All Moves</option>
- <option value="attack">Attacks</option>
- <option value="healing">Healing</option>
- <option value="buff">Buffs/Debuffs</option>
- <option value="utility">Utility</option>
- </select>
- </div>
- <div id="partnerMovesContainer">
- <!-- Partner moves will be added here -->
- </div>
- <div class="move-calculator">
- <h3>Partner Move Calculator</h3>
- <div class="form-row">
- <div class="form-group">
- <label for="calcPartnerMoveName">Move:</label>
- <select id="calcPartnerMoveName">
- <option value="">-- Select Move --</option>
- </select>
- </div>
- <div class="form-group">
- <label for="calcPartnerTarget">Target Defense:</label>
- <input type="number" id="calcPartnerTarget" min="0" value="0">
- </div>
- <div class="form-group">
- <button type="button" onclick="calculatePartnerMoveEffect()">Calculate</button>
- </div>
- </div>
- <div id="partnerMoveCalcResult" class="calc-result">
- Result will appear here...
- </div>
- </div>
- </div>
- </div>
- <div class="import-export">
- <h3>Save & Import Options</h3>
- <div class="form-row">
- <button type="button" onclick="saveCharacterSheet()" class="accent">Save Character Sheet</button>
- <button type="button" onclick="loadCharacterSheet()" class="secondary">Load Character Sheet</button>
- <button type="button" onclick="exportToJSON()" class="accent">Export to JSON</button>
- <button type="button" onclick="document.getElementById('importFile').click()">Import from JSON</button>
- <input type="file" id="importFile" accept=".json" style="display: none;" onchange="importFromJSON(this)">
- </div>
- <div class="form-row">
- <button type="button" onclick="importBackgrounds()" class="secondary">Import Backgrounds</button>
- <div class="form-group" style="display: flex; align-items: center; gap: 10px;">
- <label for="autoSaveSwitch" style="margin: 0;">Auto-Save:</label>
- <label class="switch">
- <input type="checkbox" id="autoSaveSwitch" onchange="autoSaveToggle()">
- <span class="slider"></span>
- </label>
- </div>
- </div>
- </div>
- </div>
- <!-- Notification element -->
- <div id="notification" class="notification">Save successful!</div>
- <script>
- // Global variables
- let autoSaveEnabled = false;
- let autoSaveInterval;
- const AUTOSAVE_INTERVAL = 60000; // 1 minute
- // Show notification
- function showNotification(message, isError = false) {
- const notification = document.getElementById('notification');
- notification.textContent = message;
- notification.classList.toggle('error', isError);
- notification.classList.add('show');
- setTimeout(() => {
- notification.classList.remove('show');
- }, 3000);
- }
- // Auto-save functionality
- function autoSaveToggle() {
- autoSaveEnabled = document.getElementById('autoSaveSwitch').checked;
- if (autoSaveEnabled) {
- showNotification('Auto-save enabled');
- autoSaveInterval = setInterval(saveCharacterSheet, AUTOSAVE_INTERVAL);
- saveCharacterSheet(); // Initial save
- } else {
- showNotification('Auto-save disabled');
- clearInterval(autoSaveInterval);
- }
- }
- // Handle page unload
- window.addEventListener('beforeunload', function(e) {
- saveCharacterSheet(); // Save before leaving page
- });
- // Tabs functionality
- document.querySelectorAll('.tab').forEach(tab => {
- tab.addEventListener('click', () => {
- const tabId = tab.getAttribute('data-tab');
- // Remove active class from all tabs and tab contents
- document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
- document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
- // Add active class to clicked tab and corresponding content
- tab.classList.add('active');
- document.getElementById(tabId).classList.add('active');
- // Save on tab change
- saveCharacterSheet();
- });
- });
- // Character type toggle
- document.querySelectorAll('input[name="characterType"]').forEach(input => {
- input.addEventListener('change', () => {
- const isTamer = document.querySelector('input[name="characterType"]:checked').value === 'tamer';
- document.querySelectorAll('.tamer-only').forEach(el => {
- el.style.display = isTamer ? 'block' : 'none';
- });
- document.querySelectorAll('.digimon-only').forEach(el => {
- el.style.display = isTamer ? 'none' : 'block';
- });
- updateBackgroundInfo();
- });
- });
- // Partner type toggle
- document.getElementById('partnerType').addEventListener('change', () => {
- const isHuman = document.getElementById('partnerType').value === 'tamer';
- document.querySelectorAll('.partner-tamer-only').forEach(el => {
- el.style.display = isHuman ? 'block' : 'none';
- });
- document.querySelectorAll('.partner-digimon-only').forEach(el => {
- el.style.display = isHuman ? 'none' : 'block';
- });
- updatePartnerBackgroundInfo();
- });
- // Background info display
- document.getElementById('characterBackground').addEventListener('change', () => {
- updateBackgroundInfo();
- applyBackgroundBonuses();
- });
- document.getElementById('digimonBackground').addEventListener('change', () => {
- updateBackgroundInfo();
- applyBackgroundBonuses();
- });
- document.getElementById('partnerHumanBackground').addEventListener('change', () => {
- updatePartnerBackgroundInfo();
- applyPartnerBackgroundBonuses();
- });
- document.getElementById('partnerDigimonBackground').addEventListener('change', () => {
- updatePartnerBackgroundInfo();
- applyPartnerBackgroundBonuses();
- });
- // Show/hide Isekai'd options
- function updateIsekaiOptions() {
- const characterType = document.querySelector('input[name="characterType"]:checked').value;
- const background = characterType === 'tamer'
- ? document.getElementById('characterBackground').value
- : document.getElementById('digimonBackground').value;
- document.querySelector('.isekai-only').style.display = background === 'isekai' ? 'block' : 'none';
- if (background === 'isekai') {
- // Limit to only 2 checkboxes
- const checkboxes = document.querySelectorAll('.isekai-stat-bonus');
- checkboxes.forEach(cb => {
- cb.addEventListener('change', function() {
- const checked = document.querySelectorAll('.isekai-stat-bonus:checked');
- if (checked.length > 2) {
- this.checked = false;
- showNotification('You can only select 2 stats for Isekai\'d bonus.', true);
- }
- applyIsekaiBonus();
- });
- });
- }
- }
- function updatePartnerIsekaiOptions() {
- const partnerType = document.getElementById('partnerType').value;
- const background = partnerType === 'tamer'
- ? document.getElementById('partnerHumanBackground').value
- : document.getElementById('partnerDigimonBackground').value;
- document.querySelector('.partner-isekai-only').style.display = background === 'isekai' ? 'block' : 'none';
- if (background === 'isekai') {
- // Limit to only 2 checkboxes
- const checkboxes = document.querySelectorAll('.partner-isekai-stat-bonus');
- checkboxes.forEach(cb => {
- cb.addEventListener('change', function() {
- const checked = document.querySelectorAll('.partner-isekai-stat-bonus:checked');
- if (checked.length > 2) {
- this.checked = false;
- showNotification('You can only select 2 stats for Isekai\'d bonus.', true);
- }
- applyPartnerIsekaiBonus();
- });
- });
- }
- }
- function applyIsekaiBonus() {
- // First reset all isekai bonuses
- resetIsekaiBonus();
- // Then apply new bonuses
- const checkedBoxes = document.querySelectorAll('.isekai-stat-bonus:checked');
- checkedBoxes.forEach(box => {
- const statId = box.value;
- const statElement = document.getElementById(statId);
- const currentValue = parseInt(statElement.value) || 1;
- // Store original value in data attribute if not already stored
- if (!statElement.dataset.originalValue) {
- statElement.dataset.originalValue = currentValue;
- }
- // Apply +2 bonus
- statElement.value = parseInt(statElement.dataset.originalValue) + 2;
- });
- // Recalculate after applying bonuses
- calculateStats();
- }
- function resetIsekaiBonus() {
- const statInputs = ['baseHP', 'baseSP', 'str', 'def', 'int', 'agi', 'wil'];
- statInputs.forEach(statId => {
- const statElement = document.getElementById(statId);
- if (statElement.dataset.originalValue) {
- statElement.value = statElement.dataset.originalValue;
- delete statElement.dataset.originalValue;
- }
- });
- }
- function applyPartnerIsekaiBonus() {
- // First reset all partner isekai bonuses
- resetPartnerIsekaiBonus();
- // Then apply new bonuses
- const checkedBoxes = document.querySelectorAll('.partner-isekai-stat-bonus:checked');
- checkedBoxes.forEach(box => {
- const statId = box.value;
- const statElement = document.getElementById(statId);
- const currentValue = parseInt(statElement.value) || 1;
- // Store original value in data attribute if not already stored
- if (!statElement.dataset.originalValue) {
- statElement.dataset.originalValue = currentValue;
- }
- // Apply +2 bonus
- statElement.value = parseInt(statElement.dataset.originalValue) + 2;
- });
- // Recalculate after applying bonuses
- calculatePartnerStats();
- }
- function resetPartnerIsekaiBonus() {
- const statInputs = ['partnerBaseHP', 'partnerBaseSP', 'partner-str', 'partner-def', 'partner-int', 'partner-agi', 'partner-wil'];
- statInputs.forEach(statId => {
- const statElement = document.getElementById(statId);
- if (statElement.dataset.originalValue) {
- statElement.value = statElement.dataset.originalValue;
- delete statElement.dataset.originalValue;
- }
- });
- }
- function updateBackgroundInfo() {
- const characterType = document.querySelector('input[name="characterType"]:checked').value;
- const background = characterType === 'tamer'
- ? document.getElementById('characterBackground').value
- : document.getElementById('digimonBackground').value;
- const backgroundInfoElement = document.getElementById('backgroundInfo');
- if (!background) {
- backgroundInfoElement.innerHTML = '<p>Select a background to see details</p>';
- return;
- }
- const backgroundInfo = getBackgroundInfo(background, characterType);
- backgroundInfoElement.innerHTML = backgroundInfo;
- // Update Isekai options visibility
- updateIsekaiOptions();
- }
- function updatePartnerBackgroundInfo() {
- const partnerType = document.getElementById('partnerType').value;
- const background = partnerType === 'tamer'
- ? document.getElementById('partnerHumanBackground').value
- : document.getElementById('partnerDigimonBackground').value;
- const backgroundInfoElement = document.getElementById('partnerBackgroundInfo');
- if (!background) {
- backgroundInfoElement.innerHTML = '<p>Select a background to see details</p>';
- return;
- }
- const backgroundInfo = getBackgroundInfo(background, partnerType);
- backgroundInfoElement.innerHTML = backgroundInfo;
- // Update Isekai options visibility for partner
- updatePartnerIsekaiOptions();
- }
- function applyBackgroundBonuses() {
- // Reset skills first
- resetSkillBonuses();
- const characterType = document.querySelector('input[name="characterType"]:checked').value;
- const background = characterType === 'tamer'
- ? document.getElementById('characterBackground').value
- : document.getElementById('digimonBackground').value;
- if (!background || background === 'isekai') return;
- const backgroundBonuses = getBackgroundBonuses(background);
- // Apply skill proficiencies
- if (backgroundBonuses.skills) {
- for (const skill in backgroundBonuses.skills) {
- const dice = backgroundBonuses.skills[skill];
- const radio = document.querySelector(`input[name="${skill}"][value="${dice}"]`);
- if (radio) radio.checked = true;
- }
- }
- // Apply stat bonuses
- if (backgroundBonuses.stats) {
- for (const stat in backgroundBonuses.stats) {
- const bonus = backgroundBonuses.stats[stat];
- const statElement = document.getElementById(stat);
- if (statElement) {
- // Store original value if not already stored
- if (!statElement.dataset.originalValue) {
- statElement.dataset.originalValue = statElement.value;
- }
- statElement.value = parseInt(statElement.dataset.originalValue) + bonus;
- }
- }
- }
- calculateStats();
- }
- function applyPartnerBackgroundBonuses() {
- // Reset partner skills first
- resetPartnerSkillBonuses();
- const partnerType = document.getElementById('partnerType').value;
- const background = partnerType === 'tamer'
- ? document.getElementById('partnerHumanBackground').value
- : document.getElementById('partnerDigimonBackground').value;
- if (!background || background === 'isekai') return;
- const backgroundBonuses = getBackgroundBonuses(background);
- // Apply skill proficiencies for partner
- if (backgroundBonuses.skills) {
- for (const skill in backgroundBonuses.skills) {
- const dice = backgroundBonuses.skills[skill];
- const partnerSkill = 'partner_' + skill;
- const radio = document.querySelector(`input[name="${partnerSkill}"][value="${dice}"]`);
- if (radio) radio.checked = true;
- }
- }
- // Apply stat bonuses for partner
- if (backgroundBonuses.stats) {
- for (const stat in backgroundBonuses.stats) {
- const bonus = backgroundBonuses.stats[stat];
- // Map to partner stats (prefix with 'partner-')
- const partnerStat = stat.startsWith('partner') ? stat : (stat === 'baseHP' ? 'partnerBaseHP' :
- (stat === 'baseSP' ? 'partnerBaseSP' : 'partner-' + stat));
- const statElement = document.getElementById(partnerStat);
- if (statElement) {
- // Store original value if not already stored
- if (!statElement.dataset.originalValue) {
- statElement.dataset.originalValue = statElement.value;
- }
- statElement.value = parseInt(statElement.dataset.originalValue) + bonus;
- }
- }
- }
- calculatePartnerStats();
- }
- function resetSkillBonuses() {
- const skills = document.querySelectorAll('input[type="radio"][name^="knowledge_"], input[type="radio"][name="dodge"], input[type="radio"][name="fight"], input[type="radio"][name="stealth"], input[type="radio"][name="manipulate"], input[type="radio"][name="perform"], input[type="radio"][name="persuade"], input[type="radio"][name="survival"], input[type="radio"][name="perception"]');
- skills.forEach(skill => {
- if (skill.value === 'none') {
- skill.checked = true;
- }
- });
- // Reset stat bonuses too
- const statInputs = ['baseHP', 'baseSP', 'str', 'def', 'int', 'agi', 'wil'];
- statInputs.forEach(statId => {
- const statElement = document.getElementById(statId);
- if (statElement.dataset.originalValue) {
- statElement.value = statElement.dataset.originalValue;
- delete statElement.dataset.originalValue;
- }
- });
- }
- function resetPartnerSkillBonuses() {
- const skills = document.querySelectorAll('input[type="radio"][name^="partner_"]');
- skills.forEach(skill => {
- if (skill.value === 'none') {
- skill.checked = true;
- }
- });
- // Reset partner stat bonuses
- const statInputs = ['partnerBaseHP', 'partnerBaseSP', 'partner-str', 'partner-def', 'partner-int', 'partner-agi', 'partner-wil'];
- statInputs.forEach(statId => {
- const statElement = document.getElementById(statId);
- if (statElement.dataset.originalValue) {
- statElement.value = statElement.dataset.originalValue;
- delete statElement.dataset.originalValue;
- }
- });
- }
- function getBackgroundBonuses(background) {
- const backgroundBonuses = {
- 'courtesan': {
- stats: { wil: 2 },
- skills: {
- manipulate: 'd8',
- perform: 'd6',
- persuade: 'd6',
- knowledge_human: 'd6'
- }
- },
- 'adventurer': {
- stats: { str: 2 },
- skills: {
- survival: 'd8',
- fight: 'd6',
- perception: 'd6',
- knowledge_region: 'd6'
- }
- },
- 'diplomat': {
- stats: { wil: 2 },
- skills: {
- persuade: 'd8',
- manipulate: 'd6',
- knowledge_human: 'd6',
- knowledge_local: 'd6'
- }
- },
- 'farmer': {
- stats: { agi: 2 },
- skills: {
- knowledge_cook: 'd8',
- survival: 'd6',
- perception: 'd6',
- knowledge_region: 'd6'
- }
- },
- 'janitor': {
- stats: { int: 2 },
- skills: {
- knowledge_handyman: 'd8',
- stealth: 'd6',
- perception: 'd6'
- }
- },
- 'sleuth': {
- stats: { agi: 2 },
- skills: {
- perception: 'd8',
- knowledge_local: 'd6',
- survival: 'd6'
- }
- },
- 'slave': {
- stats: { agi: 2 },
- skills: {
- stealth: 'd8',
- dodge: 'd6',
- survival: 'd6'
- }
- },
- 'wronged_hero': {
- stats: { str: 2 },
- skills: {
- fight: 'd8',
- knowledge_region: 'd6',
- persuade: 'd6'
- }
- },
- 'doctor': {
- stats: { int: 2 },
- skills: {
- knowledge_human: 'd8',
- perception: 'd6',
- knowledge_cook: 'd6'
- }
- },
- 'carpenter': {
- stats: { wil: 2 },
- skills: {
- knowledge_carpenter: 'd8',
- knowledge_handyman: 'd6',
- survival: 'd6'
- }
- },
- 'child': {
- stats: { agi: 2 },
- skills: {
- dodge: 'd8',
- stealth: 'd6',
- manipulate: 'd6'
- }
- },
- 'explorer': {
- stats: { int: 2 },
- skills: {
- knowledge_region: 'd8',
- survival: 'd6',
- perception: 'd6'
- }
- },
- 'fallen_noble': {
- stats: { wil: 2 },
- skills: {
- persuade: 'd8',
- knowledge_human: 'd6',
- knowledge_local: 'd6'
- }
- },
- 'student_of_magic': {
- stats: { int: 2 },
- skills: {
- knowledge_digimon: 'd8',
- knowledge_engineering: 'd6',
- perception: 'd6'
- }
- },
- 'survivor': {
- stats: { agi: 2 },
- skills: {
- survival: 'd8',
- perception: 'd6',
- dodge: 'd6'
- }
- },
- 'messenger': {
- stats: { agi: 2 },
- skills: {
- knowledge_local: 'd8',
- dodge: 'd6',
- perception: 'd6'
- }
- },
- 'from_the_past': {
- stats: { int: 2 },
- skills: {
- knowledge_region: 'd8',
- knowledge_human: 'd6',
- manipulate: 'd6'
- }
- },
- 'miner': {
- stats: { int: 2 },
- skills: {
- knowledge_handyman: 'd8',
- survival: 'd6',
- perception: 'd6'
- }
- },
- 'royalty': {
- stats: { wil: 2 },
- skills: {
- persuade: 'd8',
- knowledge_human: 'd6',
- knowledge_local: 'd6'
- }
- },
- 'mute': {
- stats: { agi: 2 },
- skills: {
- stealth: 'd8',
- perception: 'd6',
- manipulate: 'd6'
- }
- },
- 'artist': {
- stats: { wil: 2 },
- skills: {
- perform: 'd8',
- knowledge_human: 'd6',
- persuade: 'd6'
- }
- },
- 'negotiator': {
- stats: { wil: 2 },
- skills: {
- persuade: 'd8',
- knowledge_human: 'd6',
- perception: 'd6'
- }
- },
- 'innkeeper': {
- stats: { wil: 2 },
- skills: {
- knowledge_cook: 'd8',
- persuade: 'd6',
- knowledge_local: 'd6'
- }
- },
- 'barmaid': {
- stats: { wil: 2 },
- skills: {
- manipulate: 'd8',
- persuade: 'd6',
- knowledge_local: 'd6'
- }
- },
- 'bartender': {
- stats: { wil: 2 },
- skills: {
- persuade: 'd8',
- knowledge_cook: 'd6',
- knowledge_local: 'd6'
- }
- },
- 'sex_slave': {
- stats: { wil: 2 },
- skills: {
- manipulate: 'd8',
- persuade: 'd6',
- stealth: 'd6'
- }
- },
- 'inventor': {
- stats: { int: 2 },
- skills: {
- knowledge_engineering: 'd8',
- knowledge_hacking: 'd6',
- perception: 'd6'
- }
- },
- 'beggar': {
- stats: { agi: 2 },
- skills: {
- stealth: 'd8',
- dodge: 'd6',
- perception: 'd6'
- }
- },
- 'shepherd': {
- stats: { agi: 2 },
- skills: {
- survival: 'd8',
- knowledge_region: 'd6',
- perception: 'd6'
- }
- },
- 'pilot': {
- stats: { int: 2 },
- skills: {
- knowledge_engineering: 'd8',
- knowledge_hacking: 'd6',
- dodge: 'd6'
- }
- },
- 'bodyguard': {
- stats: { str: 2 },
- skills: {
- fight: 'd8',
- dodge: 'd6',
- perception: 'd6'
- }
- },
- 'librarian': {
- stats: { int: 2 },
- skills: {
- knowledge_human: 'd8',
- knowledge_local: 'd6',
- perception: 'd6'
- }
- },
- 'partnered': {
- skills: {
- // The player can choose 2 skills to receive d8
- }
- }
- };
- return backgroundBonuses[background] || { stats: {}, skills: {} };
- }
- function getBackgroundInfo(background, characterType) {
- const backgrounds = {
- // Human Backgrounds
- 'courtesan': `
- <h3>Courtesan</h3>
- <p><strong>Stat Bonuses:</strong> +2 to Manipulate, Perform, and Knowledge: Human</p>
- <p><strong>Skill Proficiencies:</strong></p>
- <ul>
- <li>d8: Manipulate</li>
- <li>d6: Perform, Persuade</li>
- </ul>
- `,
- 'adventurer': `
- <h3>Adventurer</h3>
- <p><strong>Stat Bonuses:</strong> +2 to Fight, Survival, and Knowledge: Region</p>
- <p><strong>Skill Proficiencies:</strong></p>
- <ul>
- <li>d8: Survival</li>
- <li>d6: Fight, Perception</li>
- </ul>
- `,
- 'diplomat': `
- <h3>Diplomat</h3>
- <p><strong>Stat Bonuses:</strong> +2 to Persuade, Knowledge: Human, and Knowledge: Local</p>
- <p><strong>Skill Proficiencies:</strong></p>
- <ul>
- <li>d8: Persuade</li>
- <li>d6: Manipulate, Knowledge: Local</li>
- </ul>
- `,
- 'farmer': `
- <h3>Farmer</h3>
- <p><strong>Stat Bonuses:</strong> +2 to Survival, Knowledge: Cook, and Knowledge: Region</p>
- <p><strong>Skill Proficiencies:</strong></p>
- <ul>
- <li>d8: Knowledge: Cook</li>
- <li>d6: Survival, Perception</li>
- </ul>
- `,
- 'janitor': `
- <h3>Janitor</h3>
- <p><strong>Stat Bonuses:</strong> +2 to Knowledge: Handyman, Stealth, and Perception</p>
- <p><strong>Skill Proficiencies:</strong></p>
- <ul>
- <li>d8: Knowledge: Handyman</li>
- <li>d6: Stealth, Perception</li>
- </ul>
- `,
- 'sleuth': `
- <h3>Sleuth</h3>
- <p><strong>Stat Bonuses:</strong> +2 to Perception, Knowledge: Local, and Survival</p>
- <p><strong>Skill Proficiencies:</strong></p>
- <ul>
- <li>d8: Perception</li>
- <li>d6: Knowledge: Local, Survival</li>
- </ul>
- `,
- 'slave': `
- <h3>Slave</h3>
- <p><strong>Stat Bonuses:</strong> +2 to Dodge, Stealth, and Survival</p>
- <p><strong>Skill Proficiencies:</strong></p>
- <ul>
- <li>d8: Stealth</li>
- <li>d6: Dodge, Survival</li>
- </ul>
- `,
- 'wronged_hero': `
- <h3>Wronged Hero</h3>
- <p><strong>Stat Bonuses:</strong> +2 to Fight, Persuade, and Knowledge: Region</p>
- <p><strong>Skill Proficiencies:</strong></p>
- <ul>
- <li>d8: Fight</li>
- <li>d6: Knowledge: Region, Persuade</li>
- </ul>
- `,
- 'doctor': `
- <h3>Doctor</h3>
- <p><strong>Stat Bonuses:</strong> +2 to Perception, Knowledge: Human, and Knowledge: Cook</p>
- <p><strong>Skill Proficiencies:</strong></p>
- <ul>
- <li>d8: Knowledge: Human</li>
- <li>d6: Perception, Knowledge: Cook</li>
- </ul>
- `,
- 'carpenter': `
- <h3>Carpenter</h3>
- <p><strong>Stat Bonuses:</strong> +2 to Knowledge: Carpenter, Survival, and Knowledge: Handyman</p>
- <p><strong>Skill Proficiencies:</strong></p>
- <ul>
- <li>d8: Knowledge: Carpenter</li>
- <li>d6: Knowledge: Handyman, Survival</li>
- </ul>
- `,
- 'child': `
- <h3>Child</h3>
- <p><strong>Stat Bonuses:</strong> +2 to Dodge, Stealth, and Manipulate</p>
- <p><strong>Skill Proficiencies:</strong></p>
- <ul>
- <li>d8: Dodge</li>
- <li>d6: Stealth, Manipulate</li>
- </ul>
- `,
- 'explorer': `
- <h3>Explorer</h3>
- <p><strong>Stat Bonuses:</strong> +2 to Knowledge: Region, Survival, and Perception</p>
- <p><strong>Skill Proficiencies:</strong></p>
- <ul>
- <li>d8: Knowledge: Region</li>
- <li>d6: Survival, Perception</li>
- </ul>
- `,
- 'fallen_noble': `
- <h3>Fallen Noble</h3>
- <p><strong>Stat Bonuses:</strong> +2 to Persuade, Knowledge: Human, and Knowledge: Local</p>
- <p><strong>Skill Proficiencies:</strong></p>
- <ul>
- <li>d8: Persuade</li>
- <li>d6: Knowledge: Human, Knowledge: Local</li>
- </ul>
- `,
- 'student_of_magic': `
- <h3>Student of Magic</h3>
- <p><strong>Stat Bonuses:</strong> +2 to Knowledge: Digimon, Knowledge: Engineering, and Perception</p>
- <p><strong>Skill Proficiencies:</strong></p>
- <ul>
- <li>d8: Knowledge: Digimon</li>
- <li>d6: Knowledge: Engineering, Perception</li>
- </ul>
- `,
- 'survivor': `
- <h3>Survivor</h3>
- <p><strong>Stat Bonuses:</strong> +2 to Survival, Perception, and Dodge</p>
- <p><strong>Skill Proficiencies:</strong></p>
- <ul>
- <li>d8: Survival</li>
- <li>d6: Perception, Dodge</li>
- </ul>
- `,
- 'messenger': `
- <h3>Messenger</h3>
- <p><strong>Stat Bonuses:</strong> +2 to Dodge, Knowledge: Local, and Perception</p>
- <p><strong>Skill Proficiencies:</strong></p>
- <ul>
- <li>d8: Knowledge: Local</li>
- <li>d6: Dodge, Perception</li>
- </ul>
- `,
- 'from_the_past': `
- <h3>From The Past</h3>
- <p><strong>Stat Bonuses:</strong> +2 to Knowledge: Human, Knowledge: Region, and Manipulate</p>
- <p><strong>Skill Proficiencies:</strong></p>
- <ul>
- <li>d8: Knowledge: Region</li>
- <li>d6: Knowledge: Human, Manipulate</li>
- </ul>
- `,
- 'miner': `
- <h3>Miner</h3>
- <p><strong>Stat Bonuses:</strong> +2 to Knowledge: Handyman, Survival, and Perception</p>
- <p><strong>Skill Proficiencies:</strong></p>
- <ul>
- <li>d8: Knowledge: Handyman</li>
- <li>d6: Survival, Perception</li>
- </ul>
- `,
- 'royalty': `
- <h3>Royalty</h3>
- <p><strong>Stat Bonuses:</strong> +2 to Persuade, Knowledge: Human, and Knowledge: Local</p>
- <p><strong>Skill Proficiencies:</strong></p>
- <ul>
- <li>d8: Persuade</li>
- <li>d6: Knowledge: Human, Knowledge: Local</li>
- </ul>
- `,
- 'mute': `
- <h3>Mute</h3>
- <p><strong>Stat Bonuses:</strong> +2 to Stealth, Perception, and Manipulate</p>
- <p><strong>Skill Proficiencies:</strong></p>
- <ul>
- <li>d8: Stealth</li>
- <li>d6: Perception, Manipulate</li>
- </ul>
- `,
- 'artist': `
- <h3>Artist</h3>
- <p><strong>Stat Bonuses:</strong> +2 to Perform, Knowledge: Human, and Persuade</p>
- <p><strong>Skill Proficiencies:</strong></p>
- <ul>
- <li>d8: Perform</li>
- <li>d6: Knowledge: Human, Persuade</li>
- </ul>
- `,
- 'negotiator': `
- <h3>Negotiator</h3>
- <p><strong>Stat Bonuses:</strong> +2 to Persuade, Knowledge: Human, and Perception</p>
- <p><strong>Skill Proficiencies:</strong></p>
- <ul>
- <li>d8: Persuade</li>
- <li>d6: Knowledge: Human, Perception</li>
- </ul>
- `,
- 'innkeeper': `
- <h3>Innkeeper</h3>
- <p><strong>Stat Bonuses:</strong> +2 to Knowledge: Cook, Persuade, and Knowledge: Local</p>
- <p><strong>Skill Proficiencies:</strong></p>
- <ul>
- <li>d8: Knowledge: Cook</li>
- <li>d6: Persuade, Knowledge: Local</li>
- </ul>
- `,
- 'barmaid': `
- <h3>Barmaid</h3>
- <p><strong>Stat Bonuses:</strong> +2 to Manipulate, Persuade, and Knowledge: Local</p>
- <p><strong>Skill Proficiencies:</strong></p>
- <ul>
- <li>d8: Manipulate</li>
- <li>d6: Persuade, Knowledge: Local</li>
- </ul>
- `,
- 'bartender': `
- <h3>Bartender</h3>
- <p><strong>Stat Bonuses:</strong> +2 to Persuade, Knowledge: Cook, and Knowledge: Local</p>
- <p><strong>Skill Proficiencies:</strong></p>
- <ul>
- <li>d8: Persuade</li>
- <li>d6: Knowledge: Cook, Knowledge: Local</li>
- </ul>
- `,
- 'sex_slave': `
- <h3>Sex Slave</h3>
- <p><strong>Stat Bonuses:</strong> +2 to Manipulate, Persuade, and Stealth</p>
- <p><strong>Skill Proficiencies:</strong></p>
- <ul>
- <li>d8: Manipulate</li>
- <li>d6: Persuade, Stealth</li>
- </ul>
- `,
- 'inventor': `
- <h3>Inventor</h3>
- <p><strong>Stat Bonuses:</strong> +2 to Knowledge: Engineering, Knowledge: Hacking, and Perception</p>
- <p><strong>Skill Proficiencies:</strong></p>
- <ul>
- <li>d8: Knowledge: Engineering</li>
- <li>d6: Knowledge: Hacking, Perception</li>
- </ul>
- `,
- 'beggar': `
- <h3>Beggar</h3>
- <p><strong>Stat Bonuses:</strong> +2 to Stealth, Dodge, and Perception</p>
- <p><strong>Skill Proficiencies:</strong></p>
- <ul>
- <li>d8: Stealth</li>
- <li>d6: Dodge, Perception</li>
- </ul>
- `,
- 'shepherd': `
- <h3>Shepherd</h3>
- <p><strong>Stat Bonuses:</strong> +2 to Knowledge: Region, Survival, and Perception</p>
- <p><strong>Skill Proficiencies:</strong></p>
- <ul>
- <li>d8: Survival</li>
- <li>d6: Knowledge: Region, Perception</li>
- </ul>
- `,
- 'pilot': `
- <h3>Pilot</h3>
- <p><strong>Stat Bonuses:</strong> +2 to Knowledge: Engineering, Knowledge: Hacking, and Dodge</p>
- <p><strong>Skill Proficiencies:</strong></p>
- <ul>
- <li>d8: Knowledge: Engineering</li>
- <li>d6: Knowledge: Hacking, Dodge</li>
- </ul>
- `,
- 'bodyguard': `
- <h3>Bodyguard</h3>
- <p><strong>Stat Bonuses:</strong> +2 to Fight, Dodge, and Perception</p>
- <p><strong>Skill Proficiencies:</strong></p>
- <ul>
- <li>d8: Fight</li>
- <li>d6: Dodge, Perception</li>
- </ul>
- `,
- 'librarian': `
- <h3>Librarian</h3>
- <p><strong>Stat Bonuses:</strong> +2 to Knowledge: Human, Knowledge: Local, and Perception</p>
- <p><strong>Skill Proficiencies:</strong></p>
- <ul>
- <li>d8: Knowledge: Human</li>
- <li>d6: Knowledge: Local, Perception</li>
- </ul>
- `,
- 'isekai': `
- <h3>Isekai'd</h3>
- <p><strong>General Features:</strong> Gain +2 to any two stats of your choice.</p>
- <p>Select which stats to enhance in the options below.</p>
- `,
- // Digimon Backgrounds
- 'partnered': `
- <h3>Partnered</h3>
- <p><strong>General Features:</strong> Gain d8 to any two skills of your choice.</p>
- <p>Select which skills to enhance when rolling.</p>
- `
- };
- return backgrounds[background] || '<p>No information available for this background.</p>';
- }
- // Image preview functionality
- function previewImage(input, imageId) {
- const file = input.files[0];
- if (file) {
- const reader = new FileReader();
- reader.onload = function(e) {
- document.getElementById(imageId).src = e.target.result;
- }
- reader.readAsDataURL(file);
- }
- }
- // Experience & Level Calculation
- document.getElementById('characterExp').addEventListener('change', () => {
- calculateNextLevel('characterExp', 'characterLevel', 'characterNextLevel');
- });
- document.getElementById('partnerExp').addEventListener('change', () => {
- calculateNextLevel('partnerExp', 'partnerLevel', 'partnerNextLevel');
- });
- function calculateNextLevel(expId, levelId, nextLevelId) {
- const exp = parseInt(document.getElementById(expId).value) || 0;
- let level = 1;
- let nextLevelExp = 10;
- // Calculate current level based on exp
- while (exp >= nextLevelExp && level < 99) {
- level++;
- nextLevelExp = Math.ceil(nextLevelExp * 1.3);
- }
- document.getElementById(levelId).value = level;
- document.getElementById(nextLevelId).value = nextLevelExp;
- // Update rank based on level
- if (expId === 'characterExp') {
- updateRankFromLevel('characterLevel', 'characterRank');
- updateStatCaps();
- calculateStats();
- } else {
- updateRankFromLevel('partnerLevel', 'partnerRank');
- updatePartnerStatCaps();
- calculatePartnerStats();
- }
- }
- // Stats management
- const statInputs = document.querySelectorAll('.stat-input');
- const partnerStatInputs = document.querySelectorAll('.partner-stat-input');
- // Initial stats calculation
- calculateStats();
- calculatePartnerStats();
- statInputs.forEach(input => {
- input.addEventListener('change', () => {
- validateStats(statInputs);
- calculateStats();
- });
- });
- partnerStatInputs.forEach(input => {
- input.addEventListener('change', () => {
- validateStats(partnerStatInputs);
- calculatePartnerStats();
- });
- });
- document.getElementById('characterRank').addEventListener('change', () => {
- updateStatCaps();
- calculateStats();
- });
- document.getElementById('partnerRank').addEventListener('change', () => {
- updatePartnerStatCaps();
- calculatePartnerStats();
- });
- document.getElementById('characterLevel').addEventListener('change', () => {
- updateRankFromLevel('characterLevel', 'characterRank');
- updateStatCaps();
- calculateStats();
- });
- document.getElementById('partnerLevel').addEventListener('change', () => {
- updateRankFromLevel('partnerLevel', 'partnerRank');
- updatePartnerStatCaps();
- calculatePartnerStats();
- });
- function updateRankFromLevel(levelId, rankId) {
- const level = parseInt(document.getElementById(levelId).value);
- const rankElement = document.getElementById(rankId);
- if (level >= 1 && level <= 5) {
- rankElement.value = 'in-training';
- } else if (level >= 6 && level <= 20) {
- rankElement.value = 'rookie';
- } else if (level >= 21 && level <= 64) {
- rankElement.value = 'champion';
- } else if (level >= 65 && level <= 99) {
- rankElement.value = 'ultimate';
- }
- }
- function updateStatCaps() {
- const rank = document.getElementById('characterRank').value;
- let cap = 6;
- let bonusPoints = 0;
- switch(rank) {
- case 'rookie':
- cap = 10;
- bonusPoints = 5;
- break;
- case 'champion':
- cap = 15;
- bonusPoints = 10;
- break;
- case 'ultimate':
- cap = 20;
- bonusPoints = 15;
- break;
- case 'mega':
- cap = 50;
- bonusPoints = 50;
- break;
- default:
- cap = 6;
- bonusPoints = 0;
- }
- statInputs.forEach(input => {
- input.setAttribute('max', cap);
- });
- document.getElementById('statPointsRemaining').textContent = 7 + bonusPoints;
- }
- function updatePartnerStatCaps() {
- const rank = document.getElementById('partnerRank').value;
- let cap = 6;
- let bonusPoints = 0;
- switch(rank) {
- case 'rookie':
- cap = 10;
- bonusPoints = 5;
- break;
- case 'champion':
- cap = 15;
- bonusPoints = 10;
- break;
- case 'ultimate':
- cap = 20;
- bonusPoints = 15;
- break;
- case 'mega':
- cap = 50;
- bonusPoints = 50;
- break;
- default:
- cap = 6;
- bonusPoints = 0;
- }
- partnerStatInputs.forEach(input => {
- input.setAttribute('max', cap);
- });
- document.getElementById('partnerStatPointsRemaining').textContent = 7 + bonusPoints;
- }
- function validateStats(inputs) {
- let total = 0;
- inputs.forEach(input => {
- total += parseInt(input.value || 0);
- });
- const isCharacter = inputs[0].id === 'baseHP';
- const rank = isCharacter ? document.getElementById('characterRank').value : document.getElementById('partnerRank').value;
- let maxPoints = 12; // 7 points + 5 starting at 1
- switch(rank) {
- case 'rookie':
- maxPoints = 17; // 12 + 5 bonus
- break;
- case 'champion':
- maxPoints = 22; // 12 + 10 bonus
- break;
- case 'ultimate':
- maxPoints = 27; // 12 + 15 bonus
- break;
- case 'mega':
- maxPoints = 62; // 12 + 50 bonus
- break;
- }
- if (total > maxPoints) {
- showNotification(`You've used ${total - maxPoints} too many stat points. Maximum is ${maxPoints}.`, true);
- // Reset to valid values
- let excess = total - maxPoints;
- for (let i = inputs.length - 1; i >= 0; i--) {
- let currentValue = parseInt(inputs[i].value);
- if (currentValue > 1 && excess > 0) {
- let reduction = Math.min(currentValue - 1, excess);
- inputs[i].value = currentValue - reduction;
- excess -= reduction;
- }
- }
- }
- // Update points remaining
- if (isCharacter) {
- document.getElementById('statPointsRemaining').textContent = maxPoints - total + 7;
- } else {
- document.getElementById('partnerStatPointsRemaining').textContent = maxPoints - total + 7;
- }
- }
- function calculateStats() {
- const baseHP = parseInt(document.getElementById('baseHP').value) || 1;
- const def = parseInt(document.getElementById('def').value) || 1;
- const baseSP = parseInt(document.getElementById('baseSP').value) || 1;
- const int = parseInt(document.getElementById('int').value) || 1;
- const rank = document.getElementById('characterRank').value;
- let modifier = 1.5;
- switch(rank) {
- case 'rookie':
- modifier = 2;
- break;
- case 'champion':
- modifier = 2.5;
- break;
- case 'ultimate':
- modifier = 3;
- break;
- case 'mega':
- modifier = 3.5;
- break;
- default:
- modifier = 1.5;
- }
- const maxHP = Math.floor((baseHP + def) * modifier);
- const maxSP = Math.floor((baseSP + int) * modifier);
- document.getElementById('hp-value').textContent = maxHP;
- document.getElementById('sp-value').textContent = maxSP;
- // Set initial current values if they're empty
- if (!document.getElementById('currentHP').value) {
- document.getElementById('currentHP').value = maxHP;
- }
- if (!document.getElementById('currentSP').value) {
- document.getElementById('currentSP').value = maxSP;
- }
- }
- function calculatePartnerStats() {
- const baseHP = parseInt(document.getElementById('partnerBaseHP').value) || 1;
- const def = parseInt(document.getElementById('partner-def').value) || 1;
- const baseSP = parseInt(document.getElementById('partnerBaseSP').value) || 1;
- const int = parseInt(document.getElementById('partner-int').value) || 1;
- const rank = document.getElementById('partnerRank').value;
- let modifier = 1.5;
- switch(rank) {
- case 'rookie':
- modifier = 2;
- break;
- case 'champion':
- modifier = 2.5;
- break;
- case 'ultimate':
- modifier = 3;
- break;
- case 'mega':
- modifier = 3.5;
- break;
- default:
- modifier = 1.5;
- }
- const maxHP = Math.floor((baseHP + def) * modifier);
- const maxSP = Math.floor((baseSP + int) * modifier);
- document.getElementById('partner-hp-value').textContent = maxHP;
- document.getElementById('partner-sp-value').textContent = maxSP;
- // Set initial current values if they're empty
- if (!document.getElementById('partnerCurrentHP').value) {
- document.getElementById('partnerCurrentHP').value = maxHP;
- }
- if (!document.getElementById('partnerCurrentSP').value) {
- document.getElementById('partnerCurrentSP').value = maxSP;
- }
- }
- // Digimon Lines
- function addDigimonLine() {
- const container = document.getElementById('digimonLinesContainer');
- const lineIndex = container.children.length;
- const line = document.createElement('div');
- line.className = 'evo-line';
- line.innerHTML = `
- <h3>Evolution Line ${lineIndex + 1}</h3>
- <button type="button" class="remove-button" onclick="removeDigimonLine(this.parentNode)">Remove Line</button>
- <div class="evo-stage">
- <div class="evo-stage-title">In-Training</div>
- <div class="form-group">
- <label>Name</label>
- <input type="text" name="inTrainingName_${lineIndex}">
- </div>
- <div class="form-group">
- <label>Description</label>
- <textarea name="inTrainingDesc_${lineIndex}"></textarea>
- </div>
- </div>
- <div id="rookieContainer_${lineIndex}">
- <!-- Rookie evolutions will go here -->
- </div>
- <button type="button" class="add-button" onclick="addEvolution('rookie', ${lineIndex})">Add Rookie Evolution</button>
- `;
- container.appendChild(line);
- // Auto-save when adding a new line
- if (autoSaveEnabled) saveCharacterSheet();
- }
- function removeDigimonLine(lineElement) {
- lineElement.remove();
- if (autoSaveEnabled) saveCharacterSheet();
- }
- function addEvolution(level, lineIndex) {
- const container = document.getElementById(`${level}Container_${lineIndex}`);
- const evoIndex = container.children.length;
- const evo = document.createElement('div');
- evo.className = 'evo-stage';
- let nextLevel = '';
- let levelTitle = '';
- switch(level) {
- case 'rookie':
- nextLevel = 'champion';
- levelTitle = 'Rookie';
- break;
- case 'champion':
- nextLevel = 'ultimate';
- levelTitle = 'Champion';
- break;
- case 'ultimate':
- nextLevel = 'mega';
- levelTitle = 'Ultimate';
- break;
- case 'mega':
- nextLevel = '';
- levelTitle = 'Mega';
- break;
- }
- evo.innerHTML = `
- <div class="evo-stage-title">${levelTitle}</div>
- <button type="button" class="remove-button" onclick="removeEvolution(this.parentNode)">Remove</button>
- <div class="form-group">
- <label>Name</label>
- <input type="text" name="${level}Name_${lineIndex}_${evoIndex}">
- </div>
- <div class="form-group">
- <label>Description</label>
- <textarea name="${level}Desc_${lineIndex}_${evoIndex}"></textarea>
- </div>
- `;
- container.appendChild(evo);
- // Add next level container if not mega
- if (nextLevel) {
- if (!document.getElementById(`${nextLevel}Container_${lineIndex}_${evoIndex}`)) {
- const nextContainer = document.createElement('div');
- nextContainer.id = `${nextLevel}Container_${lineIndex}_${evoIndex}`;
- evo.appendChild(nextContainer);
- const addButton = document.createElement('button');
- addButton.className = 'add-button';
- addButton.textContent = `Add ${nextLevel.charAt(0).toUpperCase() + nextLevel.slice(1)} Evolution`;
- addButton.onclick = function() {
- addEvolution(nextLevel, `${lineIndex}_${evoIndex}`);
- };
- evo.appendChild(addButton);
- }
- }
- // Auto-save when adding a new evolution
- if (autoSaveEnabled) saveCharacterSheet();
- }
- function removeEvolution(evoElement) {
- evoElement.remove();
- if (autoSaveEnabled) saveCharacterSheet();
- }
- // Mech Parts
- function addMechPart() {
- const partType = document.getElementById('mechPartType').value;
- const container = document.getElementById(`mech${partType.charAt(0).toUpperCase() + partType.slice(1)}sContainer`);
- const partIndex = container.children.length;
- const part = document.createElement('div');
- part.className = 'mech-part';
- part.innerHTML = `
- <h4>${partType.charAt(0).toUpperCase() + partType.slice(1)} ${partIndex + 1}</h4>
- <button type="button" class="remove-button" onclick="removeMechPart(this.parentNode)">Remove</button>
- <div class="form-group">
- <label>Name</label>
- <input type="text" name="mech${partType}Name_${partIndex}">
- </div>
- <div class="form-group">
- <label>Description</label>
- <textarea name="mech${partType}Desc_${partIndex}"></textarea>
- </div>
- <div class="form-group">
- <label>Stats</label>
- <textarea name="mech${partType}Stats_${partIndex}"></textarea>
- </div>
- `;
- container.appendChild(part);
- // Update the dropdown options for this part type
- updateMechPartDropdowns(partType);
- // Auto-save when adding a new part
- if (autoSaveEnabled) saveCharacterSheet();
- }
- function removeMechPart(partElement) {
- // Get the part type from the container ID
- const container = partElement.parentNode;
- const partType = container.id.replace('mech', '').replace('Container', '').toLowerCase();
- partElement.remove();
- // Update the dropdown options after removing
- updateMechPartDropdowns(partType);
- if (autoSaveEnabled) saveCharacterSheet();
- }
- function updateMechPartDropdowns(partType) {
- const container = document.getElementById(`mech${partType.charAt(0).toUpperCase() + partType.slice(1)}sContainer`);
- const dropdown = document.getElementById(`selected${partType.charAt(0).toUpperCase() + partType.slice(1)}`);
- // Clear current options except the first one
- while (dropdown.options.length > 1) {
- dropdown.remove(1);
- }
- // Add an option for each part in the container
- const parts = container.querySelectorAll('.mech-part');
- parts.forEach((part, index) => {
- const nameInput = part.querySelector(`input[name^="mech${partType}Name_"]`);
- const name = nameInput.value || `${partType.charAt(0).toUpperCase() + partType.slice(1)} ${index + 1}`;
- const option = document.createElement('option');
- option.value = index;
- option.textContent = name;
- dropdown.appendChild(option);
- });
- }
- // Items
- function addItem() {
- const container = document.getElementById('itemsContainer');
- const itemIndex = container.children.length;
- const item = document.createElement('div');
- item.className = 'item-entry';
- item.innerHTML = `
- <button type="button" class="remove-button" onclick="removeItem(this.parentNode)">Remove</button>
- <div class="form-group">
- <label>Name</label>
- <input type="text" name="itemName_${itemIndex}">
- </div>
- <div class="form-group">
- <label>Description</label>
- <textarea name="itemDesc_${itemIndex}"></textarea>
- </div>
- <div class="form-group">
- <label>Effects</label>
- <textarea name="itemEffects_${itemIndex}"></textarea>
- </div>
- `;
- container.appendChild(item);
- // Auto-save when adding a new item
- if (autoSaveEnabled) saveCharacterSheet();
- }
- function removeItem(itemElement) {
- itemElement.remove();
- if (autoSaveEnabled) saveCharacterSheet();
- }
- // Dice rolling
- function rollDice() {
- const diceType = document.getElementById('diceType').value;
- let max;
- switch(diceType) {
- case 'd4':
- max = 4;
- break;
- case 'd6':
- max = 6;
- break;
- case 'd8':
- max = 8;
- break;
- case 'd12':
- max = 12;
- break;
- case 'd20':
- max = 20;
- break;
- default:
- max = 6;
- }
- const result = Math.floor(Math.random() * max) + 1;
- document.getElementById('rollResult').textContent = `${result} (${diceType})`;
- // Animation effect
- const rollResult = document.getElementById('rollResult');
- rollResult.style.transform = 'scale(1.2)';
- rollResult.style.transition = 'transform 0.2s';
- setTimeout(() => {
- rollResult.style.transform = 'scale(1)';
- }, 200);
- }
- function rollSkill(skill) {
- const selectedDice = document.querySelector(`input[name="${skill}"]:checked`).value;
- if (selectedDice === 'none') {
- showNotification('Please select a proficiency level for this skill first.', true);
- return;
- }
- let max;
- switch(selectedDice) {
- case 'd4':
- max = 4;
- break;
- case 'd6':
- max = 6;
- break;
- case 'd8':
- max = 8;
- break;
- case 'd12':
- max = 12;
- break;
- case 'd20':
- max = 20;
- break;
- default:
- max = 6;
- }
- // Get the associated stat
- let statType;
- if (skill.includes('knowledge_')) {
- const knowledgeType = skill.replace('knowledge_', '');
- if (['digimon', 'local', 'region', 'handyman', 'hacking', 'engineering'].includes(knowledgeType)) {
- statType = 'int';
- } else if (['human', 'smith', 'carpenter', 'cook'].includes(knowledgeType)) {
- statType = 'wil';
- } else if (knowledgeType === 'trapper') {
- statType = 'agi';
- }
- } else if (['dodge', 'stealth', 'survival', 'perception'].includes(skill)) {
- statType = 'agi';
- } else if (skill === 'fight') {
- statType = 'str';
- } else if (['manipulate', 'perform', 'persuade'].includes(skill)) {
- statType = 'wil';
- }
- // Get stat bonus
- const statValue = parseInt(document.getElementById(statType).value) || 0;
- // Apply background bonuses if applicable
- const characterType = document.querySelector('input[name="characterType"]:checked').value;
- const background = characterType === 'tamer'
- ? document.getElementById('characterBackground').value
- : document.getElementById('digimonBackground').value;
- let backgroundBonus = 0;
- if (background && background !== 'isekai') {
- const backgroundBonuses = getBackgroundBonuses(background);
- if (backgroundBonuses.stats && backgroundBonuses.stats[statType]) {
- backgroundBonus = backgroundBonuses.stats[statType];
- }
- }
- const diceRoll = Math.floor(Math.random() * max) + 1;
- const totalResult = diceRoll + statValue + backgroundBonus;
- document.getElementById('rollResult').textContent = `${diceRoll} (${selectedDice}) + ${statValue} (${statType.toUpperCase()}) ${backgroundBonus > 0 ? '+ ' + backgroundBonus + ' (background)' : ''} = ${totalResult} - ${skill.replace('_', ' ')}`;
- // Animation effect
- const rollResult = document.getElementById('rollResult');
- rollResult.style.transform = 'scale(1.2)';
- rollResult.style.transition = 'transform 0.2s';
- setTimeout(() => {
- rollResult.style.transform = 'scale(1)';
- }, 200);
- // Switch to character tab to see the result
- document.querySelector('.tab[data-tab="character-tab"]').click();
- }
- function rollPartnerSkill(skill, statType) {
- const selectedDice = document.querySelector(`input[name="${skill}"]:checked`).value;
- if (selectedDice === 'none') {
- showNotification('Please select a proficiency level for this skill first.', true);
- return;
- }
- let max;
- switch(selectedDice) {
- case 'd4':
- max = 4;
- break;
- case 'd6':
- max = 6;
- break;
- case 'd8':
- max = 8;
- break;
- case 'd12':
- max = 12;
- break;
- case 'd20':
- max = 20;
- break;
- default:
- max = 6;
- }
- // Get stat bonus
- const actualStatId = statType === 'str' ? 'partner-str' :
- statType === 'def' ? 'partner-def' :
- statType === 'int' ? 'partner-int' :
- statType === 'agi' ? 'partner-agi' : 'partner-wil';
- const statValue = parseInt(document.getElementById(actualStatId).value) || 0;
- // Apply background bonuses if applicable
- const partnerType = document.getElementById('partnerType').value;
- const background = partnerType === 'tamer'
- ? document.getElementById('partnerHumanBackground').value
- : document.getElementById('partnerDigimonBackground').value;
- let backgroundBonus = 0;
- if (background && background !== 'isekai') {
- const backgroundBonuses = getBackgroundBonuses(background);
- if (backgroundBonuses.stats) {
- const statMapped = statType === 'str' ? 'str' :
- statType === 'def' ? 'def' :
- statType === 'int' ? 'int' :
- statType === 'agi' ? 'agi' : 'wil';
- if (backgroundBonuses.stats[statMapped]) {
- backgroundBonus = backgroundBonuses.stats[statMapped];
- }
- }
- }
- const diceRoll = Math.floor(Math.random() * max) + 1;
- const totalResult = diceRoll + statValue + backgroundBonus;
- document.getElementById('rollResult').textContent = `${diceRoll} (${selectedDice}) + ${statValue} (${statType.toUpperCase()}) ${backgroundBonus > 0 ? '+ ' + backgroundBonus + ' (background)' : ''} = ${totalResult} - Partner ${skill.replace('partner_', '').replace('_', ' ')}`;
- // Animation effect
- const rollResult = document.getElementById('rollResult');
- rollResult.style.transform = 'scale(1.2)';
- rollResult.style.transition = 'transform 0.2s';
- setTimeout(() => {
- rollResult.style.transform = 'scale(1)';
- }, 200);
- // Switch to character tab to see the result
- document.querySelector('.tab[data-tab="character-tab"]').click();
- }
- // Save/Load functionality
- function saveCharacterSheet() {
- const data = collectAllData();
- // Save to localStorage
- localStorage.setItem('digimonCharacter', JSON.stringify(data));
- // Visual indicator that save was successful
- showNotification('Character data saved!');
- }
- function loadCharacterSheet() {
- const savedData = localStorage.getItem('digimonCharacter');
- if (!savedData) {
- showNotification('No saved character data found.', true);
- return;
- }
- loadAllData(JSON.parse(savedData));
- showNotification('Character data loaded successfully!');
- }
- function exportToJSON() {
- const data = collectAllData();
- const dataStr = JSON.stringify(data, null, 2);
- const dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr);
- const exportFileName = `${data.characterName || 'digimon'}_character.json`;
- const linkElement = document.createElement('a');
- linkElement.setAttribute('href', dataUri);
- linkElement.setAttribute('download', exportFileName);
- linkElement.click();
- showNotification('Exported to JSON file!');
- }
- function importFromJSON(input) {
- const file = input.files[0];
- if (file) {
- const reader = new FileReader();
- reader.onload = function(e) {
- try {
- const data = JSON.parse(e.target.result);
- loadAllData(data);
- showNotification('Character data imported successfully!');
- } catch (error) {
- showNotification('Error parsing JSON file: ' + error.message, true);
- }
- };
- reader.readAsText(file);
- }
- }
- function importBackgrounds() {
- const input = document.createElement('input');
- input.type = 'file';
- input.accept = '.json';
- input.onchange = function() {
- const file = input.files[0];
- if (file) {
- const reader = new FileReader();
- reader.onload = function(e) {
- try {
- const data = JSON.parse(e.target.result);
- if (data.backgrounds) {
- // Process imported backgrounds
- importedBackgrounds = data.backgrounds;
- // Update background dropdowns
- updateBackgroundOptions();
- showNotification('Backgrounds imported successfully!');
- } else {
- showNotification('Invalid background file format.', true);
- }
- } catch (error) {
- showNotification('Error parsing JSON file: ' + error.message, true);
- }
- };
- reader.readAsText(file);
- }
- };
- input.click();
- }
- function updateBackgroundOptions() {
- // Update character background dropdown
- const characterBackground = document.getElementById('characterBackground');
- const digimonBackground = document.getElementById('digimonBackground');
- const partnerHumanBackground = document.getElementById('partnerHumanBackground');
- const partnerDigimonBackground = document.getElementById('partnerDigimonBackground');
- // Save current selections
- const characterSelected = characterBackground.value;
- const digimonSelected = digimonBackground.value;
- const partnerHumanSelected = partnerHumanBackground.value;
- const partnerDigimonSelected = partnerDigimonBackground.value;
- // First option is empty
- while (characterBackground.options.length > 1) {
- characterBackground.remove(1);
- }
- while (digimonBackground.options.length > 1) {
- digimonBackground.remove(1);
- }
- while (partnerHumanBackground.options.length > 1) {
- partnerHumanBackground.remove(1);
- }
- while (partnerDigimonBackground.options.length > 1) {
- partnerDigimonBackground.remove(1);
- }
- // Add standard backgrounds
- const humanBackgrounds = [
- 'courtesan', 'adventurer', 'diplomat', 'farmer', 'janitor', 'sleuth', 'slave',
- 'wronged_hero', 'doctor', 'carpenter', 'child', 'explorer', 'fallen_noble',
- 'student_of_magic', 'survivor', 'messenger', 'from_the_past', 'miner', 'royalty',
- 'mute', 'artist', 'negotiator', 'innkeeper', 'barmaid', 'bartender', 'sex_slave',
- 'inventor', 'beggar', 'shepherd', 'pilot', 'bodyguard', 'librarian', 'isekai'
- ];
- const digimonBackgrounds = ['isekai', 'partnered'];
- // Add human backgrounds to character dropdown
- humanBackgrounds.forEach(bg => {
- const displayName = bg.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase());
- const option1 = document.createElement('option');
- option1.value = bg;
- option1.textContent = displayName;
- characterBackground.appendChild(option1);
- const option2 = document.createElement('option');
- option2.value = bg;
- option2.textContent = displayName;
- partnerHumanBackground.appendChild(option2);
- });
- // Add digimon backgrounds to digimon dropdown
- digimonBackgrounds.forEach(bg => {
- const displayName = bg.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase());
- const option1 = document.createElement('option');
- option1.value = bg;
- option1.textContent = displayName;
- digimonBackground.appendChild(option1);
- const option2 = document.createElement('option');
- option2.value = bg;
- option2.textContent = displayName;
- partnerDigimonBackground.appendChild(option2);
- });
- // Add custom backgrounds if any
- if (importedBackgrounds) {
- Object.keys(importedBackgrounds).forEach(bg => {
- const bgData = importedBackgrounds[bg];
- const displayName = bg.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase());
- if (bgData.type === 'human' || bgData.type === 'both') {
- const option1 = document.createElement('option');
- option1.value = bg;
- option1.textContent = displayName;
- characterBackground.appendChild(option1);
- const option2 = document.createElement('option');
- option2.value = bg;
- option2.textContent = displayName;
- partnerHumanBackground.appendChild(option2);
- }
- if (bgData.type === 'digimon' || bgData.type === 'both') {
- const option1 = document.createElement('option');
- option1.value = bg;
- option1.textContent = displayName;
- digimonBackground.appendChild(option1);
- const option2 = document.createElement('option');
- option2.value = bg;
- option2.textContent = displayName;
- partnerDigimonBackground.appendChild(option2);
- }
- });
- }
- // Restore selections
- characterBackground.value = characterSelected;
- digimonBackground.value = digimonSelected;
- partnerHumanBackground.value = partnerHumanSelected;
- partnerDigimonBackground.value = partnerDigimonSelected;
- }
- function collectAllData() {
- // Collect all form data
- const data = {
- // Character data
- characterType: document.querySelector('input[name="characterType"]:checked').value,
- characterName: document.getElementById('characterName').value,
- characterLevel: document.getElementById('characterLevel').value,
- characterExp: document.getElementById('characterExp').value,
- characterBackground: document.getElementById('characterBackground').value,
- digimonBackground: document.getElementById('digimonBackground').value,
- characterRank: document.getElementById('characterRank').value,
- characterImage: document.getElementById('characterImage').src,
- // Isekai bonuses
- isekaiStats: Array.from(document.querySelectorAll('.isekai-stat-bonus:checked')).map(cb => cb.value),
- // Stats
- baseHP: document.getElementById('baseHP').value,
- currentHP: document.getElementById('currentHP').value,
- baseSP: document.getElementById('baseSP').value,
- currentSP: document.getElementById('currentSP').value,
- str: document.getElementById('str').value,
- def: document.getElementById('def').value,
- int: document.getElementById('int').value,
- agi: document.getElementById('agi').value,
- wil: document.getElementById('wil').value,
- // Partner data
- partnerName: document.getElementById('partnerName').value,
- partnerLevel: document.getElementById('partnerLevel').value,
- partnerExp: document.getElementById('partnerExp').value,
- partnerType: document.getElementById('partnerType').value,
- partnerHumanBackground: document.getElementById('partnerHumanBackground').value,
- partnerDigimonBackground: document.getElementById('partnerDigimonBackground').value,
- partnerRank: document.getElementById('partnerRank').value,
- partnerImage: document.getElementById('partnerImage').src,
- // Partner Isekai bonuses
- partnerIsekaiStats: Array.from(document.querySelectorAll('.partner-isekai-stat-bonus:checked')).map(cb => cb.value),
- // Partner stats
- partnerBaseHP: document.getElementById('partnerBaseHP').value,
- partnerCurrentHP: document.getElementById('partnerCurrentHP').value,
- partnerBaseSP: document.getElementById('partnerBaseSP').value,
- partnerCurrentSP: document.getElementById('partnerCurrentSP').value,
- partnerStr: document.getElementById('partner-str').value,
- partnerDef: document.getElementById('partner-def').value,
- partnerInt: document.getElementById('partner-int').value,
- partnerAgi: document.getElementById('partner-agi').value,
- partnerWil: document.getElementById('partner-wil').value,
- // Notes
- itemsNotes: document.getElementById('itemsNotes').value,
- aspectsNotes: document.getElementById('aspectsNotes').value,
- campaignNotes: document.getElementById('campaignNotes').value,
- classSkillsNotes: document.getElementById('classSkillsNotes').value,
- rulesNotes: document.getElementById('rulesNotes').value,
- // Skills
- skills: collectSkills(),
- partnerSkills: collectPartnerSkills(),
- // Items, Digimon Lines, and Mech parts
- items: collectItems(),
- digimonLines: collectDigimonLines(),
- mechParts: collectMechParts(),
- mechConfig: collectMechConfig()
- };
- return data;
- }
- function collectSkills() {
- const skills = {};
- const skillNames = [
- 'dodge', 'fight', 'stealth', 'manipulate', 'perform',
- 'persuade', 'survival', 'perception', 'knowledge_digimon',
- 'knowledge_human', 'knowledge_local', 'knowledge_region',
- 'knowledge_trapper', 'knowledge_smith', 'knowledge_carpenter',
- 'knowledge_handyman', 'knowledge_cook', 'knowledge_hacking',
- 'knowledge_engineering'
- ];
- skillNames.forEach(skill => {
- const selectedRadio = document.querySelector(`input[name="${skill}"]:checked`);
- if (selectedRadio) {
- skills[skill] = selectedRadio.value;
- }
- });
- return skills;
- }
- function collectPartnerSkills() {
- const skills = {};
- const skillNames = [
- 'partner_dodge', 'partner_fight', 'partner_stealth',
- 'partner_manipulate', 'partner_perform', 'partner_persuade',
- 'partner_survival', 'partner_perception', 'partner_knowledge_digimon',
- 'partner_knowledge_human', 'partner_knowledge_local', 'partner_knowledge_region',
- 'partner_knowledge_trapper', 'partner_knowledge_smith', 'partner_knowledge_carpenter',
- 'partner_knowledge_handyman', 'partner_knowledge_cook', 'partner_knowledge_hacking',
- 'partner_knowledge_engineering'
- ];
- skillNames.forEach(skill => {
- const selectedRadio = document.querySelector(`input[name="${skill}"]:checked`);
- if (selectedRadio) {
- skills[skill] = selectedRadio.value;
- }
- });
- return skills;
- }
- function collectItems() {
- const items = [];
- const itemElements = document.querySelectorAll('#itemsContainer .item-entry');
- itemElements.forEach((el, index) => {
- const nameInput = el.querySelector(`input[name="itemName_${index}"]`);
- const descInput = el.querySelector(`textarea[name="itemDesc_${index}"]`);
- const effectsInput = el.querySelector(`textarea[name="itemEffects_${index}"]`);
- if (nameInput) {
- items.push({
- name: nameInput.value,
- description: descInput ? descInput.value : '',
- effects: effectsInput ? effectsInput.value : ''
- });
- }
- });
- return items;
- }
- function collectDigimonLines() {
- const lines = [];
- const lineElements = document.querySelectorAll('#digimonLinesContainer .evo-line');
- lineElements.forEach((lineEl, lineIndex) => {
- const line = {
- inTraining: {
- name: lineEl.querySelector(`input[name="inTrainingName_${lineIndex}"]`)?.value || '',
- description: lineEl.querySelector(`textarea[name="inTrainingDesc_${lineIndex}"]`)?.value || ''
- },
- rookies: []
- };
- // Collect rookies (simplified implementation)
- const rookieContainer = lineEl.querySelector(`#rookieContainer_${lineIndex}`);
- if (rookieContainer) {
- const rookieElements = rookieContainer.querySelectorAll('.evo-stage');
- rookieElements.forEach((rookieEl, rookieIndex) => {
- const rookie = {
- name: rookieEl.querySelector(`input[name="rookieName_${lineIndex}_${rookieIndex}"]`)?.value || '',
- description: rookieEl.querySelector(`textarea[name="rookieDesc_${lineIndex}_${rookieIndex}"]`)?.value || '',
- champions: []
- };
- line.rookies.push(rookie);
- // We could continue collecting champions, ultimates, and megas here
- // But for simplicity we'll just store the rookies
- });
- }
- lines.push(line);
- });
- return lines;
- }
- function collectMechParts() {
- const mechParts = {
- cores: collectMechPartsOfType('core'),
- frames: collectMechPartsOfType('frame'),
- weapons: collectMechPartsOfType('weapon'),
- arms: collectMechPartsOfType('arm'),
- legs: collectMechPartsOfType('leg'),
- heads: collectMechPartsOfType('head')
- };
- return mechParts;
- }
- function collectMechPartsOfType(type) {
- const parts = [];
- const container = document.getElementById(`mech${type.charAt(0).toUpperCase() + type.slice(1)}sContainer`);
- if (!container) return parts;
- const partElements = container.querySelectorAll('.mech-part');
- partElements.forEach((partEl, index) => {
- const part = {
- name: partEl.querySelector(`input[name="mech${type}Name_${index}"]`)?.value || '',
- description: partEl.querySelector(`textarea[name="mech${type}Desc_${index}"]`)?.value || '',
- stats: partEl.querySelector(`textarea[name="mech${type}Stats_${index}"]`)?.value || ''
- };
- parts.push(part);
- });
- return parts;
- }
- function collectMechConfig() {
- return {
- name: document.getElementById('mechName')?.value || '',
- description: document.getElementById('mechDescription')?.value || '',
- core: document.getElementById('selectedCore')?.value || '',
- frame: document.getElementById('selectedFrame')?.value || '',
- weapon: document.getElementById('selectedWeapon')?.value || '',
- arm: document.getElementById('selectedArm')?.value || '',
- leg: document.getElementById('selectedLeg')?.value || '',
- head: document.getElementById('selectedHead')?.value || ''
- };
- }
- function loadAllData(data) {
- // Character data
- if (data.characterType) {
- document.querySelector(`input[name="characterType"][value="${data.characterType}"]`).checked = true;
- }
- if (data.characterName) document.getElementById('characterName').value = data.characterName;
- if (data.characterLevel) document.getElementById('characterLevel').value = data.characterLevel;
- if (data.characterExp) document.getElementById('characterExp').value = data.characterExp;
- if (data.characterBackground) document.getElementById('characterBackground').value = data.characterBackground;
- if (data.digimonBackground) document.getElementById('digimonBackground').value = data.digimonBackground;
- if (data.characterRank) document.getElementById('characterRank').value = data.characterRank;
- if (data.characterImage) document.getElementById('characterImage').src = data.characterImage;
- // Handle character type toggle
- const isTamer = data.characterType === 'tamer';
- document.querySelectorAll('.tamer-only').forEach(el => {
- el.style.display = isTamer ? 'block' : 'none';
- });
- document.querySelectorAll('.digimon-only').forEach(el => {
- el.style.display = isTamer ? 'none' : 'block';
- });
- // Update background info
- updateBackgroundInfo();
- // Isekai bonuses
- if (data.isekaiStats) {
- document.querySelectorAll('.isekai-stat-bonus').forEach(checkbox => {
- checkbox.checked = data.isekaiStats.includes(checkbox.value);
- });
- if (data.characterBackground === 'isekai' || data.digimonBackground === 'isekai') {
- applyIsekaiBonus();
- }
- }
- // Stats
- if (data.baseHP) document.getElementById('baseHP').value = data.baseHP;
- if (data.currentHP) document.getElementById('currentHP').value = data.currentHP;
- if (data.baseSP) document.getElementById('baseSP').value = data.baseSP;
- if (data.currentSP) document.getElementById('currentSP').value = data.currentSP;
- if (data.str) document.getElementById('str').value = data.str;
- if (data.def) document.getElementById('def').value = data.def;
- if (data.int) document.getElementById('int').value = data.int;
- if (data.agi) document.getElementById('agi').value = data.agi;
- if (data.wil) document.getElementById('wil').value = data.wil;
- // Partner data
- if (data.partnerName) document.getElementById('partnerName').value = data.partnerName;
- if (data.partnerLevel) document.getElementById('partnerLevel').value = data.partnerLevel;
- if (data.partnerExp) document.getElementById('partnerExp').value = data.partnerExp;
- if (data.partnerType) document.getElementById('partnerType').value = data.partnerType;
- if (data.partnerHumanBackground) document.getElementById('partnerHumanBackground').value = data.partnerHumanBackground;
- if (data.partnerDigimonBackground) document.getElementById('partnerDigimonBackground').value = data.partnerDigimonBackground;
- if (data.partnerRank) document.getElementById('partnerRank').value = data.partnerRank;
- if (data.partnerImage) document.getElementById('partnerImage').src = data.partnerImage;
- // Handle partner type toggle
- const isHuman = data.partnerType === 'tamer';
- document.querySelectorAll('.partner-tamer-only').forEach(el => {
- el.style.display = isHuman ? 'block' : 'none';
- });
- document.querySelectorAll('.partner-digimon-only').forEach(el => {
- el.style.display = isHuman ? 'none' : 'block';
- });
- // Update partner background info
- updatePartnerBackgroundInfo();
- // Partner Isekai bonuses
- if (data.partnerIsekaiStats) {
- document.querySelectorAll('.partner-isekai-stat-bonus').forEach(checkbox => {
- checkbox.checked = data.partnerIsekaiStats.includes(checkbox.value);
- });
- if (data.partnerHumanBackground === 'isekai' || data.partnerDigimonBackground === 'isekai') {
- applyPartnerIsekaiBonus();
- }
- }
- // Partner stats
- if (data.partnerBaseHP) document.getElementById('partnerBaseHP').value = data.partnerBaseHP;
- if (data.partnerCurrentHP) document.getElementById('partnerCurrentHP').value = data.partnerCurrentHP;
- if (data.partnerBaseSP) document.getElementById('partnerBaseSP').value = data.partnerBaseSP;
- if (data.partnerCurrentSP) document.getElementById('partnerCurrentSP').value = data.partnerCurrentSP;
- if (data.partnerStr) document.getElementById('partner-str').value = data.partnerStr;
- if (data.partnerDef) document.getElementById('partner-def').value = data.partnerDef;
- if (data.partnerInt) document.getElementById('partner-int').value = data.partnerInt;
- if (data.partnerAgi) document.getElementById('partner-agi').value = data.partnerAgi;
- if (data.partnerWil) document.getElementById('partner-wil').value = data.partnerWil;
- // Notes
- if (data.itemsNotes) document.getElementById('itemsNotes').value = data.itemsNotes;
- if (data.aspectsNotes) document.getElementById('aspectsNotes').value = data.aspectsNotes;
- if (data.campaignNotes) document.getElementById('campaignNotes').value = data.campaignNotes;
- if (data.classSkillsNotes) document.getElementById('classSkillsNotes').value = data.classSkillsNotes;
- if (data.rulesNotes) document.getElementById('rulesNotes').value = data.rulesNotes;
- // Skills
- if (data.skills) {
- for (const skill in data.skills) {
- const value = data.skills[skill];
- const radio = document.querySelector(`input[name="${skill}"][value="${value}"]`);
- if (radio) radio.checked = true;
- }
- }
- // Partner Skills
- if (data.partnerSkills) {
- for (const skill in data.partnerSkills) {
- const value = data.partnerSkills[skill];
- const radio = document.querySelector(`input[name="${skill}"][value="${value}"]`);
- if (radio) radio.checked = true;
- }
- }
- // Load items
- if (data.items && data.items.length > 0) {
- // Clear existing items
- document.getElementById('itemsContainer').innerHTML = '';
- // Add each item
- data.items.forEach((item, index) => {
- const container = document.getElementById('itemsContainer');
- const itemElement = document.createElement('div');
- itemElement.className = 'item-entry';
- itemElement.innerHTML = `
- <button type="button" class="remove-button" onclick="removeItem(this.parentNode)">Remove</button>
- <div class="form-group">
- <label>Name</label>
- <input type="text" name="itemName_${index}" value="${item.name || ''}">
- </div>
- <div class="form-group">
- <label>Description</label>
- <textarea name="itemDesc_${index}">${item.description || ''}</textarea>
- </div>
- <div class="form-group">
- <label>Effects</label>
- <textarea name="itemEffects_${index}">${item.effects || ''}</textarea>
- </div>
- `;
- container.appendChild(itemElement);
- });
- }
- // Load Digimon Lines
- if (data.digimonLines && data.digimonLines.length > 0) {
- // Clear existing lines
- document.getElementById('digimonLinesContainer').innerHTML = '';
- // Add each evolution line (simplified)
- data.digimonLines.forEach((line, lineIndex) => {
- const container = document.getElementById('digimonLinesContainer');
- const lineElement = document.createElement('div');
- lineElement.className = 'evo-line';
- lineElement.innerHTML = `
- <h3>Evolution Line ${lineIndex + 1}</h3>
- <button type="button" class="remove-button" onclick="removeDigimonLine(this.parentNode)">Remove</button>
- <div class="evo-stage">
- <div class="evo-stage-title">In-Training</div>
- <div class="form-group">
- <label>Name</label>
- <input type="text" name="inTrainingName_${lineIndex}" value="${line.inTraining?.name || ''}">
- </div>
- <div class="form-group">
- <label>Description</label>
- <textarea name="inTrainingDesc_${lineIndex}">${line.inTraining?.description || ''}</textarea>
- </div>
- </div>
- <div id="rookieContainer_${lineIndex}">
- <!-- Rookie evolutions will go here -->
- </div>
- <button type="button" class="add-button" onclick="addEvolution('rookie', ${lineIndex})">Add Rookie Evolution</button>
- `;
- container.appendChild(lineElement);
- // Add rookies if they exist
- if (line.rookies && line.rookies.length > 0) {
- const rookieContainer = document.getElementById(`rookieContainer_${lineIndex}`);
- line.rookies.forEach((rookie, rookieIndex) => {
- const rookieElement = document.createElement('div');
- rookieElement.className = 'evo-stage';
- rookieElement.innerHTML = `
- <div class="evo-stage-title">Rookie</div>
- <button type="button" class="remove-button" onclick="removeEvolution(this.parentNode)">Remove</button>
- <div class="form-group">
- <label>Name</label>
- <input type="text" name="rookieName_${lineIndex}_${rookieIndex}" value="${rookie.name || ''}">
- </div>
- <div class="form-group">
- <label>Description</label>
- <textarea name="rookieDesc_${lineIndex}_${rookieIndex}">${rookie.description || ''}</textarea>
- </div>
- <div id="championContainer_${lineIndex}_${rookieIndex}">
- <!-- Champion evolutions would go here -->
- </div>
- <button type="button" class="add-button" onclick="addEvolution('champion', '${lineIndex}_${rookieIndex}')">Add Champion Evolution</button>
- `;
- rookieContainer.appendChild(rookieElement);
- });
- }
- });
- }
- // Load Mech Parts
- if (data.mechParts) {
- // Load each type of mech part
- loadMechPartsOfType('core', data.mechParts.cores);
- loadMechPartsOfType('frame', data.mechParts.frames);
- loadMechPartsOfType('weapon', data.mechParts.weapons);
- loadMechPartsOfType('arm', data.mechParts.arms);
- loadMechPartsOfType('leg', data.mechParts.legs);
- loadMechPartsOfType('head', data.mechParts.heads);
- // Update dropdowns
- updateMechPartDropdowns('core');
- updateMechPartDropdowns('frame');
- updateMechPartDropdowns('weapon');
- updateMechPartDropdowns('arm');
- updateMechPartDropdowns('leg');
- updateMechPartDropdowns('head');
- }
- // Load Mech Configuration
- if (data.mechConfig) {
- if (data.mechConfig.name) document.getElementById('mechName').value = data.mechConfig.name;
- if (data.mechConfig.description) document.getElementById('mechDescription').value = data.mechConfig.description;
- if (data.mechConfig.core) document.getElementById('selectedCore').value = data.mechConfig.core;
- if (data.mechConfig.frame) document.getElementById('selectedFrame').value = data.mechConfig.frame;
- if (data.mechConfig.weapon) document.getElementById('selectedWeapon').value = data.mechConfig.weapon;
- if (data.mechConfig.arm) document.getElementById('selectedArm').value = data.mechConfig.arm;
- if (data.mechConfig.leg) document.getElementById('selectedLeg').value = data.mechConfig.leg;
- if (data.mechConfig.head) document.getElementById('selectedHead').value = data.mechConfig.head;
- }
- // Apply background bonuses
- applyBackgroundBonuses();
- applyPartnerBackgroundBonuses();
- // Recalculate after loading
- calculateStats();
- calculatePartnerStats();
- calculateNextLevel('characterExp', 'characterLevel', 'characterNextLevel');
- calculateNextLevel('partnerExp', 'partnerLevel', 'partnerNextLevel');
- }
- function loadMechPartsOfType(type, parts) {
- if (!parts || !parts.length) return;
- const container = document.getElementById(`mech${type.charAt(0).toUpperCase() + type.slice(1)}sContainer`);
- if (!container) return;
- // Clear container
- container.innerHTML = '';
- // Add each part
- parts.forEach((part, index) => {
- const partElement = document.createElement('div');
- partElement.className = 'mech-part';
- partElement.innerHTML = `
- <h4>${type.charAt(0).toUpperCase() + type.slice(1)} ${index + 1}</h4>
- <button type="button" class="remove-button" onclick="removeMechPart(this.parentNode)">Remove</button>
- <div class="form-group">
- <label>Name</label>
- <input type="text" name="mech${type}Name_${index}" value="${part.name || ''}">
- </div>
- <div class="form-group">
- <label>Description</label>
- <textarea name="mech${type}Desc_${index}">${part.description || ''}</textarea>
- </div>
- <div class="form-group">
- <label>Stats</label>
- <textarea name="mech${type}Stats_${index}">${part.stats || ''}</textarea>
- </div>
- `;
- container.appendChild(partElement);
- });
- }
- // Initialize with default settings
- updateStatCaps();
- updatePartnerStatCaps();
- // Create initial Digimon lines and Mech parts on load
- window.addEventListener('load', function() {
- // Check if we have saved data first
- const savedData = localStorage.getItem('digimonCharacter');
- if (savedData) {
- loadCharacterSheet();
- } else {
- // Otherwise create empty items
- for (let i = 0; i < 3; i++) {
- addDigimonLine();
- }
- // Add some initial mech parts
- document.getElementById('mechPartType').value = 'core';
- addMechPart();
- document.getElementById('mechPartType').value = 'frame';
- addMechPart();
- document.getElementById('mechPartType').value = 'weapon';
- addMechPart();
- document.getElementById('mechPartType').value = 'arm';
- addMechPart();
- document.getElementById('mechPartType').value = 'leg';
- addMechPart();
- document.getElementById('mechPartType').value = 'head';
- addMechPart();
- // Add an initial item
- addItem();
- }
- });
- function addMove() {
- const container = document.getElementById('movesContainer');
- const moveIndex = container.children.length;
- const move = document.createElement('div');
- move.className = 'move-entry';
- move.innerHTML = `
- <div class="move-header">
- <input type="text" id="moveName_${moveIndex}" placeholder="Move Name" class="move-name-input">
- <div class="move-actions">
- <button type="button" onclick="performMove(${moveIndex})" class="secondary">Use</button>
- <button type="button" onclick="editMove(${moveIndex})" class="secondary">Edit</button>
- <button type="button" onclick="removeMove(this.parentNode.parentNode.parentNode)" class="remove-button">Remove</button>
- </div>
- </div>
- <div class="move-details">
- <div class="form-group">
- <label for="moveType_${moveIndex}">Type:</label>
- <select id="moveType_${moveIndex}" onchange="updateMoveFields(${moveIndex})">
- <option value="attack">Attack</option>
- <option value="healing">Healing</option>
- <option value="buff">Buff/Debuff</option>
- <option value="utility">Utility</option>
- </select>
- </div>
- <div class="form-group">
- <label for="moveSPCost_${moveIndex}">SP Cost:</label>
- <input type="number" id="moveSPCost_${moveIndex}" min="0" value="5">
- </div>
- <div class="form-group move-attack-field">
- <label for="moveDamage_${moveIndex}">Base Damage:</label>
- <input type="number" id="moveDamage_${moveIndex}" min="0" value="10">
- </div>
- <div class="form-group move-attack-field">
- <label for="moveAttribute_${moveIndex}">Attribute:</label>
- <select id="moveAttribute_${moveIndex}">
- <option value="str">STR</option>
- <option value="int">INT</option>
- <option value="agi">AGI</option>
- <option value="wil">WIL</option>
- </select>
- </div>
- <div class="form-group move-healing-field" style="display: none;">
- <label for="moveHealing_${moveIndex}">Healing Amount:</label>
- <input type="number" id="moveHealing_${moveIndex}" min="0" value="10">
- </div>
- <div class="form-group move-buff-field" style="display: none;">
- <label for="moveBuffStat_${moveIndex}">Buff Stat:</label>
- <select id="moveBuffStat_${moveIndex}">
- <option value="str">STR</option>
- <option value="def">DEF</option>
- <option value="int">INT</option>
- <option value="agi">AGI</option>
- <option value="wil">WIL</option>
- </select>
- </div>
- <div class="form-group move-buff-field" style="display: none;">
- <label for="moveBuffAmount_${moveIndex}">Buff Amount:</label>
- <input type="number" id="moveBuffAmount_${moveIndex}" value="2">
- </div>
- <div class="form-group move-buff-field" style="display: none;">
- <label for="moveBuffDuration_${moveIndex}">Duration (rounds):</label>
- <input type="number" id="moveBuffDuration_${moveIndex}" min="1" value="3">
- </div>
- <div class="form-group move-description">
- <label for="moveDescription_${moveIndex}">Description:</label>
- <textarea id="moveDescription_${moveIndex}" rows="3" placeholder="Describe what the move does..."></textarea>
- </div>
- </div>
- <div class="move-display">
- <div id="moveDisplay_${moveIndex}"></div>
- </div>
- `;
- container.appendChild(move);
- updateMoveFields(moveIndex);
- updateMoveSelector();
- // Auto-save when adding a new move
- if (autoSaveEnabled) saveCharacterSheet();
- }
- function updateMoveFields(moveIndex) {
- const moveType = document.getElementById(`moveType_${moveIndex}`).value;
- // Hide all type-specific fields
- document.querySelectorAll(`#movesContainer .move-entry:nth-child(${moveIndex + 1}) .move-attack-field`).forEach(el => {
- el.style.display = 'none';
- });
- document.querySelectorAll(`#movesContainer .move-entry:nth-child(${moveIndex + 1}) .move-healing-field`).forEach(el => {
- el.style.display = 'none';
- });
- document.querySelectorAll(`#movesContainer .move-entry:nth-child(${moveIndex + 1}) .move-buff-field`).forEach(el => {
- el.style.display = 'none';
- });
- // Show relevant fields based on move type
- switch(moveType) {
- case 'attack':
- document.querySelectorAll(`#movesContainer .move-entry:nth-child(${moveIndex + 1}) .move-attack-field`).forEach(el => {
- el.style.display = 'block';
- });
- break;
- case 'healing':
- document.querySelectorAll(`#movesContainer .move-entry:nth-child(${moveIndex + 1}) .move-healing-field`).forEach(el => {
- el.style.display = 'block';
- });
- break;
- case 'buff':
- document.querySelectorAll(`#movesContainer .move-entry:nth-child(${moveIndex + 1}) .move-buff-field`).forEach(el => {
- el.style.display = 'block';
- });
- break;
- }
- updateMoveDisplay(moveIndex);
- }
- function updateMoveDisplay(moveIndex) {
- const moveName = document.getElementById(`moveName_${moveIndex}`).value || 'Unnamed Move';
- const moveType = document.getElementById(`moveType_${moveIndex}`).value;
- const spCost = document.getElementById(`moveSPCost_${moveIndex}`).value;
- let displayHTML = `<div class="move-title">${moveName}`;
- displayHTML += `<span class="move-type ${moveType}">${moveType.charAt(0).toUpperCase() + moveType.slice(1)}</span>`;
- displayHTML += `<span class="move-sp-cost">${spCost} SP</span></div>`;
- document.getElementById(`moveDisplay_${moveIndex}`).innerHTML = displayHTML;
- }
- function removeMove(moveElement) {
- moveElement.remove();
- updateMoveSelector();
- if (autoSaveEnabled) saveCharacterSheet();
- }
- function editMove(moveIndex) {
- // Toggle visibility of the move details
- const moveEntry = document.querySelector(`#movesContainer .move-entry:nth-child(${moveIndex + 1})`);
- const moveDetails = moveEntry.querySelector('.move-details');
- if (moveDetails.style.display === 'none') {
- moveDetails.style.display = 'grid';
- } else {
- moveDetails.style.display = 'none';
- }
- updateMoveDisplay(moveIndex);
- if (autoSaveEnabled) saveCharacterSheet();
- }
- function performMove(moveIndex) {
- const moveEntry = document.querySelector(`#movesContainer .move-entry:nth-child(${moveIndex + 1})`);
- const moveName = document.getElementById(`moveName_${moveIndex}`).value;
- const moveType = document.getElementById(`moveType_${moveIndex}`).value;
- const spCost = parseInt(document.getElementById(`moveSPCost_${moveIndex}`).value) || 0;
- // Check if have enough SP
- const currentSP = parseInt(document.getElementById('currentSP').value) || 0;
- if (currentSP < spCost) {
- showNotification(`Not enough SP to use ${moveName}!`, true);
- return;
- }
- // Deduct SP
- document.getElementById('currentSP').value = currentSP - spCost;
- // Visual effect
- moveEntry.classList.add('performing');
- setTimeout(() => {
- moveEntry.classList.remove('performing');
- }, 1000);
- // Effect based on move type
- let resultMessage = '';
- switch(moveType) {
- case 'attack':
- const baseDamage = parseInt(document.getElementById(`moveDamage_${moveIndex}`).value) || 0;
- const attribute = document.getElementById(`moveAttribute_${moveIndex}`).value;
- const statBonus = parseInt(document.getElementById(attribute).value) || 0;
- // Random factor (0.8 to 1.2)
- const variance = 0.8 + Math.random() * 0.4;
- const damage = Math.floor((baseDamage + statBonus) * variance);
- resultMessage = `${moveName} deals ${damage} damage! (Base: ${baseDamage}, ${attribute.toUpperCase()}: +${statBonus})`;
- break;
- case 'healing':
- const healAmount = parseInt(document.getElementById(`moveHealing_${moveIndex}`).value) || 0;
- const intBonus = parseInt(document.getElementById('int').value) || 0;
- // Random factor (0.9 to 1.1)
- const healVariance = 0.9 + Math.random() * 0.2;
- const healing = Math.floor((healAmount + intBonus) * healVariance);
- // Apply healing
- const currentHP = parseInt(document.getElementById('currentHP').value) || 0;
- const maxHP = parseInt(document.getElementById('hp-value').textContent) || 0;
- const newHP = Math.min(currentHP + healing, maxHP);
- document.getElementById('currentHP').value = newHP;
- resultMessage = `${moveName} heals for ${healing} HP! (Current HP: ${newHP}/${maxHP})`;
- break;
- case 'buff':
- const buffStat = document.getElementById(`moveBuffStat_${moveIndex}`).value;
- const buffAmount = parseInt(document.getElementById(`moveBuffAmount_${moveIndex}`).value) || 0;
- const duration = parseInt(document.getElementById(`moveBuffDuration_${moveIndex}`).value) || 1;
- resultMessage = `${moveName} buffs ${buffStat.toUpperCase()} by +${buffAmount} for ${duration} rounds!`;
- // In a real game, you'd track this buff and its duration
- break;
- case 'utility':
- resultMessage = `${moveName} activated! (Utility effect)`;
- break;
- }
- // Display result
- document.getElementById('rollResult').textContent = resultMessage;
- // Switch to character tab to see result
- document.querySelector('.tab[data-tab="character-tab"]').click();
- if (autoSaveEnabled) saveCharacterSheet();
- }
- function updateMoveSelector() {
- const moveSelector = document.getElementById('calcMoveName');
- // Clear current options except the first one
- while (moveSelector.options.length > 1) {
- moveSelector.remove(1);
- }
- // Add an option for each move
- const moveElements = document.querySelectorAll('#movesContainer .move-entry');
- moveElements.forEach((moveEl, index) => {
- const nameInput = moveEl.querySelector(`input[id^="moveName_"]`);
- const name = nameInput.value || `Move ${index + 1}`;
- const option = document.createElement('option');
- option.value = index;
- option.textContent = name;
- moveSelector.appendChild(option);
- });
- }
- function filterMoves() {
- const filterValue = document.getElementById('moveFilter').value;
- const moveElements = document.querySelectorAll('#movesContainer .move-entry');
- moveElements.forEach((moveEl, index) => {
- const moveType = document.getElementById(`moveType_${index}`).value;
- if (filterValue === 'all' || moveType === filterValue) {
- moveEl.style.display = 'block';
- } else {
- moveEl.style.display = 'none';
- }
- });
- }
- function calculateMoveEffect() {
- const moveIndex = document.getElementById('calcMoveName').value;
- if (!moveIndex) {
- document.getElementById('moveCalcResult').textContent = 'Please select a move first';
- return;
- }
- const moveName = document.getElementById(`moveName_${moveIndex}`).value;
- const moveType = document.getElementById(`moveType_${moveIndex}`).value;
- const targetDefense = parseInt(document.getElementById('calcTarget').value) || 0;
- let result = '';
- if (moveType === 'attack') {
- const baseDamage = parseInt(document.getElementById(`moveDamage_${moveIndex}`).value) || 0;
- const attribute = document.getElementById(`moveAttribute_${moveIndex}`).value;
- const statBonus = parseInt(document.getElementById(attribute).value) || 0;
- // Calculate average damage with defense reduction
- const minDamage = Math.max(Math.floor((baseDamage + statBonus) * 0.8) - targetDefense, 1);
- const maxDamage = Math.max(Math.floor((baseDamage + statBonus) * 1.2) - targetDefense, 1);
- const avgDamage = Math.floor((minDamage + maxDamage) / 2);
- result = `${moveName} vs DEF ${targetDefense}:
- Damage range: ${minDamage}-${maxDamage}
- Average damage: ${avgDamage}
- Attribute: ${attribute.toUpperCase()} (+${statBonus})`;
- } else if (moveType === 'healing') {
- const healAmount = parseInt(document.getElementById(`moveHealing_${moveIndex}`).value) || 0;
- const intBonus = parseInt(document.getElementById('int').value) || 0;
- const minHeal = Math.floor((healAmount + intBonus) * 0.9);
- const maxHeal = Math.floor((healAmount + intBonus) * 1.1);
- const avgHeal = Math.floor((minHeal + maxHeal) / 2);
- result = `${moveName} healing:
- Heal range: ${minHeal}-${maxHeal}
- Average healing: ${avgHeal}
- INT bonus: +${intBonus}`;
- } else if (moveType === 'buff') {
- const buffStat = document.getElementById(`moveBuffStat_${moveIndex}`).value;
- const buffAmount = parseInt(document.getElementById(`moveBuffAmount_${moveIndex}`).value) || 0;
- const duration = parseInt(document.getElementById(`moveBuffDuration_${moveIndex}`).value) || 1;
- result = `${moveName} buff effect:
- Stat: ${buffStat.toUpperCase()}
- Bonus: +${buffAmount}
- Duration: ${duration} rounds`;
- } else {
- result = `${moveName} (Utility)
- See move description for details.`;
- }
- document.getElementById('moveCalcResult').textContent = result;
- }
- // Function to collect moves data for saving
- function collectMoves() {
- const moves = [];
- const moveElements = document.querySelectorAll('#movesContainer .move-entry');
- moveElements.forEach((el, index) => {
- const moveType = document.getElementById(`moveType_${index}`)?.value;
- const move = {
- name: document.getElementById(`moveName_${index}`)?.value || '',
- type: moveType || 'attack',
- spCost: document.getElementById(`moveSPCost_${index}`)?.value || '0',
- description: document.getElementById(`moveDescription_${index}`)?.value || ''
- };
- // Add type-specific properties
- if (moveType === 'attack') {
- move.damage = document.getElementById(`moveDamage_${index}`)?.value || '0';
- move.attribute = document.getElementById(`moveAttribute_${index}`)?.value || 'str';
- } else if (moveType === 'healing') {
- move.healing = document.getElementById(`moveHealing_${index}`)?.value || '0';
- } else if (moveType === 'buff') {
- move.buffStat = document.getElementById(`moveBuffStat_${index}`)?.value || 'str';
- move.buffAmount = document.getElementById(`moveBuffAmount_${index}`)?.value || '0';
- move.duration = document.getElementById(`moveBuffDuration_${index}`)?.value || '1';
- }
- moves.push(move);
- });
- return moves;
- }
- // Add a notification function if it doesn't exist
- function showNotification(message, isError = false) {
- // If you already have a notification system, you can remove this function
- // Create notification element if it doesn't exist
- let notificationArea = document.getElementById('notificationArea');
- if (!notificationArea) {
- notificationArea = document.createElement('div');
- notificationArea.id = 'notificationArea';
- notificationArea.style.position = 'fixed';
- notificationArea.style.bottom = '20px';
- notificationArea.style.right = '20px';
- notificationArea.style.zIndex = '1000';
- document.body.appendChild(notificationArea);
- }
- const notification = document.createElement('div');
- notification.className = 'notification ' + (isError ? 'error' : 'success');
- notification.style.padding = '10px 15px';
- notification.style.borderRadius = '5px';
- notification.style.marginTop = '10px';
- notification.style.background = isError ? 'rgba(255, 100, 100, 0.9)' : 'rgba(100, 255, 100, 0.9)';
- notification.style.color = 'white';
- notification.style.boxShadow = '0 2px 10px rgba(0, 0, 0, 0.2)';
- notification.textContent = message;
- notificationArea.appendChild(notification);
- // Auto-remove after 3 seconds
- setTimeout(() => {
- notification.style.opacity = '0';
- notification.style.transition = 'opacity 0.5s';
- setTimeout(() => {
- notification.remove();
- }, 500);
- }, 3000);
- }
- function addPartnerMove() {
- const container = document.getElementById('partnerMovesContainer');
- const moveIndex = container.children.length;
- const move = document.createElement('div');
- move.className = 'move-entry partner-move';
- move.innerHTML = `
- <div class="move-header">
- <input type="text" id="partnerMoveName_${moveIndex}" placeholder="Move Name" class="move-name-input">
- <div class="move-actions">
- <button type="button" onclick="performPartnerMove(${moveIndex})" class="secondary">Use</button>
- <button type="button" onclick="editPartnerMove(${moveIndex})" class="secondary">Edit</button>
- <button type="button" onclick="removePartnerMove(this.parentNode.parentNode.parentNode)" class="remove-button">Remove</button>
- </div>
- </div>
- <div class="move-details">
- <div class="form-group">
- <label for="partnerMoveType_${moveIndex}">Type:</label>
- <select id="partnerMoveType_${moveIndex}" onchange="updatePartnerMoveFields(${moveIndex})">
- <option value="attack">Attack</option>
- <option value="healing">Healing</option>
- <option value="buff">Buff/Debuff</option>
- <option value="utility">Utility</option>
- </select>
- </div>
- <div class="form-group">
- <label for="partnerMoveSPCost_${moveIndex}">SP Cost:</label>
- <input type="number" id="partnerMoveSPCost_${moveIndex}" min="0" value="5">
- </div>
- <div class="form-group move-attack-field">
- <label for="partnerMoveDamage_${moveIndex}">Base Damage:</label>
- <input type="number" id="partnerMoveDamage_${moveIndex}" min="0" value="10">
- </div>
- <div class="form-group move-attack-field">
- <label for="partnerMoveAttribute_${moveIndex}">Attribute:</label>
- <select id="partnerMoveAttribute_${moveIndex}">
- <option value="str">STR</option>
- <option value="int">INT</option>
- <option value="agi">AGI</option>
- <option value="wil">WIL</option>
- </select>
- </div>
- <div class="form-group move-healing-field" style="display: none;">
- <label for="partnerMoveHealing_${moveIndex}">Healing Amount:</label>
- <input type="number" id="partnerMoveHealing_${moveIndex}" min="0" value="10">
- </div>
- <div class="form-group move-buff-field" style="display: none;">
- <label for="partnerMoveBuffStat_${moveIndex}">Buff Stat:</label>
- <select id="partnerMoveBuffStat_${moveIndex}">
- <option value="str">STR</option>
- <option value="def">DEF</option>
- <option value="int">INT</option>
- <option value="agi">AGI</option>
- <option value="wil">WIL</option>
- </select>
- </div>
- <div class="form-group move-buff-field" style="display: none;">
- <label for="partnerMoveBuffAmount_${moveIndex}">Buff Amount:</label>
- <input type="number" id="partnerMoveBuffAmount_${moveIndex}" value="2">
- </div>
- <div class="form-group move-buff-field" style="display: none;">
- <label for="partnerMoveBuffDuration_${moveIndex}">Duration (rounds):</label>
- <input type="number" id="partnerMoveBuffDuration_${moveIndex}" min="1" value="3">
- </div>
- <div class="form-group move-description">
- <label for="partnerMoveDescription_${moveIndex}">Description:</label>
- <textarea id="partnerMoveDescription_${moveIndex}" rows="3" placeholder="Describe what the move does..."></textarea>
- </div>
- </div>
- <div class="move-display">
- <div id="partnerMoveDisplay_${moveIndex}"></div>
- </div>
- `;
- container.appendChild(move);
- updatePartnerMoveFields(moveIndex);
- updatePartnerMoveSelector();
- // Auto-save when adding a new move
- if (autoSaveEnabled) saveCharacterSheet();
- }
- function updatePartnerMoveFields(moveIndex) {
- const moveType = document.getElementById(`partnerMoveType_${moveIndex}`).value;
- // Hide all type-specific fields
- document.querySelectorAll(`#partnerMovesContainer .move-entry:nth-child(${moveIndex + 1}) .move-attack-field`).forEach(el => {
- el.style.display = 'none';
- });
- document.querySelectorAll(`#partnerMovesContainer .move-entry:nth-child(${moveIndex + 1}) .move-healing-field`).forEach(el => {
- el.style.display = 'none';
- });
- document.querySelectorAll(`#partnerMovesContainer .move-entry:nth-child(${moveIndex + 1}) .move-buff-field`).forEach(el => {
- el.style.display = 'none';
- });
- // Show relevant fields based on move type
- switch(moveType) {
- case 'attack':
- document.querySelectorAll(`#partnerMovesContainer .move-entry:nth-child(${moveIndex + 1}) .move-attack-field`).forEach(el => {
- el.style.display = 'block';
- });
- break;
- case 'healing':
- document.querySelectorAll(`#partnerMovesContainer .move-entry:nth-child(${moveIndex + 1}) .move-healing-field`).forEach(el => {
- el.style.display = 'block';
- });
- break;
- case 'buff':
- document.querySelectorAll(`#partnerMovesContainer .move-entry:nth-child(${moveIndex + 1}) .move-buff-field`).forEach(el => {
- el.style.display = 'block';
- });
- break;
- }
- updatePartnerMoveDisplay(moveIndex);
- }
- function updatePartnerMoveDisplay(moveIndex) {
- const moveName = document.getElementById(`partnerMoveName_${moveIndex}`).value || 'Unnamed Move';
- const moveType = document.getElementById(`partnerMoveType_${moveIndex}`).value;
- const spCost = document.getElementById(`partnerMoveSPCost_${moveIndex}`).value;
- let displayHTML = `<div class="move-title">${moveName}`;
- displayHTML += `<span class="move-type ${moveType}">${moveType.charAt(0).toUpperCase() + moveType.slice(1)}</span>`;
- displayHTML += `<span class="move-sp-cost">${spCost} SP</span></div>`;
- document.getElementById(`partnerMoveDisplay_${moveIndex}`).innerHTML = displayHTML;
- }
- function removePartnerMove(moveElement) {
- moveElement.remove();
- updatePartnerMoveSelector();
- if (autoSaveEnabled) saveCharacterSheet();
- }
- function editPartnerMove(moveIndex) {
- // Toggle visibility of the move details
- const moveEntry = document.querySelector(`#partnerMovesContainer .move-entry:nth-child(${moveIndex + 1})`);
- const moveDetails = moveEntry.querySelector('.move-details');
- if (moveDetails.style.display === 'none') {
- moveDetails.style.display = 'grid';
- } else {
- moveDetails.style.display = 'none';
- }
- updatePartnerMoveDisplay(moveIndex);
- if (autoSaveEnabled) saveCharacterSheet();
- }
- function performPartnerMove(moveIndex) {
- const moveEntry = document.querySelector(`#partnerMovesContainer .move-entry:nth-child(${moveIndex + 1})`);
- const moveName = document.getElementById(`partnerMoveName_${moveIndex}`).value;
- const moveType = document.getElementById(`partnerMoveType_${moveIndex}`).value;
- const spCost = parseInt(document.getElementById(`partnerMoveSPCost_${moveIndex}`).value) || 0;
- // Check if have enough SP
- const currentSP = parseInt(document.getElementById('partnerCurrentSP').value) || 0;
- if (currentSP < spCost) {
- showNotification(`Not enough SP for your partner to use ${moveName}!`, true);
- return;
- }
- // Deduct SP
- document.getElementById('partnerCurrentSP').value = currentSP - spCost;
- // Visual effect
- moveEntry.classList.add('performing');
- setTimeout(() => {
- moveEntry.classList.remove('performing');
- }, 1000);
- // Effect based on move type
- let resultMessage = '';
- switch(moveType) {
- case 'attack':
- const baseDamage = parseInt(document.getElementById(`partnerMoveDamage_${moveIndex}`).value) || 0;
- const attribute = document.getElementById(`partnerMoveAttribute_${moveIndex}`).value;
- const statBonus = parseInt(document.getElementById(`partner_${attribute}`).value) || 0;
- // Random factor (0.8 to 1.2)
- const variance = 0.8 + Math.random() * 0.4;
- const damage = Math.floor((baseDamage + statBonus) * variance);
- resultMessage = `Partner's ${moveName} deals ${damage} damage! (Base: ${baseDamage}, ${attribute.toUpperCase()}: +${statBonus})`;
- break;
- case 'healing':
- const healAmount = parseInt(document.getElementById(`partnerMoveHealing_${moveIndex}`).value) || 0;
- const intBonus = parseInt(document.getElementById('partner_int').value) || 0;
- // Random factor (0.9 to 1.1)
- const healVariance = 0.9 + Math.random() * 0.2;
- const healing = Math.floor((healAmount + intBonus) * healVariance);
- // Apply healing - check whether we're healing the partner or the character
- const healTarget = document.querySelector(`input[name="healTarget_${moveIndex}"]:checked`)?.value || 'partner';
- if (healTarget === 'partner') {
- const currentHP = parseInt(document.getElementById('partnerCurrentHP').value) || 0;
- const maxHP = parseInt(document.getElementById('partner-hp-value').textContent) || 0;
- const newHP = Math.min(currentHP + healing, maxHP);
- document.getElementById('partnerCurrentHP').value = newHP;
- resultMessage = `Partner's ${moveName} heals itself for ${healing} HP! (Current HP: ${newHP}/${maxHP})`;
- } else {
- const currentHP = parseInt(document.getElementById('currentHP').value) || 0;
- const maxHP = parseInt(document.getElementById('hp-value').textContent) || 0;
- const newHP = Math.min(currentHP + healing, maxHP);
- document.getElementById('currentHP').value = newHP;
- resultMessage = `Partner's ${moveName} heals you for ${healing} HP! (Your Current HP: ${newHP}/${maxHP})`;
- }
- break;
- case 'buff':
- const buffStat = document.getElementById(`partnerMoveBuffStat_${moveIndex}`).value;
- const buffAmount = parseInt(document.getElementById(`partnerMoveBuffAmount_${moveIndex}`).value) || 0;
- const duration = parseInt(document.getElementById(`partnerMoveBuffDuration_${moveIndex}`).value) || 1;
- resultMessage = `Partner's ${moveName} buffs ${buffStat.toUpperCase()} by +${buffAmount} for ${duration} rounds!`;
- break;
- case 'utility':
- resultMessage = `Partner's ${moveName} activated! (Utility effect)`;
- break;
- }
- // Display result
- document.getElementById('rollResult').textContent = resultMessage;
- // Switch to partner tab to see result
- document.querySelector('.tab[data-tab="partner-tab"]').click();
- if (autoSaveEnabled) saveCharacterSheet();
- }
- function updatePartnerMoveSelector() {
- const moveSelector = document.getElementById('calcPartnerMoveName');
- // Clear current options except the first one
- while (moveSelector.options.length > 1) {
- moveSelector.remove(1);
- }
- // Add an option for each move
- const moveElements = document.querySelectorAll('#partnerMovesContainer .move-entry');
- moveElements.forEach((moveEl, index) => {
- const nameInput = moveEl.querySelector(`input[id^="partnerMoveName_"]`);
- const name = nameInput.value || `Move ${index + 1}`;
- const option = document.createElement('option');
- option.value = index;
- option.textContent = name;
- moveSelector.appendChild(option);
- });
- }
- function filterPartnerMoves() {
- const filterValue = document.getElementById('partnerMoveFilter').value;
- const moveElements = document.querySelectorAll('#partnerMovesContainer .move-entry');
- moveElements.forEach((moveEl, index) => {
- const moveType = document.getElementById(`partnerMoveType_${index}`).value;
- if (filterValue === 'all' || moveType === filterValue) {
- moveEl.style.display = 'block';
- } else {
- moveEl.style.display = 'none';
- }
- });
- }
- function calculatePartnerMoveEffect() {
- const moveIndex = document.getElementById('calcPartnerMoveName').value;
- if (!moveIndex) {
- document.getElementById('partnerMoveCalcResult').textContent = 'Please select a move first';
- return;
- }
- const moveName = document.getElementById(`partnerMoveName_${moveIndex}`).value;
- const moveType = document.getElementById(`partnerMoveType_${moveIndex}`).value;
- const targetDefense = parseInt(document.getElementById('calcPartnerTarget').value) || 0;
- let result = '';
- if (moveType === 'attack') {
- const baseDamage = parseInt(document.getElementById(`partnerMoveDamage_${moveIndex}`).value) || 0;
- const attribute = document.getElementById(`partnerMoveAttribute_${moveIndex}`).value;
- const statBonus = parseInt(document.getElementById(`partner_${attribute}`).value) || 0;
- // Calculate average damage with defense reduction
- const minDamage = Math.max(Math.floor((baseDamage + statBonus) * 0.8) - targetDefense, 1);
- const maxDamage = Math.max(Math.floor((baseDamage + statBonus) * 1.2) - targetDefense, 1);
- const avgDamage = Math.floor((minDamage + maxDamage) / 2);
- result = `${moveName} vs DEF ${targetDefense}:
- Damage range: ${minDamage}-${maxDamage}
- Average damage: ${avgDamage}
- Attribute: ${attribute.toUpperCase()} (+${statBonus})`;
- } else if (moveType === 'healing') {
- const healAmount = parseInt(document.getElementById(`partnerMoveHealing_${moveIndex}`).value) || 0;
- const intBonus = parseInt(document.getElementById('partner_int').value) || 0;
- const minHeal = Math.floor((healAmount + intBonus) * 0.9);
- const maxHeal = Math.floor((healAmount + intBonus) * 1.1);
- const avgHeal = Math.floor((minHeal + maxHeal) / 2);
- result = `${moveName} healing:
- Heal range: ${minHeal}-${maxHeal}
- Average healing: ${avgHeal}
- INT bonus: +${intBonus}`;
- } else if (moveType === 'buff') {
- const buffStat = document.getElementById(`partnerMoveBuffStat_${moveIndex}`).value;
- const buffAmount = parseInt(document.getElementById(`partnerMoveBuffAmount_${moveIndex}`).value) || 0;
- const duration = parseInt(document.getElementById(`partnerMoveBuffDuration_${moveIndex}`).value) || 1;
- result = `${moveName} buff effect:
- Stat: ${buffStat.toUpperCase()}
- Bonus: +${buffAmount}
- Duration: ${duration} rounds`;
- } else {
- result = `${moveName} (Utility)
- See move description for details.`;
- }
- document.getElementById('partnerMoveCalcResult').textContent = result;
- }
- // Function to collect partner moves data for saving
- function collectPartnerMoves() {
- const moves = [];
- const moveElements = document.querySelectorAll('#partnerMovesContainer .move-entry');
- moveElements.forEach((el, index) => {
- const moveType = document.getElementById(`partnerMoveType_${index}`)?.value;
- const move = {
- name: document.getElementById(`partnerMoveName_${index}`)?.value || '',
- type: moveType || 'attack',
- spCost: document.getElementById(`partnerMoveSPCost_${index}`)?.value || '0',
- description: document.getElementById(`partnerMoveDescription_${index}`)?.value || ''
- };
- // Add type-specific properties
- if (moveType === 'attack') {
- move.damage = document.getElementById(`partnerMoveDamage_${index}`)?.value || '0';
- move.attribute = document.getElementById(`partnerMoveAttribute_${index}`)?.value || 'str';
- } else if (moveType === 'healing') {
- move.healing = document.getElementById(`partnerMoveHealing_${index}`)?.value || '0';
- } else if (moveType === 'buff') {
- move.buffStat = document.getElementById(`partnerMoveBuffStat_${index}`)?.value || 'str';
- move.buffAmount = document.getElementById(`partnerMoveBuffAmount_${index}`)?.value || '0';
- move.duration = document.getElementById(`partnerMoveBuffDuration_${index}`)?.value || '1';
- }
- moves.push(move);
- });
- return moves;
- }
- </script>
- </body>
- </html>
Add Comment
Please, Sign In to add comment