valtech-components 4.0.937 → 4.0.939

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/esm2022/lib/components/molecules/group-picker/group-picker.component.mjs +97 -0
  2. package/esm2022/lib/components/organisms/group-members/group-members.component.mjs +178 -0
  3. package/esm2022/lib/components/organisms/mini-games/mini-games.i18n.mjs +1 -381
  4. package/esm2022/lib/components/organisms/mini-games/types.mjs +1 -1
  5. package/esm2022/lib/services/groups/groups.service.mjs +92 -0
  6. package/esm2022/lib/services/groups/types.mjs +2 -0
  7. package/esm2022/lib/version.mjs +2 -2
  8. package/esm2022/public-api.mjs +4 -3
  9. package/fesm2022/valtech-components.mjs +641 -7612
  10. package/fesm2022/valtech-components.mjs.map +1 -1
  11. package/lib/components/molecules/group-picker/group-picker.component.d.ts +28 -0
  12. package/lib/components/organisms/group-members/group-members.component.d.ts +43 -0
  13. package/lib/components/organisms/mini-games/types.d.ts +0 -182
  14. package/lib/services/groups/groups.service.d.ts +39 -0
  15. package/lib/services/groups/types.d.ts +69 -0
  16. package/lib/version.d.ts +1 -1
  17. package/package.json +1 -1
  18. package/public-api.d.ts +4 -2
  19. package/src/lib/assets/games/README.md +8 -0
  20. package/esm2022/lib/components/organisms/mini-games/christianity-bot.mjs +0 -94
  21. package/esm2022/lib/components/organisms/mini-games/christianity-card.component.mjs +0 -503
  22. package/esm2022/lib/components/organisms/mini-games/christianity-catalog.mjs +0 -247
  23. package/esm2022/lib/components/organisms/mini-games/christianity-game.component.mjs +0 -4580
  24. package/esm2022/lib/components/organisms/mini-games/christianity-rules.mjs +0 -1383
  25. package/esm2022/lib/components/organisms/mini-games/christianity-tutorial.mjs +0 -152
  26. package/lib/components/organisms/mini-games/christianity-bot.d.ts +0 -15
  27. package/lib/components/organisms/mini-games/christianity-card.component.d.ts +0 -55
  28. package/lib/components/organisms/mini-games/christianity-catalog.d.ts +0 -18
  29. package/lib/components/organisms/mini-games/christianity-game.component.d.ts +0 -395
  30. package/lib/components/organisms/mini-games/christianity-rules.d.ts +0 -57
  31. package/lib/components/organisms/mini-games/christianity-tutorial.d.ts +0 -38
  32. package/src/lib/assets/games/christianity/card-symbol.svg +0 -51
  33. package/src/lib/assets/games/christianity/card-template-reference/front.html +0 -114
  34. package/src/lib/assets/games/christianity/card-template-reference/front.style.css +0 -352
  35. package/src/lib/assets/games/christianity/data/01-genesis.bible-reference-suggestions.csv +0 -191
  36. package/src/lib/assets/games/christianity/data/01-genesis.bible-verses.nbv-open.csv +0 -170
  37. package/src/lib/assets/games/christianity/data/01-genesis.cards.csv +0 -281
  38. package/src/lib/assets/games/christianity/data/01-genesis.cards.i18n.csv +0 -281
  39. package/src/lib/assets/games/christianity/data/01-genesis.cards.json +0 -16453
  40. package/src/lib/assets/games/christianity/data/01-genesis.unassigned-effects.csv +0 -49
  41. package/src/lib/assets/games/christianity/data/README.md +0 -40
  42. package/src/lib/assets/games/christianity/data/data-con-habilides.csv +0 -273
  43. package/src/lib/assets/games/christianity/data/habilidades-por-tipo.xlsx +0 -0
  44. package/src/lib/assets/games/christianity/data/listado-edicion.csv +0 -281
  45. package/src/lib/assets/games/christianity/editions/1/cards/.gitkeep +0 -1
  46. package/src/lib/assets/games/christianity/editions/1/cards/PLACEHOLDER.png +0 -0
  47. package/src/lib/assets/games/christianity/editions/README.md +0 -34
  48. package/src/lib/assets/games/christianity/icons/1-icon.png +0 -0
  49. package/src/lib/assets/games/christianity/icons/action.png +0 -0
  50. package/src/lib/assets/games/christianity/icons/character.png +0 -0
  51. package/src/lib/assets/games/christianity/icons/event.png +0 -0
  52. package/src/lib/assets/games/christianity/icons/leader.png +0 -0
  53. package/src/lib/assets/games/christianity/icons/offering.png +0 -0
  54. package/src/lib/assets/games/christianity/icons/place.png +0 -0
  55. package/src/lib/assets/games/christianity/icons/tool.png +0 -0
  56. package/src/lib/assets/games/christianity/icons/wisdom.png +0 -0
  57. package/src/lib/assets/games/christianity/sources/README.md +0 -16
  58. package/src/lib/assets/games/christianity/sources/spaonbv_vpl.txt +0 -29102
  59. package/src/lib/assets/games/christianity/sources/spaonbv_vpl.xml +0 -29104
  60. package/src/lib/assets/games/christianity/spaonbv.txt +0 -52
@@ -0,0 +1,28 @@
1
+ import { EventEmitter, OnInit } from '@angular/core';
2
+ import { ValGroup } from '../../../services/groups/types';
3
+ import * as i0 from "@angular/core";
4
+ /**
5
+ * val-group-picker — elegir el grupo dueño de un recurso (ADR-083).
6
+ *
7
+ * Se auto-carga: pide sus propios grupos a `GroupsService` en vez de esperar
8
+ * que el consumer se los pase. Es la razón por la que existe como componente y
9
+ * no como un `<select>` armado a mano en cada app — "una línea, una etiqueta
10
+ * i18n" (ver ADR-083 §"Qué se construye una sola vez").
11
+ *
12
+ * Sin texto hardcodeado: los labels llegan por `@Input` (lib i18n-agnostic).
13
+ * Cada app pone su palabra ("Equipo", "Sucursal", "Refugio").
14
+ */
15
+ export declare class GroupPickerComponent implements OnInit {
16
+ label: string;
17
+ placeholder: string;
18
+ noGroupLabel: string;
19
+ value: string | null;
20
+ disabled: boolean;
21
+ valueChange: EventEmitter<string>;
22
+ private readonly groupsSvc;
23
+ groups: ValGroup[];
24
+ ngOnInit(): void;
25
+ onChange(v: string): void;
26
+ static ɵfac: i0.ɵɵFactoryDeclaration<GroupPickerComponent, never>;
27
+ static ɵcmp: i0.ɵɵComponentDeclaration<GroupPickerComponent, "val-group-picker", never, { "label": { "alias": "label"; "required": false; }; "placeholder": { "alias": "placeholder"; "required": false; }; "noGroupLabel": { "alias": "noGroupLabel"; "required": false; }; "value": { "alias": "value"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; }, { "valueChange": "valueChange"; }, never, never, true, never>;
28
+ }
@@ -0,0 +1,43 @@
1
+ import { EventEmitter, OnChanges, SimpleChanges } from '@angular/core';
2
+ import { ButtonMetadata } from '../../types';
3
+ import { ValGroupMember } from '../../../services/groups/types';
4
+ import * as i0 from "@angular/core";
5
+ /** Persona elegible para agregar al grupo (típicamente un `OrgMember`). */
6
+ export interface GroupMemberCandidate {
7
+ userId: string;
8
+ name?: string;
9
+ email?: string;
10
+ }
11
+ /**
12
+ * val-group-members — administrar la membresía de UN grupo (ADR-083).
13
+ *
14
+ * Deliberadamente NO trae su propio selector de personas: la fuente de
15
+ * "quiénes son los miembros elegibles de la organización" varía por app (org
16
+ * members, invitados, etc.), así que se recibe por `@Input candidates` en vez
17
+ * de que este organismo importe un servicio de organización que no le
18
+ * corresponde. Sin candidatos, igual funciona: solo lista y quita.
19
+ *
20
+ * Sin texto hardcodeado (lib i18n-agnostic): los labels llegan por `@Input`.
21
+ */
22
+ export declare class GroupMembersComponent implements OnChanges {
23
+ groupId: string;
24
+ /** Personas elegibles para agregar. Vacío = el organismo solo lista/quita. */
25
+ candidates: GroupMemberCandidate[];
26
+ addPlaceholder: string;
27
+ emptyLabel: string;
28
+ removeLabel: string;
29
+ membersChange: EventEmitter<ValGroupMember[]>;
30
+ private readonly groupsSvc;
31
+ members: ValGroupMember[];
32
+ busy: boolean;
33
+ get removeButtonProps(): ButtonMetadata;
34
+ ngOnChanges(changes: SimpleChanges): void;
35
+ private reload;
36
+ /** Candidatos que todavía no están en el grupo — evita ofrecer un alta duplicada. */
37
+ eligible(): GroupMemberCandidate[];
38
+ nameFor(userId: string): string;
39
+ add(userId: string): void;
40
+ remove(userId: string): void;
41
+ static ɵfac: i0.ɵɵFactoryDeclaration<GroupMembersComponent, never>;
42
+ static ɵcmp: i0.ɵɵComponentDeclaration<GroupMembersComponent, "val-group-members", never, { "groupId": { "alias": "groupId"; "required": true; }; "candidates": { "alias": "candidates"; "required": false; }; "addPlaceholder": { "alias": "addPlaceholder"; "required": false; }; "emptyLabel": { "alias": "emptyLabel"; "required": false; }; "removeLabel": { "alias": "removeLabel"; "required": false; }; }, { "membersChange": "membersChange"; }, never, never, true, never>;
43
+ }
@@ -44,185 +44,3 @@ export interface BubbleBlitzCell {
44
44
  state?: 'idle' | 'popping' | 'falling' | 'entering';
45
45
  drop?: number;
46
46
  }
47
- export type ChristianitySeat = 'P1' | 'P2';
48
- export type ChristianityGameStatus = 'in_progress' | 'finished';
49
- export type ChristianityGameMode = 'solo_bot' | 'two_player' | 'local_pass';
50
- export type ChristianityCardType = 'action' | 'character' | 'event' | 'leader' | 'offering' | 'place' | 'tool' | 'wisdom';
51
- export type ChristianityCardRarity = 'normal' | 'good' | 'very-good' | 'uncommon' | 'rare' | 'legendary';
52
- export type ChristianityStrategy = 'discard' | 'deckpeek' | 'directdamage' | 'search' | 'aggro' | 'restore' | 'accelerate' | 'slowdown' | 'mindcontrol' | 'destroy' | 'draw' | 'reveal' | 'buff';
53
- export type ChristianityEffectTrigger = 'on_play' | 'on_enter' | 'on_attack' | 'on_defend' | 'on_death' | 'passive';
54
- export type ChristianityEffectTarget = 'self' | 'combat_opponent' | 'all_friendly' | 'all_enemy' | 'opponent_leader' | 'own_leader';
55
- export type ChristianityEffectMutation = {
56
- type: 'buff_attack';
57
- amount: number;
58
- } | {
59
- type: 'buff_life';
60
- amount: number;
61
- } | {
62
- type: 'deal_damage';
63
- amount: number;
64
- } | {
65
- type: 'restore_hp';
66
- amount: number;
67
- } | {
68
- type: 'draw_card';
69
- amount: number;
70
- } | {
71
- type: 'discard_card';
72
- amount: number;
73
- who: 'self' | 'opponent';
74
- } | {
75
- type: 'destroy';
76
- } | {
77
- type: 'gain_offerings';
78
- amount: number;
79
- };
80
- export interface ChristianityCardEffect {
81
- trigger: ChristianityEffectTrigger;
82
- strategy: ChristianityStrategy;
83
- target: ChristianityEffectTarget;
84
- mutation: ChristianityEffectMutation;
85
- condition?: {
86
- targeting: 'character' | 'leader';
87
- } | null;
88
- duration?: 'combat' | 'turn' | 'permanent';
89
- }
90
- export interface ChristianityCardAbility {
91
- name?: string;
92
- text?: string;
93
- }
94
- export interface ChristianityCardTranslation {
95
- name?: string;
96
- fullName?: string;
97
- verseText?: string;
98
- scripture?: string;
99
- abilityName?: string;
100
- abilityText?: string;
101
- abilities?: ChristianityCardAbility[];
102
- }
103
- export interface ChristianityCardBibleReference {
104
- book?: string;
105
- chapter?: number;
106
- reference?: string;
107
- version?: string;
108
- text?: string;
109
- }
110
- export interface ChristianityCardArt {
111
- legend?: string;
112
- size?: number;
113
- positionX?: number;
114
- positionY?: number;
115
- imagePath?: string;
116
- imageUrl?: string;
117
- mimeType?: string;
118
- width?: number;
119
- height?: number;
120
- source?: string;
121
- model?: string;
122
- artistCredit?: string;
123
- promptId?: string;
124
- status?: string;
125
- }
126
- export interface ChristianityCardDefinition {
127
- id: string;
128
- sourceId?: number;
129
- editionNumber?: number;
130
- editionIcon?: string;
131
- name: string;
132
- fullName?: string;
133
- type: ChristianityCardType;
134
- cost?: number;
135
- offeringValue?: number;
136
- attack?: number;
137
- life?: number;
138
- leaderLife?: number;
139
- legacyRestriction?: number;
140
- abilityName?: string;
141
- abilityText?: string;
142
- abilityCost?: number;
143
- abilities?: ChristianityCardAbility[];
144
- strategy?: ChristianityStrategy;
145
- effects?: ChristianityCardEffect[];
146
- power?: string;
147
- bible?: ChristianityCardBibleReference;
148
- art?: ChristianityCardArt;
149
- imageUrl?: string;
150
- verseText?: string;
151
- scripture?: string;
152
- edition?: string;
153
- collectorNumber?: string;
154
- editionSize?: number;
155
- copyCount?: number;
156
- author?: string;
157
- year?: string;
158
- rarity?: ChristianityCardRarity;
159
- frontTemplate?: string;
160
- backTemplate?: string;
161
- i18n?: Record<string, ChristianityCardTranslation>;
162
- status?: string;
163
- sourceNotes?: string;
164
- bibleCredit?: string;
165
- }
166
- export interface ChristianityCharacterInstance {
167
- instanceId: string;
168
- cardId: string;
169
- name: string;
170
- owner: ChristianitySeat;
171
- controller: ChristianitySeat;
172
- slot?: number;
173
- attack: number;
174
- currentHp: number;
175
- maxHp: number;
176
- canAttack: boolean;
177
- equippedTools?: string[];
178
- effects?: ChristianityCardEffect[];
179
- combatAttackBuff?: number;
180
- }
181
- export interface ChristianityOfferingInstance {
182
- instanceId: string;
183
- cardId: string;
184
- name: string;
185
- owner: ChristianitySeat;
186
- ready: boolean;
187
- value: number;
188
- abilityName?: string;
189
- abilityText?: string;
190
- abilityCost?: number;
191
- abilityUsedThisTurn?: boolean;
192
- }
193
- export interface ChristianityPlayerState {
194
- seat: ChristianitySeat;
195
- displayName: string;
196
- avatar?: string;
197
- avatarColor?: string;
198
- leaderCardId?: string;
199
- leaderName: string;
200
- leaderHp: number;
201
- leaderMaxHp: number;
202
- offerings: ChristianityOfferingInstance[];
203
- temporaryOfferingReserve?: number;
204
- offeringPlayedThisTurn: boolean;
205
- hand?: string[];
206
- handCount?: number;
207
- battleZone: ChristianityCharacterInstance[];
208
- costModifiers?: Array<{
209
- sourceId: string;
210
- cardType: ChristianityCardType;
211
- delta: number;
212
- }>;
213
- handRevealed?: boolean;
214
- }
215
- export interface ChristianityGameState {
216
- gameId: string;
217
- status: ChristianityGameStatus;
218
- mode: ChristianityGameMode;
219
- turnOwner: ChristianitySeat;
220
- revision: number;
221
- turnCount: number;
222
- matchScore: Record<ChristianitySeat, number>;
223
- sharedDeck: string[];
224
- placeZone: string[];
225
- historyZone: string[];
226
- players: Record<ChristianitySeat, ChristianityPlayerState>;
227
- log: string[];
228
- }
@@ -0,0 +1,39 @@
1
+ import { HttpClient } from '@angular/common/http';
2
+ import { Observable } from 'rxjs';
3
+ import { ValtechAuthConfig } from '../auth/types';
4
+ import { ValGroup, ValGroupMember, CreateGroupRequest, UpdateGroupRequest, AddGroupMemberRequest, MyGroupsResponse } from './types';
5
+ import * as i0 from "@angular/core";
6
+ /**
7
+ * Cliente de `/v2/groups/*` (ADR-083). Servicio de PLATAFORMA: no sabe qué es
8
+ * un blueprint ni un refugio, solo agrupa recursos y personas. Cada app lo
9
+ * consume igual — es lo que evita reescribir esta capa por producto.
10
+ */
11
+ export declare class GroupsService {
12
+ private config;
13
+ private http;
14
+ constructor(config: ValtechAuthConfig, http: HttpClient);
15
+ private get baseUrl();
16
+ /** Grupos activos de la organización (filtrados por app en el backend). */
17
+ listGroups(): Observable<ValGroup[]>;
18
+ /** Incluye archivados: para la pantalla de administración. */
19
+ listAllGroups(): Observable<ValGroup[]>;
20
+ getGroup(id: string): Observable<ValGroup>;
21
+ createGroup(req: CreateGroupRequest): Observable<ValGroup>;
22
+ updateGroup(id: string, req: UpdateGroupRequest): Observable<ValGroup>;
23
+ /**
24
+ * No hay `deleteGroup`: el ADR-083 decidió que un servicio de plataforma no
25
+ * puede saber si un vertical todavía apunta al grupo, y como "sin grupo"
26
+ * significa visible para toda la organización, borrarlo publicaría sus
27
+ * recursos a la planta entera. `archiveGroup`/`restoreGroup` cubren la
28
+ * necesidad real sin ese riesgo.
29
+ */
30
+ archiveGroup(id: string): Observable<ValGroup>;
31
+ restoreGroup(id: string): Observable<ValGroup>;
32
+ listMembers(groupId: string): Observable<ValGroupMember[]>;
33
+ addMember(groupId: string, req: AddGroupMemberRequest): Observable<ValGroupMember[]>;
34
+ removeMember(groupId: string, userId: string): Observable<ValGroupMember[]>;
35
+ /** Los grupos del usuario actual. Referencial: el filtrado real corre en el backend. */
36
+ myGroups(): Observable<MyGroupsResponse>;
37
+ static ɵfac: i0.ɵɵFactoryDeclaration<GroupsService, never>;
38
+ static ɵprov: i0.ɵɵInjectableDeclaration<GroupsService>;
39
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Grupos (ADR-083) — subdivisión de una organización que agrupa RECURSOS y
3
+ * PERSONAS a la vez. Servicio de plataforma, agnóstico de dominio: el mismo
4
+ * contrato sirve a la línea de producción de Merotz, al refugio de Chesed o al
5
+ * evento de Bingo. Cada app le pone su etiqueta ("Equipo", "Sucursal") vía i18n
6
+ * — el modelo no la sabe.
7
+ */
8
+ export interface ValGroup {
9
+ id: string;
10
+ orgId: string;
11
+ appId: string;
12
+ /** Etiqueta de dominio que declara la app: "machine", "shelter", "event". */
13
+ kind?: string;
14
+ /** Texto del CLIENTE ("Línea 3"): no se traduce. */
15
+ name: string;
16
+ parentId?: string;
17
+ archived: boolean;
18
+ createdAt: string;
19
+ createdBy?: string;
20
+ updatedAt: string;
21
+ }
22
+ export interface ValGroupMember {
23
+ groupId: string;
24
+ orgId: string;
25
+ userId: string;
26
+ groupName?: string;
27
+ addedAt: string;
28
+ addedBy?: string;
29
+ }
30
+ export interface CreateGroupRequest {
31
+ name: string;
32
+ kind?: string;
33
+ }
34
+ export interface UpdateGroupRequest {
35
+ name: string;
36
+ }
37
+ export interface AddGroupMemberRequest {
38
+ userId: string;
39
+ }
40
+ export interface GroupResponse {
41
+ operationId: string;
42
+ group: ValGroup;
43
+ }
44
+ export interface ListGroupsResponse {
45
+ operationId: string;
46
+ groups: ValGroup[];
47
+ count: number;
48
+ }
49
+ export interface ListGroupMembersResponse {
50
+ operationId: string;
51
+ members: ValGroupMember[];
52
+ count: number;
53
+ }
54
+ /**
55
+ * Respuesta de "mis grupos": lo que alimenta el filtrado del lado del cliente
56
+ * cuando hace falta mostrar contexto (p.ej. qué grupo eligió el usuario por
57
+ * default), aunque el filtrado real siempre corre en el backend.
58
+ */
59
+ export interface MyGroupsResponse {
60
+ operationId: string;
61
+ groupIds: string[];
62
+ /**
63
+ * El usuario ve TODOS los grupos sin importar membresía (permiso
64
+ * `groups:all`). Sin `omitempty` del lado del backend a propósito: un
65
+ * `false` ausente sería indistinguible de una respuesta vieja.
66
+ */
67
+ allGroups: boolean;
68
+ groups: ValGroupMember[];
69
+ }
package/lib/version.d.ts CHANGED
@@ -2,4 +2,4 @@
2
2
  * Current version of valtech-components.
3
3
  * This is automatically updated during the publish process.
4
4
  */
5
- export declare const VERSION = "4.0.937";
5
+ export declare const VERSION = "4.0.939";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "valtech-components",
3
- "version": "4.0.937",
3
+ "version": "4.0.939",
4
4
  "private": false,
5
5
  "bin": {
6
6
  "valtech-firebase-config": "./src/lib/services/firebase/scripts/generate-sw-config.js"
package/public-api.d.ts CHANGED
@@ -375,8 +375,6 @@ export * from './lib/components/organisms/member-import-modal/member-import-moda
375
375
  export * from './lib/components/organisms/role-manager/role-manager.component';
376
376
  export * from './lib/components/organisms/role-manager/types';
377
377
  export * from './lib/components/organisms/mini-games/mini-games.i18n';
378
- export * from './lib/components/organisms/mini-games/christianity-rules';
379
- export * from './lib/components/organisms/mini-games/christianity-game.component';
380
378
  export * from './lib/components/organisms/mini-games/bubble-blitz-game.component';
381
379
  export * from './lib/components/organisms/mini-games/memory-game.component';
382
380
  export * from './lib/components/organisms/mini-games/mini-game-callout.component';
@@ -450,6 +448,10 @@ export * from './lib/services/legal-link/legal-link.service';
450
448
  export * from './lib/services/firebase';
451
449
  export * from './lib/services/auth';
452
450
  export { OrgService } from './lib/services/org/org.service';
451
+ export { GroupPickerComponent, } from './lib/components/molecules/group-picker/group-picker.component';
452
+ export { GroupMembersComponent, GroupMemberCandidate, } from './lib/components/organisms/group-members/group-members.component';
453
+ export { GroupsService } from './lib/services/groups/groups.service';
454
+ export { ValGroup, ValGroupMember, CreateGroupRequest as CreateValGroupRequest, UpdateGroupRequest as UpdateValGroupRequest, AddGroupMemberRequest, GroupResponse as ValGroupResponse, ListGroupsResponse as ListValGroupsResponse, ListGroupMembersResponse as ListValGroupMembersResponse, MyGroupsResponse, } from './lib/services/groups/types';
453
455
  export { PersonaService, PersonaConfig, PERSONA_CONFIG, providePersona, } from './lib/services/persona/persona.service';
454
456
  export { HandleService } from './lib/services/handles/handles.service';
455
457
  export type { HandleOwnerType, HandleResolveResult, HandleSearchResult, ResolveHandleResponse, SearchHandlesResponse, } from './lib/services/handles/handles.service';
@@ -1,5 +1,13 @@
1
1
  # Game Art Assets
2
2
 
3
+ > **Nota (2026-08-23):** esta carpeta ya no guarda arte de ningun juego. El de
4
+ > Chessimals vive en `frontend/chessimals/src/assets`, y el de Christianity en
5
+ > `frontend/christianity/src/assets`, cada uno en su propio proyecto. La
6
+ > libreria la consumen todas las apps del factory: el arte de un producto no
7
+ > puede vivir aca, porque las demas se lo llevan en su build sin usarlo. Esta
8
+ > guia se conserva porque describe el flujo de generacion, que sigue vigente.
9
+
10
+
3
11
  Guia breve para generar, limpiar y guardar arte raster para mini juegos.
4
12
 
5
13
  ## Cuando usar este flujo
@@ -1,94 +0,0 @@
1
- import { canPlayCard, cleanupDestroyed, playCardFromHand as playChristianityCardFromHand, } from './christianity-rules';
2
- export function resolveBotPlays(game, log, nameResolver = c => c.name, cardById = id => ({
3
- id,
4
- name: id,
5
- type: 'character',
6
- cost: 0,
7
- life: 0,
8
- attack: 0,
9
- })) {
10
- const hand = [...(game.players.P2.hand ?? [])];
11
- const offeringId = hand.find(cardId => {
12
- const card = cardById(cardId);
13
- return card.type === 'offering' && canPlayCard(game, 'P2', card);
14
- });
15
- if (offeringId) {
16
- const card = cardById(offeringId);
17
- const result = playChristianityCardFromHand(game, 'P2', card, {
18
- displayName: nameResolver(card),
19
- lookup: id => {
20
- const d = cardById(id);
21
- return d.id === id ? d : null;
22
- },
23
- });
24
- if (result.played) {
25
- Object.assign(game, result.game);
26
- log.push(...result.log);
27
- }
28
- }
29
- // Juega personajes mientras le alcance la reserva y haya slots libres (canPlayCard valida ambos).
30
- // Prioriza el de mayor ataque para presionar el tablero.
31
- for (;;) {
32
- const character = [...(game.players.P2.hand ?? [])]
33
- .map(cardId => cardById(cardId))
34
- .filter(card => card.type === 'character' && canPlayCard(game, 'P2', card))
35
- .sort((l, r) => (r.attack ?? 0) - (l.attack ?? 0) || (r.life ?? 0) - (l.life ?? 0))[0];
36
- if (!character)
37
- break;
38
- const result = playChristianityCardFromHand(game, 'P2', character, {
39
- displayName: nameResolver(character),
40
- lookup: id => {
41
- const d = cardById(id);
42
- return d.id === id ? d : null;
43
- },
44
- });
45
- if (!result.played)
46
- break;
47
- Object.assign(game, result.game);
48
- log.push(...result.log);
49
- }
50
- }
51
- export function resolveBotAttack(game, log, impacts, messages) {
52
- const bot = game.players.P2;
53
- const player = game.players.P1;
54
- // Ataca con TODOS los personajes listos, en orden de slot. El tablero cambia entre
55
- // ataques (mueren personajes), por eso se re-evalúan objetivos en cada iteración.
56
- const attackers = bot.battleZone
57
- .filter(card => card.canAttack && card.attack > 0)
58
- .sort((l, r) => (l.slot ?? 0) - (r.slot ?? 0));
59
- if (!attackers.length) {
60
- log.push(messages.botNoAttackers);
61
- return;
62
- }
63
- for (const attacker of attackers) {
64
- // Puede haber muerto en un combate previo de este mismo turno.
65
- if (attacker.currentHp <= 0 || !attacker.canAttack)
66
- continue;
67
- if (player.leaderHp <= 0)
68
- break;
69
- const defenders = player.battleZone.filter(card => card.currentHp > 0);
70
- if (defenders.length) {
71
- // Prioriza un defensor que pueda matar (el más amenazante entre ellos); si no, el más débil.
72
- const killable = defenders
73
- .filter(d => d.currentHp <= attacker.attack)
74
- .sort((l, r) => r.attack - l.attack || l.currentHp - r.currentHp);
75
- const target = killable[0] ??
76
- [...defenders].sort((l, r) => l.currentHp - r.currentHp || (l.slot ?? 0) - (r.slot ?? 0))[0];
77
- impacts.push({ text: `-${attacker.attack}`, tone: 'damage', seat: 'P1', target: 'battle' });
78
- impacts.push({ text: `-${target.attack}`, tone: 'damage', seat: 'P2', target: 'battle' });
79
- target.currentHp -= attacker.attack;
80
- attacker.currentHp -= target.attack;
81
- attacker.canAttack = false;
82
- log.push(`${attacker.name} ${messages.combatsAgainst} ${target.name}.`);
83
- cleanupDestroyed(player, game);
84
- cleanupDestroyed(bot, game);
85
- continue;
86
- }
87
- // Sin defensores: golpea al líder.
88
- impacts.push({ text: `-${attacker.attack}`, tone: 'damage', seat: 'P1', target: 'leader' });
89
- player.leaderHp = Math.max(0, player.leaderHp - attacker.attack);
90
- attacker.canAttack = false;
91
- log.push(`${attacker.name} ${messages.attacksYourLeader}`);
92
- }
93
- }
94
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2hyaXN0aWFuaXR5LWJvdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uL3NyYy9saWIvY29tcG9uZW50cy9vcmdhbmlzbXMvbWluaS1nYW1lcy9jaHJpc3RpYW5pdHktYm90LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUNBLE9BQU8sRUFDTCxXQUFXLEVBQ1gsZ0JBQWdCLEVBQ2hCLGdCQUFnQixJQUFJLDRCQUE0QixHQUNqRCxNQUFNLHNCQUFzQixDQUFDO0FBZ0I5QixNQUFNLFVBQVUsZUFBZSxDQUM3QixJQUEyQixFQUMzQixHQUFhLEVBQ2IsZUFBNkQsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsSUFBSSxFQUN4RSxXQUF1RCxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUM7SUFDNUQsRUFBRTtJQUNGLElBQUksRUFBRSxFQUFFO0lBQ1IsSUFBSSxFQUFFLFdBQVc7SUFDakIsSUFBSSxFQUFFLENBQUM7SUFDUCxJQUFJLEVBQUUsQ0FBQztJQUNQLE1BQU0sRUFBRSxDQUFDO0NBQ1YsQ0FBQztJQUVGLE1BQU0sSUFBSSxHQUFHLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDLElBQUksSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBRS9DLE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLEVBQUU7UUFDcEMsTUFBTSxJQUFJLEdBQUcsUUFBUSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQzlCLE9BQU8sSUFBSSxDQUFDLElBQUksS0FBSyxVQUFVLElBQUksV0FBVyxDQUFDLElBQUksRUFBRSxJQUFJLEVBQUUsSUFBSSxDQUFDLENBQUM7SUFDbkUsQ0FBQyxDQUFDLENBQUM7SUFDSCxJQUFJLFVBQVUsRUFBRSxDQUFDO1FBQ2YsTUFBTSxJQUFJLEdBQUcsUUFBUSxDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBQ2xDLE1BQU0sTUFBTSxHQUFH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RUFBRSxRQUFRLEVBQUUsQ0FBQyxDQUFDO1FBQzVGLE1BQU0sQ0FBQyxRQUFRLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsTUFBTSxDQUFDLFFBQVEsR0FBRyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDakUsUUFBUSxDQUFDLFNBQVMsR0FBRyxLQUFLLENBQUM7UUFDM0IsR0FBRyxDQUFDLElBQUksQ0FBQyxHQUFHLFFBQVEsQ0FBQyxJQUFJLElBQUksUUFBUSxDQUFDLGlCQUFpQixFQUFFLENBQUMsQ0FBQztJQUM3RCxDQUFDO0FBQ0gsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IENocmlzdGlhbml0eUNhcmREZWZpbml0aW9uLCBDaHJpc3RpYW5pdHlHYW1lU3RhdGUgfSBmcm9tICcuL3R5cGVzJztcbmltcG9ydCB7XG4gIGNhblBsYXlDYXJkLFxuICBjbGVhbnVwRGVzdHJveWVkLFxuICBwbGF5Q2FyZEZyb21IYW5kIGFzIHBsYXlDaHJpc3RpYW5pdHlDYXJkRnJvbUhhbmQsXG59IGZyb20gJy4vY2hyaXN0aWFuaXR5LXJ1bGVzJztcblxuZXhwb3J0IGludGVyZmFjZSBDaHJpc3RpYW5pdHlCb3RNZXNzYWdlcyB7XG4gIGJvdE5vQXR0YWNrZXJzOiBzdHJpbmc7XG4gIGNvbWJhdHNBZ2FpbnN0OiBzdHJpbmc7XG4gIGF0dGFja3NZb3VyTGVhZGVyOiBzdHJpbmc7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgQ2hyaXN0aWFuaXR5SW1wYWN0RXZlbnQge1xuICBpZDogc3RyaW5nO1xuICB0ZXh0OiBzdHJpbmc7XG4gIHRvbmU6ICdkYW1hZ2UnIHwgJ2hlYWwnIHwgJ3Jlc291cmNlJztcbiAgc2VhdDogJ1AxJyB8ICdQMic7XG4gIHRhcmdldDogJ2xlYWRlcicgfCAnYmF0dGxlJyB8ICdvZmZlcmluZ3MnO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gcmVzb2x2ZUJvdFBsYXlzKFxuICBnYW1lOiBDaHJpc3RpYW5pdHlHYW1lU3RhdGUsXG4gIGxvZzogc3RyaW5nW10sXG4gIG5hbWVSZXNvbHZlcjogKGNhcmQ6IENocmlzdGlhbml0eUNhcmREZWZpbml0aW9uKSA9PiBzdHJpbmcgPSBjID0+IGMubmFtZSxcbiAgY2FyZEJ5SWQ6IChpZDogc3RyaW5nKSA9PiBDaHJpc3RpYW5pdHlDYXJkRGVmaW5pdGlvbiA9IGlkID0+ICh7XG4gICAgaWQsXG4gICAgbmFtZTogaWQsXG4gICAgdHlwZTogJ2NoYXJhY3RlcicsXG4gICAgY29zdDogMCxcbiAgICBsaWZlOiAwLFxuICAgIGF0dGFjazogMCxcbiAgfSlcbik6IHZvaWQge1xuICBjb25zdCBoYW5kID0gWy4uLihnYW1lLnBsYXllcnMuUDIuaGFuZCA/PyBbXSldO1xuXG4gIGNvbnN0IG9mZmVyaW5nSWQgPSBoYW5kLmZpbmQoY2FyZElkID0+IHtcbiAgICBjb25zdCBjYXJkID0gY2FyZEJ5SWQoY2FyZElkKTtcbiAgICByZXR1cm4gY2FyZC50eXBlID09PSAnb2ZmZXJpbmcnICYmIGNhblBsYXlDYXJkKGdhbWUsICdQMicsIGNhcmQpO1xuICB9KTtcbiAgaWYgKG9mZmVyaW5nSWQpIHtcbiAgICBjb25zdCBjYXJkID0gY2FyZEJ5SWQob2ZmZXJpbmdJZCk7XG4gICAgY29uc3QgcmVzdWx0ID0gcGxheUNocmlzdGlhbml0eUNhcmRGcm9tSGFuZChnYW1lLCAnUDInLCBjYXJkLCB7XG4gICAgICBkaXNwbGF5TmFtZTogbmFtZVJlc29sdmVyKGNhcmQpLFxuICAgICAgbG9va3VwOiBpZCA9PiB7XG4gICAgICAgIGNvbnN0IGQgPSBjYXJkQnlJZChpZCk7XG4gICAgICAgIHJldHVybiBkLmlkID09PSBpZCA/IGQgOiBudWxsO1xuICAgICAgfSxcbiAgICB9KTtcbiAgICBpZiAocmVzdWx0LnBsYXllZCkge1xuICAgICAgT2JqZWN0LmFzc2lnbihnYW1lLCByZXN1bHQuZ2FtZSk7XG4gICAgICBsb2cucHVzaCguLi5yZXN1bHQubG9nKTtcbiAgICB9XG4gIH1cblxuICAvLyBKdWVnYSBwZXJzb25hamVzIG1pZW50cmFzIGxlIGFsY2FuY2UgbGEgcmVzZXJ2YSB5IGhheWEgc2xvdHMgbGlicmVzIChjYW5QbGF5Q2FyZCB2YWxpZGEgYW1ib3MpLlxuICAvLyBQcmlvcml6YSBlbCBkZSBtYXlvciBhdGFxdWUgcGFyYSBwcmVzaW9uYXIgZWwgdGFibGVyby5cbiAgZm9yICg7Oykge1xuICAgIGNvbnN0IGNoYXJhY3RlciA9IFsuLi4oZ2FtZS5wbGF5ZXJzLlAyLmhhbmQgPz8gW10pXVxuICAgICAgLm1hcChjYXJkSWQgPT4gY2FyZEJ5SWQoY2FyZElkKSlcbiAgICAgIC5maWx0ZXIoY2FyZCA9PiBjYXJkLnR5cGUgPT09ICdjaGFyYWN0ZXInICYmIGNhblBsYXlDYXJkKGdhbWUsICdQMicsIGNhcmQpKVxuICAgICAgLnNvcnQoKGwsIHIpID0+IChyLmF0dGFjayA/PyAwKSAtIChsLmF0dGFjayA/PyAwKSB8fCAoci5saWZlID8/IDApIC0gKGwubGlmZSA/PyAwKSlbMF07XG4gICAgaWYgKCFjaGFyYWN0ZXIpIGJyZWFrO1xuXG4gICAgY29uc3QgcmVzdWx0ID0gcGxheUNocmlzdGlhbml0eUNhcmRGcm9tSGFuZChnYW1lLCAnUDInLCBjaGFyYWN0ZXIsIHtcbiAgICAgIGRpc3BsYXlOYW1lOiBuYW1lUmVzb2x2ZXIoY2hhcmFjdGVyKSxcbiAgICAgIGxvb2t1cDogaWQgPT4ge1xuICAgICAgICBjb25zdCBkID0gY2FyZEJ5SWQoaWQpO1xuICAgICAgICByZXR1cm4gZC5pZCA9PT0gaWQgPyBkIDogbnVsbDtcbiAgICAgIH0sXG4gICAgfSk7XG4gICAgaWYgKCFyZXN1bHQucGxheWVkKSBicmVhaztcbiAgICBPYmplY3QuYXNzaWduKGdhbWUsIHJlc3VsdC5nYW1lKTtcbiAgICBsb2cucHVzaCguLi5yZXN1bHQubG9nKTtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gcmVzb2x2ZUJvdEF0dGFjayhcbiAgZ2FtZTogQ2hyaXN0aWFuaXR5R2FtZVN0YXRlLFxuICBsb2c6IHN0cmluZ1tdLFxuICBpbXBhY3RzOiBBcnJheTxPbWl0PENocmlzdGlhbml0eUltcGFjdEV2ZW50LCAnaWQnPj4sXG4gIG1lc3NhZ2VzOiBDaHJpc3RpYW5pdHlCb3RNZXNzYWdlc1xuKTogdm9pZCB7XG4gIGNvbnN0IGJvdCA9IGdhbWUucGxheWVycy5QMjtcbiAgY29uc3QgcGxheWVyID0gZ2FtZS5wbGF5ZXJzLlAxO1xuXG4gIC8vIEF0YWNhIGNvbiBUT0RPUyBsb3MgcGVyc29uYWplcyBsaXN0b3MsIGVuIG9yZGVuIGRlIHNsb3QuIEVsIHRhYmxlcm8gY2FtYmlhIGVudHJlXG4gIC8vIGF0YXF1ZXMgKG11ZXJlbiBwZXJzb25hamVzKSwgcG9yIGVzbyBzZSByZS1ldmFsw7phbiBvYmpldGl2b3MgZW4gY2FkYSBpdGVyYWNpw7NuLlxuICBjb25zdCBhdHRhY2tlcnMgPSBib3QuYmF0dGxlWm9uZVxuICAgIC5maWx0ZXIoY2FyZCA9PiBjYXJkLmNhbkF0dGFjayAmJiBjYXJkLmF0dGFjayA+IDApXG4gICAgLnNvcnQoKGwsIHIpID0+IChsLnNsb3QgPz8gMCkgLSAoci5zbG90ID8/IDApKTtcblxuICBpZiAoIWF0dGFja2Vycy5sZW5ndGgpIHtcbiAgICBsb2cucHVzaChtZXNzYWdlcy5ib3ROb0F0dGFja2Vycyk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgZm9yIChjb25zdCBhdHRhY2tlciBvZiBhdHRhY2tlcnMpIHtcbiAgICAvLyBQdWVkZSBoYWJlciBtdWVydG8gZW4gdW4gY29tYmF0ZSBwcmV2aW8gZGUgZXN0ZSBtaXNtbyB0dXJuby5cbiAgICBpZiAoYXR0YWNrZXIuY3VycmVudEhwIDw9IDAgfHwgIWF0dGFja2VyLmNhbkF0dGFjaykgY29udGludWU7XG4gICAgaWYgKHBsYXllci5sZWFkZXJIcCA8PSAwKSBicmVhaztcblxuICAgIGNvbnN0IGRlZmVuZGVycyA9IHBsYXllci5iYXR0bGVab25lLmZpbHRlcihjYXJkID0+IGNhcmQuY3VycmVudEhwID4gMCk7XG5cbiAgICBpZiAoZGVmZW5kZXJzLmxlbmd0aCkge1xuICAgICAgLy8gUHJpb3JpemEgdW4gZGVmZW5zb3IgcXVlIHB1ZWRhIG1hdGFyIChlbCBtw6FzIGFtZW5hemFudGUgZW50cmUgZWxsb3MpOyBzaSBubywgZWwgbcOhcyBkw6liaWwuXG4gICAgICBjb25zdCBraWxsYWJsZSA9IGRlZmVuZGVyc1xuICAgICAgICAuZmlsdGVyKGQgPT4gZC5jdXJyZW50SHAgPD0gYXR0YWNrZXIuYXR0YWNrKVxuICAgICAgICAuc29ydCgobCwgcikgPT4gci5hdHRhY2sgLSBsLmF0dGFjayB8fCBsLmN1cnJlbnRIcCAtIHIuY3VycmVudEhwKTtcbiAgICAgIGNvbnN0IHRhcmdldCA9XG4gICAgICAgIGtpbGxhYmxlWzBdID8/XG4gICAgICAgIFsuLi5kZWZlbmRlcnNdLnNvcnQoXG4gICAgICAgICAgKGwsIHIpID0+IGwuY3VycmVudEhwIC0gci5jdXJyZW50SHAgfHwgKGwuc2xvdCA/PyAwKSAtIChyLnNsb3QgPz8gMClcbiAgICAgICAgKVswXTtcblxuICAgICAgaW1wYWN0cy5wdXNoKHsgdGV4dDogYC0ke2F0dGFja2VyLmF0dGFja31gLCB0b25lOiAnZGFtYWdlJywgc2VhdDogJ1AxJywgdGFyZ2V0OiAnYmF0dGxlJyB9KTtcbiAgICAgIGltcGFjdHMucHVzaCh7IHRleHQ6IGAtJHt0YXJnZXQuYXR0YWNrfWAsIHRvbmU6ICdkYW1hZ2UnLCBzZWF0OiAnUDInLCB0YXJnZXQ6ICdiYXR0bGUnIH0pO1xuICAgICAgdGFyZ2V0LmN1cnJlbnRIcCAtPSBhdHRhY2tlci5hdHRhY2s7XG4gICAgICBhdHRhY2tlci5jdXJyZW50SHAgLT0gdGFyZ2V0LmF0dGFjaztcbiAgICAgIGF0dGFja2VyLmNhbkF0dGFjayA9IGZhbHNlO1xuICAgICAgbG9nLnB1c2goYCR7YXR0YWNrZXIubmFtZX0gJHttZXNzYWdlcy5jb21iYXRzQWdhaW5zdH0gJHt0YXJnZXQubmFtZX0uYCk7XG4gICAgICBjbGVhbnVwRGVzdHJveWVkKHBsYXllciwgZ2FtZSk7XG4gICAgICBjbGVhbnVwRGVzdHJveWVkKGJvdCwgZ2FtZSk7XG4gICAgICBjb250aW51ZTtcbiAgICB9XG5cbiAgICAvLyBTaW4gZGVmZW5zb3JlczogZ29scGVhIGFsIGzDrWRlci5cbiAgICBpbXBhY3RzLnB1c2goeyB0ZXh0OiBgLSR7YXR0YWNrZXIuYXR0YWNrfWAsIHRvbmU6ICdkYW1hZ2UnLCBzZWF0OiAnUDEnLCB0YXJnZXQ6ICdsZWFkZXInIH0pO1xuICAgIHBsYXllci5sZWFkZXJIcCA9IE1hdGgubWF4KDAsIHBsYXllci5sZWFkZXJIcCAtIGF0dGFja2VyLmF0dGFjayk7XG4gICAgYXR0YWNrZXIuY2FuQXR0YWNrID0gZmFsc2U7XG4gICAgbG9nLnB1c2goYCR7YXR0YWNrZXIubmFtZX0gJHttZXNzYWdlcy5hdHRhY2tzWW91ckxlYWRlcn1gKTtcbiAgfVxufVxuIl19