Chronicles of Darkness grimoire-Artifact PDF https://git.lain.church/TheStranjer/ShardsOfPower
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

362 lines
13KB

  1. require "prawn"
  2. require "prawn/grouping"
  3. require "prawn/table"
  4. require "pry"
  5. require_relative "spell"
  6. require_relative "spells"
  7. require_relative "condition"
  8. require_relative "conditions"
  9. require_relative "tilt"
  10. require_relative "tilts"
  11. require_relative "primitive_adds"
  12. puts "Shards of Power PDF generator"
  13. class ShardsOfPower
  14. FONT_SIZE_CHAPTER = 48
  15. FONT_SIZE_SECTION = 24
  16. FONT_SIZE_SUBSECTION = 18
  17. FONT_SIZE_BODY = 10
  18. attr_accessor :pdf, :number_name, :chapter_name, :chapter_title, :section_name, :spells, :conditions, :tilts
  19. def start_chapter(num, name)
  20. @number_name = num
  21. @chapter_name = name
  22. @chapter_title = "Chapter #{number_name}: #{chapter_name}"
  23. puts "Starting #{chapter_title}"
  24. pdf.start_new_page
  25. pdf.outline.update do
  26. puts "\tAdding #{@shards_of_power.chapter_title} on page #{page_number}"
  27. section(@shards_of_power.chapter_title, :destination => page_number)
  28. end
  29. puts "\tSetting font size"
  30. pdf.font_size FONT_SIZE_CHAPTER
  31. pdf.font "Abess"
  32. puts "\tPrinting chapter to document"
  33. pdf.text "Chapter #{number_name}: #{chapter_name}", :align => :center
  34. puts "\tRestoring body text"
  35. pdf.font "Goudy"
  36. pdf.font_size FONT_SIZE_BODY
  37. end
  38. def start_section(section_name)
  39. puts "Beginning section #{section_name}: "
  40. @section_name = section_name
  41. pdf.outline.add_subsection_to(chapter_title) do
  42. @pdf.outline.section section_name, :destination => @pdf.page_number
  43. end
  44. puts "\tSetting font size"
  45. pdf.font_size FONT_SIZE_SECTION
  46. pdf.text section_name, :align => :center
  47. puts "\tRestoring body text"
  48. pdf.font_size FONT_SIZE_BODY
  49. pdf.font "Goudy"
  50. end
  51. def roll_results(dramatic_failure:, failure:, success:, exceptional_success:)
  52. pdf.font_size FONT_SIZE_BODY
  53. pdf.font "Goudy"
  54. pdf.text "Roll Results", :style => :bold
  55. pdf.formatted_text [
  56. { :text => "Dramatic Failure: ", :styles => [:bold] },
  57. { :text => dramatic_failure }
  58. ]
  59. pdf.formatted_text [
  60. { :text => "Failure: ", :styles => [:bold] },
  61. { :text => failure }
  62. ]
  63. pdf.formatted_text [
  64. { :text => "Success: ", :styles => [:bold] },
  65. { :text => success }
  66. ]
  67. pdf.formatted_text [
  68. { :text => "Exceptional Success: ", :styles => [:bold] },
  69. { :text => exceptional_success }
  70. ]
  71. end
  72. def generate
  73. puts "Parsing spells..."
  74. @spells = Spell.spells
  75. @conditions = Condition.conditions
  76. @conditions.sort_by!(&:name)
  77. @tilts = Tilt.tilts
  78. @tilts.sort_by!(&:name)
  79. puts "Creating PDF object..."
  80. @pdf = Prawn::Document.new(:margin => 0)
  81. pdf.default_leading 5
  82. pdf.font_families.update(
  83. "Goudy" => {
  84. :normal => "goudy.ttf",
  85. :bold => "goudy-bold.ttf",
  86. :italic => "goudy-italic.ttf"
  87. },
  88. "Abess" => {
  89. :normal => "abess.ttf"
  90. },
  91. "Lilith" => {
  92. :normal => "lilith.ttf"
  93. },
  94. "Goblin Hand" => {
  95. :normal => "VTCGoblinHand.ttf",
  96. :bold => "VTCGoblinHandBold.ttf",
  97. :italic => "VTCGoblinHandItalic.ttf"
  98. }
  99. )
  100. pdf.outline.instance_variable_set(:@shards_of_power, self)
  101. puts "Importing cover..."
  102. pdf.image("cover.png", :width => 612, :height => 792)
  103. puts "Printing inner cover..."
  104. pdf.start_new_page(:margin => 50)
  105. pdf.font "Abess"
  106. pdf.font_size 72
  107. pdf.text "Shards of Power", :align => :center
  108. puts "\tShards of Power big text..."
  109. pdf.font "Goudy"
  110. pdf.font_size 36
  111. pdf.text "\nA compendium of spells, Conditions, Artifacts, and other interesting tidbits for Mage: the Awakening\n\n", :align => :center
  112. puts "\tDescription..."
  113. pdf.font_size 24
  114. pdf.text "Written by members of the /cofd/ Discord, edited by NEETzsche", :align => :center
  115. puts "\tMain credit..."
  116. pdf.outline.define do
  117. end
  118. start_chapter "One", "Introduction"
  119. pdf.column_box([0, pdf.cursor], :columns => 2, :width => pdf.bounds.width) do
  120. pdf.text "Like every supernatural entity in the Chronicles of Darkness, there is a boundless breadth and depth of topics to discuss within them. The Awakened are by no stretch of the imagination an exception to this rule. This book does not dare claim that this will cover all of them, but it does dare to cover the broad strokes on a certain subtopic of the lives of those touched by the Supernal who still reside in the Fallen World: their toolkit.\n\n<b>Mage: the Awakening</b> covered the broadest strokes on Awakened life in general, but there is so much more to the means by which Mages conduct their affairs and pursue their Obsessions that simply is not covered in that volume. There will be yet more that this one fails to consider. However, it is designed to touch on the high level ideas, and dig into some of the specifics for them, leaving you, the reader compelled, and hopefully, inspired as to where to explore next.", :inline_format => true
  121. start_section "Tools Of Awakened Life"
  122. pdf.text "This book is meant to be used as a toolkit. Each of the spells are modular, and for the most part do not operate off of each other. You, as the Storyteller or player, are meant to take the things you find fun, interesting and compelling while leaving the things you find silly, improper, or frustrating behind. This line of thought naturally extends to all of the Conditions, optional Arcana principles, alternative Relinquishment methods, and so on.\n\nThis book was written on a rolling basis by a number of people. It is a compilation, that accumulated over time. It did not get created overnight. As such, by the time you receive this file, there is a good likelihood that it's still in development, which means there might be more up-to-date versions of it, and additionally it may be open to input for your spells, your Artifacts, and your optional rules. The invite code at the time of publication is <b>Cx3JBpB</b>.", :inline_format => true
  123. start_section "Chapters"
  124. pdf.text "There are multiple chapters in this book. Each of them covers a specific set of topics.\n\n<b>Chapter One</b> is ithe Introduction. It is what you are reading now. It gives the mission statement, the credits, and a few other key details before you get into the spells and the Conditions.\n\n<b>Chapter Two</b> is the megagrimoire. It is a grimoire of grimoires, much like the book that White Wolf released when they still called themselves that, and is a combined set of spells that the players of this community have mutually agreed are at least somewhat reasonable. Many of us helped each other write these spells. All of their names will be listed in them, alphabetically.\n\n<b>Chapter Three: Conditions & Tilts</b> discusses new Conditions and Tilts that can be imposed by spells, or by other means, such as dramatic failures.", :inline_format => true
  125. start_section "Credits"
  126. spell_authors = spells.collect { |spell| spell.authors }.flatten.uniq.sort
  127. pdf.text "The following users have contributed to Shards of Power, in alphabetical order: #{spell_authors.to_list}"
  128. end
  129. start_chapter("Two", "Spells")
  130. spells.group_by(&:primary_arcanum).sort_by { |arcanum, spells| arcanum }.to_h.each do |arcanum, arcanum_spells|
  131. arcanum_name = arcanum.to_s.titleize
  132. puts "Compiling spells for #{arcanum_name}..."
  133. start_section("#{arcanum_name} Spells")
  134. pdf.column_box([0, pdf.cursor], :columns => 2, :width => pdf.bounds.width) do
  135. arcanum_spells.group_by(&arcanum).sort_by { |rating, spells| rating }.to_h.each do |rating, rating_spells|
  136. rating_title = "#{rating.to_dots} #{Spell::RANKS[rating - 1]} of #{arcanum_name}"
  137. puts "\tCompiling #{rating_title} spells..."
  138. pdf.outline.add_subsection_to("#{arcanum_name} Spells") do
  139. @pdf.outline.section rating_title, :destination => @pdf.page_number
  140. end
  141. rating_spells.sort_by! { |spell| spell.name }
  142. rating_spells.each do |spell|
  143. puts "\t\tCompiling #{spell.full_title} by #{spell.authors.join(', ')}"
  144. pdf.outline.add_subsection_to(rating_title) do
  145. @pdf.outline.section spell.full_title, :destination => @pdf.page_number
  146. end
  147. pdf.group do |g|
  148. if spell == rating_spells.first
  149. g.font_size FONT_SIZE_SECTION
  150. g.text rating_title
  151. end
  152. spell_ary = [{:text => "#{spell.name} (", :font => "Lilith", :color => "004E6D", :size => FONT_SIZE_SUBSECTION}]
  153. g.font_size FONT_SIZE_BODY
  154. previous = false
  155. spell.arcana.each do |arcanum|
  156. spell_ary << {:text => ", ", :font => "Lilith", :color => "004E6D", :size => FONT_SIZE_SUBSECTION} if previous
  157. spell_ary << {:text => arcanum.to_s.titleize, :font => "Lilith", :color => "004E6D", :size => FONT_SIZE_SUBSECTION}
  158. spell_ary << {:text => spell.send(arcanum).to_dots, :color => "004E6D", :size => FONT_SIZE_SUBSECTION, :character_spacing => -5}
  159. previous = true
  160. end
  161. spell_ary << {:text => " )", :font => "Lilith", :styles => [], :color => "004E6D", :size => FONT_SIZE_SUBSECTION, :character_spacing => -3}
  162. g.formatted_text spell_ary
  163. g.formatted_text [
  164. {:text => "Practice: ", :styles => [:bold], :font => "Goudy"},
  165. {:text => spell.practice, :font => "Goudy"},
  166. ]
  167. g.formatted_text [
  168. {:text => "Primary Factor: ", :styles => [:bold], :font => "Goudy"},
  169. {:text => spell.primary_factor, :font => "Goudy"},
  170. ]
  171. g.formatted_text [
  172. {:text => "Withstand: ", :styles => [:bold], :font => "Goudy"},
  173. {:text => spell.withstand, :font => "Goudy"},
  174. ] if !spell.withstand.nil?
  175. g.formatted_text [
  176. {:text => "Cost: ", :styles => [:bold], :font => "Goudy"},
  177. {:text => spell.cost, :font => "Goudy"},
  178. ] if !spell.cost.nil?
  179. g.formatted_text [
  180. {:text => "Suggested Rote Skills: ", :styles => [:bold], :font => "Goudy"},
  181. {:text => spell.suggested_rote_skills.join(', '), :font => "Goudy"},
  182. ]
  183. g.formatted_text [
  184. {:text => spell.authors.length == 1 ? "Author: " : "Authors: ", :styles => [:bold], :font => "Goudy"},
  185. {:text => spell.authors.join(', '), :font => "Goudy"},
  186. ]
  187. g.text spell.rules_text, :inline_format => true
  188. spell.reaches.each do |reach|
  189. reach_cost = mana_cost = reach_rules = nil
  190. if reach.size == 3
  191. g.formatted_text [
  192. {:text => "+#{reach[0]} Reach, #{reach[1]} Mana: ", :styles => [:bold], :font => "Goudy"},
  193. {:text => reach[2], :font => "Goudy"},
  194. ]
  195. else
  196. g.formatted_text [
  197. {:text => "+#{reach[0]} Reach: ", :styles => [:bold], :font => "Goudy"},
  198. {:text => reach[1], :font => "Goudy"},
  199. ]
  200. end
  201. end
  202. spell.adds.each do |add|
  203. g.formatted_text [
  204. {: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"},
  205. {:text => add[:effect], :font => "Goudy"},
  206. ]
  207. end
  208. end
  209. end
  210. end
  211. end
  212. pdf.start_new_page
  213. end
  214. start_chapter "Three", "Conditions & Tilts"
  215. start_section "Conditions"
  216. puts "Compiling Conditions..."
  217. pdf.column_box([0, pdf.cursor], :columns => 2, :width => pdf.bounds.width) do
  218. conditions.each do |condition|
  219. puts "\tCompiling #{condition.name}..."
  220. pdf.outline.add_subsection_to("Conditions") do
  221. @pdf.outline.section condition.name, :destination => @pdf.page_number
  222. end
  223. pdf.image "conditionbox.png", :position => :center
  224. pdf.move_up 30
  225. pdf.font "Goblin Hand"
  226. pdf.font_size FONT_SIZE_SUBSECTION
  227. pdf.fill_color "FFFFFF"
  228. pdf.text condition.name, :align => :center
  229. pdf.move_down 10
  230. pdf.font "Goudy"
  231. pdf.fill_color "000000"
  232. pdf.font_size FONT_SIZE_BODY
  233. pdf.text condition.rules_text, :inline_format => true
  234. pdf.text "<b>Possible Sources:</b> #{condition.possible_sources}", :inline_format => true if condition.possible_sources
  235. pdf.text "<b>Resolution:</b> #{condition.resolution}", :inline_format => true
  236. pdf.text "<b>Beat:</b> #{condition.beat}", :inline_format => true
  237. pdf.text "<b>#{condition.authors.size > 1 ? "Authors" : "Author"}:</b> #{condition.authors.to_list}", :inline_format => true
  238. end
  239. end
  240. start_section "Tilts"
  241. puts "Compiling Tilts..."
  242. pdf.column_box([0, pdf.cursor], :columns => 2, :width => pdf.bounds.width) do
  243. tilts.each do |tilt|
  244. puts "\tCompiling #{tilt.full_title}..."
  245. pdf.outline.add_subsection_to("Tilts") do
  246. @pdf.outline.section tilt.full_title, :destination => @pdf.page_number
  247. end
  248. pdf.image "conditionbox.png", :position => :center
  249. pdf.font "Goblin Hand"
  250. pdf.fill_color "FFFFFF"
  251. pdf.font_size FONT_SIZE_SUBSECTION
  252. if tilt.environmental?
  253. pdf.move_up 35
  254. pdf.text tilt.name, :align => :center
  255. pdf.font_size FONT_SIZE_BODY
  256. pdf.text "(Environmental)", :align => :center
  257. else
  258. pdf.move_up 30
  259. pdf.text tilt.name, :align => :center
  260. end
  261. pdf.move_down 10
  262. pdf.font "Goudy"
  263. pdf.fill_color "000000"
  264. pdf.font_size FONT_SIZE_BODY
  265. pdf.text tilt.rules_text, :inline_format => true
  266. pdf.text "<b>Effect:</b> #{tilt.effect}", :inline_format => true
  267. pdf.text "<b>Causing the Tilt:</b> #{tilt.causing}", :inline_format => true
  268. pdf.text "<b>Ending the Tilt:</b> #{tilt.ending}", :inline_format => true
  269. pdf.text "<b>#{tilt.authors.size > 1 ? "Authors" : "Author"}:</b> #{tilt.authors.to_list}", :inline_format => true
  270. end
  271. end
  272. pdf.render_file("shards_of_power.pdf")
  273. end
  274. end
  275. shards_of_power = ShardsOfPower.new
  276. shards_of_power.generate