Plusieurs ajouts :
- Possibilité de configurer des lieux dans une scène : permet de configurer un donjon par exemple avec les pièces, les trésors par pièce, la narration..... Mise en place également de conditions permettant de conditionner le déblocage des quêtes. - Possibilité de transformé un arc en instance non linéaire afin de faire un hub. Permet de jouer de préparer des campagnes type Dragon of Icespire peak plus facilement. - Configuration de partie : chaque partie va contenir les séances, ce qui permettra de suivre le déblocage des conditions pour les quêtes. Passage en 0.9.1-beta
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
<div class="flags-manager">
|
||||
|
||||
<p class="flags-empty" *ngIf="!loading && rows.length === 0">
|
||||
Aucun fait référencé. Ajoutez une condition « Quand un fait est vrai » sur
|
||||
au moins une quête pour qu'il apparaisse ici.
|
||||
</p>
|
||||
|
||||
<ul class="flag-list" *ngIf="rows.length > 0">
|
||||
<li class="flag-row" *ngFor="let r of rows" [class.flag-on]="r.value">
|
||||
<label class="flag-toggle">
|
||||
<input type="checkbox" [checked]="r.value" (change)="toggle(r)" />
|
||||
<span class="flag-toggle-slider"></span>
|
||||
</label>
|
||||
<div class="flag-info">
|
||||
<span class="flag-name">{{ r.name }}</span>
|
||||
</div>
|
||||
<span class="flag-value">{{ r.value ? 'vrai' : 'faux' }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,133 @@
|
||||
.flags-manager {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.85rem;
|
||||
}
|
||||
|
||||
.flag-add-row {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.flag-add-input {
|
||||
flex: 1;
|
||||
padding: 0.4rem 0.6rem;
|
||||
border: 1px solid var(--color-border, #d8d8d8);
|
||||
border-radius: 6px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.flags-empty {
|
||||
margin: 0;
|
||||
font-size: 0.85rem;
|
||||
color: var(--color-text-muted, #777);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.flag-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.flag-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.6rem;
|
||||
padding: 0.5rem 0.7rem;
|
||||
border: 1px solid var(--color-border, #e2e2e2);
|
||||
border-radius: 8px;
|
||||
background: var(--color-surface, #fff);
|
||||
}
|
||||
|
||||
.flag-row.flag-on {
|
||||
border-color: rgba(52, 168, 83, 0.45);
|
||||
background: rgba(52, 168, 83, 0.05);
|
||||
}
|
||||
|
||||
.flag-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.15rem;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.flag-name {
|
||||
font-family: 'JetBrains Mono', ui-monospace, Menlo, Consolas, monospace;
|
||||
font-size: 0.88rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.flag-desc {
|
||||
font-size: 0.78rem;
|
||||
color: var(--color-text-muted, #666);
|
||||
}
|
||||
|
||||
.flag-value {
|
||||
font-size: 0.78rem;
|
||||
color: var(--color-text-muted, #777);
|
||||
min-width: 40px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.flag-remove {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--color-danger, #c0392b);
|
||||
cursor: pointer;
|
||||
padding: 0.25rem;
|
||||
border-radius: 6px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&:hover { background: rgba(192, 57, 43, 0.08); }
|
||||
}
|
||||
|
||||
// Toggle switch
|
||||
.flag-toggle {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 36px;
|
||||
height: 20px;
|
||||
flex: 0 0 36px;
|
||||
|
||||
input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
|
||||
&:checked + .flag-toggle-slider {
|
||||
background: var(--color-primary, #34a853);
|
||||
}
|
||||
|
||||
&:checked + .flag-toggle-slider::before {
|
||||
transform: translateX(16px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.flag-toggle-slider {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: #ccc;
|
||||
border-radius: 999px;
|
||||
cursor: pointer;
|
||||
transition: background 150ms ease;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
left: 2px;
|
||||
top: 2px;
|
||||
background: #fff;
|
||||
border-radius: 50%;
|
||||
transition: transform 150ms ease;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import { Component, Input, OnInit, OnChanges, SimpleChanges } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { LucideAngularModule } from 'lucide-angular';
|
||||
import { CampaignFlagService } from '../../services/campaign-flag.service';
|
||||
import { PlaythroughFlagService } from '../../services/playthrough-flag.service';
|
||||
import { forkJoin } from 'rxjs';
|
||||
|
||||
/**
|
||||
* Toggle de l'état des faits d'une Partie.
|
||||
*
|
||||
* <p>La liste des faits est déduite des conditions FLAG_SET référencées par les
|
||||
* quêtes de la campagne. Pour chaque fait référencé, on affiche un toggle qui
|
||||
* met à jour la valeur du Playthrough courant.</p>
|
||||
*/
|
||||
@Component({
|
||||
selector: 'app-playthrough-flags-manager',
|
||||
standalone: true,
|
||||
imports: [CommonModule, FormsModule, LucideAngularModule],
|
||||
templateUrl: './playthrough-flags-manager.component.html',
|
||||
styleUrls: ['./playthrough-flags-manager.component.scss']
|
||||
})
|
||||
export class PlaythroughFlagsManagerComponent implements OnInit, OnChanges {
|
||||
|
||||
@Input() campaignId!: string;
|
||||
@Input() playthroughId!: string;
|
||||
|
||||
rows: { name: string; value: boolean }[] = [];
|
||||
loading = false;
|
||||
|
||||
constructor(
|
||||
private campaignFlagService: CampaignFlagService,
|
||||
private playthroughFlagService: PlaythroughFlagService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void { this.reload(); }
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (changes['campaignId'] || changes['playthroughId']) this.reload();
|
||||
}
|
||||
|
||||
reload(): void {
|
||||
if (!this.campaignId || !this.playthroughId) {
|
||||
this.rows = [];
|
||||
return;
|
||||
}
|
||||
this.loading = true;
|
||||
forkJoin({
|
||||
referenced: this.campaignFlagService.listReferenced(this.campaignId),
|
||||
values: this.playthroughFlagService.list(this.playthroughId)
|
||||
}).subscribe({
|
||||
next: ({ referenced, values }) => {
|
||||
const valueByName: Record<string, boolean> = {};
|
||||
for (const v of values) valueByName[v.name] = v.value;
|
||||
this.rows = referenced.map(name => ({
|
||||
name,
|
||||
value: valueByName[name] === true
|
||||
}));
|
||||
this.loading = false;
|
||||
},
|
||||
error: () => { this.loading = false; }
|
||||
});
|
||||
}
|
||||
|
||||
toggle(row: { name: string; value: boolean }): void {
|
||||
const next = !row.value;
|
||||
this.playthroughFlagService.setFlag(this.playthroughId, row.name, next).subscribe({
|
||||
next: () => { row.value = next; }
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user