diff --git a/css/fvtt-prism-rpg.css b/css/fvtt-prism-rpg.css index 3261d6f..34c2800 100644 --- a/css/fvtt-prism-rpg.css +++ b/css/fvtt-prism-rpg.css @@ -2669,44 +2669,6 @@ i.prismrpg { .prismrpg .vulnerability-content input[type="checkbox"]:checked::after { color: rgba(0, 0, 0, 0.1); } -.prismrpg.fortune img { - border: 0px; -} -.prismrpg.fortune .intro-chat { - border-radius: 20px; - display: flex; - flex-direction: row; -} -.prismrpg.fortune .intro-chat .intro-img { - padding: 5px; - width: 80px; - align-self: center; -} -.prismrpg.fortune .intro-chat .intro-right { - display: flex; - flex-direction: column; -} -.prismrpg.fortune .intro-chat .intro-right .introText { - font-family: var(--font-secondary); - font-size: calc(var(--font-size-standard) * 1.2); - width: 210px; - text-align: center; -} -.prismrpg.fortune .button.control, -.prismrpg.fortune .fortune-accepted { - display: flex; - justify-content: center; - align-items: center; - font-size: calc(var(--font-size-standard) * 1.3); -} -.prismrpg.ask-roll { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-family: var(--font-secondary); - font-size: calc(var(--font-size-standard) * 1.2); -} .prismrpg .equipment-content { font-family: var(--font-primary); font-size: calc(var(--font-size-standard) * 1); @@ -3700,6 +3662,446 @@ i.prismrpg { gap: 1rem; align-items: flex-start; } +.chat-log .message-content .prismrpg-chat-card { + font-family: var(--font-primary); + border-radius: 6px; + overflow: hidden; + background: linear-gradient(135deg, rgba(0, 0, 0, 0.05) 0%, rgba(0, 0, 0, 0.02) 100%); + border: 1px solid rgba(0, 0, 0, 0.2); + margin: 2px 0; +} +.chat-log .message-content .prismrpg-chat-card .chat-header { + display: flex; + gap: 8px; + padding: 6px 8px; + background: linear-gradient(135deg, #2c2c2c 0%, #1a1a1a 100%); + border-bottom: 1px solid #444; +} +.chat-log .message-content .prismrpg-chat-card .chat-header .chat-portrait { + flex-shrink: 0; + width: 36px; + height: 36px; + border-radius: 50%; + overflow: hidden; + border: 2px solid #666; + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4); +} +.chat-log .message-content .prismrpg-chat-card .chat-header .chat-portrait img { + width: 100%; + height: 100%; + object-fit: cover; +} +.chat-log .message-content .prismrpg-chat-card .chat-header .chat-title { + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; + gap: 2px; +} +.chat-log .message-content .prismrpg-chat-card .chat-header .chat-title .actor-name { + font-weight: bold; + font-size: 0.95em; + color: #e0e0e0; + text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5); + line-height: 1.1; +} +.chat-log .message-content .prismrpg-chat-card .chat-header .chat-title .roll-name { + font-size: 0.8em; + color: #aaa; + line-height: 1.1; +} +.chat-log .message-content .prismrpg-chat-card .chat-header .chat-title .roll-type-badge { + display: inline-block; + padding: 1px 6px; + border-radius: 10px; + font-size: 0.7em; + font-weight: bold; + text-transform: uppercase; + margin-top: 2px; + width: fit-content; +} +.chat-log .message-content .prismrpg-chat-card .chat-header .chat-title .roll-type-badge.attack { + background: linear-gradient(135deg, #c41e3a 0%, #8b0000 100%); + color: white; + box-shadow: 0 1px 2px rgba(196, 30, 58, 0.4); +} +.chat-log .message-content .prismrpg-chat-card .chat-header .chat-title .bad-result { + color: #ff6b6b; + font-size: 0.75em; + margin-top: 1px; +} +.chat-log .message-content .prismrpg-chat-card .chat-content { + padding: 6px 8px; + display: flex; + flex-direction: column; + gap: 6px; +} +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card { + background: rgba(255, 255, 255, 0.05); + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 4px; + padding: 6px; +} +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .weapon-header, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .weapon-header, +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .spell-header, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .spell-header { + display: flex; + align-items: center; + gap: 6px; + margin-bottom: 4px; + padding-bottom: 4px; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); +} +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .weapon-header .weapon-name, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .weapon-header .weapon-name, +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .spell-header .weapon-name, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .spell-header .weapon-name, +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .weapon-header .spell-name, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .weapon-header .spell-name, +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .spell-header .spell-name, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .spell-header .spell-name { + font-size: 0.95em; + color: #d4af37; +} +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .weapon-header .badge, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .weapon-header .badge, +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .spell-header .badge, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .spell-header .badge { + padding: 1px 4px; + border-radius: 3px; + font-size: 0.65em; + font-weight: bold; + text-transform: uppercase; +} +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .weapon-header .badge.implement, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .weapon-header .badge.implement, +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .spell-header .badge.implement, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .spell-header .badge.implement { + background: #4a5cf7; + color: white; +} +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .weapon-header .badge.upcast, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .weapon-header .badge.upcast, +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .spell-header .badge.upcast, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .spell-header .badge.upcast { + background: #9b59b6; + color: white; +} +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .weapon-header .attribute-used, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .weapon-header .attribute-used, +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .spell-header .attribute-used, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .spell-header .attribute-used { + font-size: 0.75em; + color: #999; + font-style: italic; +} +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .weapon-stats, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .weapon-stats, +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .spell-stats, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .spell-stats { + display: flex; + flex-wrap: wrap; + gap: 4px; +} +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .weapon-stats .stat-item, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .weapon-stats .stat-item, +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .spell-stats .stat-item, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .spell-stats .stat-item { + display: flex; + align-items: center; + gap: 3px; + padding: 2px 6px; + background: rgba(0, 0, 0, 0.2); + border-radius: 3px; + font-size: 0.75em; +} +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .weapon-stats .stat-item i, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .weapon-stats .stat-item i, +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .spell-stats .stat-item i, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .spell-stats .stat-item i { + color: #888; + font-size: 0.85em; +} +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .weapon-stats .stat-item.apc, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .weapon-stats .stat-item.apc, +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .spell-stats .stat-item.apc, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .spell-stats .stat-item.apc { + background: rgba(255, 193, 7, 0.2); + border: 1px solid rgba(255, 193, 7, 0.4); +} +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .weapon-stats .stat-item.apc i, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .weapon-stats .stat-item.apc i, +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .spell-stats .stat-item.apc i, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .spell-stats .stat-item.apc i { + color: #ffc107; +} +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .weapon-stats .stat-item.damage, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .weapon-stats .stat-item.damage, +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .spell-stats .stat-item.damage, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .spell-stats .stat-item.damage { + background: rgba(244, 67, 54, 0.2); + border: 1px solid rgba(244, 67, 54, 0.4); +} +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .weapon-stats .stat-item.damage i, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .weapon-stats .stat-item.damage i, +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .spell-stats .stat-item.damage i, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .spell-stats .stat-item.damage i { + color: #f44336; +} +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .weapon-stats .stat-item.range, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .weapon-stats .stat-item.range, +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .spell-stats .stat-item.range, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .spell-stats .stat-item.range { + background: rgba(76, 175, 80, 0.2); + border: 1px solid rgba(76, 175, 80, 0.4); +} +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .weapon-stats .stat-item.range i, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .weapon-stats .stat-item.range i, +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .spell-stats .stat-item.range i, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .spell-stats .stat-item.range i { + color: #4caf50; +} +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .weapon-stats .stat-item.reload, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .weapon-stats .stat-item.reload, +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .spell-stats .stat-item.reload, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .spell-stats .stat-item.reload { + background: rgba(255, 152, 0, 0.2); + border: 1px solid rgba(255, 152, 0, 0.4); +} +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .weapon-stats .stat-item.reload i, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .weapon-stats .stat-item.reload i, +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .spell-stats .stat-item.reload i, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .spell-stats .stat-item.reload i { + color: #ff9800; +} +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .weapon-stats .stat-item.mana, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .weapon-stats .stat-item.mana, +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .spell-stats .stat-item.mana, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .spell-stats .stat-item.mana { + background: rgba(33, 150, 243, 0.2); + border: 1px solid rgba(33, 150, 243, 0.4); +} +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .weapon-stats .stat-item.mana i, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .weapon-stats .stat-item.mana i, +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .spell-stats .stat-item.mana i, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .spell-stats .stat-item.mana i { + color: #2196f3; +} +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .weapon-stats .stat-item.upkeep, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .weapon-stats .stat-item.upkeep, +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .spell-stats .stat-item.upkeep, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .spell-stats .stat-item.upkeep { + background: rgba(156, 39, 176, 0.2); + border: 1px solid rgba(156, 39, 176, 0.4); +} +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .weapon-stats .stat-item.upkeep i, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .weapon-stats .stat-item.upkeep i, +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .spell-stats .stat-item.upkeep i, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .spell-stats .stat-item.upkeep i { + color: #9c27b0; +} +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .weapon-stats .stat-item.characteristic, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .weapon-stats .stat-item.characteristic, +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .spell-stats .stat-item.characteristic, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .spell-stats .stat-item.characteristic { + background: rgba(103, 58, 183, 0.2); + border: 1px solid rgba(103, 58, 183, 0.4); +} +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .weapon-stats .stat-item.characteristic i, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .weapon-stats .stat-item.characteristic i, +.chat-log .message-content .prismrpg-chat-card .chat-content .weapon-info-card .spell-stats .stat-item.characteristic i, +.chat-log .message-content .prismrpg-chat-card .chat-content .spell-info-card .spell-stats .stat-item.characteristic i { + color: #673ab7; +} +.chat-log .message-content .prismrpg-chat-card .chat-content .special-badge { + display: inline-block; + padding: 2px 6px; + background: linear-gradient(135deg, #ff6b6b 0%, #c92a2a 100%); + color: white; + border-radius: 3px; + font-size: 0.75em; + font-weight: bold; + width: fit-content; + box-shadow: 0 1px 2px rgba(255, 107, 107, 0.4); +} +.chat-log .message-content .prismrpg-chat-card .chat-content .aiming-info { + display: flex; + align-items: center; + gap: 4px; + padding: 3px 6px; + background: rgba(76, 175, 80, 0.1); + border-left: 2px solid #4caf50; + border-radius: 3px; + font-size: 0.8em; +} +.chat-log .message-content .prismrpg-chat-card .chat-content .aiming-info i { + color: #4caf50; +} +.chat-log .message-content .prismrpg-chat-card .chat-content .formula-display { + display: flex; + align-items: center; + gap: 6px; + padding: 4px 8px; + background: rgba(0, 0, 0, 0.3); + border-radius: 3px; + font-family: 'Courier New', monospace; + font-size: 0.85em; + border: 1px dashed rgba(255, 255, 255, 0.2); +} +.chat-log .message-content .prismrpg-chat-card .chat-content .formula-display i { + color: #888; +} +.chat-log .message-content .prismrpg-chat-card .chat-content .modifier-info { + display: flex; + gap: 8px; + padding: 3px 6px; + background: rgba(255, 255, 255, 0.05); + border-radius: 3px; + font-size: 0.75em; +} +.chat-log .message-content .prismrpg-chat-card .chat-content .modifier-info span { + color: #aaa; +} +.chat-log .message-content .prismrpg-chat-card .chat-content .dice-breakdown { + display: flex; + align-items: center; + gap: 4px; + padding: 2px 6px; + background: rgba(255, 255, 255, 0.05); + border-radius: 3px; + font-size: 0.8em; +} +.chat-log .message-content .prismrpg-chat-card .chat-content .dice-breakdown i { + color: #888; +} +.chat-log .message-content .prismrpg-chat-card .chat-content .roll-damage-button { + padding: 4px 10px; + background: linear-gradient(135deg, #c41e3a 0%, #8b0000 100%); + color: white; + border: none; + border-radius: 4px; + font-weight: bold; + font-size: 0.85em; + cursor: pointer; + display: flex; + align-items: center; + gap: 6px; + justify-content: center; + transition: all 0.2s; + box-shadow: 0 1px 3px rgba(196, 30, 58, 0.4); + margin-top: 4px; +} +.chat-log .message-content .prismrpg-chat-card .chat-content .roll-damage-button:hover { + transform: translateY(-1px); + box-shadow: 0 2px 4px rgba(196, 30, 58, 0.6); +} +.chat-log .message-content .prismrpg-chat-card .chat-content .roll-damage-button i { + font-size: 1em; +} +.chat-log .message-content .prismrpg-chat-card .roll-result { + padding: 8px; + background: linear-gradient(135deg, rgba(212, 175, 55, 0.2) 0%, rgba(212, 175, 55, 0.1) 100%); + border-top: 1px solid rgba(212, 175, 55, 0.5); +} +.chat-log .message-content .prismrpg-chat-card .roll-result .result-total { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; +} +.chat-log .message-content .prismrpg-chat-card .roll-result .result-total .total-label { + font-size: 0.85em; + color: #aaa; + text-transform: uppercase; + letter-spacing: 0.5px; +} +.chat-log .message-content .prismrpg-chat-card .roll-result .result-total .total-value { + font-size: 1.8em; + font-weight: bold; + color: #d4af37; + text-shadow: 0 1px 3px rgba(212, 175, 55, 0.5); + line-height: 1; +} +.chat-log .message-content .prismrpg-chat-card .roll-result .d30-result { + margin-top: 4px; + padding: 3px 6px; + background: rgba(0, 0, 0, 0.2); + border-radius: 3px; + font-size: 0.8em; + text-align: center; +} +.chat-log .message-content .prismrpg-chat-card .roll-result .d30-result i { + color: #d4af37; + margin-right: 4px; +} +.chat-log .message-content .prismrpg-chat-card .result-badge { + padding: 6px; + text-align: center; + font-size: 0.9em; + font-weight: bold; + border-top: 1px solid; +} +.chat-log .message-content .prismrpg-chat-card .result-badge.success { + background: linear-gradient(135deg, rgba(76, 175, 80, 0.3) 0%, rgba(76, 175, 80, 0.1) 100%); + border-color: #4caf50; + color: #4caf50; +} +.chat-log .message-content .prismrpg-chat-card .result-badge.failure { + background: linear-gradient(135deg, rgba(244, 67, 54, 0.3) 0%, rgba(244, 67, 54, 0.1) 100%); + border-color: #f44336; + color: #f44336; +} +.chat-log .message-content .prismrpg-chat-card .result-badge i { + margin-right: 6px; + font-size: 1em; +} +.chat-log .message-content .prismrpg-chat-card .damage-info { + padding: 6px; + background: rgba(255, 255, 255, 0.05); + border-radius: 3px; + font-size: 0.8em; + color: #aaa; +} +.chat-log .message-content.fortune img { + border: 0px; +} +.chat-log .message-content.fortune .intro-chat { + border-radius: 20px; + display: flex; + flex-direction: row; +} +.chat-log .message-content.fortune .intro-chat .intro-img { + padding: 5px; + width: 80px; + align-self: center; +} +.chat-log .message-content.fortune .intro-chat .intro-right { + display: flex; + flex-direction: column; +} +.chat-log .message-content.fortune .intro-chat .intro-right .introText { + font-family: var(--font-secondary); + font-size: calc(var(--font-size-standard) * 1.2); + width: 210px; + text-align: center; +} +.chat-log .message-content.fortune .button.control, +.chat-log .message-content.fortune .fortune-accepted { + display: flex; + justify-content: center; + align-items: center; + font-size: calc(var(--font-size-standard) * 1.3); +} +.chat-log .message-content.ask-roll { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-family: var(--font-secondary); + font-size: calc(var(--font-size-standard) * 1.2); +} .application.dialog.prismrpg { color: var(--color-dark-1); } @@ -3724,6 +4126,25 @@ i.prismrpg { border-radius: 4px; padding: 0.5rem; } +.prismrpg-roll-dialog .dialog-weapon-options { + margin-top: 8px; +} +.prismrpg-roll-dialog .dialog-weapon-options .dialog-save { + margin: 4px 0; +} +.prismrpg-roll-dialog .dialog-weapon-options .dialog-save label { + display: flex; + align-items: center; + cursor: pointer; +} +.prismrpg-roll-dialog .dialog-weapon-options .dialog-save label input[type="checkbox"] { + margin-right: 8px; + cursor: pointer; +} +.prismrpg-roll-dialog .dialog-weapon-options .dialog-save select { + margin-left: 8px; + min-width: 10rem; +} .prismrpg-range-defense-dialog { width: 18rem; } @@ -3847,6 +4268,285 @@ i.prismrpg { font-size: calc(var(--font-size-standard) * 1); text-shadow: 0 0 10px var(--color-shadow-primary); } +.dice-roll .damage-roll-button { + margin-top: 0.5em; + margin-bottom: 0.25em; +} +.dice-roll .damage-roll-button .roll-damage-button { + background: linear-gradient(135deg, #8b0000 0%, #dc143c 100%); + color: white; + border: 1px solid #6b0000; + border-radius: 3px; + padding: 4px 10px; + font-weight: 600; + cursor: pointer; + font-size: 0.85em; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); + transition: all 0.2s ease; +} +.dice-roll .damage-roll-button .roll-damage-button:hover { + background: linear-gradient(135deg, #a00000 0%, #ff1744 100%); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); + transform: translateY(-1px); +} +.dice-roll .damage-roll-button .roll-damage-button i { + margin-right: 4px; + font-size: 0.9em; +} +.prismrpg-roll-dialog-modern { + font-family: var(--font-primary); +} +.prismrpg-roll-dialog-modern .dialog-header { + background: linear-gradient(135deg, rgba(33, 33, 33, 0.95) 0%, rgba(66, 66, 66, 0.95) 100%); + border-bottom: 2px solid #d4af37; + padding: 8px 10px; + margin: -8px -8px 8px -8px; + border-radius: 4px 4px 0 0; +} +.prismrpg-roll-dialog-modern .dialog-header .character-info { + display: flex; + flex-direction: column; + gap: 4px; +} +.prismrpg-roll-dialog-modern .dialog-header .character-info .character-name { + font-size: 1em; + font-weight: bold; + color: #d4af37; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5); +} +.prismrpg-roll-dialog-modern .dialog-header .character-info .item-name { + display: flex; + align-items: center; + gap: 4px; + font-size: 0.9em; + color: #e0e0e0; +} +.prismrpg-roll-dialog-modern .dialog-header .character-info .item-name i { + color: #d4af37; + font-size: 0.85em; +} +.prismrpg-roll-dialog-modern .dialog-header .character-info .item-name.weapon i { + color: #f44336; +} +.prismrpg-roll-dialog-modern .dialog-header .character-info .item-name.spell i { + color: #9c27b0; +} +.prismrpg-roll-dialog-modern .dialog-content { + display: flex; + flex-direction: column; + gap: 8px; +} +.prismrpg-roll-dialog-modern .option-section { + background: rgba(0, 0, 0, 0.1); + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 4px; + padding: 6px 8px; +} +.prismrpg-roll-dialog-modern .option-section.weapon-section { + border-left: 3px solid #f44336; +} +.prismrpg-roll-dialog-modern .option-section.spell-section { + border-left: 3px solid #9c27b0; +} +.prismrpg-roll-dialog-modern .option-section .section-title { + display: flex; + align-items: center; + gap: 4px; + font-weight: bold; + font-size: 0.9em; + color: var(--color-dark-1); + margin-bottom: 6px; + padding-bottom: 4px; + border-bottom: 1px solid rgba(0, 0, 0, 0.15); +} +.prismrpg-roll-dialog-modern .option-section .section-title i { + color: #d4af37; + font-size: 0.85em; +} +.prismrpg-roll-dialog-modern .info-display { + display: flex; + align-items: center; + gap: 4px; + padding: 4px 6px; + background: rgba(0, 0, 0, 0.15); + border-radius: 3px; + margin-bottom: 6px; + font-size: 0.8em; + color: #666; +} +.prismrpg-roll-dialog-modern .info-display i { + color: #4caf50; + font-size: 0.85em; +} +.prismrpg-roll-dialog-modern .info-display .info-text { + flex: 1; +} +.prismrpg-roll-dialog-modern .spell-info-display { + display: flex; + flex-wrap: wrap; + gap: 4px; + margin-bottom: 6px; +} +.prismrpg-roll-dialog-modern .spell-info-display .info-badge { + display: flex; + align-items: center; + gap: 3px; + padding: 3px 6px; + border-radius: 3px; + font-size: 0.75em; + font-weight: 500; +} +.prismrpg-roll-dialog-modern .spell-info-display .info-badge i { + font-size: 0.85em; +} +.prismrpg-roll-dialog-modern .spell-info-display .info-badge.characteristic { + background: rgba(103, 58, 183, 0.2); + border: 1px solid rgba(103, 58, 183, 0.4); + color: #673ab7; +} +.prismrpg-roll-dialog-modern .spell-info-display .info-badge.characteristic i { + color: #673ab7; +} +.prismrpg-roll-dialog-modern .spell-info-display .info-badge.mana { + background: rgba(33, 150, 243, 0.2); + border: 1px solid rgba(33, 150, 243, 0.4); + color: #2196f3; +} +.prismrpg-roll-dialog-modern .spell-info-display .info-badge.mana i { + color: #2196f3; +} +.prismrpg-roll-dialog-modern .spell-info-display .info-badge.apc { + background: rgba(255, 193, 7, 0.2); + border: 1px solid rgba(255, 193, 7, 0.4); + color: #ffc107; +} +.prismrpg-roll-dialog-modern .spell-info-display .info-badge.apc i { + color: #ffc107; +} +.prismrpg-roll-dialog-modern .spell-info-display .info-badge.upkeep { + background: rgba(156, 39, 176, 0.2); + border: 1px solid rgba(156, 39, 176, 0.4); + color: #9c27b0; +} +.prismrpg-roll-dialog-modern .spell-info-display .info-badge.upkeep i { + color: #9c27b0; +} +.prismrpg-roll-dialog-modern .option-row { + display: flex; + align-items: center; + gap: 6px; + margin-bottom: 4px; +} +.prismrpg-roll-dialog-modern .option-row:last-child { + margin-bottom: 0; +} +.prismrpg-roll-dialog-modern .option-row label { + font-size: 0.85em; + font-weight: 500; + color: var(--color-dark-2); + min-width: 90px; +} +.prismrpg-roll-dialog-modern .option-row select { + flex: 1; +} +.prismrpg-roll-dialog-modern .styled-select { + background: rgba(255, 255, 255, 0.9); + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 3px; + padding: 4px 8px; + font-size: 0.85em; + color: var(--color-dark-1); + cursor: pointer; + transition: all 0.2s ease; +} +.prismrpg-roll-dialog-modern .styled-select:hover { + border-color: #d4af37; + box-shadow: 0 0 4px rgba(212, 175, 55, 0.3); +} +.prismrpg-roll-dialog-modern .styled-select:focus { + outline: none; + border-color: #d4af37; + box-shadow: 0 0 6px rgba(212, 175, 55, 0.5); +} +.prismrpg-roll-dialog-modern .styled-select.advantage-select, +.prismrpg-roll-dialog-modern .styled-select.modifier-select { + text-align: center; + font-weight: 500; +} +.prismrpg-roll-dialog-modern .checkbox-group { + display: flex; + flex-direction: column; + gap: 3px; + margin-bottom: 6px; +} +.prismrpg-roll-dialog-modern .checkbox-group .checkbox-label { + display: flex; + align-items: center; + cursor: pointer; + padding: 3px 4px; + border-radius: 3px; + transition: background 0.2s ease; +} +.prismrpg-roll-dialog-modern .checkbox-group .checkbox-label:hover { + background: rgba(0, 0, 0, 0.05); +} +.prismrpg-roll-dialog-modern .checkbox-group .checkbox-label input[type="checkbox"] { + margin-right: 6px; + cursor: pointer; + width: 14px; + height: 14px; + accent-color: #d4af37; +} +.prismrpg-roll-dialog-modern .checkbox-group .checkbox-label .checkbox-text { + display: flex; + align-items: center; + gap: 4px; + font-size: 0.8em; + color: var(--color-dark-2); +} +.prismrpg-roll-dialog-modern .checkbox-group .checkbox-label .checkbox-text i { + color: #888; + font-size: 0.85em; +} +.prismrpg-roll-dialog-modern .checkbox-group .checkbox-label input[type="checkbox"]:checked ~ .checkbox-text { + color: var(--color-dark-1); + font-weight: 500; +} +.prismrpg-roll-dialog-modern .checkbox-group .checkbox-label input[type="checkbox"]:checked ~ .checkbox-text i { + color: #d4af37; +} +.application.dialog.prismrpg .window-content { + background: linear-gradient(135deg, #f5f5f5 0%, #e0e0e0 100%); + padding: 8px; +} +.application.dialog.prismrpg .dialog-buttons { + padding: 6px 8px; + gap: 6px; +} +.application.dialog.prismrpg .dialog-buttons button { + background: linear-gradient(135deg, #4a4a4a 0%, #6a6a6a 100%); + border: 1px solid #3a3a3a; + color: white; + font-weight: 600; + padding: 6px 12px; + border-radius: 4px; + transition: all 0.2s ease; +} +.application.dialog.prismrpg .dialog-buttons button:hover { + background: linear-gradient(135deg, #5a5a5a 0%, #7a7a7a 100%); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + transform: translateY(-1px); +} +.application.dialog.prismrpg .dialog-buttons button.default, +.application.dialog.prismrpg .dialog-buttons button[data-button="roll"] { + background: linear-gradient(135deg, #d4af37 0%, #f4cf67 100%); + border-color: #b49030; + color: #2a2a2a; +} +.application.dialog.prismrpg .dialog-buttons button.default:hover, +.application.dialog.prismrpg .dialog-buttons button[data-button="roll"]:hover { + background: linear-gradient(135deg, #e4bf47 0%, #ffdf77 100%); +} #token-hud .hp-loss-wrap { position: absolute; left: 75px; diff --git a/lang/en.json b/lang/en.json index 8a13676..c72c16e 100644 --- a/lang/en.json +++ b/lang/en.json @@ -140,34 +140,34 @@ }, "FIELDS": { "moneys": { - "tinbit": { - "label": "Tin Bits", + "coppercoin": { + "label": "Copper Coin", "value": { - "label": "Tin Bits" + "label": "Copper Coin" } }, - "silver": { - "label": "Silver", + "silvercoin": { + "label": "Silver Coin", "value": { - "label": "Silver" + "label": "Silver Coin" } }, - "copper": { - "label": "Copper", + "goldcoin": { + "label": "Gold Coin", "value": { - "label": "Copper" + "label": "Gold Coin" } }, - "gold": { - "label": "Gold", + "note": { + "label": "Note", "value": { - "label": "Gold" + "label": "Note" } }, - "platinum": { - "label": "Platinum", + "steam": { + "label": "Steam", "value": { - "label": "Platinum" + "label": "Steam" } } }, @@ -415,7 +415,7 @@ "rollProgressionDice": "Roll progression/Lethargy dice", "earned": "Earned", "divinityPoints": "Divinity points", - "aetherPoints": "Aether points", + "manaPoints": "Mana points", "attacks": "Attacks", "monster": "Monster", "Resist" :"Resist", @@ -588,7 +588,8 @@ "piercing": "Piercing (P)", "bludgeoning": "Bludgeoning (B)", "slashing": "Slashing (S)", - "isProjectile": "Is Projectile?", + "shortRange": "Short Range", + "longRange": "Long Range", "range": "Range", "reloadAPC": "Reload APC", "bonuses": "Bonuses", @@ -879,11 +880,11 @@ } }, "Money": { - "Coppers": "Copper", - "Golds": "Gold", - "Platinums": "Platinum", - "Silvers": "Silver", - "Tinbits": "Tin Bits" + "CopperCoin": "Copper Coin", + "SilverCoin": "Silver Coin", + "GoldCoin": "Gold Coin", + "Note": "Note", + "Steam": "Steam" }, "Notifications": { "rollFromWeapon": "Roll from an equipped weapon", diff --git a/module/applications/sheets/character-sheet.mjs b/module/applications/sheets/character-sheet.mjs index a542f66..0992d44 100644 --- a/module/applications/sheets/character-sheet.mjs +++ b/module/applications/sheets/character-sheet.mjs @@ -18,6 +18,10 @@ export default class PrismRPGCharacterSheet extends PrismRPGActorSheet { rollInitiative: PrismRPGCharacterSheet.#onRollInitiative, armorHitPointsPlus: PrismRPGCharacterSheet.#onArmorHitPointsPlus, armorHitPointsMinus: PrismRPGCharacterSheet.#onArmorHitPointsMinus, + manaPointsPlus: PrismRPGCharacterSheet.#onManaPointsPlus, + manaPointsMinus: PrismRPGCharacterSheet.#onManaPointsMinus, + hpPlus: PrismRPGCharacterSheet.#onHpPlus, + hpMinus: PrismRPGCharacterSheet.#onHpMinus, }, } @@ -166,6 +170,30 @@ export default class PrismRPGCharacterSheet extends PrismRPGActorSheet { this.actor.update({ "system.combat.armorHitPoints": Math.max(armorHP, 0) }) } + static #onManaPointsPlus(event, target) { + let mana = this.actor.system.manaPoints.value + mana += 1 + this.actor.update({ "system.manaPoints.value": Math.min(mana, this.actor.system.manaPoints.max) }) + } + + static #onManaPointsMinus(event, target) { + let mana = this.actor.system.manaPoints.value + mana -= 1 + this.actor.update({ "system.manaPoints.value": Math.max(mana, 0) }) + } + + static #onHpPlus(event, target) { + let hp = this.actor.system.hp.value + hp += 1 + this.actor.update({ "system.hp.value": Math.min(hp, this.actor.system.hp.max) }) + } + + static #onHpMinus(event, target) { + let hp = this.actor.system.hp.value + hp -= 1 + this.actor.update({ "system.hp.value": Math.max(hp, 0) }) + } + static #onCreateEquipment(event, target) { } diff --git a/module/config/characteristic-tables.mjs b/module/config/characteristic-tables.mjs index e80aad3..950a5d1 100644 --- a/module/config/characteristic-tables.mjs +++ b/module/config/characteristic-tables.mjs @@ -9,7 +9,7 @@ export const TABLES = { "damage": -7, "attack": -4, "challenge": -9, - "aether_points": -20, + "mana_points": -20, "hp": -3, "encumbered": 1, "lift": 3, @@ -20,7 +20,7 @@ export const TABLES = { "damage": -6, "attack": -4, "challenge": -8, - "aether_points": -20, + "mana_points": -20, "hp": -2, "encumbered": 1, "lift": 4, @@ -31,7 +31,7 @@ export const TABLES = { "damage": -5, "attack": -3, "challenge": -7, - "aether_points": -20, + "mana_points": -20, "hp": -1, "encumbered": 1, "lift": 5, @@ -42,7 +42,7 @@ export const TABLES = { "damage": -4, "attack": -3, "challenge": -6, - "aether_points": -20, + "mana_points": -20, "hp": -1, "encumbered": 2, "lift": 6, @@ -53,7 +53,7 @@ export const TABLES = { "damage": -3, "attack": -2, "challenge": -5, - "aether_points": -20, + "mana_points": -20, "hp": 0, "encumbered": 2, "lift": 7, @@ -64,7 +64,7 @@ export const TABLES = { "damage": -2, "attack": -1, "challenge": -4, - "aether_points": -10, + "mana_points": -10, "hp": 0, "encumbered": 3, "lift": 8, @@ -75,7 +75,7 @@ export const TABLES = { "damage": -2, "attack": 0, "challenge": -3, - "aether_points": -10, + "mana_points": -10, "hp": 0, "encumbered": 3, "lift": 9, @@ -86,7 +86,7 @@ export const TABLES = { "damage": -1, "attack": 0, "challenge": -2, - "aether_points": 0, + "mana_points": 0, "hp": 0, "encumbered": 4, "lift": 11, @@ -97,7 +97,7 @@ export const TABLES = { "damage": -1, "attack": 0, "challenge": -1, - "aether_points": 0, + "mana_points": 0, "hp": 0, "encumbered": 5, "lift": 12, @@ -108,7 +108,7 @@ export const TABLES = { "damage": 0, "attack": 0, "challenge": 0, - "aether_points": 0, + "mana_points": 0, "hp": 0, "encumbered": 6, "lift": 13, @@ -119,7 +119,7 @@ export const TABLES = { "damage": 0, "attack": 0, "challenge": 0, - "aether_points": 0, + "mana_points": 0, "hp": 0, "encumbered": 7, "lift": 15, @@ -130,7 +130,7 @@ export const TABLES = { "damage": 1, "attack": 0, "challenge": 1, - "aether_points": 0, + "mana_points": 0, "hp": 0, "encumbered": 8, "lift": 17, @@ -141,7 +141,7 @@ export const TABLES = { "damage": 1, "attack": 0, "challenge": 2, - "aether_points": 0, + "mana_points": 0, "hp": 0, "encumbered": 9, "lift": 20, @@ -152,7 +152,7 @@ export const TABLES = { "damage": 2, "attack": 1, "challenge": 3, - "aether_points": 0, + "mana_points": 0, "hp": 1, "encumbered": 10, "lift": 22, @@ -163,7 +163,7 @@ export const TABLES = { "damage": 3, "attack": 1, "challenge": 4, - "aether_points": 0, + "mana_points": 0, "hp": 2, "encumbered": 11, "lift": 24, @@ -174,7 +174,7 @@ export const TABLES = { "damage": 4, "attack": 2, "challenge": 5, - "aether_points": 0, + "mana_points": 0, "hp": 3, "encumbered": 12, "lift": 26, @@ -185,7 +185,7 @@ export const TABLES = { "damage": 5, "attack": 2, "challenge": 6, - "aether_points": 10, + "mana_points": 10, "hp": 4, "encumbered": 13, "lift": 28, @@ -196,7 +196,7 @@ export const TABLES = { "damage": 6, "attack": 3, "challenge": 7, - "aether_points": 20, + "mana_points": 20, "hp": 5, "encumbered": 14, "lift": 30, @@ -207,7 +207,7 @@ export const TABLES = { "damage": 7, "attack": 3, "challenge": 8, - "aether_points": 20, + "mana_points": 20, "hp": 6, "encumbered": 15, "lift": 31, @@ -218,7 +218,7 @@ export const TABLES = { "damage": 8, "attack": 4, "challenge": 9, - "aether_points": 30, + "mana_points": 30, "hp": 7, "encumbered": 15, "lift": 32, @@ -229,7 +229,7 @@ export const TABLES = { "damage": 9, "attack": 4, "challenge": 10, - "aether_points": 30, + "mana_points": 30, "hp": 8, "encumbered": 16, "lift": 33, @@ -240,7 +240,7 @@ export const TABLES = { "damage": 10, "attack": 5, "challenge": 11, - "aether_points": 40, + "mana_points": 40, "hp": 9, "encumbered": 16, "lift": 34, @@ -251,7 +251,7 @@ export const TABLES = { "damage": 12, "attack": 5, "challenge": 12, - "aether_points": 40, + "mana_points": 40, "hp": 10, "encumbered": 17, "lift": 35, @@ -262,7 +262,7 @@ export const TABLES = { "damage": 14, "attack": 5, "challenge": 13, - "aether_points": 50, + "mana_points": 50, "hp": 11, "encumbered": 18, "lift": 36, @@ -273,7 +273,7 @@ export const TABLES = { "damage": 16, "attack": 6, "challenge": 14, - "aether_points": 60, + "mana_points": 60, "hp": 12, "encumbered": 19, "lift": 38, @@ -286,7 +286,7 @@ export const TABLES = { "attack": -5, "defense": -3, "development_points": 0, - "aether": -50, + "mana": -50, "spell_cognition": 0, "arkane_casting_mod": -4 }, @@ -295,7 +295,7 @@ export const TABLES = { "attack": -4, "defense": -3, "development_points": 0, - "aether": -50, + "mana": -50, "spell_cognition": 0, "arkane_casting_mod": -4 }, @@ -304,7 +304,7 @@ export const TABLES = { "attack": -3, "defense": -3, "development_points": 0, - "aether": -50, + "mana": -50, "spell_cognition": 0.01, "arkane_casting_mod": -3 }, @@ -313,7 +313,7 @@ export const TABLES = { "attack": -2, "defense": -2, "development_points": 0, - "aether": -45, + "mana": -45, "spell_cognition": 0.05, "arkane_casting_mod": -3 }, @@ -322,7 +322,7 @@ export const TABLES = { "attack": -2, "defense": -2, "development_points": 0, - "aether": -45, + "mana": -45, "spell_cognition": 0.1, "arkane_casting_mod": -2 }, @@ -331,7 +331,7 @@ export const TABLES = { "attack": -2, "defense": -2, "development_points": 0, - "aether": -40, + "mana": -40, "spell_cognition": 0.15, "arkane_casting_mod": -2 }, @@ -340,7 +340,7 @@ export const TABLES = { "attack": -1, "defense": -1, "development_points": 0, - "aether": -40, + "mana": -40, "spell_cognition": 0.2, "arkane_casting_mod": -1 }, @@ -349,7 +349,7 @@ export const TABLES = { "attack": -1, "defense": 0, "development_points": 0, - "aether": -30, + "mana": -30, "spell_cognition": 0.25, "arkane_casting_mod": -1 }, @@ -358,7 +358,7 @@ export const TABLES = { "attack": -1, "defense": 0, "development_points": 0, - "aether": -30, + "mana": -30, "spell_cognition": 0.3, "arkane_casting_mod": 0 }, @@ -367,7 +367,7 @@ export const TABLES = { "attack": 0, "defense": 0, "development_points": 0, - "aether": -20, + "mana": -20, "spell_cognition": 0.35, "arkane_casting_mod": 0 }, @@ -376,7 +376,7 @@ export const TABLES = { "attack": 0, "defense": 0, "development_points": 1, - "aether": -10, + "mana": -10, "spell_cognition": 0.45, "arkane_casting_mod": 0 }, @@ -385,7 +385,7 @@ export const TABLES = { "attack": 1, "defense": 0, "development_points": 2, - "aether": 0, + "mana": 0, "spell_cognition": 0.5, "arkane_casting_mod": 1 }, @@ -394,7 +394,7 @@ export const TABLES = { "attack": 1, "defense": 0, "development_points": 3, - "aether": 0, + "mana": 0, "spell_cognition": 0.6, "arkane_casting_mod": 1 }, @@ -403,7 +403,7 @@ export const TABLES = { "attack": 1, "defense": 1, "development_points": 4, - "aether": 10, + "mana": 10, "spell_cognition": 0.65, "arkane_casting_mod": 2 }, @@ -412,7 +412,7 @@ export const TABLES = { "attack": 2, "defense": 1, "development_points": 5, - "aether": 20, + "mana": 20, "spell_cognition": 0.75, "arkane_casting_mod": 2 }, @@ -421,7 +421,7 @@ export const TABLES = { "attack": 2, "defense": 1, "development_points": 7, - "aether": 30, + "mana": 30, "spell_cognition": 0.8, "arkane_casting_mod": 3 }, @@ -430,7 +430,7 @@ export const TABLES = { "attack": 2, "defense": 1, "development_points": 9, - "aether": 40, + "mana": 40, "spell_cognition": 0.85, "arkane_casting_mod": 3 }, @@ -439,7 +439,7 @@ export const TABLES = { "attack": 3, "defense": 2, "development_points": 11, - "aether": 50, + "mana": 50, "spell_cognition": 0.9, "arkane_casting_mod": 4 }, @@ -448,7 +448,7 @@ export const TABLES = { "attack": 3, "defense": 2, "development_points": 13, - "aether": 60, + "mana": 60, "spell_cognition": 0.92, "arkane_casting_mod": 5 }, @@ -457,7 +457,7 @@ export const TABLES = { "attack": 3, "defense": 2, "development_points": 15, - "aether": 70, + "mana": 70, "spell_cognition": 0.94, "arkane_casting_mod": 6 }, @@ -466,7 +466,7 @@ export const TABLES = { "attack": 4, "defense": 2, "development_points": 18, - "aether": 80, + "mana": 80, "spell_cognition": 0.95, "arkane_casting_mod": 7 }, @@ -475,7 +475,7 @@ export const TABLES = { "attack": 4, "defense": 3, "development_points": 21, - "aether": 90, + "mana": 90, "spell_cognition": 0.96, "arkane_casting_mod": 7 }, @@ -484,7 +484,7 @@ export const TABLES = { "attack": 4, "defense": 3, "development_points": 24, - "aether": 100, + "mana": 100, "spell_cognition": 0.97, "arkane_casting_mod": 8 }, @@ -493,7 +493,7 @@ export const TABLES = { "attack": 5, "defense": 3, "development_points": 27, - "aether": 110, + "mana": 110, "spell_cognition": 0.98, "arkane_casting_mod": 8 }, @@ -502,7 +502,7 @@ export const TABLES = { "attack": 5, "defense": 4, "development_points": 30, - "aether": 125, + "mana": 125, "spell_cognition": 0.99, "arkane_casting_mod": 9 } @@ -688,7 +688,7 @@ export const TABLES = { { "value": 1, "hp ": 1, - "aether_points": -50, + "mana_points": -50, "pain_save": 1, "toughness_save": -5, "stabilization_dice": "D6", @@ -698,7 +698,7 @@ export const TABLES = { "value": 2, "hp ": 2, - "aether_points": -40, + "mana_points": -40, "pain_save": 2, "toughness_saave": -4, "stabilization_dice": "D6", @@ -708,7 +708,7 @@ export const TABLES = { "value": 3, "hp ": 3, - "aether_points": -35, + "mana_points": -35, "pain_save": 2, "toughness_save": -3, "stabilization_dice": "D6", @@ -718,7 +718,7 @@ export const TABLES = { "value": 4, "hp ": 4, - "aether_points": -30, + "mana_points": -30, "pain_save": 2, "toughness_save": -3, "stabilization_dice": "D6", @@ -728,7 +728,7 @@ export const TABLES = { "value": 5, "hp ": 5, - "aether_points": -25, + "mana_points": -25, "pain_save": 3, "toughness_save": -2, "stabilization_dice": "D6", @@ -737,7 +737,7 @@ export const TABLES = { { "value": 6, "hp ": 6, - "aether_points": -20, + "mana_points": -20, "pain_save": 3, "toughness_save": -2, "stabilization_dice": "D6", @@ -746,7 +746,7 @@ export const TABLES = { { "value": 7, "hp ": 7, - "aether_points": -15, + "mana_points": -15, "pain_save": 3, "toughness_save": -1, "stabilization_dice": "D6", @@ -756,7 +756,7 @@ export const TABLES = { "value": 8, "hp ": 8, - "aether_points": -10, + "mana_points": -10, "pain_save": 4, "toughness_save": -1, "stabilization_dice": "D8", @@ -766,7 +766,7 @@ export const TABLES = { "value": 9, "hp ": 9, - "aether_points": -5, + "mana_points": -5, "pain_save": 4, "toughness_save": 0, "stabilization_dice": "D8", @@ -776,7 +776,7 @@ export const TABLES = { "value": 10, "hp ": 10, - "aether_points": 0, + "mana_points": 0, "pain_save": 5, "toughness_save": 0, "stabilization_dice": "D8", @@ -786,7 +786,7 @@ export const TABLES = { "value": 11, "hp ": 11, - "aether_points": 0, + "mana_points": 0, "pain_save": 5, "toughness_save": 0, "stabilization_dice": "D8", @@ -796,7 +796,7 @@ export const TABLES = { "value": 12, "hp ": 12, - "aether_points": 5, + "mana_points": 5, "pain_save": 6, "toughness_save": 0, "stabilization_dice": "D10", @@ -806,7 +806,7 @@ export const TABLES = { "value": 13, "hp ": 13, - "aether_points": 10, + "mana_points": 10, "pain_save": 7, "toughness_save": 1, "stabilization_dice": "D10", @@ -816,7 +816,7 @@ export const TABLES = { "value": 14, "hp ": 14, - "aether_points": 20, + "mana_points": 20, "pain_save": 7, "toughness_save": 2, "stabilization_dice": "D10", @@ -826,7 +826,7 @@ export const TABLES = { "value": 15, "hp ": 15, - "aether_points": 30, + "mana_points": 30, "pain_save": 8, "toughness_save": 3, "stabilization_dice": "D12", @@ -836,7 +836,7 @@ export const TABLES = { "value": 16, "hp ": 16, - "aether_points": 40, + "mana_points": 40, "pain_save": 8, "toughness_save": 4, "stabilization_dice": "D12", @@ -846,7 +846,7 @@ export const TABLES = { "value": 17, "hp ": 17, - "aether_points": 50, + "mana_points": 50, "pain_save": 9, "toughness_save": 5, "stabilization_dice": "D12", @@ -856,7 +856,7 @@ export const TABLES = { "value": 18, "hp ": 18, - "aether_points": 60, + "mana_points": 60, "pain_save": 9, "toughness_save": 6, "stabilization_dice": "D12", @@ -866,7 +866,7 @@ export const TABLES = { "value": 19, "hp ": 19, - "aether_points": 70, + "mana_points": 70, "pain_save": 10, "toughness_save": 7, "stabilization_dice": "D12", @@ -876,7 +876,7 @@ export const TABLES = { "value": 20, "hp ": 20, - "aether_points": 80, + "mana_points": 80, "pain_save": 10, "toughness_save": 8, "stabilization_dice": "D12", @@ -886,7 +886,7 @@ export const TABLES = { "value": 21, "hp ": 21, - "aether_points": 90, + "mana_points": 90, "pain_save": 11, "toughness_save": 9, "stabilization_dice": "D20", @@ -896,7 +896,7 @@ export const TABLES = { "value": 22, "hp ": 22, - "aether_points": 100, + "mana_points": 100, "pain_save": 11, "toughness_save": 10, "stabilization_dice": "D20", @@ -906,7 +906,7 @@ export const TABLES = { "value": 23, "hp ": 23, - "aether_points": 110, + "mana_points": 110, "pain_save": 12, "toughness_save": 11, "stabilization_dice": "D20", @@ -916,7 +916,7 @@ export const TABLES = { "value": 24, "hp ": 24, - "aether_points": 120, + "mana_points": 120, "pain_save": 12, "toughness_save": 12, "stabilization_dice": "D20", @@ -925,7 +925,7 @@ export const TABLES = { { "value": 25, "hp ": 25, - "aether_points": 130, + "mana_points": 130, "pain_save": 13, "toughness_save": 13, "stabilization_dice": "D20", diff --git a/module/config/system.mjs b/module/config/system.mjs index 294bfb3..2428016 100644 --- a/module/config/system.mjs +++ b/module/config/system.mjs @@ -14,35 +14,35 @@ export const SYSTEM_ID = "fvtt-prism-rpg" export const DEV_MODE = false export const MONEY = { - tinbit: { - id: "tinbit", - abbrev: "tb", - label: "PRISMRPG.Money.Tinbits", + coppercoin: { + id: "coppercoin", + abbrev: "cc", + label: "PRISMRPG.Money.CopperCoin", valuetb: 1 }, - copper: { - id: "copper", - abbrev: "cp", - label: "PRISMRPG.Money.Coppers", + silvercoin: { + id: "silvercoin", + abbrev: "sc", + label: "PRISMRPG.Money.SilverCoin", + valuetb: 5 + }, + goldcoin: { + id: "goldcoin", + abbrev: "gc", + label: "PRISMRPG.Money.GoldCoin", valuetb: 10 }, - silver: { - id: "silver", - abbrev: "sp", - label: "PRISMRPG.Money.Silvers", + note: { + id: "note", + abbrev: "nt", + label: "PRISMRPG.Money.Note", valuetb: 100 }, - gold: { - id: "gold", - abbrev: "gp", - label: "PRISMRPG.Money.Golds", + steam: { + id: "steam", + abbrev: "st", + label: "PRISMRPG.Money.Steam", valuetb: 1000 - }, - platinum: { - id: "platinum", - abbrev: "pp", - label: "PRISMRPG.Money.Platinums", - valuetb: 10000 } } @@ -156,7 +156,7 @@ export const INITIATIVE_DICE_CHOICES_PER_CLASS = { { "name": "Aware and know exactly where the enemy is (1D4)", "value": "1D4" }*/ ], "magicuser": [ - { "name": "Sleeping to recover Aether Points (2D20)", "value": "2D20" }, + { "name": "Sleeping to recover Mana Points (2D20)", "value": "2D20" }, { "name": "Asleep or totally distracted (1D20)", "value": "1D20" }, { "name": "Awake but unsuspecting (1D12)", "value": "1D12" }, { "name": "Declared Ready on Alert (1)", "value": "1" }, diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index 55aabc4..17744fb 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -1,4 +1,6 @@ import PrismRPGUtils from "../utils.mjs" +import PrismRPGRoll from "./roll.mjs" + export default class PrismRPGActor extends Actor { static async create(data, options) { @@ -42,31 +44,12 @@ export default class PrismRPGActor extends Actor { } /* *************************************************/ + // This method is no longer needed in D&D 5e style system + // Weapon proficiency is handled through character class/race features getBestWeaponClassSkill(skills, rollType, multiplier = 1.0) { - let maxValue = 0 - let goodSkill = skills[0] - for (let s of skills) { - if (rollType === "weapon-attack") { - if (s.system.weaponBonus.attack > maxValue) { - maxValue = Number(s.system.weaponBonus.attack) - goodSkill = s - } - } - if (rollType === "weapon-defense") { - if (s.system.weaponBonus.defense > maxValue) { - maxValue = Number(s.system.weaponBonus.defense) - goodSkill = s - } - } - if (rollType.includes("weapon-damage")) { - if (s.system.weaponBonus.damage > maxValue) { - maxValue = Number(s.system.weaponBonus.damage) - goodSkill = s - } - } - } - goodSkill.weaponSkillModifier = maxValue * multiplier - return goodSkill + // In D&D 5e, we don't need weapon skills with bonuses + // Just return the first skill (or could be removed entirely) + return skills[0] } /* *************************************************/ @@ -150,6 +133,7 @@ export default class PrismRPGActor extends Actor { break case "spell-attack": case "spell-power": + case "spell-cast": case "miracle-attack": case "miracle-power": rollTarget = this.items.find((i) => (i.type === "miracle" || i.type === "spell") && i.id === rollKey) @@ -164,8 +148,7 @@ export default class PrismRPGActor extends Actor { break; case "weapon-damage-small": case "weapon-damage-medium": - case "weapon-attack": - case "weapon-defense": { + case "weapon-attack": { let weapon = this.items.find((i) => i.type === "weapon" && i.id === rollKey) let skill let skills = this.items.filter((i) => i.type === "skill" && i.name.toLowerCase() === weapon.name.toLowerCase()) @@ -195,19 +178,15 @@ export default class PrismRPGActor extends Actor { ui.notifications.warn(game.i18n.localize("PRISMRPG.Notifications.skillNotFound")) return } - rollTarget = skill - rollTarget.weapon = weapon - rollTarget.weaponSkillModifier = skill.weaponSkillModifier - rollTarget.rollKey = rollKey - rollTarget.combat = foundry.utils.duplicate(this.system.combat) - if (rollType === "weapon-damage-small" || rollType === "weapon-damage-medium") { - rollTarget.grantedDice = this.system.granted.damageDice - } - if (rollType === "weapon-attack") { - rollTarget.grantedDice = this.system.granted.attackDice - } - if (rollType === "weapon-defense") { - rollTarget.grantedDice = this.system.granted.defenseDice + + // Create a plain object for rollTarget to ensure weapon data is preserved + rollTarget = { + ...skill.toObject(), + weapon: weapon.toObject(), + rollKey: rollKey, + combat: foundry.utils.duplicate(this.system.combat), + strMod: PrismRPGRoll.getAbilityModifier(this.system.characteristics.str.value), + dexMod: PrismRPGRoll.getAbilityModifier(this.system.characteristics.dex.value) } } break diff --git a/module/documents/roll.mjs b/module/documents/roll.mjs index 4064d48..9663b24 100644 --- a/module/documents/roll.mjs +++ b/module/documents/roll.mjs @@ -136,30 +136,40 @@ export default class PrismRPGRoll extends Roll { case "weapon-attack": options.rollName = options.rollTarget.name + // Default to STR for melee, DEX for ranged (will be updated by dialog choice) if (options.rollTarget.weapon.system.weaponType === "melee") { - options.rollTarget.value = options.rollTarget.combat.attackModifier + - options.rollTarget.weaponSkillModifier + + options.rollTarget.value = options.rollTarget.strMod + options.rollTarget.weapon.system.bonuses.attackBonus } else { - options.rollTarget.value = options.rollTarget.combat.rangedAttackModifier + - options.rollTarget.weaponSkillModifier + + options.rollTarget.value = options.rollTarget.dexMod + options.rollTarget.weapon.system.bonuses.attackBonus } break - case "weapon-defense": - options.rollName = options.rollTarget.name - options.rollTarget.value = options.rollTarget.combat.defenseModifier + - options.rollTarget.weaponSkillModifier + - options.rollTarget.weapon.system.bonuses.defenseBonus - break - case "spell": case "spell-attack": case "spell-power": + case "spell-cast": options.rollName = options.rollTarget.name - options.rollTarget.value = options.rollTarget.actorModifiers.levelSpellModifier + - options.rollTarget.actorModifiers.intSpellModifier + // Find best mental characteristic (INT, WIS, CHA) + const actor = game.actors.get(options.actorId) + const intMod = this.getAbilityModifier(actor.system.characteristics.int.value) + const wisMod = this.getAbilityModifier(actor.system.characteristics.wis.value) + const chaMod = this.getAbilityModifier(actor.system.characteristics.cha.value) + const bestMentalMod = Math.max(intMod, wisMod, chaMod) + options.rollTarget.value = bestMentalMod + + // Store which characteristic is being used + if (bestMentalMod === intMod) { + options.rollTarget.mentalCharacteristic = "INT" + options.rollTarget.mentalCharValue = actor.system.characteristics.int.value + } else if (bestMentalMod === wisMod) { + options.rollTarget.mentalCharacteristic = "WIS" + options.rollTarget.mentalCharValue = actor.system.characteristics.wis.value + } else { + options.rollTarget.mentalCharacteristic = "CHA" + options.rollTarget.mentalCharValue = actor.system.characteristics.cha.value + } break case "miracle": @@ -188,17 +198,18 @@ export default class PrismRPGRoll extends Roll { if (options.rollType.includes("weapon-damage")) { isDamageRoll = true hasAdvantage = false - options.rollName = options.rollTarget.name - let damageBonus = options.rollTarget.combat.damageModifier - options.rollTarget.value = damageBonus + - options.rollTarget.weaponSkillModifier + - options.rollTarget.weapon.system.bonuses.damageBonus - - if (options.rollType.includes("small")) { - dice = options.rollTarget.weapon.system.damage.damageS + options.rollName = options.rollTarget.weapon.name + // Default to STR for melee, DEX for ranged (will be updated by dialog choice) + if (options.rollTarget.weapon.system.weaponType === "melee") { + options.rollTarget.value = options.rollTarget.strMod + + options.rollTarget.weapon.system.bonuses.damageBonus } else { - dice = options.rollTarget.weapon.system.damage.damageM + options.rollTarget.value = options.rollTarget.dexMod + + options.rollTarget.weapon.system.bonuses.damageBonus } + + // Use the weapon's damage dice + dice = options.rollTarget.weapon.system.damage || "1d6" dice = dice.replace(/E/gi, "") } else if (options.rollType.includes("monster-damage")) { isDamageRoll = true @@ -218,6 +229,44 @@ export default class PrismRPGRoll extends Roll { const rollModes = foundry.utils.duplicate(CONFIG.Dice.rollModes) const choiceModifier = SYSTEM.CHOICE_MODIFIERS const choiceAdvantage = SYSTEM.ADVANTAGE_CHOICES + const attackerAimChoices = SYSTEM.ATTACKER_AIM_CHOICES + + // For weapon damage rolls, skip dialog and roll directly + if (options.rollType.includes("weapon-damage")) { + // Just roll the weapon's damage dice, no modifiers + const finalFormula = dice + + const rollData = { + type: options.rollType, + rollType: options.rollType, + target: options.rollTarget, + rollName: options.rollName, + actorId: options.actorId, + actorName: options.actorName, + actorImage: options.actorImage, + rollMode: "publicroll", + hasTarget: options.hasTarget, + titleFormula: finalFormula + } + + if (Hooks.call("fvtt-prism-rpg.preRoll", options, rollData) === false) return + + // Execute the roll + let roll = new this(finalFormula, options.data, rollData) + await roll.evaluate() + + // Store results + const duplicatedRollTarget = foundry.utils.duplicate(options.rollTarget) + roll.options.resultType = "success" + roll.options.rollTotal = roll.total + roll.options.rollTarget = duplicatedRollTarget + roll.options.titleFormula = finalFormula + roll.options.rollData = foundry.utils.duplicate(rollData) + + if (Hooks.call("fvtt-prism-rpg.Roll", options, rollData, roll) === false) return + + return roll + } let dialogContext = { rollType: options.rollType, @@ -232,13 +281,14 @@ export default class PrismRPGRoll extends Roll { dice, choiceModifier, choiceAdvantage, + attackerAimChoices, hasTarget: options.hasTarget, modifier: "+0", advantage: "none" } const content = await foundry.applications.handlebars.renderTemplate( - "systems/fvtt-prism-rpg/templates/roll-dialog.hbs", + "systems/fvtt-prism-rpg/templates/roll-dialog-v2.hbs", dialogContext ) @@ -255,7 +305,13 @@ export default class PrismRPGRoll extends Roll { callback: (event, button, dialog) => { game.user.setFlag(SYSTEM.id, "roll-dialog-pos", foundry.utils.duplicate(dialog.position)) const output = Array.from(button.form.elements).reduce((obj, input) => { - if (input.name) obj[input.name] = input.value + if (input.name) { + if (input.type === "checkbox") { + obj[input.name] = input.checked + } else { + obj[input.name] = input.value + } + } return obj }, {}) return output @@ -272,9 +328,29 @@ export default class PrismRPGRoll extends Roll { if (hasModifier) { let bonus = Number(options.rollTarget.value) || 0 - let extraModifier = rollContext.modifier === "" ? 0 : parseInt(rollContext.modifier, 10) + + // Recalculate bonus if player chose different attribute for weapon attack/damage + if (rollContext.attackAttribute && options.rollTarget.weapon) { + const chosenMod = rollContext.attackAttribute === "str" ? options.rollTarget.strMod : options.rollTarget.dexMod + const weaponBonus = options.rollTarget.weapon.system.bonuses.attackBonus || 0 + const damageBonus = options.rollTarget.weapon.system.bonuses.damageBonus || 0 + + if (options.rollType === "weapon-attack") { + bonus = chosenMod + weaponBonus + } else if (options.rollType.includes("weapon-damage")) { + bonus = chosenMod + damageBonus + } + } + + let extraModifier = rollContext.modifier === "" ? 0 : Number.parseInt(rollContext.modifier, 10) totalModifier = bonus + extraModifier + // Apply aiming modifier for ranged attacks + if (rollContext.attackerAim && rollContext.attackerAim !== "0") { + const aimModifier = Number.parseInt(rollContext.attackerAim, 10) + totalModifier += aimModifier + } + if (totalModifier !== 0) { finalFormula = totalModifier > 0 ? `${dice} + ${totalModifier}` : @@ -289,6 +365,35 @@ export default class PrismRPGRoll extends Roll { finalFormula = finalFormula.replace(dice, `2${dice}kl`) } + // Special ranged weapon modifiers + if (rollContext.letItFly) { + // Let it Fly: Pure D20E (replace with 1d20 if it was modified) + finalFormula = finalFormula.replace(/2d20k[hl]/, "1d20") + } + if (rollContext.pointBlank) { + // Point Blank: Add special advantage or bonus (implement based on your rules) + // This could add advantage or a flat bonus + } + + // Handle spell upcast + let upcastLevel = 0 + let totalManaCost = 0 + let totalAPC = 0 + let manaUpkeep = 0 + let mentalCharacteristic = null + let mentalCharValue = null + + if (options.rollType === "spell-cast") { + upcastLevel = rollContext.upcastLevel ? Number.parseInt(rollContext.upcastLevel, 10) : 0 + totalManaCost = options.rollTarget.system.manaCost + upcastLevel + totalAPC = options.rollTarget.system.apc + upcastLevel + manaUpkeep = options.rollTarget.system.manaUpkeep + + // Get mental characteristic info from rollTarget + mentalCharacteristic = options.rollTarget.mentalCharacteristic + mentalCharValue = options.rollTarget.mentalCharValue + } + const rollData = { type: options.rollType, rollType: options.rollType, @@ -300,19 +405,49 @@ export default class PrismRPGRoll extends Roll { rollMode: rollContext.visibility, hasTarget: options.hasTarget, titleFormula: finalFormula, + upcastLevel, + totalManaCost, + totalAPC, + manaUpkeep, + mentalCharacteristic, + mentalCharValue, ...rollContext, } if (Hooks.call("fvtt-prism-rpg.preRoll", options, rollData) === false) return + // Handle mana spending for spell-cast + if (options.rollType === "spell-cast" && totalManaCost > 0) { + const actor = game.actors.get(options.actorId) + const currentMana = actor.system.manaPoints.value + + // Check if enough mana + if (currentMana < totalManaCost) { + ui.notifications.error( + `Not enough Mana! Need ${totalManaCost}, but only have ${currentMana} Mana points.` + ) + return null + } + + // Spend mana + await actor.update({ + "system.manaPoints.value": currentMana - totalManaCost + }) + + ui.notifications.info( + `Spent ${totalManaCost} Mana (${currentMana} → ${currentMana - totalManaCost})` + ) + } + // Execute the roll let roll = new this(finalFormula, options.data, rollData) await roll.evaluate() - // Store results + // Store results - duplicate rollTarget to properly serialize weapon Item + const duplicatedRollTarget = foundry.utils.duplicate(options.rollTarget) roll.options.resultType = "success" roll.options.rollTotal = roll.total - roll.options.rollTarget = options.rollTarget + roll.options.rollTarget = duplicatedRollTarget roll.options.titleFormula = finalFormula roll.options.rollData = foundry.utils.duplicate(rollData) diff --git a/module/models/armor.mjs b/module/models/armor.mjs index 4407f92..4e1053d 100644 --- a/module/models/armor.mjs +++ b/module/models/armor.mjs @@ -24,7 +24,7 @@ export default class PrismRPGArmor extends foundry.abstract.TypeDataModel { schema.isHelmet = new fields.BooleanField({ required: true, initial: false }) schema.cost = new fields.NumberField({ required: true, initial: 0, min: 0 }) - schema.money = new fields.StringField({ required: true, initial: "tinbit", choices: SYSTEM.MONEY }) + schema.money = new fields.StringField({ required: true, initial: "coppercoin", choices: SYSTEM.MONEY }) return schema } diff --git a/module/models/character.mjs b/module/models/character.mjs index 187f2d6..7c3b9ff 100644 --- a/module/models/character.mjs +++ b/module/models/character.mjs @@ -84,11 +84,6 @@ export default class PrismRPGCharacter extends foundry.abstract.TypeDataModel { temp: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }) }) - schema.magicPoints = new fields.SchemaField({ - value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), - max: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }) - }) - schema.armorPoints = new fields.SchemaField({ value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), max: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }) @@ -110,15 +105,7 @@ export default class PrismRPGCharacter extends foundry.abstract.TypeDataModel { total: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), remaining: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }) }) - schema.spellMiraclePoints = new fields.SchemaField({ - total: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), - used: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }) - }) - schema.aetherPoints = new fields.SchemaField({ - max: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), - value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }) - }) - schema.divinityPoints = new fields.SchemaField({ + schema.manaPoints = new fields.SchemaField({ max: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }) }) diff --git a/module/models/equipment.mjs b/module/models/equipment.mjs index d6957a2..1ffe3c5 100644 --- a/module/models/equipment.mjs +++ b/module/models/equipment.mjs @@ -7,11 +7,11 @@ export default class PrismRPGEquipment extends foundry.abstract.TypeDataModel { const requiredInteger = { required: true, nullable: false, integer: true } schema.description = new fields.HTMLField({ required: true, textSearch: true }) - schema.category = new fields.StringField({ required: true, initial: "tinbit", choices: SYSTEM.EQUIPMENT_CATEGORIES }) + schema.category = new fields.StringField({ required: true, initial: "coppercoin", choices: SYSTEM.EQUIPMENT_CATEGORIES }) schema.encLoad = new fields.NumberField({ required: true, initial: 0, min: 0 }) schema.cost = new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 }) - schema.money = new fields.StringField({ required: true, initial: "tinbit", choices: SYSTEM.MONEY }) + schema.money = new fields.StringField({ required: true, initial: "coppercoin", choices: SYSTEM.MONEY }) // Kit properties schema.isKit = new fields.BooleanField({ @@ -54,4 +54,29 @@ export default class PrismRPGEquipment extends foundry.abstract.TypeDataModel { /** @override */ static LOCALIZATION_PREFIXES = ["PRISMRPG.Equipment"] + static migrateData(data) { + // Migrate old money types to new ones + if (data?.money) { + const moneyMigration = { + "tinbit": "coppercoin", + "copper": "coppercoin", + "silver": "silvercoin", + "gold": "goldcoin", + "platinum": "note" + } + + if (moneyMigration[data.money]) { + data.money = moneyMigration[data.money] + } + + // If still invalid, default to coppercoin + if (!SYSTEM.MONEY[data.money]) { + console.warn(`Prism RPG | Migrate equipment: Invalid money type "${data.money}", defaulting to coppercoin`) + data.money = "coppercoin" + } + } + + return super.migrateData(data) + } + } diff --git a/module/models/shield.mjs b/module/models/shield.mjs index 64105ae..4e42267 100644 --- a/module/models/shield.mjs +++ b/module/models/shield.mjs @@ -61,7 +61,7 @@ export default class PrismRPGShield extends foundry.abstract.TypeDataModel { // Equipment properties schema.encLoad = new fields.NumberField({ required: true, initial: 0, min: 0 }) schema.cost = new fields.NumberField({ required: true, initial: 0, min: 0 }) - schema.money = new fields.StringField({ required: true, initial: "tinbit", choices: SYSTEM.MONEY }) + schema.money = new fields.StringField({ required: true, initial: "coppercoin", choices: SYSTEM.MONEY }) schema.equipped = new fields.BooleanField({ required: true, initial: false }) return schema diff --git a/module/models/weapon.mjs b/module/models/weapon.mjs index 6f4daaf..ad9df6f 100644 --- a/module/models/weapon.mjs +++ b/module/models/weapon.mjs @@ -96,16 +96,17 @@ export default class PrismRPGWeapon extends foundry.abstract.TypeDataModel { initial: "" }) - // Projectile-specific properties - schema.isProjectile = new fields.BooleanField({ - required: true, - initial: false + // Range properties + schema.shortRange = new fields.NumberField({ + ...requiredInteger, + initial: 0, + min: 0 }) - schema.range = new fields.SchemaField({ - short: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), - medium: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }), - long: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }) + schema.longRange = new fields.NumberField({ + ...requiredInteger, + initial: 0, + min: 0 }) schema.reloadAPC = new fields.NumberField({ @@ -123,7 +124,7 @@ export default class PrismRPGWeapon extends foundry.abstract.TypeDataModel { schema.encLoad = new fields.NumberField({ required: true, initial: 0, min: 0 }) schema.cost = new fields.NumberField({ ...requiredInteger, required: true, initial: 0, min: 0 }) - schema.money = new fields.StringField({ required: true, initial: "tinbit", choices: SYSTEM.MONEY }) + schema.money = new fields.StringField({ required: true, initial: "coppercoin", choices: SYSTEM.MONEY }) schema.equipped = new fields.BooleanField({ required: true, initial: false }) schema.isImplement = new fields.BooleanField({ required: true, initial: false }) diff --git a/prism-rpg.mjs b/prism-rpg.mjs index ab093a4..73436f6 100644 --- a/prism-rpg.mjs +++ b/prism-rpg.mjs @@ -171,15 +171,19 @@ if (foundry.utils.isNewerVersion(game.version, "12.0",)) { } Hooks.on(hookName, (message, html, data) => { const typeMessage = data.message.flags.prismRPG?.typeMessage + + // Convertir html en jQuery pour compatibilité avec le code existant si nécessaire + const $html = html instanceof jQuery ? html : $(html) + // Message de demande de jet de dés if (typeMessage === "askRoll") { // Affichage des boutons de jet de dés uniquement pour les joueurs if (game.user.isGM) { - html.find(".ask-roll-dice").each((i, btn) => { + $html.find(".ask-roll-dice").each((i, btn) => { btn.style.display = "none" }) } else { - html.find(".ask-roll-dice").click((event) => { + $html.find(".ask-roll-dice").click((event) => { const btn = $(event.currentTarget) const type = btn.data("type") const value = btn.data("value") @@ -190,6 +194,21 @@ Hooks.on(hookName, (message, html, data) => { }) } } + + // Handle Roll Damage button click in weapon attack messages + $html.find(".roll-damage-button").click(async (event) => { + const btn = event.currentTarget + const actorId = btn.dataset.actorId + const weaponId = btn.dataset.weaponId + + const actor = game.actors.get(actorId) + if (!actor) { + ui.notifications.error("Actor not found") + return + } + + await actor.prepareRoll("weapon-damage-medium", weaponId) + }) }) Hooks.on("getCombatTrackerEntryContext", (html, options) => { diff --git a/styles/chat.less b/styles/chat.less index 7c6ce7a..60429d0 100644 --- a/styles/chat.less +++ b/styles/chat.less @@ -1,33 +1,406 @@ -&.fortune { - img { - border: 0px; +// Chat Message Card Styles - Applied globally for chat messages +.chat-log .message-content { + .prismrpg-chat-card { + font-family: var(--font-primary); + border-radius: 6px; + overflow: hidden; + background: linear-gradient(135deg, rgba(0,0,0,0.05) 0%, rgba(0,0,0,0.02) 100%); + border: 1px solid rgba(0,0,0,0.2); + margin: 2px 0; + + .chat-header { + display: flex; + gap: 8px; + padding: 6px 8px; + background: linear-gradient(135deg, #2c2c2c 0%, #1a1a1a 100%); + border-bottom: 1px solid #444; + + .chat-portrait { + flex-shrink: 0; + width: 36px; + height: 36px; + border-radius: 50%; + overflow: hidden; + border: 2px solid #666; + box-shadow: 0 1px 4px rgba(0,0,0,0.4); + + img { + width: 100%; + height: 100%; + object-fit: cover; } - .intro-chat { - border-radius: 20px; - display: flex; - flex-direction: row; - .intro-img { - padding: 5px; - width: 80px; - align-self: center; + } + + .chat-title { + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; + gap: 2px; + + .actor-name { + font-weight: bold; + font-size: 0.95em; + color: #e0e0e0; + text-shadow: 1px 1px 2px rgba(0,0,0,0.5); + line-height: 1.1; + } + + .roll-name { + font-size: 0.8em; + color: #aaa; + line-height: 1.1; + } + + .roll-type-badge { + display: inline-block; + padding: 1px 6px; + border-radius: 10px; + font-size: 0.7em; + font-weight: bold; + text-transform: uppercase; + margin-top: 2px; + width: fit-content; + + &.attack { + background: linear-gradient(135deg, #c41e3a 0%, #8b0000 100%); + color: white; + box-shadow: 0 1px 2px rgba(196, 30, 58, 0.4); } - .intro-right { + } + + .bad-result { + color: #ff6b6b; + font-size: 0.75em; + margin-top: 1px; + } + } + } + + .chat-content { + padding: 6px 8px; + display: flex; + flex-direction: column; + gap: 6px; + + .weapon-info-card, + .spell-info-card { + background: rgba(255, 255, 255, 0.05); + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 4px; + padding: 6px; + + .weapon-header, + .spell-header { + display: flex; + align-items: center; + gap: 6px; + margin-bottom: 4px; + padding-bottom: 4px; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); + + .weapon-name, + .spell-name { + font-size: 0.95em; + color: #d4af37; + } + + .badge { + padding: 1px 4px; + border-radius: 3px; + font-size: 0.65em; + font-weight: bold; + text-transform: uppercase; + + &.implement { + background: #4a5cf7; + color: white; + } + + &.upcast { + background: #9b59b6; + color: white; + } + } + + .attribute-used { + font-size: 0.75em; + color: #999; + font-style: italic; + } + } + + .weapon-stats, + .spell-stats { + display: flex; + flex-wrap: wrap; + gap: 4px; + + .stat-item { display: flex; - flex-direction: column; - .introText { - font-family: var(--font-secondary); - font-size: calc(var(--font-size-standard) * 1.2); - width: 210px; - text-align: center; + align-items: center; + gap: 3px; + padding: 2px 6px; + background: rgba(0, 0, 0, 0.2); + border-radius: 3px; + font-size: 0.75em; + + i { + color: #888; + font-size: 0.85em; + } + + &.apc { + background: rgba(255, 193, 7, 0.2); + border: 1px solid rgba(255, 193, 7, 0.4); + i { color: #ffc107; } + } + + &.damage { + background: rgba(244, 67, 54, 0.2); + border: 1px solid rgba(244, 67, 54, 0.4); + i { color: #f44336; } + } + + &.range { + background: rgba(76, 175, 80, 0.2); + border: 1px solid rgba(76, 175, 80, 0.4); + i { color: #4caf50; } + } + + &.reload { + background: rgba(255, 152, 0, 0.2); + border: 1px solid rgba(255, 152, 0, 0.4); + i { color: #ff9800; } + } + + &.mana { + background: rgba(33, 150, 243, 0.2); + border: 1px solid rgba(33, 150, 243, 0.4); + i { color: #2196f3; } + } + + &.upkeep { + background: rgba(156, 39, 176, 0.2); + border: 1px solid rgba(156, 39, 176, 0.4); + i { color: #9c27b0; } + } + + &.characteristic { + background: rgba(103, 58, 183, 0.2); + border: 1px solid rgba(103, 58, 183, 0.4); + i { color: #673ab7; } } } } - .button.control, .fortune-accepted { - display: flex; - justify-content: center; - align-items: center; - font-size: calc(var(--font-size-standard) * 1.3); + } + + .special-badge { + display: inline-block; + padding: 2px 6px; + background: linear-gradient(135deg, #ff6b6b 0%, #c92a2a 100%); + color: white; + border-radius: 3px; + font-size: 0.75em; + font-weight: bold; + width: fit-content; + box-shadow: 0 1px 2px rgba(255, 107, 107, 0.4); + } + + .aiming-info { + display: flex; + align-items: center; + gap: 4px; + padding: 3px 6px; + background: rgba(76, 175, 80, 0.1); + border-left: 2px solid #4caf50; + border-radius: 3px; + font-size: 0.8em; + + i { + color: #4caf50; } + } + + .formula-display { + display: flex; + align-items: center; + gap: 6px; + padding: 4px 8px; + background: rgba(0, 0, 0, 0.3); + border-radius: 3px; + font-family: 'Courier New', monospace; + font-size: 0.85em; + border: 1px dashed rgba(255, 255, 255, 0.2); + + i { + color: #888; + } + } + + .modifier-info { + display: flex; + gap: 8px; + padding: 3px 6px; + background: rgba(255, 255, 255, 0.05); + border-radius: 3px; + font-size: 0.75em; + + span { + color: #aaa; + } + } + + .dice-breakdown { + display: flex; + align-items: center; + gap: 4px; + padding: 2px 6px; + background: rgba(255, 255, 255, 0.05); + border-radius: 3px; + font-size: 0.8em; + + i { + color: #888; + } + } + + .roll-damage-button { + padding: 4px 10px; + background: linear-gradient(135deg, #c41e3a 0%, #8b0000 100%); + color: white; + border: none; + border-radius: 4px; + font-weight: bold; + font-size: 0.85em; + cursor: pointer; + display: flex; + align-items: center; + gap: 6px; + justify-content: center; + transition: all 0.2s; + box-shadow: 0 1px 3px rgba(196, 30, 58, 0.4); + margin-top: 4px; + + &:hover { + transform: translateY(-1px); + box-shadow: 0 2px 4px rgba(196, 30, 58, 0.6); + } + + i { + font-size: 1em; + } + } + } + + .roll-result { + padding: 8px; + background: linear-gradient(135deg, rgba(212, 175, 55, 0.2) 0%, rgba(212, 175, 55, 0.1) 100%); + border-top: 1px solid rgba(212, 175, 55, 0.5); + + .result-total { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + + .total-label { + font-size: 0.85em; + color: #aaa; + text-transform: uppercase; + letter-spacing: 0.5px; + } + + .total-value { + font-size: 1.8em; + font-weight: bold; + color: #d4af37; + text-shadow: 0 1px 3px rgba(212, 175, 55, 0.5); + line-height: 1; + } + } + + .d30-result { + margin-top: 4px; + padding: 3px 6px; + background: rgba(0, 0, 0, 0.2); + border-radius: 3px; + font-size: 0.8em; + text-align: center; + + i { + color: #d4af37; + margin-right: 4px; + } + } + } + + .result-badge { + padding: 6px; + text-align: center; + font-size: 0.9em; + font-weight: bold; + border-top: 1px solid; + + &.success { + background: linear-gradient(135deg, rgba(76, 175, 80, 0.3) 0%, rgba(76, 175, 80, 0.1) 100%); + border-color: #4caf50; + color: #4caf50; + } + + &.failure { + background: linear-gradient(135deg, rgba(244, 67, 54, 0.3) 0%, rgba(244, 67, 54, 0.1) 100%); + border-color: #f44336; + color: #f44336; + } + + i { + margin-right: 6px; + font-size: 1em; + } + } + + .damage-info { + padding: 6px; + background: rgba(255, 255, 255, 0.05); + border-radius: 3px; + font-size: 0.8em; + color: #aaa; + } +} + +// Old fortune and ask-roll styles +&.fortune { + img { + border: 0px; + } + .intro-chat { + border-radius: 20px; + display: flex; + flex-direction: row; + .intro-img { + padding: 5px; + width: 80px; + align-self: center; + } + .intro-right { + display: flex; + flex-direction: column; + .introText { + font-family: var(--font-secondary); + font-size: calc(var(--font-size-standard) * 1.2); + width: 210px; + text-align: center; + } + } + } + .button.control, + .fortune-accepted { + display: flex; + justify-content: center; + align-items: center; + font-size: calc(var(--font-size-standard) * 1.3); + } } &.ask-roll { @@ -37,4 +410,5 @@ justify-content: center; font-family: var(--font-secondary); font-size: calc(var(--font-size-standard) * 1.2); -} \ No newline at end of file +} +} diff --git a/styles/fvtt-prism-rpg.less b/styles/fvtt-prism-rpg.less index d5a7f48..6546247 100644 --- a/styles/fvtt-prism-rpg.less +++ b/styles/fvtt-prism-rpg.less @@ -13,7 +13,6 @@ @import "armor.less"; @import "spell.less"; @import "vulnerability.less"; - @import "chat.less"; @import "equipment.less"; @import "shield.less"; @import "miracle.less"; @@ -24,5 +23,7 @@ @import "weapon-types-config.less"; } +@import "chat.less"; @import "roll.less"; +@import "roll-dialog-modern.less"; @import "hud.less"; diff --git a/styles/roll-dialog-modern.less b/styles/roll-dialog-modern.less new file mode 100644 index 0000000..ad14d0b --- /dev/null +++ b/styles/roll-dialog-modern.less @@ -0,0 +1,305 @@ +// Modern Roll Dialog Styling +// Matches the new chat message card design + +.prismrpg-roll-dialog-modern { + font-family: var(--font-primary); + + // Header section + .dialog-header { + background: linear-gradient(135deg, rgba(33, 33, 33, 0.95) 0%, rgba(66, 66, 66, 0.95) 100%); + border-bottom: 2px solid #d4af37; + padding: 8px 10px; + margin: -8px -8px 8px -8px; + border-radius: 4px 4px 0 0; + + .character-info { + display: flex; + flex-direction: column; + gap: 4px; + + .character-name { + font-size: 1em; + font-weight: bold; + color: #d4af37; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5); + } + + .item-name { + display: flex; + align-items: center; + gap: 4px; + font-size: 0.9em; + color: #e0e0e0; + + i { + color: #d4af37; + font-size: 0.85em; + } + + &.weapon i { + color: #f44336; + } + + &.spell i { + color: #9c27b0; + } + } + } + } + + // Content area + .dialog-content { + display: flex; + flex-direction: column; + gap: 8px; + } + + // Section styling + .option-section { + background: rgba(0, 0, 0, 0.1); + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 4px; + padding: 6px 8px; + + &.weapon-section { + border-left: 3px solid #f44336; + } + + &.spell-section { + border-left: 3px solid #9c27b0; + } + + .section-title { + display: flex; + align-items: center; + gap: 4px; + font-weight: bold; + font-size: 0.9em; + color: var(--color-dark-1); + margin-bottom: 6px; + padding-bottom: 4px; + border-bottom: 1px solid rgba(0, 0, 0, 0.15); + + i { + color: #d4af37; + font-size: 0.85em; + } + } + } + + // Info displays (ranges, spell stats) + .info-display { + display: flex; + align-items: center; + gap: 4px; + padding: 4px 6px; + background: rgba(0, 0, 0, 0.15); + border-radius: 3px; + margin-bottom: 6px; + font-size: 0.8em; + color: #666; + + i { + color: #4caf50; + font-size: 0.85em; + } + + .info-text { + flex: 1; + } + } + + // Spell info badges (like in chat) + .spell-info-display { + display: flex; + flex-wrap: wrap; + gap: 4px; + margin-bottom: 6px; + + .info-badge { + display: flex; + align-items: center; + gap: 3px; + padding: 3px 6px; + border-radius: 3px; + font-size: 0.75em; + font-weight: 500; + + i { + font-size: 0.85em; + } + + &.characteristic { + background: rgba(103, 58, 183, 0.2); + border: 1px solid rgba(103, 58, 183, 0.4); + color: #673ab7; + i { color: #673ab7; } + } + + &.mana { + background: rgba(33, 150, 243, 0.2); + border: 1px solid rgba(33, 150, 243, 0.4); + color: #2196f3; + i { color: #2196f3; } + } + + &.apc { + background: rgba(255, 193, 7, 0.2); + border: 1px solid rgba(255, 193, 7, 0.4); + color: #ffc107; + i { color: #ffc107; } + } + + &.upkeep { + background: rgba(156, 39, 176, 0.2); + border: 1px solid rgba(156, 39, 176, 0.4); + color: #9c27b0; + i { color: #9c27b0; } + } + } + } + + // Option rows (label + control) + .option-row { + display: flex; + align-items: center; + gap: 6px; + margin-bottom: 4px; + + &:last-child { + margin-bottom: 0; + } + + label { + font-size: 0.85em; + font-weight: 500; + color: var(--color-dark-2); + min-width: 90px; + } + + select { + flex: 1; + } + } + + // Styled selects + .styled-select { + background: rgba(255, 255, 255, 0.9); + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 3px; + padding: 4px 8px; + font-size: 0.85em; + color: var(--color-dark-1); + cursor: pointer; + transition: all 0.2s ease; + + &:hover { + border-color: #d4af37; + box-shadow: 0 0 4px rgba(212, 175, 55, 0.3); + } + + &:focus { + outline: none; + border-color: #d4af37; + box-shadow: 0 0 6px rgba(212, 175, 55, 0.5); + } + + &.advantage-select, + &.modifier-select { + text-align: center; + font-weight: 500; + } + } + + // Checkbox group + .checkbox-group { + display: flex; + flex-direction: column; + gap: 3px; + margin-bottom: 6px; + + .checkbox-label { + display: flex; + align-items: center; + cursor: pointer; + padding: 3px 4px; + border-radius: 3px; + transition: background 0.2s ease; + + &:hover { + background: rgba(0, 0, 0, 0.05); + } + + input[type="checkbox"] { + margin-right: 6px; + cursor: pointer; + width: 14px; + height: 14px; + accent-color: #d4af37; + } + + .checkbox-text { + display: flex; + align-items: center; + gap: 4px; + font-size: 0.8em; + color: var(--color-dark-2); + + i { + color: #888; + font-size: 0.85em; + } + } + + input[type="checkbox"]:checked ~ .checkbox-text { + color: var(--color-dark-1); + font-weight: 500; + + i { + color: #d4af37; + } + } + } + } +} + +// Dialog application styling +.application.dialog.prismrpg { + .window-content { + background: linear-gradient(135deg, #f5f5f5 0%, #e0e0e0 100%); + padding: 8px; + } + + // Make buttons match the modern style + .dialog-buttons { + padding: 6px 8px; + gap: 6px; + + button { + background: linear-gradient(135deg, #4a4a4a 0%, #6a6a6a 100%); + border: 1px solid #3a3a3a; + color: white; + font-weight: 600; + padding: 6px 12px; + border-radius: 4px; + transition: all 0.2s ease; + + &:hover { + background: linear-gradient(135deg, #5a5a5a 0%, #7a7a7a 100%); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + transform: translateY(-1px); + } + + &.default, + &[data-button="roll"] { + background: linear-gradient(135deg, #d4af37 0%, #f4cf67 100%); + border-color: #b49030; + color: #2a2a2a; + + &:hover { + background: linear-gradient(135deg, #e4bf47 0%, #ffdf77 100%); + } + } + } + } +} diff --git a/styles/roll.less b/styles/roll.less index 4c5fadb..da477e3 100644 --- a/styles/roll.less +++ b/styles/roll.less @@ -25,6 +25,30 @@ border-radius: 4px; padding: 0.5rem; } + + .dialog-weapon-options { + margin-top: 8px; + + .dialog-save { + margin: 4px 0; + + label { + display: flex; + align-items: center; + cursor: pointer; + + input[type="checkbox"] { + margin-right: 8px; + cursor: pointer; + } + } + + select { + margin-left: 8px; + min-width: 10rem; + } + } + } } .prismrpg-range-defense-dialog { @@ -156,4 +180,34 @@ font-size: calc(var(--font-size-standard) * 1); text-shadow: 0 0 10px var(--color-shadow-primary); } + + // Roll Damage button in weapon attack messages + .damage-roll-button { + margin-top: 0.5em; + margin-bottom: 0.25em; + + .roll-damage-button { + background: linear-gradient(135deg, #8b0000 0%, #dc143c 100%); + color: white; + border: 1px solid #6b0000; + border-radius: 3px; + padding: 4px 10px; + font-weight: 600; + cursor: pointer; + font-size: 0.85em; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); + transition: all 0.2s ease; + + &:hover { + background: linear-gradient(135deg, #a00000 0%, #ff1744 100%); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); + transform: translateY(-1px); + } + + i { + margin-right: 4px; + font-size: 0.9em; + } + } + } } diff --git a/templates/character-biography.hbs b/templates/character-biography.hbs index f8dd423..6a46fab 100644 --- a/templates/character-biography.hbs +++ b/templates/character-biography.hbs @@ -82,13 +82,13 @@ value=system.biodata.magicUser }} -