tormenta20 0.3.0 → 1.0.0

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 (31) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/db/schema.sql +437 -6
  4. data/db/tormenta20.sqlite3 +0 -0
  5. data/src/ruby/tormenta20/concerns/livro_scoped.rb +26 -0
  6. data/src/ruby/tormenta20/models/acessorio_magico.rb +7 -10
  7. data/src/ruby/tormenta20/models/ameaca.rb +73 -0
  8. data/src/ruby/tormenta20/models/arma_magica_especifica.rb +32 -0
  9. data/src/ruby/tormenta20/models/armadura_magica_especifica.rb +35 -0
  10. data/src/ruby/tormenta20/models/artefato.rb +23 -0
  11. data/src/ruby/tormenta20/models/base.rb +15 -2
  12. data/src/ruby/tormenta20/models/base_tipo.rb +33 -0
  13. data/src/ruby/tormenta20/models/classe_variante.rb +41 -0
  14. data/src/ruby/tormenta20/models/comodo.rb +32 -0
  15. data/src/ruby/tormenta20/models/complicacao.rb +40 -0
  16. data/src/ruby/tormenta20/models/construcao_dominio.rb +39 -0
  17. data/src/ruby/tormenta20/models/distincao.rb +39 -0
  18. data/src/ruby/tormenta20/models/divindade.rb +14 -1
  19. data/src/ruby/tormenta20/models/encantamento.rb +6 -3
  20. data/src/ruby/tormenta20/models/esoterico_magico.rb +51 -0
  21. data/src/ruby/tormenta20/models/item_liturgico.rb +41 -0
  22. data/src/ruby/tormenta20/models/mobilia.rb +33 -0
  23. data/src/ruby/tormenta20/models/poder.rb +15 -0
  24. data/src/ruby/tormenta20/models/raca.rb +14 -1
  25. data/src/ruby/tormenta20/models/tabela.rb +33 -3
  26. data/src/ruby/tormenta20/rules/damage_step.rb +37 -0
  27. data/src/ruby/tormenta20/rules/spell_cost.rb +18 -0
  28. data/src/ruby/tormenta20/rules.rb +2 -0
  29. data/src/ruby/tormenta20/seeder.rb +229 -19
  30. data/src/ruby/tormenta20.rb +103 -1
  31. metadata +20 -4
data/db/schema.sql CHANGED
@@ -15,10 +15,13 @@ CREATE TABLE IF NOT EXISTS origens (
15
15
  items JSON DEFAULT '[]',
16
16
  benefits JSON DEFAULT '{}',
17
17
  unique_power TEXT,
18
+ livro_id TEXT NOT NULL DEFAULT 't20_eja' REFERENCES livros(id),
18
19
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
19
20
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
20
21
  );
21
22
 
23
+ CREATE INDEX IF NOT EXISTS idx_origens_livro ON origens(livro_id);
24
+
22
25
  CREATE INDEX IF NOT EXISTS idx_origens_name ON origens(name);
23
26
 
24
27
  CREATE TRIGGER IF NOT EXISTS update_origens_timestamp
@@ -42,7 +45,10 @@ CREATE TABLE IF NOT EXISTS poderes (
42
45
  'poder_geral',
43
46
  'poder_combate',
44
47
  'poder_destino',
45
- 'poder_magia'
48
+ 'poder_magia',
49
+ 'poder_de_raca',
50
+ 'poder_de_grupo',
51
+ 'poder_distincao'
46
52
  )),
47
53
  description TEXT,
48
54
  effects JSON DEFAULT '[]',
@@ -50,14 +56,19 @@ CREATE TABLE IF NOT EXISTS poderes (
50
56
  prerequisites JSON DEFAULT '[]',
51
57
  origin_id TEXT,
52
58
  class_id TEXT,
59
+ distincao_id TEXT REFERENCES distincoes(id), -- set only when type = 'poder_distincao'
53
60
  deities JSON DEFAULT '[]',
61
+ livro_id TEXT NOT NULL DEFAULT 't20_eja' REFERENCES livros(id),
54
62
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
55
63
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
56
64
  );
57
65
 
66
+ CREATE INDEX IF NOT EXISTS idx_poderes_livro ON poderes(livro_id);
67
+
58
68
  CREATE INDEX IF NOT EXISTS idx_poderes_type ON poderes(type);
59
69
  CREATE INDEX IF NOT EXISTS idx_poderes_name ON poderes(name);
60
70
  CREATE INDEX IF NOT EXISTS idx_poderes_origin ON poderes(origin_id);
71
+ CREATE INDEX IF NOT EXISTS idx_poderes_distincao ON poderes(distincao_id);
61
72
 
62
73
  CREATE TRIGGER IF NOT EXISTS update_poderes_timestamp
63
74
  AFTER UPDATE ON poderes
@@ -80,10 +91,16 @@ CREATE TABLE IF NOT EXISTS divindades (
80
91
  devotees JSON DEFAULT '{}',
81
92
  granted_powers JSON DEFAULT '[]',
82
93
  obligations_restrictions TEXT,
94
+ rank TEXT DEFAULT 'maior' CHECK(rank IN ('maior', 'menor')),
95
+ natureza TEXT, -- deuses menores apenas: conceito_vivo | entidade_primordial | dragao_real | mortal_ascendido | objeto_desperto
96
+ status_divino INTEGER, -- deuses menores apenas: 1 a 5
97
+ livro_id TEXT NOT NULL DEFAULT 't20_eja' REFERENCES livros(id),
83
98
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
84
99
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
85
100
  );
86
101
 
102
+ CREATE INDEX IF NOT EXISTS idx_divindades_livro ON divindades(livro_id);
103
+
87
104
  CREATE INDEX IF NOT EXISTS idx_divindades_name ON divindades(name);
88
105
  CREATE INDEX IF NOT EXISTS idx_divindades_energy ON divindades(energy);
89
106
 
@@ -107,10 +124,13 @@ CREATE TABLE IF NOT EXISTS classes (
107
124
  powers JSON DEFAULT '[]',
108
125
  progression JSON DEFAULT '[]',
109
126
  spellcasting JSON,
127
+ livro_id TEXT NOT NULL DEFAULT 't20_eja' REFERENCES livros(id),
110
128
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
111
129
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
112
130
  );
113
131
 
132
+ CREATE INDEX IF NOT EXISTS idx_classes_livro ON classes(livro_id);
133
+
114
134
  CREATE INDEX IF NOT EXISTS idx_classes_name ON classes(name);
115
135
 
116
136
  CREATE TRIGGER IF NOT EXISTS update_classes_timestamp
@@ -167,10 +187,13 @@ CREATE TABLE IF NOT EXISTS magias (
167
187
  effects JSON NOT NULL DEFAULT '[]',
168
188
 
169
189
  -- Metadata
190
+ livro_id TEXT NOT NULL DEFAULT 't20_eja' REFERENCES livros(id),
170
191
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
171
192
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
172
193
  );
173
194
 
195
+ CREATE INDEX IF NOT EXISTS idx_magias_livro ON magias(livro_id);
196
+
174
197
  CREATE INDEX IF NOT EXISTS idx_magias_type ON magias(type);
175
198
  CREATE INDEX IF NOT EXISTS idx_magias_circle ON magias(circle);
176
199
  CREATE INDEX IF NOT EXISTS idx_magias_school ON magias(school);
@@ -197,10 +220,13 @@ CREATE TABLE IF NOT EXISTS armas (
197
220
  weight REAL,
198
221
  properties JSON DEFAULT '[]',
199
222
  description TEXT,
223
+ livro_id TEXT NOT NULL DEFAULT 't20_eja' REFERENCES livros(id),
200
224
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
201
225
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
202
226
  );
203
227
 
228
+ CREATE INDEX IF NOT EXISTS idx_armas_livro ON armas(livro_id);
229
+
204
230
  CREATE INDEX IF NOT EXISTS idx_armas_category ON armas(category);
205
231
  CREATE INDEX IF NOT EXISTS idx_armas_name ON armas(name);
206
232
 
@@ -223,10 +249,13 @@ CREATE TABLE IF NOT EXISTS armaduras (
223
249
  weight REAL,
224
250
  properties JSON DEFAULT '[]',
225
251
  description TEXT,
252
+ livro_id TEXT NOT NULL DEFAULT 't20_eja' REFERENCES livros(id),
226
253
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
227
254
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
228
255
  );
229
256
 
257
+ CREATE INDEX IF NOT EXISTS idx_armaduras_livro ON armaduras(livro_id);
258
+
230
259
  CREATE INDEX IF NOT EXISTS idx_armaduras_category ON armaduras(category);
231
260
  CREATE INDEX IF NOT EXISTS idx_armaduras_name ON armaduras(name);
232
261
 
@@ -248,10 +277,13 @@ CREATE TABLE IF NOT EXISTS escudos (
248
277
  weight REAL,
249
278
  properties JSON DEFAULT '[]',
250
279
  description TEXT,
280
+ livro_id TEXT NOT NULL DEFAULT 't20_eja' REFERENCES livros(id),
251
281
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
252
282
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
253
283
  );
254
284
 
285
+ CREATE INDEX IF NOT EXISTS idx_escudos_livro ON escudos(livro_id);
286
+
255
287
  CREATE INDEX IF NOT EXISTS idx_escudos_name ON escudos(name);
256
288
 
257
289
  CREATE TRIGGER IF NOT EXISTS update_escudos_timestamp
@@ -271,10 +303,13 @@ CREATE TABLE IF NOT EXISTS itens (
271
303
  weight REAL,
272
304
  description TEXT,
273
305
  effects JSON DEFAULT '{}',
306
+ livro_id TEXT NOT NULL DEFAULT 't20_eja' REFERENCES livros(id),
274
307
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
275
308
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
276
309
  );
277
310
 
311
+ CREATE INDEX IF NOT EXISTS idx_itens_livro ON itens(livro_id);
312
+
278
313
  CREATE INDEX IF NOT EXISTS idx_itens_category ON itens(category);
279
314
  CREATE INDEX IF NOT EXISTS idx_itens_name ON itens(name);
280
315
 
@@ -294,10 +329,13 @@ CREATE TABLE IF NOT EXISTS materiais_especiais (
294
329
  applicable_to JSON DEFAULT '[]',
295
330
  price_modifier JSON,
296
331
  effects JSON DEFAULT '{}',
332
+ livro_id TEXT NOT NULL DEFAULT 't20_eja' REFERENCES livros(id),
297
333
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
298
334
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
299
335
  );
300
336
 
337
+ CREATE INDEX IF NOT EXISTS idx_materiais_especiais_livro ON materiais_especiais(livro_id);
338
+
301
339
  CREATE INDEX IF NOT EXISTS idx_materiais_especiais_name ON materiais_especiais(name);
302
340
 
303
341
  CREATE TRIGGER IF NOT EXISTS update_materiais_especiais_timestamp
@@ -313,15 +351,19 @@ CREATE TABLE IF NOT EXISTS encantamentos (
313
351
  id TEXT PRIMARY KEY,
314
352
  name TEXT NOT NULL,
315
353
  description TEXT,
316
- categoria TEXT NOT NULL, -- 'arma' | 'armadura_escudo'
354
+ categoria TEXT NOT NULL, -- 'arma' | 'armadura' | 'esoterico' | 'acessorio'
317
355
  escudo_only INTEGER DEFAULT 0, -- boolean: only for shields
318
356
  conta_como_dois INTEGER DEFAULT 0, -- boolean: counts as two enchantments
357
+ maldicao INTEGER DEFAULT 0, -- boolean: item amaldiçoado (efeito negativo), não um encanto positivo
319
358
  prerequisitos JSON DEFAULT '[]',
320
- efeito JSON DEFAULT '{}',
359
+ effects JSON DEFAULT '[]', -- array of typed mechanical effect objects
360
+ livro_id TEXT NOT NULL DEFAULT 't20_eja' REFERENCES livros(id),
321
361
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
322
362
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
323
363
  );
324
364
 
365
+ CREATE INDEX IF NOT EXISTS idx_encantamentos_livro ON encantamentos(livro_id);
366
+
325
367
  CREATE INDEX IF NOT EXISTS idx_encantamentos_categoria ON encantamentos(categoria);
326
368
 
327
369
  CREATE TRIGGER IF NOT EXISTS update_encantamentos_timestamp
@@ -339,11 +381,14 @@ CREATE TABLE IF NOT EXISTS melhorias (
339
381
  description TEXT,
340
382
  applicable_to JSON DEFAULT '[]',
341
383
  price INTEGER,
342
- effects JSON DEFAULT '{}',
384
+ effects JSON DEFAULT '[]', -- array of typed mechanical effect objects
385
+ livro_id TEXT NOT NULL DEFAULT 't20_eja' REFERENCES livros(id),
343
386
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
344
387
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
345
388
  );
346
389
 
390
+ CREATE INDEX IF NOT EXISTS idx_melhorias_livro ON melhorias(livro_id);
391
+
347
392
  CREATE INDEX IF NOT EXISTS idx_melhorias_name ON melhorias(name);
348
393
 
349
394
  CREATE TRIGGER IF NOT EXISTS update_melhorias_timestamp
@@ -360,10 +405,13 @@ CREATE TABLE IF NOT EXISTS regras (
360
405
  name TEXT NOT NULL,
361
406
  description TEXT,
362
407
  data JSON NOT NULL,
408
+ livro_id TEXT NOT NULL DEFAULT 't20_eja' REFERENCES livros(id),
363
409
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
364
410
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
365
411
  );
366
412
 
413
+ CREATE INDEX IF NOT EXISTS idx_regras_livro ON regras(livro_id);
414
+
367
415
  CREATE INDEX IF NOT EXISTS idx_regras_name ON regras(name);
368
416
 
369
417
  CREATE TRIGGER IF NOT EXISTS update_regras_timestamp
@@ -384,14 +432,21 @@ CREATE TABLE IF NOT EXISTS racas (
384
432
  vision TEXT DEFAULT 'normal',
385
433
  vision_range INTEGER,
386
434
  attribute_bonuses JSON DEFAULT '{}',
435
+ chosen_attribute_bonus JSON, -- { count, value, exclude: [...] } — "escolha N atributos" (Heróis de Arton); null for fixed-bonus races
387
436
  skill_bonuses JSON DEFAULT '[]',
388
437
  racial_abilities JSON DEFAULT '[]',
389
438
  chosen_abilities_amount INTEGER DEFAULT 0,
390
439
  available_chosen_abilities JSON DEFAULT '[]',
440
+ longevidade TEXT, -- multiplicador de expectativa de vida (ex: "x2", "x5", "Normal"); conceito introduzido em Heróis de Arton, ausente no EJA
441
+ devocao JSON, -- array de ids de divindades comumente devotadas por essa raça, ou null
442
+ ability_swap JSON, -- { replaces: id, options: [ids] } — habilidade racial fixa substituível por uma de várias variantes (ex: suraggel/Deuses de Arton); null quando não há variantes
443
+ livro_id TEXT NOT NULL DEFAULT 't20_eja' REFERENCES livros(id),
391
444
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
392
445
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
393
446
  );
394
447
 
448
+ CREATE INDEX IF NOT EXISTS idx_racas_livro ON racas(livro_id);
449
+
395
450
  CREATE INDEX IF NOT EXISTS idx_racas_name ON racas(name);
396
451
 
397
452
  CREATE TRIGGER IF NOT EXISTS update_racas_timestamp
@@ -410,10 +465,13 @@ CREATE TABLE IF NOT EXISTS condicoes (
410
465
  effects JSON DEFAULT '[]',
411
466
  condition_type TEXT,
412
467
  escalates_to TEXT,
468
+ livro_id TEXT NOT NULL DEFAULT 't20_eja' REFERENCES livros(id),
413
469
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
414
470
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
415
471
  );
416
472
 
473
+ CREATE INDEX IF NOT EXISTS idx_condicoes_livro ON condicoes(livro_id);
474
+
417
475
  CREATE INDEX IF NOT EXISTS idx_condicoes_name ON condicoes(name);
418
476
  CREATE INDEX IF NOT EXISTS idx_condicoes_type ON condicoes(condition_type);
419
477
 
@@ -482,10 +540,13 @@ CREATE TABLE IF NOT EXISTS pocoes (
482
540
  roll_max INTEGER,
483
541
  aprimoramento TEXT,
484
542
  description TEXT,
543
+ livro_id TEXT NOT NULL DEFAULT 't20_eja' REFERENCES livros(id),
485
544
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
486
545
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
487
546
  );
488
547
 
548
+ CREATE INDEX IF NOT EXISTS idx_pocoes_livro ON pocoes(livro_id);
549
+
489
550
  CREATE INDEX IF NOT EXISTS idx_pocoes_categoria ON pocoes(categoria);
490
551
  CREATE INDEX IF NOT EXISTS idx_pocoes_subtipo ON pocoes(subtipo);
491
552
 
@@ -496,7 +557,34 @@ BEGIN
496
557
  END;
497
558
 
498
559
  -- -----------------------------------------------------------------------------
499
- -- ITENS MÁGICOS - ACESSÓRIOS (Magic Accessories)
560
+ -- ITENS MÁGICOS - ESOTÉRICOS (Caster Implements: staffs, wands, holy symbols)
561
+ -- -----------------------------------------------------------------------------
562
+ CREATE TABLE IF NOT EXISTS esotericos_magicos (
563
+ id TEXT PRIMARY KEY,
564
+ name TEXT NOT NULL,
565
+ categoria TEXT NOT NULL, -- 'menor' | 'medio' | 'maior'
566
+ preco INTEGER,
567
+ roll_min INTEGER,
568
+ roll_max INTEGER,
569
+ description TEXT,
570
+ effects JSON DEFAULT '[]', -- array of typed mechanical effect objects
571
+ livro_id TEXT NOT NULL DEFAULT 't20_eja' REFERENCES livros(id),
572
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
573
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
574
+ );
575
+
576
+ CREATE INDEX IF NOT EXISTS idx_esotericos_magicos_livro ON esotericos_magicos(livro_id);
577
+
578
+ CREATE INDEX IF NOT EXISTS idx_esotericos_magicos_categoria ON esotericos_magicos(categoria);
579
+
580
+ CREATE TRIGGER IF NOT EXISTS update_esotericos_magicos_timestamp
581
+ AFTER UPDATE ON esotericos_magicos
582
+ BEGIN
583
+ UPDATE esotericos_magicos SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
584
+ END;
585
+
586
+ -- -----------------------------------------------------------------------------
587
+ -- ITENS MÁGICOS - ACESSÓRIOS (Amulets, Rings, Cloaks, etc.)
500
588
  -- -----------------------------------------------------------------------------
501
589
  CREATE TABLE IF NOT EXISTS acessorios_magicos (
502
590
  id TEXT PRIMARY KEY,
@@ -506,11 +594,14 @@ CREATE TABLE IF NOT EXISTS acessorios_magicos (
506
594
  roll_min INTEGER,
507
595
  roll_max INTEGER,
508
596
  description TEXT,
509
- efeito TEXT DEFAULT '{}',
597
+ effects JSON DEFAULT '[]', -- array of typed mechanical effect objects
598
+ livro_id TEXT NOT NULL DEFAULT 't20_eja' REFERENCES livros(id),
510
599
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
511
600
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
512
601
  );
513
602
 
603
+ CREATE INDEX IF NOT EXISTS idx_acessorios_magicos_livro ON acessorios_magicos(livro_id);
604
+
514
605
  CREATE INDEX IF NOT EXISTS idx_acessorios_magicos_categoria ON acessorios_magicos(categoria);
515
606
 
516
607
  CREATE TRIGGER IF NOT EXISTS update_acessorios_magicos_timestamp
@@ -531,10 +622,13 @@ CREATE TABLE IF NOT EXISTS pericias (
531
622
  resistance_skill INTEGER NOT NULL DEFAULT 0,
532
623
  description TEXT,
533
624
  uses JSON DEFAULT '[]',
625
+ livro_id TEXT NOT NULL DEFAULT 't20_eja' REFERENCES livros(id),
534
626
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
535
627
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
536
628
  );
537
629
 
630
+ CREATE INDEX IF NOT EXISTS idx_pericias_livro ON pericias(livro_id);
631
+
538
632
  CREATE INDEX IF NOT EXISTS idx_pericias_atributo ON pericias(atributo);
539
633
  CREATE INDEX IF NOT EXISTS idx_pericias_trained_only ON pericias(trained_only);
540
634
 
@@ -553,10 +647,14 @@ CREATE TABLE IF NOT EXISTS tabelas (
553
647
  description TEXT,
554
648
  headers JSON NOT NULL DEFAULT '[]',
555
649
  rows JSON NOT NULL DEFAULT '[]',
650
+ dice TEXT, -- ex: "1d100", "1d20"; se presente, a 1ª coluna de cada row é uma faixa (ex: "01-05") e a tabela é sorteável via #roll
651
+ livro_id TEXT NOT NULL DEFAULT 't20_eja' REFERENCES livros(id),
556
652
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
557
653
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
558
654
  );
559
655
 
656
+ CREATE INDEX IF NOT EXISTS idx_tabelas_livro ON tabelas(livro_id);
657
+
560
658
  CREATE INDEX IF NOT EXISTS idx_tabelas_name ON tabelas(name);
561
659
 
562
660
  CREATE TRIGGER IF NOT EXISTS update_tabelas_timestamp
@@ -565,6 +663,339 @@ BEGIN
565
663
  UPDATE tabelas SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
566
664
  END;
567
665
 
666
+ -- -----------------------------------------------------------------------------
667
+ -- CRIATURAS (Monsters & Enemies)
668
+ -- -----------------------------------------------------------------------------
669
+ CREATE TABLE IF NOT EXISTS ameacas (
670
+ id TEXT PRIMARY KEY,
671
+ name TEXT NOT NULL,
672
+ nd REAL NOT NULL,
673
+ tipo TEXT,
674
+ subtipo TEXT,
675
+ tamanho TEXT NOT NULL DEFAULT 'médio',
676
+ grupo TEXT NOT NULL,
677
+ pv INTEGER,
678
+ defesa INTEGER,
679
+ for INTEGER, des INTEGER, con INTEGER, int INTEGER, sab INTEGER, car INTEGER,
680
+ iniciativa INTEGER,
681
+ deslocamento TEXT,
682
+ salvaguardas JSON DEFAULT '{}',
683
+ percepcao INTEGER,
684
+ visao TEXT,
685
+ ataques JSON DEFAULT '[]',
686
+ poderes_especiais JSON DEFAULT '[]',
687
+ imunidades JSON DEFAULT '[]',
688
+ resistencias JSON DEFAULT '{}',
689
+ vulnerabilidades JSON DEFAULT '[]',
690
+ reducao_de_dano JSON, -- inteiro (ex: 10) ou string com exceção (ex: "10/luz")
691
+ cura_acelerada JSON, -- inteiro (ex: 10) ou string com exceção (ex: "100/kaiju")
692
+ resistencia_a_magia INTEGER,
693
+ pericias JSON DEFAULT '{}',
694
+ equipamento JSON DEFAULT '[]',
695
+ tesouro TEXT,
696
+ pm INTEGER,
697
+ familiar JSON, -- benefício simples como familiar de conjurador (texto livre), ou null
698
+ parceiro JSON, -- benefícios de parceiro especial, por patamar: {tipo, restricao, iniciante, veterano, mestre}
699
+ description TEXT,
700
+ livro_id TEXT NOT NULL DEFAULT 't20_eja' REFERENCES livros(id),
701
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
702
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
703
+ );
704
+
705
+ CREATE INDEX IF NOT EXISTS idx_ameacas_livro ON ameacas(livro_id);
706
+
707
+ CREATE INDEX IF NOT EXISTS idx_ameacas_nd ON ameacas(nd);
708
+ CREATE INDEX IF NOT EXISTS idx_ameacas_grupo ON ameacas(grupo);
709
+ CREATE INDEX IF NOT EXISTS idx_ameacas_name ON ameacas(name);
710
+
711
+ CREATE TRIGGER IF NOT EXISTS update_ameacas_timestamp
712
+ AFTER UPDATE ON ameacas
713
+ BEGIN
714
+ UPDATE ameacas SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
715
+ END;
716
+
717
+ -- -----------------------------------------------------------------------------
718
+ -- ITENS MÁGICOS - ARMAS ESPECÍFICAS (Named Magic Weapons)
719
+ -- -----------------------------------------------------------------------------
720
+ CREATE TABLE IF NOT EXISTS armas_magicas_especificas (
721
+ id TEXT PRIMARY KEY,
722
+ name TEXT NOT NULL,
723
+ base_weapon TEXT,
724
+ preco INTEGER,
725
+ description TEXT,
726
+ effects JSON DEFAULT '[]',
727
+ livro_id TEXT NOT NULL DEFAULT 't20_eja' REFERENCES livros(id),
728
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
729
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
730
+ );
731
+
732
+ CREATE INDEX IF NOT EXISTS idx_armas_magicas_especificas_livro ON armas_magicas_especificas(livro_id);
733
+
734
+ CREATE INDEX IF NOT EXISTS idx_armas_magicas_name ON armas_magicas_especificas(name);
735
+
736
+ CREATE TRIGGER IF NOT EXISTS update_armas_magicas_especificas_timestamp
737
+ AFTER UPDATE ON armas_magicas_especificas
738
+ BEGIN
739
+ UPDATE armas_magicas_especificas SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
740
+ END;
741
+
742
+ -- -----------------------------------------------------------------------------
743
+ -- ITENS MÁGICOS - ARMADURAS ESPECÍFICAS (Named Magic Armors & Shields)
744
+ -- -----------------------------------------------------------------------------
745
+ CREATE TABLE IF NOT EXISTS armaduras_magicas_especificas (
746
+ id TEXT PRIMARY KEY,
747
+ name TEXT NOT NULL,
748
+ subtipo TEXT NOT NULL DEFAULT 'armadura',
749
+ base_item TEXT,
750
+ preco INTEGER,
751
+ description TEXT,
752
+ effects JSON DEFAULT '[]',
753
+ livro_id TEXT NOT NULL DEFAULT 't20_eja' REFERENCES livros(id),
754
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
755
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
756
+ );
757
+
758
+ CREATE INDEX IF NOT EXISTS idx_armaduras_magicas_especificas_livro ON armaduras_magicas_especificas(livro_id);
759
+
760
+ CREATE INDEX IF NOT EXISTS idx_armaduras_magicas_name ON armaduras_magicas_especificas(name);
761
+ CREATE INDEX IF NOT EXISTS idx_armaduras_magicas_subtipo ON armaduras_magicas_especificas(subtipo);
762
+
763
+ CREATE TRIGGER IF NOT EXISTS update_armaduras_magicas_especificas_timestamp
764
+ AFTER UPDATE ON armaduras_magicas_especificas
765
+ BEGIN
766
+ UPDATE armaduras_magicas_especificas SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
767
+ END;
768
+
769
+ -- -----------------------------------------------------------------------------
770
+ -- ITENS LITÚRGICOS (Liturgical Items) — Deuses de Arton
771
+ -- Itens mágicos ligados a uma divindade específica: ficam mais fortes (ou só
772
+ -- funcionam) nas mãos de devotos dessa divindade. Podem ser armas, armaduras
773
+ -- ou acessórios — por isso usam `deities` (array) em vez de FK rígida.
774
+ -- -----------------------------------------------------------------------------
775
+ CREATE TABLE IF NOT EXISTS itens_liturgicos (
776
+ id TEXT PRIMARY KEY,
777
+ name TEXT NOT NULL,
778
+ deities JSON DEFAULT '[]',
779
+ subtipo TEXT, -- 'arma' | 'armadura' | 'acessorio'
780
+ base_item TEXT,
781
+ classificacao TEXT, -- ex.: "Arma específica menor", "Acessório maior"
782
+ preco INTEGER,
783
+ description TEXT,
784
+ effects JSON DEFAULT '[]',
785
+ livro_id TEXT NOT NULL DEFAULT 't20_eja' REFERENCES livros(id),
786
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
787
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
788
+ );
789
+
790
+ CREATE INDEX IF NOT EXISTS idx_itens_liturgicos_livro ON itens_liturgicos(livro_id);
791
+
792
+ CREATE INDEX IF NOT EXISTS idx_itens_liturgicos_name ON itens_liturgicos(name);
793
+ CREATE INDEX IF NOT EXISTS idx_itens_liturgicos_subtipo ON itens_liturgicos(subtipo);
794
+
795
+ CREATE TRIGGER IF NOT EXISTS update_itens_liturgicos_timestamp
796
+ AFTER UPDATE ON itens_liturgicos
797
+ BEGIN
798
+ UPDATE itens_liturgicos SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
799
+ END;
800
+
801
+ -- -----------------------------------------------------------------------------
802
+ -- ARTEFATOS (Artifacts)
803
+ -- -----------------------------------------------------------------------------
804
+ CREATE TABLE IF NOT EXISTS artefatos (
805
+ id TEXT PRIMARY KEY,
806
+ name TEXT NOT NULL,
807
+ description TEXT,
808
+ effects JSON DEFAULT '[]',
809
+ livro_id TEXT NOT NULL DEFAULT 't20_eja' REFERENCES livros(id),
810
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
811
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
812
+ );
813
+
814
+ CREATE INDEX IF NOT EXISTS idx_artefatos_livro ON artefatos(livro_id);
815
+
816
+ CREATE INDEX IF NOT EXISTS idx_artefatos_name ON artefatos(name);
817
+
818
+ CREATE TRIGGER IF NOT EXISTS update_artefatos_timestamp
819
+ AFTER UPDATE ON artefatos
820
+ BEGIN
821
+ UPDATE artefatos SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
822
+ END;
823
+
824
+ -- -----------------------------------------------------------------------------
825
+ -- DISTINÇÕES (Distinctions) — Heróis de Arton / Deuses de Arton
826
+ -- Cada distinção tem admissão (requisito narrativo), marca (habilidade
827
+ -- automática) e uma árvore de poderes exclusivos. Os poderes de fato ficam
828
+ -- na tabela `poderes` (type = 'poder_distincao', distincao_id aponta aqui).
829
+ -- -----------------------------------------------------------------------------
830
+ CREATE TABLE IF NOT EXISTS distincoes (
831
+ id TEXT PRIMARY KEY,
832
+ name TEXT NOT NULL,
833
+ description TEXT, -- resumo curto
834
+ admissao TEXT, -- requisitos narrativos de admissão
835
+ marca_nome TEXT, -- nome da "Marca da Distinção"
836
+ marca_descricao TEXT, -- efeito da marca (concedida automaticamente na admissão)
837
+ nivel_minimo INTEGER DEFAULT 5, -- nível mínimo para obter poderes da distinção (veterano)
838
+ livro_id TEXT NOT NULL DEFAULT 't20_eja' REFERENCES livros(id),
839
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
840
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
841
+ );
842
+
843
+ CREATE INDEX IF NOT EXISTS idx_distincoes_livro ON distincoes(livro_id);
844
+ CREATE INDEX IF NOT EXISTS idx_distincoes_name ON distincoes(name);
845
+
846
+ -- -----------------------------------------------------------------------------
847
+ -- COMPLICAÇÕES (Character Complications — Heróis de Arton, Cap. 4)
848
+ -- -----------------------------------------------------------------------------
849
+ -- Desvantagens escolhidas na criação de personagem: concedem uma penalidade
850
+ -- mecânica em troca de um poder geral gratuito.
851
+ CREATE TABLE IF NOT EXISTS complicacoes (
852
+ id TEXT PRIMARY KEY,
853
+ name TEXT NOT NULL,
854
+ description TEXT,
855
+ categoria TEXT NOT NULL DEFAULT 'geral', -- 'geral' ou id de classe (ex: 'guerreiro') p/ complicações específicas de classe
856
+ effects JSON DEFAULT '[]', -- penalidades mecânicas tipadas
857
+ -- Sem coluna de "poder concedido": a regra do livro é escolha livre de QUALQUER poder geral
858
+ -- cujos pré-requisitos sejam cumpridos, não um poder fixo por complicação (confirmado no texto:
859
+ -- "Você é livre para escolher o poder, mas deve preencher seus pré-requisitos").
860
+ livro_id TEXT NOT NULL DEFAULT 't20_eja' REFERENCES livros(id),
861
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
862
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
863
+ );
864
+
865
+ CREATE INDEX IF NOT EXISTS idx_complicacoes_livro ON complicacoes(livro_id);
866
+ CREATE INDEX IF NOT EXISTS idx_complicacoes_categoria ON complicacoes(categoria);
867
+
868
+ CREATE TRIGGER IF NOT EXISTS update_distincoes_timestamp
869
+ AFTER UPDATE ON distincoes
870
+ BEGIN
871
+ UPDATE distincoes SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
872
+ END;
873
+
874
+ -- -----------------------------------------------------------------------------
875
+ -- BASES (Group Headquarters — Heróis de Arton, Cap. 3)
876
+ -- -----------------------------------------------------------------------------
877
+ -- Só o conteúdo de referência/catálogo (tipos, cômodos, mobílias) mora na gem — a
878
+ -- instância real de uma base específica de um grupo (tipo+porte+cômodos escolhidos)
879
+ -- é estado de campanha, não dado da gem, e vive no consumidor (arkheion_api).
880
+ -- A escala de porte (Tabela 3-6: custo/manutenção/cômodos por porte) é modelada
881
+ -- como uma entrada comum em `tabelas`, não uma tabela própria.
882
+
883
+ CREATE TABLE IF NOT EXISTS bases_tipos (
884
+ id TEXT PRIMARY KEY,
885
+ name TEXT NOT NULL,
886
+ description TEXT,
887
+ effects JSON DEFAULT '[]',
888
+ livro_id TEXT NOT NULL DEFAULT 't20_eja' REFERENCES livros(id),
889
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
890
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
891
+ );
892
+
893
+ CREATE INDEX IF NOT EXISTS idx_bases_tipos_livro ON bases_tipos(livro_id);
894
+
895
+ CREATE TRIGGER IF NOT EXISTS update_bases_tipos_timestamp
896
+ AFTER UPDATE ON bases_tipos
897
+ BEGIN
898
+ UPDATE bases_tipos SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
899
+ END;
900
+
901
+ CREATE TABLE IF NOT EXISTS comodos (
902
+ id TEXT PRIMARY KEY,
903
+ name TEXT NOT NULL,
904
+ description TEXT,
905
+ effects JSON DEFAULT '[]',
906
+ prerequisito TEXT, -- texto livre (ex: "base formidável ou melhor") ou id de outro cômodo pré-requisito
907
+ livro_id TEXT NOT NULL DEFAULT 't20_eja' REFERENCES livros(id),
908
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
909
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
910
+ );
911
+
912
+ CREATE INDEX IF NOT EXISTS idx_comodos_livro ON comodos(livro_id);
913
+
914
+ CREATE TRIGGER IF NOT EXISTS update_comodos_timestamp
915
+ AFTER UPDATE ON comodos
916
+ BEGIN
917
+ UPDATE comodos SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
918
+ END;
919
+
920
+ CREATE TABLE IF NOT EXISTS mobilias (
921
+ id TEXT PRIMARY KEY,
922
+ name TEXT NOT NULL,
923
+ description TEXT,
924
+ effects JSON DEFAULT '[]',
925
+ preco INTEGER,
926
+ comodo_alvo TEXT, -- id do cômodo onde deve ser instalada, se restrita (ex: banheira -> suite); null = qualquer cômodo
927
+ livro_id TEXT NOT NULL DEFAULT 't20_eja' REFERENCES livros(id),
928
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
929
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
930
+ );
931
+
932
+ CREATE INDEX IF NOT EXISTS idx_mobilias_livro ON mobilias(livro_id);
933
+
934
+ CREATE TRIGGER IF NOT EXISTS update_mobilias_timestamp
935
+ AFTER UPDATE ON mobilias
936
+ BEGIN
937
+ UPDATE mobilias SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
938
+ END;
939
+
940
+ -- -----------------------------------------------------------------------------
941
+ -- CONSTRUÇÕES DE DOMÍNIO (Domain Buildings — Heróis de Arton, Cap. 4 "Domínios")
942
+ -- -----------------------------------------------------------------------------
943
+ -- Só o catálogo de construções mora na gem (mesmo princípio de bases_tipos/comodos/
944
+ -- mobilias) — o domínio real de um regente específico (nível, corte, popularidade,
945
+ -- construções erguidas) é estado de campanha, não dado da gem. Terrenos (Tabela 4-9)
946
+ -- viram uma entrada comum em `tabelas`; corte/popularidade/fortificação (poucas
947
+ -- categorias fixas) ficam no `data` de `regras/dominios.json` em vez de tabelas próprias.
948
+ CREATE TABLE IF NOT EXISTS construcoes_dominio (
949
+ id TEXT PRIMARY KEY,
950
+ name TEXT NOT NULL,
951
+ description TEXT,
952
+ categoria TEXT NOT NULL, -- 'nobreza' | 'guerra' | 'enganacao' | 'religiao' | 'misticismo'
953
+ custo INTEGER, -- custo em Lingotes de Ouro (LO)
954
+ effects JSON DEFAULT '[]',
955
+ prerequisito TEXT, -- texto livre ou id de outra construção/terreno pré-requisito
956
+ livro_id TEXT NOT NULL DEFAULT 't20_eja' REFERENCES livros(id),
957
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
958
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
959
+ );
960
+
961
+ CREATE INDEX IF NOT EXISTS idx_construcoes_dominio_livro ON construcoes_dominio(livro_id);
962
+ CREATE INDEX IF NOT EXISTS idx_construcoes_dominio_categoria ON construcoes_dominio(categoria);
963
+
964
+ CREATE TRIGGER IF NOT EXISTS update_construcoes_dominio_timestamp
965
+ AFTER UPDATE ON construcoes_dominio
966
+ BEGIN
967
+ UPDATE construcoes_dominio SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
968
+ END;
969
+
970
+ -- -----------------------------------------------------------------------------
971
+ -- CLASSES VARIANTES (Variant Classes) — Heróis de Arton
972
+ -- Representadas como diff sobre a classe-base: `diffs` é um array de
973
+ -- { nivel, remove: [habilidade_ids], adiciona: [{...habilidade}] }.
974
+ -- Consumidores devem aplicar o diff sobre `classes.powers`/`abilities` da
975
+ -- classe-base para montar a ficha final.
976
+ -- NB: coluna chama-se `diffs`, não `changes` — `changes` é reservado pelo
977
+ -- ActiveRecord (dirty tracking) e quebraria o modelo Ruby.
978
+ -- -----------------------------------------------------------------------------
979
+ CREATE TABLE IF NOT EXISTS classes_variantes (
980
+ id TEXT PRIMARY KEY,
981
+ name TEXT NOT NULL,
982
+ base_class_id TEXT NOT NULL REFERENCES classes(id),
983
+ description TEXT,
984
+ diffs JSON DEFAULT '[]',
985
+ livro_id TEXT NOT NULL DEFAULT 't20_eja' REFERENCES livros(id),
986
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
987
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
988
+ );
989
+
990
+ CREATE INDEX IF NOT EXISTS idx_classes_variantes_livro ON classes_variantes(livro_id);
991
+ CREATE INDEX IF NOT EXISTS idx_classes_variantes_base ON classes_variantes(base_class_id);
992
+
993
+ CREATE TRIGGER IF NOT EXISTS update_classes_variantes_timestamp
994
+ AFTER UPDATE ON classes_variantes
995
+ BEGIN
996
+ UPDATE classes_variantes SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
997
+ END;
998
+
568
999
  -- =============================================================================
569
1000
  -- VIEWS ÚTEIS
570
1001
  -- =============================================================================
Binary file