tormenta20 0.2.22 → 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 +3 -3
  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
@@ -48,6 +48,9 @@ module Tormenta20
48
48
  poder_combate
49
49
  poder_destino
50
50
  poder_magia
51
+ poder_de_raca
52
+ poder_de_grupo
53
+ poder_distincao
51
54
  ].freeze
52
55
 
53
56
  validates :id, presence: true, uniqueness: true
@@ -99,6 +102,17 @@ module Tormenta20
99
102
  # @return [ActiveRecord::Relation<Poder>]
100
103
  scope :by_deity, ->(deity_id) { where("json_extract(deities, '$') LIKE ?", "%#{deity_id}%") }
101
104
 
105
+ # @!method poderes_distincao
106
+ # Filter distinction powers.
107
+ # @return [ActiveRecord::Relation<Poder>]
108
+ scope :poderes_distincao, -> { where(type: "poder_distincao") }
109
+
110
+ # @!method by_distincao(distincao_id)
111
+ # Filter powers belonging to a specific distinction.
112
+ # @param distincao_id [String] Distinction ID
113
+ # @return [ActiveRecord::Relation<Poder>]
114
+ scope :by_distincao, ->(distincao_id) { where(distincao_id: distincao_id) }
115
+
102
116
  # @!endgroup
103
117
 
104
118
  # Convert power to a hash representation.
@@ -113,6 +127,7 @@ module Tormenta20
113
127
  effects: effects,
114
128
  prerequisites: prerequisites,
115
129
  origin_id: origin_id,
130
+ distincao_id: distincao_id,
116
131
  deities: deities
117
132
  }.compact
118
133
  end
@@ -28,6 +28,8 @@ module Tormenta20
28
28
  # @return [Integer, nil] Range of special vision in metres, or +nil+ for normal vision
29
29
  # @!attribute [rw] attribute_bonuses
30
30
  # @return [Hash{String => Integer}] Map of attribute key to bonus value
31
+ # @!attribute [rw] chosen_attribute_bonus
32
+ # @return [Hash, nil] +{ count:, value:, exclude: }+ — "choose N attributes" pattern (Heróis de Arton); +nil+ for fixed-bonus races
31
33
  # @!attribute [rw] skill_bonuses
32
34
  # @return [Array] Skill bonus descriptors
33
35
  # @!attribute [rw] racial_abilities
@@ -36,6 +38,13 @@ module Tormenta20
36
38
  # @return [Integer] Number of abilities the player selects at character creation
37
39
  # @!attribute [rw] available_chosen_abilities
38
40
  # @return [Array] Pool of ability IDs to choose from
41
+ # @!attribute [rw] longevidade
42
+ # @return [String, nil] Lifespan multiplier (e.g. +"x2"+, +"x5"+, +"Normal"+); introduced in Heróis de Arton, absent for EJA races
43
+ # @!attribute [rw] devocao
44
+ # @return [Array<String>, nil] Deity IDs commonly devoted to by this race, or +nil+
45
+ # @!attribute [rw] ability_swap
46
+ # @return [Hash, nil] +{ "replaces" => id, "options" => [ids] }+ — a fixed racial ability
47
+ # that may be swapped for one of several variants (e.g. suraggel/Deuses de Arton); +nil+ when none exist
39
48
  class Raca < Base
40
49
  self.table_name = "racas"
41
50
 
@@ -84,10 +93,14 @@ module Tormenta20
84
93
  vision: vision,
85
94
  vision_range: vision_range,
86
95
  attribute_bonuses: attribute_bonuses || {},
96
+ chosen_attribute_bonus: chosen_attribute_bonus,
87
97
  skill_bonuses: skill_bonuses || [],
88
98
  racial_abilities: racial_abilities || [],
89
99
  chosen_abilities_amount: chosen_abilities_amount || 0,
90
- available_chosen_abilities: available_chosen_abilities || []
100
+ available_chosen_abilities: available_chosen_abilities || [],
101
+ longevidade: longevidade,
102
+ devocao: devocao,
103
+ ability_swap: ability_swap
91
104
  }.compact
92
105
  end
93
106
  end
@@ -13,7 +13,7 @@ module Tormenta20
13
13
  # @example
14
14
  # t = Tabela.find_by(id: "tesouros_por_nd")
15
15
  # t.headers # => ["ND", "Tibares", ...]
16
- # t.rows # => [["1", "50", ...], ...]
16
+ # t.rows # => [{ "ND" => "1", "Tibares" => "50", ... }, ...]
17
17
  #
18
18
  # @!attribute [rw] id
19
19
  # @return [String] Unique identifier
@@ -24,7 +24,9 @@ module Tormenta20
24
24
  # @!attribute [rw] headers
25
25
  # @return [Array<String>] Column header labels
26
26
  # @!attribute [rw] rows
27
- # @return [Array<Array>] Table rows, each an array of string values
27
+ # @return [Array<Hash>] Table rows, each a Hash keyed by header
28
+ # @!attribute [rw] dice
29
+ # @return [String, nil] Dice expression (e.g. +"1d100"+) if this table is rollable via {#roll}
28
30
  class Tabela < Base
29
31
  self.table_name = "tabelas"
30
32
 
@@ -37,9 +39,37 @@ module Tormenta20
37
39
  name: name,
38
40
  description: description,
39
41
  headers: headers || [],
40
- rows: rows || []
42
+ rows: rows || [],
43
+ dice: dice
41
44
  }.compact
42
45
  end
46
+
47
+ # @return [Boolean] true if this table has a +dice+ column set and can be rolled on
48
+ def sorteavel?
49
+ dice.present?
50
+ end
51
+
52
+ # Rolls the table's dice and returns the matching row (a Hash keyed by header, same
53
+ # shape as {#rows} entries). Assumes the first header's column holds a range string
54
+ # (e.g. +"01-05"+) or a single value (e.g. +"06"+) that the roll falls within.
55
+ #
56
+ # @return [Hash, nil] the matching row, or +nil+ if no row's range matches
57
+ # @raise [RuntimeError] if the table has no +dice+ column set
58
+ def roll
59
+ raise "Tabela '#{id}' não é sorteável (sem coluna dice)" unless sorteavel?
60
+
61
+ faces = dice[/\d+\z/].to_i
62
+ result = rand(1..faces)
63
+ range_key = headers.first
64
+ rows.find { |r| self.class.in_range?(r[range_key], result) }
65
+ end
66
+
67
+ # @return [Boolean] true if +value+ falls within +range_str+ (e.g. +"01-05"+ or +"06"+)
68
+ def self.in_range?(range_str, value)
69
+ bounds = range_str.to_s.split("-").map(&:to_i)
70
+ bounds << bounds.first if bounds.size == 1
71
+ value.between?(bounds.min, bounds.max)
72
+ end
43
73
  end
44
74
  end
45
75
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tormenta20
4
+ module Rules
5
+ # Damage step rules for weapon damage dice (Tormenta20 core rules, Tabela: Passos de Dano).
6
+ #
7
+ # Effects that increase or decrease weapon damage "by steps" move the damage die
8
+ # along a fixed progression, from -2 to +3 steps relative to the weapon's normal die.
9
+ module DamageStep
10
+ STEPS = %w[menos_2 menos_1 normal mais_1 mais_2 mais_3].freeze
11
+
12
+ TABLE = [
13
+ { "menos_2" => "1", "menos_1" => "1d2", "normal" => "1d3", "mais_1" => "1d4", "mais_2" => "1d6", "mais_3" => "1d8" },
14
+ { "menos_2" => "1d2", "menos_1" => "1d3", "normal" => "1d4", "mais_1" => "1d6", "mais_2" => "1d8", "mais_3" => "1d10" },
15
+ { "menos_2" => "1d3", "menos_1" => "1d4", "normal" => "1d6", "mais_1" => "1d8", "mais_2" => "1d10", "mais_3" => "1d12" },
16
+ { "menos_2" => "1d4", "menos_1" => "1d6", "normal" => "1d8", "mais_1" => "1d10", "mais_2" => "1d12", "mais_3" => "3d6" },
17
+ { "menos_2" => "1d6", "menos_1" => "1d8", "normal" => "1d10", "mais_1" => "1d12", "mais_2" => "3d6", "mais_3" => "4d6" },
18
+ { "menos_2" => "1d8", "menos_1" => "1d10", "normal" => "1d12", "mais_1" => "3d6", "mais_2" => "4d6", "mais_3" => "4d8" },
19
+ { "menos_2" => "1d10", "menos_1" => "2d6", "normal" => "2d8", "mais_1" => "3d8", "mais_2" => "4d8", "mais_3" => "4d10" },
20
+ { "menos_2" => "2d6", "menos_1" => "2d8", "normal" => "2d10", "mais_1" => "3d10", "mais_2" => "4d10", "mais_3" => "4d12" }
21
+ ].freeze
22
+
23
+ # Returns the damage die after shifting it a number of steps up or down.
24
+ #
25
+ # @param dice [String] the weapon's current normal damage die (e.g. "1d8")
26
+ # @param steps [Integer] steps to shift, positive (up) or negative (down);
27
+ # clamped to the table's range (-2 to +3)
28
+ # @return [String, nil] the resulting die, or nil if `dice` isn't a known normal damage die
29
+ def self.adjust(dice, steps)
30
+ row = TABLE.find { |r| r["normal"] == dice }
31
+ return nil unless row
32
+
33
+ row[STEPS[steps.clamp(-2, 3) + 2]]
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tormenta20
4
+ module Rules
5
+ # Spell PM cost rules (Tormenta20 core rules, Tabela 4-1: Custo de Magias).
6
+ module SpellCost
7
+ COSTS_BY_CIRCLE = { 1 => 1, 2 => 3, 3 => 6, 4 => 10, 5 => 15 }.freeze
8
+
9
+ # Returns the base PM cost for a spell of the given circle.
10
+ #
11
+ # @param circle [String, Integer] the spell's circle (1-5)
12
+ # @return [Integer, nil] PM cost, or nil if the circle is unknown
13
+ def self.for_circle(circle)
14
+ COSTS_BY_CIRCLE[circle.to_i]
15
+ end
16
+ end
17
+ end
18
+ end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "rules/pm_cap"
4
+ require_relative "rules/spell_cost"
5
+ require_relative "rules/damage_step"
4
6
 
5
7
  module Tormenta20
6
8
  # Namespace for Tormenta20 rule calculations and constraints.
@@ -12,23 +12,39 @@ module Tormenta20
12
12
  @verbose = verbose
13
13
  log "Seeding database..."
14
14
 
15
- seed_racas
16
- seed_habilidades_de_raca
17
- seed_origens
18
- seed_poderes_habilidades_unicas
19
- seed_divindades
20
- seed_poderes_concedidos
21
- seed_poderes_tormenta
22
- seed_poderes_gerais
23
- seed_classes
24
- seed_habilidades_de_classe
25
- seed_magias
26
- seed_equipamentos
27
- seed_itens_superiores
28
- seed_regras
29
- seed_condicoes
30
- seed_pericias
31
- seed_tabelas
15
+ Thread.current[:tormenta20_seeding] = true
16
+ begin
17
+ seed_livros
18
+ seed_racas
19
+ seed_habilidades_de_raca
20
+ seed_origens
21
+ seed_poderes_habilidades_unicas
22
+ seed_divindades
23
+ seed_poderes_concedidos
24
+ seed_poderes_tormenta
25
+ seed_poderes_gerais
26
+ seed_classes
27
+ seed_habilidades_de_classe
28
+ seed_classes_variantes
29
+ seed_distincoes
30
+ seed_poderes_distincao
31
+ seed_complicacoes
32
+ seed_magias
33
+ seed_equipamentos
34
+ seed_itens_superiores
35
+ seed_regras
36
+ seed_condicoes
37
+ seed_pericias
38
+ seed_tabelas
39
+ seed_itens_magicos
40
+ seed_ameacas
41
+ seed_bases_tipos
42
+ seed_comodos
43
+ seed_mobilias
44
+ seed_indice_remissivo
45
+ ensure
46
+ Thread.current[:tormenta20_seeding] = false
47
+ end
32
48
 
33
49
  log "Database seeded successfully!"
34
50
  end
@@ -45,6 +61,41 @@ module Tormenta20
45
61
  puts message if @verbose
46
62
  end
47
63
 
64
+ def seed_livros
65
+ import_json_files(
66
+ "livros",
67
+ Models::Livro,
68
+ %i[id nome nome_curto]
69
+ )
70
+ end
71
+
72
+ def seed_indice_remissivo
73
+ json_dir = File.join(JSON_BASE_PATH, "indice_remissivo")
74
+ return unless Dir.exist?(json_dir)
75
+
76
+ Dir.glob(File.join(json_dir, "*.json")).each do |file|
77
+ livro_id = File.basename(file, ".json")
78
+ entries = JSON.parse(File.read(file), symbolize_names: true)
79
+ next unless entries.is_a?(Array)
80
+
81
+ entries.each do |entry|
82
+ next unless entry[:termo] && entry[:pagina]
83
+
84
+ Models::IndiceRemissivo.new(
85
+ livro_id: livro_id,
86
+ termo: entry[:termo],
87
+ pagina: entry[:pagina],
88
+ tabela: entry[:tabela],
89
+ registro_id: entry[:registro_id]
90
+ ).save!
91
+ rescue StandardError
92
+ next
93
+ end
94
+ rescue StandardError
95
+ next
96
+ end
97
+ end
98
+
48
99
  def seed_racas
49
100
  import_json_files(
50
101
  "racas",
@@ -106,7 +157,8 @@ module Tormenta20
106
157
  "deuses",
107
158
  Models::Divindade,
108
159
  %i[id name title description beliefs_objectives holy_symbol energy
109
- preferred_weapon devotees granted_powers obligations_restrictions]
160
+ preferred_weapon devotees granted_powers obligations_restrictions
161
+ rank natureza status_divino]
110
162
  )
111
163
  end
112
164
 
@@ -187,6 +239,82 @@ module Tormenta20
187
239
  end
188
240
  end
189
241
 
242
+ def seed_classes_variantes
243
+ import_json_files(
244
+ "classes_variantes",
245
+ Models::ClasseVariante,
246
+ %i[id name base_class_id description diffs]
247
+ )
248
+ end
249
+
250
+ def seed_distincoes
251
+ import_json_files(
252
+ "distincoes",
253
+ Models::Distincao,
254
+ %i[id name description admissao marca_nome marca_descricao nivel_minimo]
255
+ )
256
+ end
257
+
258
+ def seed_complicacoes
259
+ import_json_files(
260
+ "complicacoes",
261
+ Models::Complicacao,
262
+ %i[id name description categoria effects]
263
+ )
264
+ end
265
+
266
+ def seed_bases_tipos
267
+ import_json_files(
268
+ "bases_tipos",
269
+ Models::BaseTipo,
270
+ %i[id name description effects]
271
+ )
272
+ end
273
+
274
+ def seed_comodos
275
+ import_json_files(
276
+ "comodos",
277
+ Models::Comodo,
278
+ %i[id name description effects prerequisito]
279
+ )
280
+ end
281
+
282
+ def seed_mobilias
283
+ import_json_files(
284
+ "mobilias",
285
+ Models::Mobilia,
286
+ %i[id name description effects preco comodo_alvo]
287
+ )
288
+ end
289
+
290
+ def seed_poderes_distincao
291
+ base_dir = File.join(JSON_BASE_PATH, "poderes", "poderes_distincao")
292
+ return unless Dir.exist?(base_dir)
293
+
294
+ Dir.each_child(base_dir) do |distincao_dir|
295
+ distincao_path = File.join(base_dir, distincao_dir)
296
+ next unless File.directory?(distincao_path)
297
+
298
+ distincao_id = distincao_dir
299
+
300
+ Dir.glob(File.join(distincao_path, "*.json")).each do |file|
301
+ data = JSON.parse(File.read(file), symbolize_names: true)
302
+ next if data[:id].nil?
303
+
304
+ record = Models::Poder.find_or_initialize_by(id: data[:id])
305
+ record.name = data[:name] || data[:id].to_s.humanize
306
+ record.type = "poder_distincao"
307
+ record.distincao_id = distincao_id
308
+ record.description = data[:description]
309
+ record.effects = data[:effects] || []
310
+ record.prerequisites = data[:requirements] || data[:prerequisites] || []
311
+ record.save!
312
+ rescue StandardError
313
+ next
314
+ end
315
+ end
316
+ end
317
+
190
318
  def seed_magias
191
319
  json_dir = File.join(JSON_BASE_PATH, "magias")
192
320
  return unless Dir.exist?(json_dir)
@@ -375,8 +503,90 @@ module Tormenta20
375
503
  import_json_files(
376
504
  "tabelas",
377
505
  Models::Tabela,
378
- %i[id name description headers rows]
506
+ %i[id name description headers rows dice]
507
+ )
508
+ end
509
+
510
+ def seed_itens_magicos
511
+ import_json_files(
512
+ "itens_magicos/armas_especificas",
513
+ Models::ArmaMagicaEspecifica,
514
+ %i[id name base_weapon preco description effects]
515
+ )
516
+
517
+ import_json_files(
518
+ "itens_magicos/armaduras_especificas",
519
+ Models::ArmaduraMagicaEspecifica,
520
+ %i[id name subtipo base_item preco description effects]
521
+ )
522
+
523
+ import_json_files(
524
+ "itens_magicos/itens_liturgicos",
525
+ Models::ItemLiturgico,
526
+ %i[id name deities subtipo base_item classificacao preco description effects]
527
+ )
528
+
529
+ import_json_files(
530
+ "itens_magicos/artefatos",
531
+ Models::Artefato,
532
+ %i[id name description effects]
379
533
  )
534
+
535
+ import_json_files(
536
+ "itens_magicos/esotericos",
537
+ Models::EsotericoMagico,
538
+ %i[id name categoria preco roll_min roll_max description effects]
539
+ )
540
+ end
541
+
542
+ def seed_ameacas
543
+ json_dir = File.join(JSON_BASE_PATH, "ameacas")
544
+ return unless Dir.exist?(json_dir)
545
+
546
+ Dir.glob(File.join(json_dir, "**", "*.json")).each do |file|
547
+ data = JSON.parse(File.read(file), symbolize_names: true)
548
+ next if data[:id].nil?
549
+
550
+ attrs = data[:atributos] || {}
551
+ record = Models::Ameaca.find_or_initialize_by(id: data[:id])
552
+ record.name = data[:name]
553
+ record.nd = data[:nd]
554
+ record.tipo = data[:tipo]
555
+ record.subtipo = data[:subtipo]
556
+ record.tamanho = data[:tamanho] || "médio"
557
+ record.grupo = data[:grupo]
558
+ record.pv = data[:pv]
559
+ record.defesa = data[:defesa]
560
+ record[:for] = attrs[:for]
561
+ record.des = attrs[:des]
562
+ record.con = attrs[:con]
563
+ record.int = attrs[:int]
564
+ record.sab = attrs[:sab]
565
+ record.car = attrs[:car]
566
+ record.iniciativa = data[:iniciativa]
567
+ record.deslocamento = data[:deslocamento]
568
+ record.salvaguardas = data[:salvaguardas] || {}
569
+ record.percepcao = data[:percepcao]
570
+ record.visao = data[:visao]
571
+ record.ataques = data[:ataques] || []
572
+ record.poderes_especiais = data[:poderes_especiais] || []
573
+ record.imunidades = data[:imunidades] || []
574
+ record.resistencias = data[:resistencias] || {}
575
+ record.vulnerabilidades = data[:vulnerabilidades] || []
576
+ record.reducao_de_dano = data[:reducao_de_dano]
577
+ record.cura_acelerada = data[:cura_acelerada]
578
+ record.resistencia_a_magia = data[:resistencia_a_magia]
579
+ record.pericias = data[:pericias] || {}
580
+ record.equipamento = data[:equipamento] || []
581
+ record.tesouro = data[:tesouro]
582
+ record.pm = data[:pm]
583
+ record.description = data[:description]
584
+ record.familiar = data[:familiar]
585
+ record.parceiro = data[:parceiro]
586
+ record.save!
587
+ rescue StandardError
588
+ next
589
+ end
380
590
  end
381
591
 
382
592
  def import_json_files(subdir, model, attrs, extra_attrs: {}, transform: nil)
@@ -8,10 +8,11 @@ require_relative "tormenta20/database"
8
8
 
9
9
  # Load concerns before models
10
10
  require_relative "tormenta20/concerns/book_referenceable"
11
+ require_relative "tormenta20/concerns/livro_scoped"
11
12
 
12
13
  # Load base model first, then other models
13
14
  require_relative "tormenta20/models/base"
14
- Dir["#{__dir__}/tormenta20/models/*.rb"].each { |file| require file unless file.include?("base") }
15
+ Dir["#{__dir__}/tormenta20/models/*.rb"].each { |file| require file unless File.basename(file) == "base.rb" }
15
16
 
16
17
  # Load other modules
17
18
  Dir["#{__dir__}/tormenta20/*.rb"].each { |file| require file unless file.include?("database") }
@@ -70,6 +71,16 @@ module Tormenta20
70
71
  Models::Poder
71
72
  end
72
73
 
74
+ # Access the Distincao (Distinction) model for querying distinctions.
75
+ #
76
+ # @return [Class<Models::Distincao>] The Distincao model class
77
+ # @example
78
+ # Tormenta20.distincoes.find("amazona")
79
+ # Tormenta20.poderes.by_distincao("amazona")
80
+ def distincoes
81
+ Models::Distincao
82
+ end
83
+
73
84
  # Access the Divindade (Deity) model for querying deities.
74
85
  #
75
86
  # @return [Class<Models::Divindade>] The Divindade model class
@@ -249,6 +260,97 @@ module Tormenta20
249
260
  Models::AcessorioMagico
250
261
  end
251
262
 
263
+ # Access the EsotericoMagico (Magic Esoteric Implement) model for querying staffs,
264
+ # wands, holy symbols and other spellcasting foci.
265
+ #
266
+ # @return [Class<Models::EsotericoMagico>] The EsotericoMagico model class
267
+ # @example
268
+ # Tormenta20.esotericos.maiores
269
+ # Tormenta20.esotericos.find("cajado_do_poder")
270
+ def esotericos
271
+ Models::EsotericoMagico
272
+ end
273
+
274
+ # Access the Complicacao (Character Complication) model.
275
+ #
276
+ # @return [Class<Models::Complicacao>] The Complicacao model class
277
+ # @example
278
+ # Tormenta20.complicacoes.gerais
279
+ # Tormenta20.complicacoes.by_categoria("guerreiro")
280
+ def complicacoes
281
+ Models::Complicacao
282
+ end
283
+
284
+ # Access the BaseTipo (Headquarters Type) model.
285
+ # @return [Class<Models::BaseTipo>]
286
+ def bases_tipos
287
+ Models::BaseTipo
288
+ end
289
+
290
+ # Access the Comodo (Headquarters Room) model.
291
+ # @return [Class<Models::Comodo>]
292
+ def comodos
293
+ Models::Comodo
294
+ end
295
+
296
+ # Access the Mobilia (Headquarters Furniture) model.
297
+ # @return [Class<Models::Mobilia>]
298
+ def mobilias
299
+ Models::Mobilia
300
+ end
301
+
302
+ # Access the ConstrucaoDominio (Domain Building) model.
303
+ # @return [Class<Models::ConstrucaoDominio>]
304
+ def construcoes_dominio
305
+ Models::ConstrucaoDominio
306
+ end
307
+
308
+ # Access named specific magic weapons (Cap. 8, Tabela 8-9).
309
+ #
310
+ # @return [Class<Models::ArmaMagicaEspecifica>]
311
+ # @example
312
+ # Tormenta20.armas_magicas.order(:preco)
313
+ def armas_magicas
314
+ Models::ArmaMagicaEspecifica
315
+ end
316
+
317
+ # Access named specific magic armors and shields (Cap. 8, Tabela 8-11).
318
+ #
319
+ # @return [Class<Models::ArmaduraMagicaEspecifica>]
320
+ # @example
321
+ # Tormenta20.armaduras_magicas.escudos
322
+ def armaduras_magicas
323
+ Models::ArmaduraMagicaEspecifica
324
+ end
325
+
326
+ # Access liturgical items — named magic items tied to a specific deity
327
+ # (Deuses de Arton, Cap. 1).
328
+ #
329
+ # @return [Class<Models::ItemLiturgico>]
330
+ # @example
331
+ # Tormenta20.itens_liturgicos.by_deity("aharadak")
332
+ def itens_liturgicos
333
+ Models::ItemLiturgico
334
+ end
335
+
336
+ # Access artifacts — unique, extremely powerful magic items (Cap. 8, pp. 350–353).
337
+ #
338
+ # @return [Class<Models::Artefato>]
339
+ # @example
340
+ # Tormenta20.artefatos.all
341
+ def artefatos
342
+ Models::Artefato
343
+ end
344
+
345
+ # Access monster and enemy stat blocks (Cap. 7).
346
+ #
347
+ # @return [Class<Models::Ameaca>]
348
+ # @example
349
+ # Tormenta20.ameacas.dragoes.order(:nd)
350
+ def ameacas
351
+ Models::Ameaca
352
+ end
353
+
252
354
  # @!endgroup
253
355
  end
254
356
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tormenta20
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.22
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - LuanGB
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-06-03 00:00:00.000000000 Z
11
+ date: 2026-10-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -98,22 +98,36 @@ files:
98
98
  - docs/ruby/regras.md
99
99
  - src/ruby/tormenta20.rb
100
100
  - src/ruby/tormenta20/concerns/book_referenceable.rb
101
+ - src/ruby/tormenta20/concerns/livro_scoped.rb
101
102
  - src/ruby/tormenta20/database.rb
102
103
  - src/ruby/tormenta20/models/acessorio_magico.rb
104
+ - src/ruby/tormenta20/models/ameaca.rb
103
105
  - src/ruby/tormenta20/models/arma.rb
106
+ - src/ruby/tormenta20/models/arma_magica_especifica.rb
104
107
  - src/ruby/tormenta20/models/armadura.rb
108
+ - src/ruby/tormenta20/models/armadura_magica_especifica.rb
109
+ - src/ruby/tormenta20/models/artefato.rb
105
110
  - src/ruby/tormenta20/models/base.rb
111
+ - src/ruby/tormenta20/models/base_tipo.rb
106
112
  - src/ruby/tormenta20/models/classe.rb
113
+ - src/ruby/tormenta20/models/classe_variante.rb
114
+ - src/ruby/tormenta20/models/comodo.rb
115
+ - src/ruby/tormenta20/models/complicacao.rb
107
116
  - src/ruby/tormenta20/models/condicao.rb
117
+ - src/ruby/tormenta20/models/construcao_dominio.rb
118
+ - src/ruby/tormenta20/models/distincao.rb
108
119
  - src/ruby/tormenta20/models/divindade.rb
109
120
  - src/ruby/tormenta20/models/encantamento.rb
110
121
  - src/ruby/tormenta20/models/escudo.rb
122
+ - src/ruby/tormenta20/models/esoterico_magico.rb
111
123
  - src/ruby/tormenta20/models/indice_remissivo.rb
112
124
  - src/ruby/tormenta20/models/item.rb
125
+ - src/ruby/tormenta20/models/item_liturgico.rb
113
126
  - src/ruby/tormenta20/models/livro.rb
114
127
  - src/ruby/tormenta20/models/magia.rb
115
128
  - src/ruby/tormenta20/models/material_especial.rb
116
129
  - src/ruby/tormenta20/models/melhoria.rb
130
+ - src/ruby/tormenta20/models/mobilia.rb
117
131
  - src/ruby/tormenta20/models/origem.rb
118
132
  - src/ruby/tormenta20/models/pericia.rb
119
133
  - src/ruby/tormenta20/models/pocao.rb
@@ -122,14 +136,16 @@ files:
122
136
  - src/ruby/tormenta20/models/regra.rb
123
137
  - src/ruby/tormenta20/models/tabela.rb
124
138
  - src/ruby/tormenta20/rules.rb
139
+ - src/ruby/tormenta20/rules/damage_step.rb
125
140
  - src/ruby/tormenta20/rules/pm_cap.rb
141
+ - src/ruby/tormenta20/rules/spell_cost.rb
126
142
  - src/ruby/tormenta20/seeder.rb
127
143
  - src/ruby/tormenta20/version.rb
128
- homepage: https://github.com/YggdrasilGeekNetwork/tormenta20
144
+ homepage: https://yggdrasilgeeknetwork.github.io/tormenta20/
129
145
  licenses:
130
146
  - MIT
131
147
  metadata:
132
- homepage_uri: https://github.com/YggdrasilGeekNetwork/tormenta20
148
+ homepage_uri: https://yggdrasilgeeknetwork.github.io/tormenta20/
133
149
  source_code_uri: https://github.com/YggdrasilGeekNetwork/tormenta20
134
150
  changelog_uri: https://github.com/YggdrasilGeekNetwork/tormenta20/blob/main/CHANGELOG.md
135
151
  documentation_uri: https://rubydoc.info/gems/tormenta20