Ajout de la possibilité de faire des stats blocs pour tout ce qui est ennemis / créatures adverses.
All checks were successful
All checks were successful
Dorénavant, l'IA est capable de prendre en compte le format des quêtes, chapitres, Arc.... pour proposer des blocs plus complets. Les ennemis sont également référençables directement dans la campagne. Les références vers les ennemis dans la partie "donjon" est en cours d'ajout
This commit is contained in:
@@ -4,6 +4,7 @@ import { CampaignService } from './campaign.service';
|
||||
import { CharacterService } from './character.service';
|
||||
import { NpcService } from './npc.service';
|
||||
import { RandomTableService } from './random-table.service';
|
||||
import { EnemyService } from './enemy.service';
|
||||
import { LayoutService } from './layout.service';
|
||||
import { loadCampaignTreeData, buildCampaignSidebarConfig } from '../campaigns/campaign-tree.helper';
|
||||
|
||||
@@ -28,6 +29,7 @@ export class CampaignSidebarService {
|
||||
private characterService: CharacterService,
|
||||
private npcService: NpcService,
|
||||
private randomTableService: RandomTableService,
|
||||
private enemyService: EnemyService,
|
||||
private layoutService: LayoutService
|
||||
) {}
|
||||
|
||||
@@ -45,7 +47,8 @@ export class CampaignSidebarService {
|
||||
campaignId,
|
||||
this.characterService,
|
||||
this.npcService,
|
||||
this.randomTableService
|
||||
this.randomTableService,
|
||||
this.enemyService
|
||||
)
|
||||
}).subscribe(({ campaign, allCampaigns, treeData }) => {
|
||||
this.layoutService.show(buildCampaignSidebarConfig(campaign, allCampaigns, treeData, campaignId));
|
||||
|
||||
@@ -202,6 +202,8 @@ export interface Room {
|
||||
name: string;
|
||||
description?: string;
|
||||
enemies?: string;
|
||||
/** IDs des fiches du bestiaire présentes dans la pièce (weak refs). */
|
||||
enemyIds?: string[];
|
||||
loot?: string;
|
||||
traps?: string;
|
||||
gmNotes?: string;
|
||||
@@ -231,6 +233,9 @@ export interface Scene {
|
||||
combatDifficulty?: string;
|
||||
enemies?: string;
|
||||
|
||||
/** IDs des fiches du bestiaire engagées dans la rencontre (weak refs). */
|
||||
enemyIds?: string[];
|
||||
|
||||
relatedPageIds?: string[];
|
||||
illustrationImageIds?: string[];
|
||||
mapImageIds?: string[];
|
||||
@@ -258,6 +263,9 @@ export interface SceneCreate {
|
||||
combatDifficulty?: string;
|
||||
enemies?: string;
|
||||
|
||||
/** IDs des fiches du bestiaire engagées dans la rencontre (weak refs). */
|
||||
enemyIds?: string[];
|
||||
|
||||
relatedPageIds?: string[];
|
||||
illustrationImageIds?: string[];
|
||||
mapImageIds?: string[];
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Character, CharacterCreate } from './character.model';
|
||||
|
||||
/** Résultat de recherche d'un PJ, enrichi du campaignId pour la navigation. */
|
||||
export interface CharacterSearchResult {
|
||||
id: string;
|
||||
name: string;
|
||||
playthroughId: string;
|
||||
campaignId: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Service HTTP pour les fiches de personnages (PJ) d'une campagne.
|
||||
*/
|
||||
@@ -31,4 +39,10 @@ export class CharacterService {
|
||||
delete(id: string): Observable<void> {
|
||||
return this.http.delete<void>(`${this.apiUrl}/${id}`);
|
||||
}
|
||||
|
||||
/** Recherche par nom — alimente la recherche globale (Ctrl+K). */
|
||||
search(q: string): Observable<CharacterSearchResult[]> {
|
||||
const params = new HttpParams().set('q', q);
|
||||
return this.http.get<CharacterSearchResult[]>(`${this.apiUrl}/search`, { params });
|
||||
}
|
||||
}
|
||||
|
||||
32
web/src/app/services/enemy.model.ts
Normal file
32
web/src/app/services/enemy.model.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Fiche d'ennemi (monstre/créature) d'une campagne — le bestiaire du MJ.
|
||||
* Même structure de templating que Npc : champs pilotés par le template
|
||||
* ENNEMI du GameSystem, + champs universels niveau/dossier.
|
||||
*/
|
||||
export interface Enemy {
|
||||
id?: string;
|
||||
name: string;
|
||||
/** Niveau / FP / dangerosité — texte libre (« 5 », « FP 8 », « Boss »). */
|
||||
level?: string | null;
|
||||
/** Dossier de classement (« Démons », « Humanoïdes »…). Vide = non classé. */
|
||||
folder?: string | null;
|
||||
portraitImageId?: string | null;
|
||||
headerImageId?: string | null;
|
||||
values?: Record<string, string>;
|
||||
imageValues?: Record<string, string[]>;
|
||||
keyValueValues?: Record<string, Record<string, string>>;
|
||||
campaignId: string;
|
||||
order?: number;
|
||||
}
|
||||
|
||||
export interface EnemyCreate {
|
||||
name: string;
|
||||
level?: string | null;
|
||||
folder?: string | null;
|
||||
portraitImageId?: string | null;
|
||||
headerImageId?: string | null;
|
||||
values?: Record<string, string>;
|
||||
imageValues?: Record<string, string[]>;
|
||||
keyValueValues?: Record<string, Record<string, string>>;
|
||||
campaignId: string;
|
||||
}
|
||||
39
web/src/app/services/enemy.service.ts
Normal file
39
web/src/app/services/enemy.service.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Enemy, EnemyCreate } from './enemy.model';
|
||||
|
||||
/**
|
||||
* Service HTTP des fiches d'ennemis (bestiaire de campagne).
|
||||
*/
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class EnemyService {
|
||||
private apiUrl = '/api/enemies';
|
||||
|
||||
constructor(private http: HttpClient) {}
|
||||
|
||||
getByCampaign(campaignId: string): Observable<Enemy[]> {
|
||||
return this.http.get<Enemy[]>(`${this.apiUrl}/campaign/${campaignId}`);
|
||||
}
|
||||
|
||||
getById(id: string): Observable<Enemy> {
|
||||
return this.http.get<Enemy>(`${this.apiUrl}/${id}`);
|
||||
}
|
||||
|
||||
create(payload: EnemyCreate): Observable<Enemy> {
|
||||
return this.http.post<Enemy>(this.apiUrl, payload);
|
||||
}
|
||||
|
||||
update(id: string, payload: Enemy): Observable<Enemy> {
|
||||
return this.http.put<Enemy>(`${this.apiUrl}/${id}`, payload);
|
||||
}
|
||||
|
||||
delete(id: string): Observable<void> {
|
||||
return this.http.delete<void>(`${this.apiUrl}/${id}`);
|
||||
}
|
||||
|
||||
/** Recherche par nom — alimente la recherche globale (Ctrl+K). */
|
||||
search(q: string): Observable<Enemy[]> {
|
||||
return this.http.get<Enemy[]>(`${this.apiUrl}/search`, { params: { q } });
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ export interface GameSystem {
|
||||
rulesMarkdown?: string | null;
|
||||
characterTemplate?: TemplateField[];
|
||||
npcTemplate?: TemplateField[];
|
||||
enemyTemplate?: TemplateField[];
|
||||
author?: string | null;
|
||||
isPublic?: boolean;
|
||||
}
|
||||
@@ -50,6 +51,7 @@ export interface GameSystemCreate {
|
||||
rulesMarkdown?: string | null;
|
||||
characterTemplate?: TemplateField[];
|
||||
npcTemplate?: TemplateField[];
|
||||
enemyTemplate?: TemplateField[];
|
||||
author?: string | null;
|
||||
isPublic: boolean;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,11 @@ export class ItemCatalogService {
|
||||
return this.http.delete<void>(`${this.apiUrl}/${id}`);
|
||||
}
|
||||
|
||||
/** Recherche par nom — alimente la recherche globale (Ctrl+K). */
|
||||
search(q: string): Observable<ItemCatalog[]> {
|
||||
return this.http.get<ItemCatalog[]>(`${this.apiUrl}/search`, { params: { q } });
|
||||
}
|
||||
|
||||
/** Génère une PROPOSITION de catalogue via l'IA (non persistée) à préremplir. */
|
||||
generate(campaignId: string, description: string): Observable<ItemCatalog> {
|
||||
return this.http.post<ItemCatalog>(`${this.apiUrl}/generate`, { campaignId, description });
|
||||
|
||||
@@ -15,10 +15,37 @@ export interface NotebookAction {
|
||||
type: 'npc' | 'scene' | 'chapter' | 'arc' | 'table';
|
||||
name: string;
|
||||
description?: string;
|
||||
/** Legacy (anciens messages) : déroulé d'une scène → repli sur playerNarration. */
|
||||
content?: string;
|
||||
arcType?: 'LINEAR' | 'HUB';
|
||||
diceFormula?: string;
|
||||
entries?: NotebookActionEntry[];
|
||||
|
||||
// --- PNJ : valeurs des champs TEXT de la fiche (clés = template du système). ---
|
||||
values?: Record<string, string>;
|
||||
|
||||
// --- Scène : champs narratifs enrichis (miroir de SceneCreate). ---
|
||||
location?: string;
|
||||
timing?: string;
|
||||
atmosphere?: string;
|
||||
playerNarration?: string;
|
||||
gmSecretNotes?: string;
|
||||
choicesConsequences?: string;
|
||||
combatDifficulty?: string;
|
||||
enemies?: string;
|
||||
|
||||
// --- Chapitre. ---
|
||||
playerObjectives?: string;
|
||||
narrativeStakes?: string;
|
||||
|
||||
// --- Chapitre + Arc. ---
|
||||
gmNotes?: string;
|
||||
|
||||
// --- Arc. ---
|
||||
themes?: string;
|
||||
stakes?: string;
|
||||
rewards?: string;
|
||||
resolution?: string;
|
||||
}
|
||||
|
||||
const ACTION_RE = /```loremind-action\s*([\s\S]*?)```/g;
|
||||
|
||||
@@ -36,4 +36,9 @@ export class NpcService {
|
||||
delete(id: string): Observable<void> {
|
||||
return this.http.delete<void>(`${this.apiUrl}/${id}`);
|
||||
}
|
||||
|
||||
/** Recherche par nom — alimente la recherche globale (Ctrl+K). */
|
||||
search(q: string): Observable<Npc[]> {
|
||||
return this.http.get<Npc[]>(`${this.apiUrl}/search`, { params: { q } });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,11 @@ export class RandomTableService {
|
||||
return this.http.delete<void>(`${this.apiUrl}/${id}`);
|
||||
}
|
||||
|
||||
/** Recherche par nom — alimente la recherche globale (Ctrl+K). */
|
||||
search(q: string): Observable<RandomTable[]> {
|
||||
return this.http.get<RandomTable[]>(`${this.apiUrl}/search`, { params: { q } });
|
||||
}
|
||||
|
||||
/** Génère une PROPOSITION de table via l'IA (non persistée) à préremplir dans le formulaire. */
|
||||
generate(campaignId: string, description: string, diceFormula: string): Observable<RandomTable> {
|
||||
return this.http.post<RandomTable>(`${this.apiUrl}/generate`, { campaignId, description, diceFormula });
|
||||
|
||||
Reference in New Issue
Block a user