Phase 2.5: Skill Check System Implementation
Features:
- ApplicationV2-based roll dialogs with HandlebarsApplicationMixin
- Base VagabondRollDialog class for shared dialog functionality
- SkillCheckDialog for skill checks with auto-calculated difficulty
- Favor/Hinder system using Active Effects flags (simplified from schema)
- FavorHinderDebug panel for testing flags without actor sheets
- Auto-created development macros (Favor/Hinder Debug, Skill Check)
- Custom chat cards for skill roll results
Technical Changes:
- Removed favorHinder from character schema (now uses flags)
- Updated getNetFavorHinder() to use flag-based approach
- Returns { net, favorSources, hinderSources } for transparency
- Universal form styling fixes for Foundry dark theme compatibility
- Added Macro to ESLint globals
Flag Convention:
- flags.vagabond.favor.skills.<skillId>
- flags.vagabond.hinder.skills.<skillId>
- flags.vagabond.favor.attacks
- flags.vagabond.hinder.attacks
- flags.vagabond.favor.saves.<saveType>
- flags.vagabond.hinder.saves.<saveType>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
335 lines
6.5 KiB
SCSS
335 lines
6.5 KiB
SCSS
// Vagabond RPG - Chat Card Styles
|
|
// ================================
|
|
|
|
// Base chat card
|
|
.vagabond.chat-card {
|
|
@include panel;
|
|
overflow: hidden;
|
|
|
|
// Card header
|
|
.card-header {
|
|
@include flex-between;
|
|
padding: $spacing-2 $spacing-3;
|
|
background-color: $color-parchment-dark;
|
|
border-bottom: 1px solid $color-border;
|
|
|
|
.card-title,
|
|
h3 {
|
|
font-family: $font-family-header;
|
|
font-size: $font-size-base;
|
|
font-weight: $font-weight-bold;
|
|
margin: 0;
|
|
}
|
|
|
|
.card-subtitle {
|
|
font-size: $font-size-sm;
|
|
color: $color-text-muted;
|
|
}
|
|
|
|
.trained-badge {
|
|
font-size: $font-size-xs;
|
|
padding: $spacing-1 $spacing-2;
|
|
background-color: rgba($color-success, 0.2);
|
|
color: $color-success;
|
|
border-radius: $radius-full;
|
|
font-weight: $font-weight-medium;
|
|
}
|
|
}
|
|
|
|
// Card content
|
|
.card-content {
|
|
padding: $spacing-3;
|
|
}
|
|
|
|
// Roll result (large total display)
|
|
.roll-result {
|
|
@include flex-column;
|
|
align-items: center;
|
|
gap: $spacing-2;
|
|
padding: $spacing-3;
|
|
margin: $spacing-2 0;
|
|
background-color: $color-parchment-light;
|
|
border-radius: $radius-md;
|
|
border: 2px solid $color-border;
|
|
|
|
.roll-total {
|
|
font-family: $font-family-header;
|
|
font-size: $font-size-4xl;
|
|
font-weight: $font-weight-bold;
|
|
line-height: 1;
|
|
}
|
|
|
|
.roll-status {
|
|
.status {
|
|
display: inline-block;
|
|
padding: $spacing-1 $spacing-3;
|
|
font-size: $font-size-sm;
|
|
font-weight: $font-weight-bold;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.1em;
|
|
border-radius: $radius-md;
|
|
}
|
|
|
|
.success {
|
|
background-color: rgba($color-success, 0.2);
|
|
color: $color-success;
|
|
}
|
|
|
|
.failure {
|
|
background-color: rgba($color-danger, 0.2);
|
|
color: $color-danger;
|
|
}
|
|
|
|
.critical {
|
|
background-color: rgba($color-warning, 0.3);
|
|
color: $color-warning;
|
|
animation: pulse 1s ease-in-out;
|
|
}
|
|
|
|
.fumble {
|
|
background-color: rgba($color-danger, 0.3);
|
|
color: $color-danger;
|
|
animation: shake 0.5s ease-in-out;
|
|
}
|
|
}
|
|
|
|
// Conditional styling based on result
|
|
&.success {
|
|
border-color: $color-success;
|
|
}
|
|
|
|
&.failure {
|
|
border-color: $color-danger;
|
|
}
|
|
|
|
&.critical {
|
|
border-color: $color-warning;
|
|
background-color: rgba($color-warning, 0.1);
|
|
}
|
|
|
|
&.fumble {
|
|
border-color: $color-danger;
|
|
background-color: rgba($color-danger, 0.1);
|
|
}
|
|
}
|
|
|
|
// Roll details
|
|
.roll-details {
|
|
@include flex-column;
|
|
gap: $spacing-2;
|
|
padding: $spacing-2;
|
|
background-color: $color-parchment-dark;
|
|
border-radius: $radius-md;
|
|
font-size: $font-size-sm;
|
|
|
|
.roll-formula {
|
|
@include flex-between;
|
|
|
|
.label {
|
|
color: $color-text-muted;
|
|
}
|
|
|
|
.value {
|
|
font-family: $font-family-mono;
|
|
}
|
|
}
|
|
|
|
.roll-breakdown {
|
|
@include flex-center;
|
|
gap: $spacing-3;
|
|
padding-top: $spacing-2;
|
|
border-top: 1px solid $color-border;
|
|
|
|
span {
|
|
@include flex-center;
|
|
gap: $spacing-1;
|
|
}
|
|
|
|
.d20-result {
|
|
font-family: $font-family-mono;
|
|
font-weight: $font-weight-bold;
|
|
}
|
|
|
|
.favor-die {
|
|
font-family: $font-family-mono;
|
|
|
|
&.favor {
|
|
color: $color-success;
|
|
}
|
|
|
|
&.hinder {
|
|
color: $color-danger;
|
|
}
|
|
}
|
|
|
|
.modifier {
|
|
font-family: $font-family-mono;
|
|
color: $color-text-secondary;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Target info (difficulty, crit threshold)
|
|
.target-info {
|
|
@include grid(2, $spacing-2);
|
|
margin-top: $spacing-2;
|
|
padding: $spacing-2;
|
|
font-size: $font-size-sm;
|
|
|
|
> div {
|
|
@include flex-between;
|
|
}
|
|
|
|
.label {
|
|
color: $color-text-muted;
|
|
}
|
|
|
|
.value {
|
|
font-weight: $font-weight-medium;
|
|
}
|
|
}
|
|
|
|
// Favor/Hinder sources
|
|
.favor-sources,
|
|
.hinder-sources {
|
|
@include flex-center;
|
|
gap: $spacing-2;
|
|
margin-top: $spacing-2;
|
|
padding: $spacing-2;
|
|
font-size: $font-size-sm;
|
|
border-radius: $radius-md;
|
|
}
|
|
|
|
.favor-sources {
|
|
background-color: rgba($color-success, 0.1);
|
|
color: $color-success;
|
|
}
|
|
|
|
.hinder-sources {
|
|
background-color: rgba($color-danger, 0.1);
|
|
color: $color-danger;
|
|
}
|
|
|
|
// Result status (legacy)
|
|
.result-status {
|
|
@include flex-center;
|
|
padding: $spacing-2;
|
|
font-weight: $font-weight-bold;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.1em;
|
|
border-radius: $radius-md;
|
|
|
|
&.success {
|
|
background-color: rgba($color-success, 0.2);
|
|
color: $color-success;
|
|
}
|
|
|
|
&.failure {
|
|
background-color: rgba($color-danger, 0.2);
|
|
color: $color-danger;
|
|
}
|
|
|
|
&.critical {
|
|
background-color: rgba($color-warning, 0.2);
|
|
color: $color-warning;
|
|
animation: pulse 1s ease-in-out;
|
|
}
|
|
}
|
|
|
|
// Damage display
|
|
.damage-display {
|
|
@include flex-center;
|
|
gap: $spacing-2;
|
|
margin-top: $spacing-3;
|
|
padding: $spacing-2;
|
|
background-color: rgba($color-danger, 0.1);
|
|
border: 1px solid $color-danger;
|
|
border-radius: $radius-md;
|
|
|
|
.damage-label {
|
|
font-size: $font-size-sm;
|
|
color: $color-text-secondary;
|
|
}
|
|
|
|
.damage-value {
|
|
font-family: $font-family-header;
|
|
font-size: $font-size-xl;
|
|
font-weight: $font-weight-bold;
|
|
color: $color-danger;
|
|
}
|
|
|
|
.damage-type {
|
|
font-size: $font-size-sm;
|
|
color: $color-text-muted;
|
|
}
|
|
}
|
|
|
|
// Card buttons (for interactive cards)
|
|
.card-buttons {
|
|
display: flex;
|
|
gap: $spacing-2;
|
|
padding: $spacing-3;
|
|
border-top: 1px solid $color-border;
|
|
|
|
button {
|
|
flex: 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Skill roll card specific
|
|
.vagabond.chat-card.skill-roll {
|
|
.skill-name {
|
|
margin: 0;
|
|
}
|
|
}
|
|
|
|
// Spell card specific
|
|
.vagabond.chat-card.spell-card {
|
|
.spell-effect {
|
|
padding: $spacing-3;
|
|
font-style: italic;
|
|
border-left: 3px solid $color-accent-primary;
|
|
background-color: $color-parchment-light;
|
|
margin: $spacing-3 0;
|
|
}
|
|
|
|
.spell-meta {
|
|
@include grid(2, $spacing-2);
|
|
font-size: $font-size-sm;
|
|
|
|
.meta-item {
|
|
@include flex-between;
|
|
padding: $spacing-1;
|
|
|
|
.meta-label {
|
|
color: $color-text-muted;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Animations
|
|
@keyframes pulse {
|
|
0%,
|
|
100% {
|
|
transform: scale(1);
|
|
}
|
|
50% {
|
|
transform: scale(1.05);
|
|
}
|
|
}
|
|
|
|
@keyframes shake {
|
|
0%,
|
|
100% {
|
|
transform: translateX(0);
|
|
}
|
|
25% {
|
|
transform: translateX(-5px);
|
|
}
|
|
75% {
|
|
transform: translateX(5px);
|
|
}
|
|
}
|