Compare commits

...

6 Commits

Author SHA1 Message Date
The Stranjer
917ebb67d4 Group spells (or attempt to) 2019-06-12 13:07:24 -04:00
The Stranjer
05297ec4c7 Grease and Glue spells with Sticky Ground Environmental Tilt 2019-06-12 12:29:49 -04:00
The Stranjer
aab1b5248b Tilts 2019-06-12 12:13:21 -04:00
The Stranjer
e5da36bda0 Arcane Trouble Condition 2019-06-12 10:26:40 -04:00
The Stranjer
b4780ffb82 Inline format Condition rules text 2019-06-12 10:26:23 -04:00
The Stranjer
75b94f69fa Actually sort Conditions alphabetically by name 2019-06-12 09:21:29 -04:00
5 changed files with 179 additions and 65 deletions

View File

@ -4,13 +4,33 @@ class Condition
conditions = []
conditions << Condition.new(
:name => "Vicarious Story",
:rules_text => "Your character shares experiences with another character on an ongoing basis. This Condition is usually inflicted on someone for whom another person wants to watch a life event unfold, such as a marriage/divorce, a First Change, a pregnancy, or an Awakening. Both the person who is being monitored and the person doing the vicarious viewing have this Condition, and if it is resolved on one party, it resolves on both.\n\nExperiences that are directly pertinent to the life event deliver sensory visions to the viewer. These visions might be shared dreams with the person being monitored, but might also be feeling a baby's first kick against the womb's walls or looking in a mirror and seeing a Wolf-Blood's teeth growing sharper. The exact nature of these details is up to Storyteller discretion, but pertinent details should not be withheld. Experiences that are not pertinent are completely omitted. Vicariously watching a person's Awakening skips scenes where the target spills coffee on their TPS reports.\n\nThese visions impose the Stunned Tilt on the one vicariously watching for a turn, but since these visions are so transient, it is not enough to interrupt a supernatural ritual or an extended action, but can be extremely inconvenient in time-sensitive circumstances such as combat.",
:resolution => "The life experience ends. The Awakening or First Change terminates. The child is born. The marital vows are complete or the divorce finalized.",
:beat => "The one vicariously viewing loses an action during combat or some similarly fast-paced situation.",
:authors => ["NEETzsche"]
:name => "Vicarious Story",
:rules_text => "Your character shares experiences with another character on an ongoing basis. This Condition is usually inflicted on someone for whom another person wants to watch a life event unfold, such as a marriage/divorce, a First Change, a pregnancy, or an Awakening. Both the person who is being monitored and the person doing the vicarious viewing have this Condition, and if it is resolved on one party, it resolves on both.\n\nExperiences that are directly pertinent to the life event deliver sensory visions to the viewer. These visions might be shared dreams with the person being monitored, but might also be feeling a baby's first kick against the womb's walls or looking in a mirror and seeing a Wolf-Blood's teeth growing sharper. The exact nature of these details is up to Storyteller discretion, but pertinent details should not be withheld. Experiences that are not pertinent are completely omitted. Vicariously watching a person's Awakening skips scenes where the target spills coffee on their TPS reports.\n\nThese visions impose the Stunned Tilt on the one vicariously watching for a turn, but since these visions are so transient, it is not enough to interrupt a supernatural ritual or an extended action, but can be extremely inconvenient in time-sensitive circumstances such as combat.",
:resolution => "The life experience ends. The Awakening or First Change terminates. The child is born. The marital vows are complete or the divorce finalized.",
:beat => "The one vicariously viewing loses an action during combat or some similarly fast-paced situation.",
:authors => ["NEETzsche"]
)
# conditions << Condition.new(
# :name => "Abyssal Alert",
# :rules_text => "The Abyss seeks to find the character through the souls of the Sleepers, enhancing the effect of the Abyssal fragments within their souls. For the duration of the Condition, any spell the character casts within sensory range of any Sleeper nearby that uses any Yantra becomes an obvious use of magic for the purposes of Paradox. For example, a spell using High Speech becomes vulgar, even if the Sleepers cannot hear the magician, but can see him.\n\nThis Condition yields Arcane Beats.",
# :resolution => "Successfully absorb the Paradox in the amount of PS with the help of Wisdom.",
# :authors => ["NEETzsche"],
# :possible_sources => "A successful contained Paradox roll. The number of successes is referred to as PS."
# )
conditions << Condition.new(
:name => "Arcane Trouble",
:rules_text => "When assigning this Condition, roll the spellcasting dicepool again. The number of successes + 1 will be referred to as SS. The Immediate Nimbus of the Mage becomes active for the entire duration of the Arcane Trouble Condition. The mage becomes a bright beacon for any mage with an Active Mage Sight, and Peripheral Mage Sight can sense the mage's presence by rolling Perception + SS.\n\nIf Death was used to cast the spell causing this Condition, the dead and Twilight reject the caster, making his every step very heavy, as if he was bound in a dense fog. The mage and everyone in the same scene subtracts SS from the dice pools of all action that have an impact on the Underworld and on ghosts or Twilight. In addition, any ghost can temporarily use the Mage as his Anchor.\n\nIf Fate was used to cast the spell causing this Condition, then once per scene, the Storyteller can give give SS as a penalty to any action near the mage. Also, once for the Condition duration, the Storyteller can turn any mage action into a dramatic failure. This dramatic failure cannot be used to cause another Arcane Trouble.\n\nIf Forces was used to cast the spell causing this Condition, then electronics go crazy around the mage. Subtract SS from the equipment bonuses of any equipment near the Mage, which includes most modern machines. If the equipment bonus is reduced to zero or less, the equipment refuses to work in the presence of the mage. In addition, if the Mage uses an electronic device, the Storyteller may roll Forces + Gnosis - Durability, where each success deals a point of damage to the object's Structure.\n\nIf Life was used to cast the spell causing this Condition, then the mage radiates an aura of weakness and vulnerability. The mage and everyone in the scene subtracts SS from all Strength, Dexterity, and Stamina rolls. Any attempts to poison or infect the mage receive a bonus equal to SS instead of the penalty listed.\n\nIf Matter was used to cast the spell causing this Condition, then objects become brittle and unreliable around the mage. The mage and everyone in the scene subtracts SS from the equipment bonuses of anything containing mechanical parts, including most modern machines and weapons like guns. If the equipment bonus is reduced to zero or less, the equipment refuses to work in the presence of the mage. In addition, the Storyteller may at any time roll Matter + Gnosis - Durability, where each success deals a point of damage to the object's Structure.\n\nIf Mind was used to cast the spell causing this Condition, then the mage radiates an aura of mental noise that interferes with concentration around him. The magician and everyone in the scene subtracts SS from all rolls that use Intelligence, Wits, or Resolve. Any attempt to confuse the mind of the mage or prevent his concentration will receive a bonus equal to SS instead of the penalty listed.\n\nIf Prime was used to cast the spell causing this Condition, then the Mage Sight of the mage is disrupted: instead of seeing Supernal Truths, it gives a meaningless stream of symbols. The mage and everyone in the same scene subtracts SS from attempts to use Mage Sight. They also receive the same penalty for any attempts to use the Practice of Knowing or Unveiling of any Arcana.\n\nIf Spirit was used to cast the spell causing this Condition, then the Gauntlet between the Shadow and the mortal world becomes especially thin around the magician, and this attracts the attention of the spirits. Any attempts by spirits to break through the Gauntlet or use Numina through the Gauntlet will receive a bonus equal to SS. Also, any place in which the mage is located receives an Open Condition for all spirits to use.\n\nIf Space was used to cast the spell causing this Condition, then strangely distort the space around the mage. The mage and everyone in the same scene deducts SS from any attempts to find something or get somewhere. Also, Storyteller can once per Condition duration strengthen or weaken any sympathetic mage connection Withstand by SS.\n\nIf Time was used to cast the spell causing this Condition, then make the sense of time jump strangely around the mage, making it difficult to coordinate any effort and not allowing people to concentrate. The mage and everyone in the same scene receive a penalty equal to SS for all extended actions, including ritual casting. Also, the mage subtracts SS from any successes gained in teamwork rolls.\n\nIf the spell that caused this Condition was conjunctional or combined, use the effects of all Arcana used for the spell. For example, if a spell used both Life and Spirit, then he would both attract the negative attention of spirits as described above <i>and</i> have the mentioned sense of illness and tiredness.",
:possible_sources => "Dramatic failure on a spellcasting roll.",
:resolution => "Successfully cast a spell that uses all Arcana creating this Condition, after waiting one day or one chapter, whichever is longer.",
:authors => ["Dark Archon", "NEETzsche"]
)
conditions
end
end

View File

@ -1,4 +1,5 @@
require "prawn"
require "prawn/grouping"
require "pry"
require_relative "spell"
@ -7,6 +8,9 @@ require_relative "spells"
require_relative "condition"
require_relative "conditions"
require_relative "tilt"
require_relative "tilts"
require_relative "primitive_adds"
puts "Shards of Power PDF generator"
@ -16,7 +20,7 @@ class ShardsOfPower
FONT_SIZE_SUBSECTION = 18
FONT_SIZE_BODY = 10
attr_accessor :pdf, :number_name, :chapter_name, :chapter_title, :section_name, :spells, :conditions
attr_accessor :pdf, :number_name, :chapter_name, :chapter_title, :section_name, :spells, :conditions, :tilts
def start_chapter(num, name)
@number_name = num
@ -93,8 +97,9 @@ class ShardsOfPower
puts "Parsing spells..."
@spells = Spell.spells
@conditions = Condition.conditions
@conditions.sort_by(&:name)
@conditions.sort_by!(&:name)
@tilts = Tilt.tilts
@tilts.sort_by!(&:name)
puts "Creating PDF object..."
@pdf = Prawn::Document.new(:margin => 0)
@ -176,75 +181,80 @@ class ShardsOfPower
@pdf.outline.section rating_title, :destination => @pdf.page_number
end
pdf.font_size FONT_SIZE_SECTION
pdf.text rating_title
rating_spells.sort_by! { |spell| spell.name }
rating_spells.sort_by { |spell| spell.name }.each do |spell|
rating_spells.each do |spell|
puts "\t\tCompiling #{spell.full_title} by #{spell.authors.join(', ')}"
pdf.outline.add_subsection_to(rating_title) do
@pdf.outline.section spell.full_title, :destination => @pdf.page_number
end
pdf.group do |g|
if spell == rating_spells.first
g.font_size FONT_SIZE_SECTION
g.text rating_title
end
spell_ary = [{:text => "#{spell.name} (", :font => "Lilith", :color => "004E6D", :size => FONT_SIZE_SUBSECTION}]
spell_ary = [{:text => "#{spell.name} (", :font => "Lilith", :color => "004E6D", :size => FONT_SIZE_SUBSECTION}]
pdf.font_size FONT_SIZE_BODY
previous = false
spell.arcana.each do |arcanum|
spell_ary << {:text => ", ", :font => "Lilith", :color => "004E6D", :size => FONT_SIZE_SUBSECTION} if previous
spell_ary << {:text => arcanum.to_s.titleize, :font => "Lilith", :color => "004E6D", :size => FONT_SIZE_SUBSECTION}
spell_ary << {:text => spell.send(arcanum).to_dots, :color => "004E6D", :size => FONT_SIZE_SUBSECTION, :character_spacing => -5}
previous = true
end
g.font_size FONT_SIZE_BODY
previous = false
spell.arcana.each do |arcanum|
spell_ary << {:text => ", ", :font => "Lilith", :color => "004E6D", :size => FONT_SIZE_SUBSECTION} if previous
spell_ary << {:text => arcanum.to_s.titleize, :font => "Lilith", :color => "004E6D", :size => FONT_SIZE_SUBSECTION}
spell_ary << {:text => spell.send(arcanum).to_dots, :color => "004E6D", :size => FONT_SIZE_SUBSECTION, :character_spacing => -5}
previous = true
end
spell_ary << {:text => " )", :font => "Lilith", :styles => [], :color => "004E6D", :size => FONT_SIZE_SUBSECTION, :character_spacing => -3}
spell_ary << {:text => " )", :font => "Lilith", :styles => [], :color => "004E6D", :size => FONT_SIZE_SUBSECTION, :character_spacing => -3}
pdf.formatted_text spell_ary
g.formatted_text spell_ary
pdf.formatted_text [
{:text => "Practice: ", :styles => [:bold], :font => "Goudy"},
{:text => spell.practice, :font => "Goudy"},
]
pdf.formatted_text [
{:text => "Primary Factor: ", :styles => [:bold], :font => "Goudy"},
{:text => spell.primary_factor, :font => "Goudy"},
]
pdf.formatted_text [
{:text => "Withstand: ", :styles => [:bold], :font => "Goudy"},
{:text => spell.withstand, :font => "Goudy"},
] if !spell.withstand.nil?
pdf.formatted_text [
{:text => "Cost: ", :styles => [:bold], :font => "Goudy"},
{:text => spell.cost, :font => "Goudy"},
] if !spell.cost.nil?
pdf.formatted_text [
{:text => "Suggested Rote Skills: ", :styles => [:bold], :font => "Goudy"},
{:text => spell.suggested_rote_skills.join(', '), :font => "Goudy"},
]
pdf.formatted_text [
{:text => spell.authors.length == 1 ? "Author: " : "Authors: ", :styles => [:bold], :font => "Goudy"},
{:text => spell.authors.join(', '), :font => "Goudy"},
]
pdf.text spell.rules_text, :inline_format => true
spell.reaches.each do |reach|
pdf.formatted_text [
{:text => "+#{reach[0]} Reach: ", :styles => [:bold], :font => "Goudy"},
{:text => reach[1], :font => "Goudy"},
g.formatted_text [
{:text => "Practice: ", :styles => [:bold], :font => "Goudy"},
{:text => spell.practice, :font => "Goudy"},
]
end
spell.adds.each do |add|
pdf.formatted_text [
{:text => "#{add[:verb] ? add[:verb] : "Add"} #{add[:arcana].collect { |arcanum, rating| [arcanum, rating].to_arcanum_with_dots }.to_list(:separator => add[:separator])}: ", :styles => [:bold], :font => "Goudy"},
{:text => add[:effect], :font => "Goudy"},
g.formatted_text [
{:text => "Primary Factor: ", :styles => [:bold], :font => "Goudy"},
{:text => spell.primary_factor, :font => "Goudy"},
]
g.formatted_text [
{:text => "Withstand: ", :styles => [:bold], :font => "Goudy"},
{:text => spell.withstand, :font => "Goudy"},
] if !spell.withstand.nil?
g.formatted_text [
{:text => "Cost: ", :styles => [:bold], :font => "Goudy"},
{:text => spell.cost, :font => "Goudy"},
] if !spell.cost.nil?
g.formatted_text [
{:text => "Suggested Rote Skills: ", :styles => [:bold], :font => "Goudy"},
{:text => spell.suggested_rote_skills.join(', '), :font => "Goudy"},
]
g.formatted_text [
{:text => spell.authors.length == 1 ? "Author: " : "Authors: ", :styles => [:bold], :font => "Goudy"},
{:text => spell.authors.join(', '), :font => "Goudy"},
]
g.text spell.rules_text, :inline_format => true
spell.reaches.each do |reach|
g.formatted_text [
{:text => "+#{reach[0]} Reach: ", :styles => [:bold], :font => "Goudy"},
{:text => reach[1], :font => "Goudy"},
]
end
spell.adds.each do |add|
g.formatted_text [
{:text => "#{add[:verb] ? add[:verb] : "Add"} #{add[:arcana].collect { |arcanum, rating| [arcanum, rating].to_arcanum_with_dots }.to_list(:separator => add[:separator])}: ", :styles => [:bold], :font => "Goudy"},
{:text => add[:effect], :font => "Goudy"},
]
end
end
end
end
@ -269,13 +279,37 @@ class ShardsOfPower
condition_ary = [{:text => condition.name, :font => "Lilith", :color => "004E6D", :size => FONT_SIZE_SUBSECTION}]
pdf.formatted_text condition_ary
pdf.text condition.rules_text
pdf.text condition.rules_text, :inline_format => true
pdf.text "<b>Possible Sources:</b> #{condition.possible_sources}", :inline_format => true if condition.possible_sources
pdf.text "<b>Resolution:</b> #{condition.resolution}", :inline_format => true
pdf.text "<b>Beat:</b> #{condition.beat}", :inline_format => true
pdf.text "<b>#{condition.authors.size > 1 ? "Authors" : "Author"}:</b> #{condition.authors.to_list}", :inline_format => true
end
pdf.start_new_page
end
start_section "Tilts"
puts "Compiling Tilts..."
pdf.column_box([0, pdf.cursor], :columns => 2, :width => pdf.bounds.width) do
tilts.each do |tilt|
puts "\tCompiling #{tilt.full_title}..."
pdf.outline.add_subsection_to("Tilts") do
@pdf.outline.section tilt.full_title, :destination => @pdf.page_number
end
tilt_ary = [{:text => tilt.full_title, :font => "Lilith", :color => "004E6D", :size => FONT_SIZE_SUBSECTION}]
pdf.formatted_text tilt_ary
pdf.text tilt.rules_text, :inline_format => true
pdf.text "<b>Effect:</b> #{tilt.effect}", :inline_format => true
pdf.text "<b>Causing the Tilt:</b> #{tilt.causing}", :inline_format => true
pdf.text "<b>Ending the Tilt:</b> #{tilt.ending}", :inline_format => true
end
end
pdf.render_file("shards_of_power.pdf")

View File

@ -1119,6 +1119,27 @@ class Spell
{ :arcana => { :spirit => 2}, :effect => "He can make the item appear to be unduly influenced spiritually. It might appear possessed or as a Fetish."} ]
)
spells << Spell.new(
:name => "Grease",
:matter => 2,
:practice => "Ruling",
:primary_factor => "Duration",
:suggested_rote_skills => ["Crafts", "Brawl", "Survival"],
:authors => ["Dark Archon"],
:rules_text => "One easy way to turn the tide of physical combat and make those damn wheels spin, this spell changes the properties of the ground surface it is cast upon, causing the following Ice Environmental Tilt."
)
spells << Spell.new(
:name => "Glue",
:matter => 2,
:practice => "Ruling",
:primary_factor => "Duration",
:suggested_rote_skills => ["Crafts", "Brawl", "Survival"],
:authors => ["Dark Archon", "NEETzsche"],
:rules_text => "An easy way to turn the tide of physical combat and make those damn wheels spin, this spell changes the properties of the ground surface it is cast upon, causing the following Sticky Ground Environmental Tilt."
)
# TESTS
duplicate_spells = spells.group_by(&:name).select {| k, v| v.length > 1 }

21
tilt.rb Normal file
View File

@ -0,0 +1,21 @@
class Tilt
attr_accessor :name, :rules_text, :effect, :causing, :ending, :environmental, :authors
def initialize(name:, rules_text:, effect:, causing:, ending:, environmental: false , authors:)
@name = name
@rules_text = rules_text
@effect = effect
@causing = causing
@ending = ending
@environmental = environmental
@authors = authors
end
def full_title
if environmental
"#{name} (Environmental)"
else
name
end
end
end

18
tilts.rb Normal file
View File

@ -0,0 +1,18 @@
class Tilt
def self.tilts
tilts = []
tilts << Tilt.new(
:name => "Sticky Ground",
:rules_text => "The ground is covered in a sticky substance that precludes people, animals, and vehicles from treading on it. The substance could be a thin layer on top, or a thick quick sand that will eventually suck them in.",
:effect => "Divide the Speed of all who might tread on this ground in half, rounding up, and all rolls that require her to move her feet suffer a -2 penalty. Any dramatic failure on a Physical roll inflicts the Immobilized Tilt.",
:causing => "This Tilt can be caused by anything ranging from a spell, to a natural feature of the terrain. A character might choose to pour hot pine tar on the ground, making it difficult to move.",
:ending => "\"Get out of the quicksand!\" might be a good idea, but that is easier said than done. Characters can pour sand on the ground to give it traction. They can lower the temperature of the area, making the substance they're walking on solidify.",
:environmental => true,
:authors => ["NEETzsche", "Dark Archon"]
)
return tilts
end
end