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:
133
web/src/app/shared/rooms-editor/rooms-editor.component.ts
Normal file
133
web/src/app/shared/rooms-editor/rooms-editor.component.ts
Normal file
@@ -0,0 +1,133 @@
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { LucideAngularModule, Plus, Trash2, ChevronDown, ChevronUp, GitBranch, X } from 'lucide-angular';
|
||||
import { Room, RoomBranch } from '../../services/campaign.model';
|
||||
|
||||
/**
|
||||
* Éditeur de pièces (Rooms) d'une Scene explorable.
|
||||
*
|
||||
* Contrôlé : reçoit la liste, émet la nouvelle liste à chaque mutation.
|
||||
* Une pièce est repliée par défaut (juste le nom visible) ; un clic l'expand
|
||||
* pour éditer narration / ennemis / loot / pièges / notes MJ / étage.
|
||||
*
|
||||
* Les branches (graphe inter-pièces) sont éditables dans la card expand.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'app-rooms-editor',
|
||||
standalone: true,
|
||||
imports: [CommonModule, FormsModule, LucideAngularModule],
|
||||
templateUrl: './rooms-editor.component.html',
|
||||
styleUrls: ['./rooms-editor.component.scss']
|
||||
})
|
||||
export class RoomsEditorComponent {
|
||||
|
||||
@Input() rooms: Room[] = [];
|
||||
@Output() roomsChange = new EventEmitter<Room[]>();
|
||||
|
||||
readonly Plus = Plus;
|
||||
readonly Trash2 = Trash2;
|
||||
readonly ChevronDown = ChevronDown;
|
||||
readonly ChevronUp = ChevronUp;
|
||||
readonly GitBranch = GitBranch;
|
||||
readonly X = X;
|
||||
|
||||
/** Set des IDs de pièces expandées (UI state local). */
|
||||
private expanded = new Set<string>();
|
||||
|
||||
isExpanded(id: string): boolean { return this.expanded.has(id); }
|
||||
toggleExpanded(id: string): void {
|
||||
if (this.expanded.has(id)) this.expanded.delete(id);
|
||||
else this.expanded.add(id);
|
||||
}
|
||||
|
||||
/** Génère un UUID v4 simplifié pour l'ID stable d'une pièce. */
|
||||
private newId(): string {
|
||||
return 'room-' + Math.random().toString(36).slice(2) + '-' + Date.now().toString(36);
|
||||
}
|
||||
|
||||
addRoom(): void {
|
||||
const nextOrder = this.rooms.length > 0
|
||||
? Math.max(...this.rooms.map(r => r.order ?? 0)) + 1
|
||||
: 0;
|
||||
const newRoom: Room = {
|
||||
id: this.newId(),
|
||||
name: `Salle ${this.rooms.length + 1}`,
|
||||
order: nextOrder,
|
||||
branches: []
|
||||
};
|
||||
this.rooms = [...this.rooms, newRoom];
|
||||
this.expanded.add(newRoom.id);
|
||||
this.emit();
|
||||
}
|
||||
|
||||
removeRoom(room: Room): void {
|
||||
this.rooms = this.rooms
|
||||
.filter(r => r.id !== room.id)
|
||||
.map(r => ({
|
||||
...r,
|
||||
// Nettoyer les branches qui pointaient vers la pièce supprimée
|
||||
branches: (r.branches ?? []).filter(b => b.targetRoomId !== room.id)
|
||||
}));
|
||||
this.expanded.delete(room.id);
|
||||
this.emit();
|
||||
}
|
||||
|
||||
/** Patch un champ d'une pièce et émet. */
|
||||
patch(room: Room, patch: Partial<Room>): void {
|
||||
this.rooms = this.rooms.map(r => r.id === room.id ? { ...r, ...patch } : r);
|
||||
this.emit();
|
||||
}
|
||||
|
||||
// === Branches inter-pièces ===
|
||||
|
||||
addBranch(room: Room): void {
|
||||
const others = this.rooms.filter(r => r.id !== room.id);
|
||||
const branch: RoomBranch = {
|
||||
label: 'Porte',
|
||||
targetRoomId: others[0]?.id ?? '',
|
||||
condition: ''
|
||||
};
|
||||
this.patch(room, { branches: [...(room.branches ?? []), branch] });
|
||||
}
|
||||
|
||||
removeBranch(room: Room, index: number): void {
|
||||
const next = (room.branches ?? []).filter((_, i) => i !== index);
|
||||
this.patch(room, { branches: next });
|
||||
}
|
||||
|
||||
updateBranch(room: Room, index: number, patch: Partial<RoomBranch>): void {
|
||||
const next = (room.branches ?? []).map((b, i) => i === index ? { ...b, ...patch } : b);
|
||||
this.patch(room, { branches: next });
|
||||
}
|
||||
|
||||
/** Pièces candidates pour cibler une branche (toutes sauf celle d'origine). */
|
||||
otherRooms(room: Room): Room[] {
|
||||
return this.rooms.filter(r => r.id !== room.id);
|
||||
}
|
||||
|
||||
/** Réorganise l'ordre via boutons up/down (simple : swap avec voisin). */
|
||||
moveUp(room: Room): void {
|
||||
const idx = this.rooms.findIndex(r => r.id === room.id);
|
||||
if (idx <= 0) return;
|
||||
const arr = [...this.rooms];
|
||||
[arr[idx - 1], arr[idx]] = [arr[idx], arr[idx - 1]];
|
||||
this.rooms = arr.map((r, i) => ({ ...r, order: i }));
|
||||
this.emit();
|
||||
}
|
||||
|
||||
moveDown(room: Room): void {
|
||||
const idx = this.rooms.findIndex(r => r.id === room.id);
|
||||
if (idx === -1 || idx >= this.rooms.length - 1) return;
|
||||
const arr = [...this.rooms];
|
||||
[arr[idx], arr[idx + 1]] = [arr[idx + 1], arr[idx]];
|
||||
this.rooms = arr.map((r, i) => ({ ...r, order: i }));
|
||||
this.emit();
|
||||
}
|
||||
|
||||
trackById(_: number, r: Room): string { return r.id; }
|
||||
|
||||
private emit(): void {
|
||||
this.roomsChange.emit(this.rooms);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user