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
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tormenta20
4
+ module Concerns
5
+ # Mixin that gives any gem model with a +livro_id+ column access to its
6
+ # source book and a scope to filter by it.
7
+ #
8
+ # Distinct from {BookReferenceable}: that concern looks up a record's page
9
+ # number via the (sparse) printed índice remissivo, while +livro_id+ is
10
+ # the authoritative "book this record was published in" column, present
11
+ # on every content table and defaulting to +"t20_eja"+.
12
+ #
13
+ # @example
14
+ # Tormenta20.classes.do_livro("t20_eja").all
15
+ # Tormenta20.classes.first.livro.nome_curto # => "T20 - EJA"
16
+ module LivroScoped
17
+ extend ActiveSupport::Concern
18
+
19
+ included do
20
+ belongs_to :livro, class_name: "Tormenta20::Models::Livro", foreign_key: "livro_id", optional: true
21
+
22
+ scope :do_livro, ->(livro_id) { where(livro_id: livro_id) }
23
+ end
24
+ end
25
+ end
26
+ end
@@ -8,7 +8,7 @@ module Tormenta20
8
8
  #
9
9
  # @example
10
10
  # AcessorioMagico.menores.all
11
- # AcessorioMagico.find_by(id: "anel_de_protecao").efeito
11
+ # AcessorioMagico.find_by(id: "anel_de_protecao").effects
12
12
  #
13
13
  # @!attribute [rw] id
14
14
  # @return [String] Unique identifier
@@ -39,15 +39,12 @@ module Tormenta20
39
39
  scope :maiores, -> { where(categoria: "maior") }
40
40
  # @!endgroup
41
41
 
42
- # Returns the parsed effect hash, handling both raw JSON strings and pre-parsed hashes.
43
- # @return [Hash] Effect data, or +{}+ on parse failure
44
- def efeito
45
- raw = read_attribute(:efeito)
46
- return {} if raw.blank?
47
-
48
- raw.is_a?(Hash) ? raw : JSON.parse(raw)
49
- rescue JSON::ParserError
50
- {}
42
+ def to_h
43
+ {
44
+ id: id, name: name, categoria: categoria, preco: preco,
45
+ roll_min: roll_min, roll_max: roll_max,
46
+ description: description, effects: effects
47
+ }.compact
51
48
  end
52
49
  end
53
50
  end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record"
4
+
5
+ module Tormenta20
6
+ module Models
7
+ # Monster and enemy stat blocks from Tormenta20.
8
+ #
9
+ # @example
10
+ # Tormenta20.ameacas.where(grupo: "masmorras").order(:nd)
11
+ # Tormenta20.ameacas.find("basilisco")
12
+ class Ameaca < Base
13
+ self.table_name = "ameacas"
14
+
15
+ validates :id, presence: true, uniqueness: true
16
+ validates :name, presence: true
17
+ validates :nd, presence: true
18
+ validates :grupo, presence: true
19
+
20
+ scope :by_grupo, ->(g) { where(grupo: g) }
21
+ scope :by_nd, ->(n) { where(nd: n) }
22
+ scope :masmorras, -> { where(grupo: "masmorras") }
23
+ scope :ermos, -> { where(grupo: "ermos") }
24
+ scope :puristas, -> { where(grupo: "puristas") }
25
+ scope :reino_dos_mortos, -> { where(grupo: "reino_dos_mortos") }
26
+ scope :duyshidakk, -> { where(grupo: "duyshidakk") }
27
+ scope :sszzaazitas, -> { where(grupo: "sszzaazitas") }
28
+ scope :trolls_nobres, -> { where(grupo: "trolls_nobres") }
29
+ scope :dragoes, -> { where(grupo: "dragoes") }
30
+ scope :tormenta, -> { where(grupo: "tormenta") }
31
+ scope :avatares, -> { where(grupo: "avatares") }
32
+
33
+ def atributos
34
+ { for: self[:for], des: des, con: con, int: self[:int], sab: sab, car: car }
35
+ end
36
+
37
+ def to_h
38
+ {
39
+ id: id,
40
+ name: name,
41
+ nd: nd,
42
+ tipo: tipo,
43
+ subtipo: subtipo,
44
+ tamanho: tamanho,
45
+ grupo: grupo,
46
+ pv: pv,
47
+ defesa: defesa,
48
+ atributos: atributos,
49
+ iniciativa: iniciativa,
50
+ deslocamento: deslocamento,
51
+ salvaguardas: salvaguardas,
52
+ percepcao: percepcao,
53
+ visao: visao,
54
+ ataques: ataques,
55
+ poderes_especiais: poderes_especiais,
56
+ imunidades: imunidades,
57
+ resistencias: resistencias,
58
+ vulnerabilidades: vulnerabilidades,
59
+ reducao_de_dano: reducao_de_dano,
60
+ cura_acelerada: cura_acelerada,
61
+ resistencia_a_magia: resistencia_a_magia,
62
+ pericias: pericias,
63
+ equipamento: equipamento,
64
+ tesouro: tesouro,
65
+ pm: pm,
66
+ description: description,
67
+ familiar: familiar,
68
+ parceiro: parceiro
69
+ }.compact
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record"
4
+
5
+ module Tormenta20
6
+ module Models
7
+ # Named specific magic weapons from Tormenta20 (Cap. 8, Tabela 8-9).
8
+ #
9
+ # @example
10
+ # Tormenta20.armas_magicas.find("cajado_do_poder")
11
+ # Tormenta20.armas_magicas.order(:preco)
12
+ class ArmaMagicaEspecifica < Base
13
+ self.table_name = "armas_magicas_especificas"
14
+
15
+ validates :id, presence: true, uniqueness: true
16
+ validates :name, presence: true
17
+
18
+ scope :by_base, ->(w) { where(base_weapon: w) }
19
+
20
+ def to_h
21
+ {
22
+ id: id,
23
+ name: name,
24
+ base_weapon: base_weapon,
25
+ preco: preco,
26
+ description: description,
27
+ effects: effects
28
+ }.compact
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record"
4
+
5
+ module Tormenta20
6
+ module Models
7
+ # Named specific magic armors and shields from Tormenta20 (Cap. 8, Tabela 8-11).
8
+ #
9
+ # @example
10
+ # Tormenta20.armaduras_magicas.where(subtipo: "escudo")
11
+ # Tormenta20.armaduras_magicas.find("cota_elfica")
12
+ class ArmaduraMagicaEspecifica < Base
13
+ self.table_name = "armaduras_magicas_especificas"
14
+
15
+ validates :id, presence: true, uniqueness: true
16
+ validates :name, presence: true
17
+ validates :subtipo, inclusion: { in: %w[armadura escudo] }
18
+
19
+ scope :armaduras, -> { where(subtipo: "armadura") }
20
+ scope :escudos, -> { where(subtipo: "escudo") }
21
+
22
+ def to_h
23
+ {
24
+ id: id,
25
+ name: name,
26
+ subtipo: subtipo,
27
+ base_item: base_item,
28
+ preco: preco,
29
+ description: description,
30
+ effects: effects
31
+ }.compact
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record"
4
+
5
+ module Tormenta20
6
+ module Models
7
+ # Artifacts — unique, extremely powerful magic items from Tormenta20 (Cap. 8, pp. 350–353).
8
+ #
9
+ # @example
10
+ # Tormenta20.artefatos.all
11
+ # Tormenta20.artefatos.find("espada_deus")
12
+ class Artefato < Base
13
+ self.table_name = "artefatos"
14
+
15
+ validates :id, presence: true, uniqueness: true
16
+ validates :name, presence: true
17
+
18
+ def to_h
19
+ { id: id, name: name, description: description, effects: effects }.compact
20
+ end
21
+ end
22
+ end
23
+ end
@@ -13,12 +13,15 @@ module Tormenta20
13
13
  #
14
14
  # Every concrete model adds {Concerns::BookReferenceable}, which provides
15
15
  # the {Concerns::BookReferenceable#book_reference} method for sourcebook
16
- # page lookup.
16
+ # page lookup, and {Concerns::LivroScoped}, which provides the +livro+
17
+ # association and +.do_livro+ scope for filtering by source book.
17
18
  #
18
19
  # @abstract Subclass and set +table_name+ to create a new model
19
20
  #
20
21
  # @!attribute [rw] id
21
22
  # @return [String] Unique string identifier (e.g. +"guerreiro"+, +"espada_longa"+)
23
+ # @!attribute [rw] livro_id
24
+ # @return [String] Slug of the sourcebook this record was published in (default +"t20_eja"+)
22
25
  # @!attribute [r] created_at
23
26
  # @return [Time] Record creation timestamp
24
27
  # @!attribute [r] updated_at
@@ -27,6 +30,7 @@ module Tormenta20
27
30
  self.abstract_class = true
28
31
 
29
32
  include Concerns::BookReferenceable
33
+ include Concerns::LivroScoped
30
34
 
31
35
  # Raised when any write operation is attempted on the Tormenta20 database.
32
36
  # The database is read-only by design — it ships as pre-built data.
@@ -38,8 +42,17 @@ module Tormenta20
38
42
 
39
43
  # @!group Read-only enforcement
40
44
 
45
+ class << self
46
+ # @return [Boolean] true while {Tormenta20::Seeder} is populating a fresh database.
47
+ # Internal escape hatch: seeding is a legitimate write path, distinct from a
48
+ # consumer of the built gem mutating data.
49
+ def seeding?
50
+ Thread.current[:tormenta20_seeding] == true
51
+ end
52
+ end
53
+
41
54
  def readonly?
42
- true
55
+ !self.class.seeding?
43
56
  end
44
57
 
45
58
  def destroy
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record"
4
+
5
+ module Tormenta20
6
+ module Models
7
+ # A Tipo de Base (headquarters type) in Tormenta20.
8
+ #
9
+ # Represents the physical nature of a group's headquarters (Esconderijo,
10
+ # Fortificação, Móvel, Residência, Centro de Poder, Empreendimento) and the
11
+ # passive bonuses it grants its residents. Only catalog data lives here — an
12
+ # actual base built by a group (with a chosen type, porte, cômodos, and
13
+ # mobílias) is campaign state, not gem content.
14
+ #
15
+ # @example
16
+ # Tormenta20.bases_tipos.find("fortificacao")
17
+ class BaseTipo < Base
18
+ self.table_name = "bases_tipos"
19
+
20
+ validates :id, presence: true, uniqueness: true
21
+ validates :name, presence: true
22
+
23
+ def to_h
24
+ {
25
+ id: id,
26
+ name: name,
27
+ description: description,
28
+ effects: effects || []
29
+ }.compact
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record"
4
+
5
+ module Tormenta20
6
+ module Models
7
+ # Model representing a Classe Variante (variant class) in Tormenta20.
8
+ #
9
+ # A variant class is a specialized version of a base class (e.g. Duelista
10
+ # is a Bucaneiro variant). It is stored as a diff over the base class: the
11
+ # +diffs+ array lists, per level, which base-class abilities are removed
12
+ # and which new ones are added. Consumers must resolve the diff against
13
+ # the base {Classe} record to build the final character sheet.
14
+ #
15
+ # @example
16
+ # variante = Tormenta20.classes_variantes.find("duelista")
17
+ # variante.base_classe #=> the Bucaneiro Classe record
18
+ # variante.diffs #=> [{ "nivel" => 1, "remove" => [...], "adiciona" => [...] }, ...]
19
+ class ClasseVariante < Base
20
+ self.table_name = "classes_variantes"
21
+
22
+ belongs_to :base_classe, class_name: "Tormenta20::Models::Classe", foreign_key: "base_class_id"
23
+
24
+ validates :id, presence: true, uniqueness: true
25
+ validates :name, presence: true
26
+ validates :base_class_id, presence: true
27
+
28
+ scope :by_base, ->(class_id) { where(base_class_id: class_id) }
29
+
30
+ def to_h
31
+ {
32
+ id: id,
33
+ name: name,
34
+ base_class_id: base_class_id,
35
+ description: description,
36
+ diffs: diffs
37
+ }.compact
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record"
4
+
5
+ module Tormenta20
6
+ module Models
7
+ # A Cômodo (headquarters room) in Tormenta20.
8
+ #
9
+ # A catalog of rooms a group can build inside their base, each granting a
10
+ # passive bonus to residents. Some have a +prerequisito+ (e.g. another cômodo,
11
+ # or a minimum porte). Only catalog data lives here — see {BaseTipo}.
12
+ #
13
+ # @example
14
+ # Tormenta20.comodos.find("biblioteca")
15
+ class Comodo < Base
16
+ self.table_name = "comodos"
17
+
18
+ validates :id, presence: true, uniqueness: true
19
+ validates :name, presence: true
20
+
21
+ def to_h
22
+ {
23
+ id: id,
24
+ name: name,
25
+ description: description,
26
+ effects: effects || [],
27
+ prerequisito: prerequisito
28
+ }.compact
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record"
4
+
5
+ module Tormenta20
6
+ module Models
7
+ # A Complicação (character complication) in Tormenta20.
8
+ #
9
+ # Chosen at character creation: the player accepts a mechanical drawback ({#effects})
10
+ # in exchange for freely picking any one general power whose prerequisites they meet
11
+ # (not a fixed grant tied to the complication — the gem doesn't model that choice,
12
+ # it belongs to the consumer's character-creation flow). +categoria+ is +"geral"+ for
13
+ # complications any character can take, or a class id (e.g. +"guerreiro"+) for
14
+ # complications specific to that class.
15
+ #
16
+ # @example
17
+ # Tormenta20.complicacoes.gerais
18
+ # Tormenta20.complicacoes.by_categoria("guerreiro")
19
+ class Complicacao < Base
20
+ self.table_name = "complicacoes"
21
+
22
+ validates :id, presence: true, uniqueness: true
23
+ validates :name, presence: true
24
+ validates :categoria, presence: true
25
+
26
+ scope :gerais, -> { where(categoria: "geral") }
27
+ scope :by_categoria, ->(categoria) { where(categoria: categoria) }
28
+
29
+ def to_h
30
+ {
31
+ id: id,
32
+ name: name,
33
+ description: description,
34
+ categoria: categoria,
35
+ effects: effects || []
36
+ }.compact
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record"
4
+
5
+ module Tormenta20
6
+ module Models
7
+ # A Construção de Domínio (domain building) in Tormenta20.
8
+ #
9
+ # A catalog of buildings a regent can erect in their domain (Cap. 4 "Domínios"),
10
+ # each with a cost in Lingotes de Ouro (LO) and a mechanical benefit. Only catalog
11
+ # data lives here — an actual domain's characteristics (level, corte, popularidade,
12
+ # buildings erected) are campaign state, not gem content. See also {BaseTipo} for
13
+ # the analogous pattern applied to group headquarters.
14
+ #
15
+ # @example
16
+ # Tormenta20.construcoes_dominio.by_categoria("religiao")
17
+ class ConstrucaoDominio < Base
18
+ self.table_name = "construcoes_dominio"
19
+
20
+ validates :id, presence: true, uniqueness: true
21
+ validates :name, presence: true
22
+ validates :categoria, presence: true, inclusion: { in: %w[nobreza guerra enganacao religiao misticismo] }
23
+
24
+ scope :by_categoria, ->(categoria) { where(categoria: categoria) }
25
+
26
+ def to_h
27
+ {
28
+ id: id,
29
+ name: name,
30
+ description: description,
31
+ categoria: categoria,
32
+ custo: custo,
33
+ effects: effects || [],
34
+ prerequisito: prerequisito
35
+ }.compact
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record"
4
+
5
+ module Tormenta20
6
+ module Models
7
+ # Model representing a Distinção in Tormenta20.
8
+ #
9
+ # A distinction ties a character to an organization, philosophy, or people
10
+ # of Arton. It has three parts: admissão (narrative requirement), marca
11
+ # (an ability granted automatically on admission), and a set of exclusive
12
+ # powers. The powers themselves live in {Poder} (+type: "poder_distincao"+,
13
+ # +distincao_id+ pointing back here) — not duplicated on this model.
14
+ #
15
+ # @example Find a distinction and its powers
16
+ # Tormenta20.distincoes.find("cavaleiro_do_corvo")
17
+ # Tormenta20.poderes.where(distincao_id: "cavaleiro_do_corvo")
18
+ class Distincao < Base
19
+ self.table_name = "distincoes"
20
+
21
+ has_many :poderes, class_name: "Tormenta20::Models::Poder"
22
+
23
+ validates :id, presence: true, uniqueness: true
24
+ validates :name, presence: true
25
+
26
+ def to_h
27
+ {
28
+ id: id,
29
+ name: name,
30
+ description: description,
31
+ admissao: admissao,
32
+ marca_nome: marca_nome,
33
+ marca_descricao: marca_descricao,
34
+ nivel_minimo: nivel_minimo
35
+ }.compact
36
+ end
37
+ end
38
+ end
39
+ end
@@ -59,6 +59,16 @@ module Tormenta20
59
59
  # @return [ActiveRecord::Relation<Divindade>]
60
60
  scope :by_energy, ->(e) { where(energy: e) }
61
61
 
62
+ # @!method maiores
63
+ # Filter major (greater) deities.
64
+ # @return [ActiveRecord::Relation<Divindade>]
65
+ scope :maiores, -> { where(rank: "maior") }
66
+
67
+ # @!method menores
68
+ # Filter minor (lesser) deities (Deuses de Arton, Cap. 3).
69
+ # @return [ActiveRecord::Relation<Divindade>]
70
+ scope :menores, -> { where(rank: "menor") }
71
+
62
72
  # @!endgroup
63
73
 
64
74
  # Get races that typically worship this deity.
@@ -90,7 +100,10 @@ module Tormenta20
90
100
  preferred_weapon: preferred_weapon,
91
101
  devotees: devotees,
92
102
  granted_powers: granted_powers,
93
- obligations_restrictions: obligations_restrictions
103
+ obligations_restrictions: obligations_restrictions,
104
+ rank: rank,
105
+ natureza: natureza,
106
+ status_divino: status_divino
94
107
  }.compact
95
108
  end
96
109
  end
@@ -21,11 +21,13 @@ module Tormenta20
21
21
 
22
22
  validates :id, presence: true, uniqueness: true
23
23
  validates :name, presence: true
24
- validates :categoria, presence: true, inclusion: { in: %w[arma armadura_escudo] }
24
+ validates :categoria, presence: true, inclusion: { in: %w[arma armadura esoterico acessorio] }
25
25
 
26
26
  scope :armas, -> { where(categoria: "arma") }
27
- scope :armaduras_escudos, -> { where(categoria: "armadura_escudo") }
27
+ scope :armaduras_escudos, -> { where(categoria: "armadura") }
28
28
  scope :escudo_only, -> { where(escudo_only: true) }
29
+ scope :maldicoes, -> { where(maldicao: true) }
30
+ scope :nao_maldicoes, -> { where(maldicao: [false, nil]) }
29
31
 
30
32
  def to_h
31
33
  {
@@ -35,8 +37,9 @@ module Tormenta20
35
37
  categoria: categoria,
36
38
  escudo_only: escudo_only == 1,
37
39
  conta_como_dois: conta_como_dois == 1,
40
+ maldicao: maldicao == 1,
38
41
  prerequisitos: prerequisitos,
39
- efeito: efeito
42
+ effects: effects
40
43
  }.compact
41
44
  end
42
45
  end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record"
4
+
5
+ module Tormenta20
6
+ module Models
7
+ # Magic esoteric implements (staffs, wands, holy symbols) used by spellcasters.
8
+ #
9
+ # @example
10
+ # EsotericoMagico.menores.all
11
+ # EsotericoMagico.find_by(id: "cajado_do_poder").effects
12
+ #
13
+ # @!attribute [rw] id
14
+ # @return [String] Unique identifier
15
+ # @!attribute [rw] name
16
+ # @return [String] Item name
17
+ # @!attribute [rw] categoria
18
+ # @return [String] Power tier: +"menor"+, +"medio"+, or +"maior"+
19
+ # @!attribute [rw] preco
20
+ # @return [Integer, nil] Price in tibares
21
+ # @!attribute [rw] roll_min
22
+ # @return [Integer, nil] Minimum roll on random loot table
23
+ # @!attribute [rw] roll_max
24
+ # @return [Integer, nil] Maximum roll on random loot table
25
+ # @!attribute [rw] description
26
+ # @return [String, nil] Item description
27
+ class EsotericoMagico < Base
28
+ self.table_name = "esotericos_magicos"
29
+
30
+ # @!group Scopes
31
+ # @!method menores
32
+ # @return [ActiveRecord::Relation] Minor-tier esoterics
33
+ scope :menores, -> { where(categoria: "menor") }
34
+ # @!method medios
35
+ # @return [ActiveRecord::Relation] Medium-tier esoterics
36
+ scope :medios, -> { where(categoria: "medio") }
37
+ # @!method maiores
38
+ # @return [ActiveRecord::Relation] Major-tier esoterics
39
+ scope :maiores, -> { where(categoria: "maior") }
40
+ # @!endgroup
41
+
42
+ def to_h
43
+ {
44
+ id: id, name: name, categoria: categoria, preco: preco,
45
+ roll_min: roll_min, roll_max: roll_max,
46
+ description: description, effects: effects
47
+ }.compact
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record"
4
+
5
+ module Tormenta20
6
+ module Models
7
+ # Named magic item tied to a specific deity (Deuses de Arton, Cap. 1).
8
+ #
9
+ # Liturgical items (weapons, armors or accessories) only reach their full
10
+ # potential — or only work at all — in the hands of a devotee of the
11
+ # associated deity. They can span multiple item categories, so `deities`
12
+ # is a JSON array instead of a rigid foreign key.
13
+ #
14
+ # @example
15
+ # Tormenta20.itens_liturgicos.find("cilicio_vivo")
16
+ # Tormenta20.itens_liturgicos.by_deity("aharadak")
17
+ class ItemLiturgico < Base
18
+ self.table_name = "itens_liturgicos"
19
+
20
+ validates :id, presence: true, uniqueness: true
21
+ validates :name, presence: true
22
+
23
+ scope :by_deity, ->(deity_id) { where("json_extract(deities, '$') LIKE ?", "%#{deity_id}%") }
24
+ scope :by_subtipo, ->(s) { where(subtipo: s) }
25
+
26
+ def to_h
27
+ {
28
+ id: id,
29
+ name: name,
30
+ deities: deities,
31
+ subtipo: subtipo,
32
+ base_item: base_item,
33
+ classificacao: classificacao,
34
+ preco: preco,
35
+ description: description,
36
+ effects: effects
37
+ }.compact
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record"
4
+
5
+ module Tormenta20
6
+ module Models
7
+ # A Mobília (headquarters furniture) in Tormenta20.
8
+ #
9
+ # A catalog of furniture items that can be installed in a {Comodo} to grant an
10
+ # additional bonus. Some are restricted to a specific cômodo (+comodo_alvo+).
11
+ # Only catalog data lives here — see {BaseTipo}.
12
+ #
13
+ # @example
14
+ # Tormenta20.mobilias.find("bar")
15
+ class Mobilia < Base
16
+ self.table_name = "mobilias"
17
+
18
+ validates :id, presence: true, uniqueness: true
19
+ validates :name, presence: true
20
+
21
+ def to_h
22
+ {
23
+ id: id,
24
+ name: name,
25
+ description: description,
26
+ effects: effects || [],
27
+ preco: preco,
28
+ comodo_alvo: comodo_alvo
29
+ }.compact
30
+ end
31
+ end
32
+ end
33
+ end