diff --git a/CONTRIBUTING b/CONTRIBUTING
new file mode 100644
index 0000000..33562e4
--- /dev/null
+++ b/CONTRIBUTING
@@ -0,0 +1,37 @@
+Should be pretty simple. Download. Read the CONTRIBUTOR AGREEMENT. Make changes.
+Document changes. Add self to CONTRIBUTORS file, using any name you choose (or
+be anonymous if you prefer). Send changed files back using whatever method you
+prefer.
+
+For some changes (like adding new games to PFSB's monster generator), you may
+want to read the OGL too. A copy should have been included with the program, as
+should the CONTRIBUTORS file. It shouldn't be necessary to update the OGL's
+copyright notice.
+
+Is is preferred that you try to match styles with the existing codebase. This
+doesn't have to include profane rants or sophomoric jokes in the code comments,
+but it can if you feel it's warranted. Matching styles includes keeping the
+documentation cat-friendly.
+
+Also preferred is testing your changes. See if it still builds, didn't include
+too many regressions, the fix actually fixes what it's supposed to fix, etc.
+Obviously pobody's nerfect, but a modicum of testing helps out immensely. Note:
+If it doesn't build on my machine, AFAIC it doesn't build on anyone's machine
+except maybe the submitter's.
+
+That said: If you send me something better than what I have, I'll almost
+certainly include it. Even a half-baked implementation is better than no
+implementation, unless it somehow reduces functionality of working things.
+
+
+ROADMAP
+ *SLAs and spells planned for 0.3
+ *GUI will probably show up before 1.0
+ *Save/load IP creatures expected to appear with GUI.
+ *Change to a PDF output will likely be near the 1.0 release. As long as the
+ output remains HTML-based, it is expected that the program may make
+ mistakes that the user will have to correct by manually editing the
+ HTML file.
+ *Move all OGL stuff to a seperate repo, which PFSB grabs as part of the
+ install/build process. Probably pre-1.0, maybe even before 0.10.
+ *Templates are complicated, almost definitely post-1.0
diff --git a/CONTRIBUTOR AGREEMENT b/CONTRIBUTOR AGREEMENT
new file mode 100644
index 0000000..7a0d2d6
--- /dev/null
+++ b/CONTRIBUTOR AGREEMENT
@@ -0,0 +1,49 @@
+DEFINITIONS:
+
+ 1) "Code" - Source code, object code, machine code, or any documentation
+ relating to source code, object code, or machine code.
+
+ 2) "You" - Any person or group that submits additions to the Code and/or
+ changes to the Code the PFSB project.
+
+ 3) "Project Owner" - The current owner of the PFSB project, whoever that
+ may be at any time.
+
+ 4) "Submit" - To attempt to incorporate Your Code, whether it is new Code
+ or changes to existing Code, into the PFSB project.
+
+By Submitting Code via any method and adding Yourself to the CONTRIBUTORS text file, You understand and agree to the following:
+
+ *The Submitted Code's copyright belongs to You, or that You a) Have a legal
+ right to propagate the Submitted Code, b) The Submitted Code is
+ licensed to You in such a way that its inclusion in PFSB in any way
+ does not violate any terms PFSB is currently distributed under, and
+ c) The Submitted Code does not violate any part of this document.
+
+ *The Submitted Code's patents, if they exist, belong to You, or that You
+ a) Have a legal right to propagate the Submitted Code, b) The
+ Submitted Code is licensed to You in such a way that its inclusion in
+ PFSB in any way does not violate any terms PFSB is currently
+ distributed under, and c) The Submitted Code does not violate any part
+ of this document.
+
+ *You keep ownership of Your Submitted Code's copyright, if You possess it,
+ unless You explicitly grant it to the PFSB project.
+
+ *You grant the Project Owner a non-exclusive, perpetual, irrevocable,
+ worldwide, no-charge, royalty-free, transferrable copyright license to
+ use, distribute, propagate, modify, publicly display, publicly perform
+ and prepare derivative works from the Submitted Code for use in the
+ PFSB project with the license it is using at the time of Your
+ Submission. You also grant the Project Owner the right to relicense
+ Your Submitted Code under any software license that the Free Software
+ Foundation has declared to be a free software license. The Project
+ Owner may exercise this right without contacting or requesting
+ permission from You.
+
+ *Should the Submitted Code be patented, all patent licenses similar to
+ those listed above for copyright shall be granted to the Project
+ Owner.
+
+
+This document was last updated for the release of PFSB v00.002.
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
new file mode 100644
index 0000000..7c4e202
--- /dev/null
+++ b/CONTRIBUTORS
@@ -0,0 +1,29 @@
+ This file follows the example of the Linux kernel's CREDITS file.
+ People who submit changes are expected to add themselves to this
+ file.
+
+ (N) is name.
+ (E) is email.
+ (W) is web address.
+ (D) is a brief description of the person's contribution.
+ "Contribution" can mean someone directly sent a pull request or
+ patch to me, or it can mean that I found a piece of license-
+ compatible code and decided to use it.
+ (S) is the date of acknowledgement and agreement to the
+ contributor agreement. Effectively an electronic signature.
+
+ If you wish to remain an anonymous contributor or otherwise not post
+ your N/E/W/D(s), write "Anonymous" or something similar in the appropriate
+ fields. If you don't write anything for (S), your change will be
+ rejected, so please don't forget to add yourself.
+
+ Above message last updated in v00.002.
+ Listings below last updated 2017-12-18.
+--------------------------------------------------------------------------------
+(N) Frozen Mustelid
+(E) hppyhnd@yahoo.com
+(W) N/A
+(D) Project owner/maintainer, main program designer/programmer
+(S) Unnecessary, project owner
+
+
diff --git a/DEPENDENCIES.txt b/DEPENDENCIES.txt
index b47b834..cd995e1 100644
--- a/DEPENDENCIES.txt
+++ b/DEPENDENCIES.txt
@@ -1,9 +1,9 @@
-1. A C++ compiler that supports C++14.
-2. Better make sura that compiler is G++. There's no program logic tied to the
- compiler or its extensions, but the Makefile won't work if it can't use G++
- to compile.
-3. A /usr/bin folder.
-4. A /usr/share folder.
-5. A web browser. Strictly speaking, the browser isn't necessary, but unless you
- prefer reading raw HTML over a nicely CSS-styled HTML document, you'll want
- one.
+1. A C++ compiler that supports C++14.
+2. Better make sure that compiler is G++. There's no program logic tied to the
+ compiler or its extensions, but the Makefile won't work if it can't use G++
+ to compile.
+3. A /usr/bin folder.
+4. A /usr/share folder.
+5. A web browser. Strictly speaking, the browser isn't necessary, but unless you
+ prefer reading raw HTML over a nicely CSS-styled HTML document, you'll want
+ one.
diff --git a/Known Issues.txt b/Known Issues.txt
index 32329a8..63ff55c 100644
--- a/Known Issues.txt
+++ b/Known Issues.txt
@@ -1,16 +1,28 @@
-Current:
- *DR and SR not implemented due to oversight
- *SLAs not implemented... Yet.
- *Program does little user entry verification and is fragile as a result
- *Only creature icon implemented is "construct"
- *No icons for environment or climate
- *No logic to pick an icon for environment or client
- *Program, despite being written in standard C++14, only runs on *nix
- *Program, despite being written in standard C++14, requires G++
- *Program produces unrepresentable characters at the end of the file. I
- suspect that this is an extra EOF character. xed renders this
- as umlaut y when I set the html charset tage to utf-8, but
- without that tag, xed instead renders '\FF'. Firefox renders
- these as question mark diamonds.
-
-Changed:
+Current:
+ *No icons for environment or climate
+ *No logic to pick an icon for environment or client
+ *Only creature icon implemented is "construct"
+ *Program, despite being written in standard C++14, only runs on *nix
+ *Program, despite being written in standard C++14, requires G++
+ *Program does little user entry verification and is fragile as a result
+ *Program is not distributed as Free Software
+ *Program produces unrepresentable characters at the end of the file. I
+ suspect that this is an extra EOF character. xed renders this
+ as umlaut y when I set the HTML charset tag to utf-8, but
+ without that tag, xed instead renders '\FF'. Firefox renders
+ these as question mark diamonds.
+ *SLAs not implemented... Yet.
+ *XP value always has at least 1 extra space. However, this doesn't appear to
+ actually render.
+
+
+Changed since last release:
+ +Program is not distributed as Free Software
+ +XP value always has at least 1 extra space. However, this doesn't appear to
+ actually render.
+
+ -DR and SR not implemented due to oversight
+ -The program will produce empty parentheses next to the the creature type if
+ there are no subtypes.
+
+ *Sorted list
diff --git a/Makefile b/Makefile
index c01435e..3f98653 100644
--- a/Makefile
+++ b/Makefile
@@ -1,26 +1,48 @@
-bin/pfsb:
- mkdir bin/
- g++ -v -std=c++14 -o bin/pfsb src/pfsb.cpp
+all:bin/pfsb
-.PHONY: install clean uninstall
+bin/pfsb: obj/pfMon.o obj/main.o
+ @mkdir -p bin/
+ g++ -v -ansi -std=c++14 -o bin/pfsb obj/main.o obj/pfMon.o
+
+obj/pfMon.o:
+ @mkdir -p obj/
+ g++ -c -v -ansi -std=c++14 -o obj/pfMon.o src/pfMon.cpp
+
+obj/main.o:
+ @mkdir -p obj/
+ g++ -c -v -ansi -std=c++14 -o obj/main.o src/main.cpp
+
+
+
+
+.PHONY: install clean distclean uninstall total-uninstall
install:
- mkdir -p /usr/share/pfsb/pfrpg/html
- mkdir -p /usr/share/pfsb/pfrpg/images/environ /usr/share/pfsb/pfrpg/images/temp /usr/share/pfsb/pfrpg/images/type
+ @mkdir -p /usr/share/pfsb/pfrpg/html/
+
+ @mkdir -p /usr/share/pfsb/pfrpg/images/environ/
+ @mkdir -p /usr/share/pfsb/pfrpg/images/temp/
+ @mkdir -p /usr/share/pfsb/pfrpg/images/type/
cp html/pfrpg.html html/pfrpg.css -t /usr/share/pfsb/pfrpg/html/
- -cp images/type/* /usr/share/pfsb/pfrpg/images/type
- -cp images/temp/* /usr/share/pfsb/pfrpg/images/temp
- -cp images/environ/* /usr/share/pfsb/pfrpg/images/environ
+ cp images/type/* /usr/share/pfsb/pfrpg/images/type/
+# -cp images/temp/* /usr/share/pfsb/pfrpg/images/temp/
+# -cp images/environ/* /usr/share/pfsb/pfrpg/images/environ/
- cp bin/pfsb /usr/bin
+ cp bin/pfsb /usr/bin/
clean:
- @rm -r bin/
+ @rm -rf bin/
+ @rm -rf obj/
+
+distclean: clean
+
#CSS file left alone so that generated monsters still display correctly
uninstall:
- rm /usr/bin/pfsb
- rm -r /usr/share/pfsb/pfrpg/images
- rm /usr/share/pfsb/pfrpg/html/pfrpg.html
-
+ @rm -f /usr/bin/pfsb
+ @rm -rf /usr/share/pfsb/pfrpg/images
+ @rm -f /usr/share/pfsb/pfrpg/html/pfrpg.html
+
+total-uninstall: uninstall
+ @rm -rf /usr/share/pfsb/
diff --git a/OGL v1.0a b/OGL v1.0a
index 14f5655..1c12f5d 100644
--- a/OGL v1.0a
+++ b/OGL v1.0a
@@ -1,143 +1,143 @@
-OPEN GAME LICENSE Version 1.0a
-
-The following text is the property of Wizards of the Coast, Inc. and is Copyright 2000 Wizards of the Coast, Inc ("Wizards"). All Rights Reserved.
-
- 1. Definitions: (a) "Contributors" means the copyright and/or trademark owners who have contributed Open Game Content; (b) "Derivative Material" means copyrighted material including derivative works and translations (including into other computer languages), potation, modification, correction, addition, extension, upgrade, improvement, compilation, abridgment or other form in which an existing work may be recast, transformed or adapted; (c) "Distribute" means to reproduce, license, rent, lease, sell, broadcast, publicly display, transmit or otherwise distribute; (d) "Open Game Content" means the game mechanic and includes the methods, procedures, processes and routines to the extent such content does not embody the Product Identity and is an enhancement over the prior art and any additional content clearly identified as Open Game Content by the Contributor, and means any work covered by this License, including translations and derivative works under copyright law, but specifically excludes Product Identity. (e) "Product Identity" means product and product line names, logos and identifying marks including trade dress; artifacts, creatures, characters, stories, storylines, plots, thematic elements, dialogue, incidents, language, artwork, symbols, designs, depictions, likenesses, formats, poses, concepts, themes and graphic, photographic and other visual or audio representations; names and descriptions of characters, spells, enchantments, personalities, teams, personas, likenesses and special abilities; places, locations, environments, creatures, equipment, magical or supernatural abilities or effects, logos, symbols, or graphic designs; and any other trademark or registered trademark clearly identified as Product identity by the owner of the Product Identity, and which specifically excludes the Open Game Content; (f) "Trademark" means the logos, names, mark, sign, motto, designs that are used by a Contributor to identify itself or its products or the associated products contributed to the Open Game License by the Contributor (g) "Use", "Used" or "Using" means to use, Distribute, copy, edit, format, modify, translate and otherwise create Derivative Material of Open Game Content. (h) "You" or "Your" means the licensee in terms of this agreement.
-
- 2. The License: This License applies to any Open Game Content that contains a notice indicating that the Open Game Content may only be Used under and in terms of this License. You must affix such a notice to any Open Game Content that you Use. No terms may be added to or subtracted from this License except as described by the License itself. No other terms or conditions may be applied to any Open Game Content distributed using this License.
-
- 3. Offer and Acceptance: By Using the Open Game Content You indicate Your acceptance of the terms of this License.
-
- 4. Grant and Consideration: In consideration for agreeing to use this License, the Contributors grant You a perpetual, worldwide, royalty-free, non-exclusive license with the exact terms of this License to Use, the Open Game Content.
-
- 5. Representation of Authority to Contribute: If You are contributing original material as Open Game Content, You represent that Your Contributions are Your original creation and/or You have sufficient rights to grant the rights conveyed by this License.
-
- 6. Notice of License Copyright: You must update the COPYRIGHT NOTICE portion of this License to include the exact text of the COPYRIGHT NOTICE of any Open Game Content You are copying, modifying or distributing, and You must add the title, the copyright date, and the copyright holder name to the COPYRIGHT NOTICE of any original Open Game Content you Distribute.
-
- 7. Use of Product Identity: You agree not to Use any Product Identity, including as an indication as to compatibility, except as expressly licensed in another, independent Agreement with the owner of each element of that Product Identity. You agree not to indicate compatibility or co-adaptability with any Trademark or Registered Trademark in conjunction with a work containing Open Game Content except as expressly licensed in another, independent Agreement with the owner of such Trademark or Registered Trademark. The use of any Product Identity in Open Game Content does not constitute a challenge to the ownership of that Product Identity. The owner of any Product Identity used in Open Game Content shall retain all rights, title and interest in and to that Product Identity.
-
- 8. Identification: If you distribute Open Game Content You must clearly indicate which portions of the work that you are distributing are Open Game Content.
-
- 9. Updating the License: Wizards or its designated Agents may publish updated versions of this License. You may use any authorized version of this License to copy, modify and distribute any Open Game Content originally distributed under any version of this License.
-
- 10. Copy of this License: You MUST include a copy of this License with every copy of the Open Game Content You distribute.
-
- 11. Use of Contributor Credits: You may not market or advertise the Open Game Content using the name of any Contributor unless You have written permission from the Contributor to do so.
-
- 12. Inability to Comply: If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Open Game Content due to statute, judicial order, or governmental regulation then You may not Use any Open Game Material so affected.
-
- 13. Termination: This License will terminate automatically if You fail to comply with all terms herein and fail to cure such breach within 30 days of becoming aware of the breach. All sublicenses shall survive the termination of this License.
-
- 14. Reformation: If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable.
-
- 15. COPYRIGHT NOTICE
-
- Open Game License v 1.0a, © 2000, Wizards of the Coast, Inc.; .
-
- System Reference Document, © 2000, Wizards of the Coast, Inc.; Authors: Jonathan Tweet, Monte Cook, Skip Williams, based on material by E. Gary Gygax and Dave Arneson.
-
- System Reference Document, © 2000, Wizards of the Coast, Inc; Authors: Jonathan Tweet, Monte Cook, Skip Williams, based on material by E. Gary Gygax and Dave Arneson.
-
- Pathfinder RPG Bestiary, © 2009, Paizo Publishing, LLC; Author: Jason Bulmahn, based on material by Jonathan Tweet, Monte Cook, and Skip Williams.
-
- Pathfinder Roleplaying Game Bestiary 6, © 2017, Paizo Inc.; Authors: Robert Brookes, Benjamin Bruck, John Compton, Paris Crenshaw, Adam Daigle, Crystal Frasier, James Jacobs, Thurston Hillman, Tim Hitchcock, Brandon Hodge, Jason Keeley, Isabelle Lee, Jason Nelson, Tim Nightengale, F. Wesley Schneider, David Schwartz, Mark Seifter, Todd Stewart, Josh Vogt, and Linda Zayas-Palmer.
-
- Pathfinder Roleplaying Game Core Rulebook, © 2009, Paizo Publishing, LLC; Author: Jason Bulmahn, based on material by Jonathan Tweet, Monte Cook, and Skip Williams.
-
- Pathfinder Roleplaying Game Core Rulebook, © 2011, Paizo Publishing, LLC; Author: Jason Bulmahn, based on material by Jonathan Tweet, Monte Cook, and Skip Williams.
-
- Pathfinder Roleplaying Game Monster Codex, © 2014, Paizo Inc.; Authors: Dennis Baker, Jesse Benner, Logan Bonner, Jason Bulmahn, Ross Byers, John Compton, Robert N. Emerson, Jonathan H. Keith, Dale C. McCoy, Jr., Mark Moreland, Tom Phillips, Stephen Radney-MacFarland, Sean K Reynolds, Thomas M. Reid, Patrick Renie, Mark Seifter, Tork Shaw, Neil Spicer, Owen K.C. Stephens, and Russ Taylor.
-
- The Book of Experimental Might, © 2008, Monte J. Cook; All rights reserved.
-
- The Book of Fiends, © 2003, Green Ronin Publishing; Authors: Aaron Loeb, Erik Mona, Chris Pramas, Robert J. Schwalb.
-
- Tome of Horrors III, © 2005, Necromancer Games, Inc; Author: Scott Greene, with Casey Christofferson, Erica Balsley, Kevin Baase, Lance Hawvermale, Travis Hawvermale, Ian S. Johnston, Patrick Lawinger, Nathan Paul, Clark Peterson, Greg Ragland, Robert Schwalb and Bill Webb.
-
- Tome of Horrors, © 2002, Necromancer Games, Inc.; Authors: Scott Greene, with Clark Peterson, Erica Balsley, Kevin Baase, Casey Christofferson, Lance Hawvermale, Travis Hawvermale, Patrick Lawinger, and Bill Webb; Based on original content from TSR.
-
- Basidirond from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Author: Scott Greene, based on original material by Gary Gygax.
-
- Brownie from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Gary Gygax.
-
- Cave Fisher from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Author: Scott Greene, based on original material by Lawrence Schick.
-
- Crystal Ooze from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Author: Scott Greene, based on original material by Gary Gygax.
-
- Daemon, Ceustodaemon (Guardian Daemon) from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on.
-
- Daemon, Derghodaemon from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Gary Gygax.
-
- Daemon, Hydrodaemon from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Gary Gygax.
-
- Daemon, Piscodaemon from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Gary Gygax.
-
- Dark Creeper from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Author: Scott Greene, based on original material by Rik Shepard.
-
- Dark Stalker from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Author: Scott Greene, based on original material by Simon Muth.
-
- Demon, Shadow from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Neville White.
-
- Dracolisk from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Author: Scott Greene, based on original material by Gary Gygax.
-
- Flind and Flindbar from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by J.D. Morris.
-
- Froghemoth from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Author: Scott Greene, based on original material by Gary Gygax.
-
- Giant Slug from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Author: Scott Greene, based on original material by Gary Gygax.
-
- Giant, Wood from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Wizards of the Coast.
-
- Grippli from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Gary Gygax.
-
- Gryph from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Peter Brown.
-
- Huecuva from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Underworld Oracle.
-
- Ice Golem from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Author: Scott Greene.
-
- Iron Cobra from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Philip Masters.
-
- Iron Cobra from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Author: Scott Greene, based on original material by Philip Masters.
-
- Jubilex from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Gary Gygax.
-
- Mite from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Author: Scott Greene, based on original material by Ian Livingstone and Mark Barnes.
-
- Nabasu Demon from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Author: Scott Greene, based on original material by Gary Gygax.
-
- Pazuzu from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Gary Gygax.
-
- Russet Mold from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Gary Gygax.
-
- Russet Mold from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Author: Scott Greene, based on original material by Gary Gygax.
-
- Scarecrow from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Roger Musson.
-
- Shadow Demon from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Author: Scott Greene, based on original material by Neville White.
-
- Slithering Tracker from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Gary Gygax.
-
- Thessalhydra from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Gary Gygax and Wizards of the Coast.
-
- Troll, Ice from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Russell Cole.
-
- Vegepygmy from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Gary Gygax.
-
- Vegepygmy from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Author: Scott Greene, based on original material by Gary Gygax.
-
- Wood Golem from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Authors: Scott Greene and Patrick Lawinger.
-
- Yellow Musk Creeper from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Author: Scott Greene, based on original material by Albie Fiore.
-
- Yellow Musk Zombie from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Author: Scott Greene, based on original material by Albie Fiore.
-
- Yeti from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Gary Gygax.
-
- Yeti from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Author: Scott Greene, based on original material by Gary Gygax.
-
- Zombie, Juju from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Gary Gygax.
-
- PFSB, Copyright 2017, /*No associated company*/; Author: Frozen Mustelid, based on material by Dennis Baker, Jesse Benner, Ross Beyers, Logan Bonner, Robert Brookes, Benjamin Bruck, Jason Bulmahn, John Compton, Paris Crenshaw, Adam Daigle, Robert Emerson, Crystal Frasier, James Jacobs, Thurston Hillman, Tim Hitchcock, Brandon Hodge, Jason Keeley, Isabelle Lee, Jason Nelson, Tim Nightengale, Tom Phillips, Stephen Radney-MacFarland, Thomas M. Reid, F. Wesley Schneider, Robert Schwalb, David Schwartz, Mark Seifter, Todd Stewart, Russ Taylor, Josh Vogt, and Linda Zayas-Palmer.
-
-
+OPEN GAME LICENSE Version 1.0a
+
+The following text is the property of Wizards of the Coast, Inc. and is Copyright 2000 Wizards of the Coast, Inc ("Wizards"). All Rights Reserved.
+
+ 1. Definitions: (a) "Contributors" means the copyright and/or trademark owners who have contributed Open Game Content; (b) "Derivative Material" means copyrighted material including derivative works and translations (including into other computer languages), potation, modification, correction, addition, extension, upgrade, improvement, compilation, abridgment or other form in which an existing work may be recast, transformed or adapted; (c) "Distribute" means to reproduce, license, rent, lease, sell, broadcast, publicly display, transmit or otherwise distribute; (d) "Open Game Content" means the game mechanic and includes the methods, procedures, processes and routines to the extent such content does not embody the Product Identity and is an enhancement over the prior art and any additional content clearly identified as Open Game Content by the Contributor, and means any work covered by this License, including translations and derivative works under copyright law, but specifically excludes Product Identity. (e) "Product Identity" means product and product line names, logos and identifying marks including trade dress; artifacts, creatures, characters, stories, storylines, plots, thematic elements, dialogue, incidents, language, artwork, symbols, designs, depictions, likenesses, formats, poses, concepts, themes and graphic, photographic and other visual or audio representations; names and descriptions of characters, spells, enchantments, personalities, teams, personas, likenesses and special abilities; places, locations, environments, creatures, equipment, magical or supernatural abilities or effects, logos, symbols, or graphic designs; and any other trademark or registered trademark clearly identified as Product identity by the owner of the Product Identity, and which specifically excludes the Open Game Content; (f) "Trademark" means the logos, names, mark, sign, motto, designs that are used by a Contributor to identify itself or its products or the associated products contributed to the Open Game License by the Contributor (g) "Use", "Used" or "Using" means to use, Distribute, copy, edit, format, modify, translate and otherwise create Derivative Material of Open Game Content. (h) "You" or "Your" means the licensee in terms of this agreement.
+
+ 2. The License: This License applies to any Open Game Content that contains a notice indicating that the Open Game Content may only be Used under and in terms of this License. You must affix such a notice to any Open Game Content that you Use. No terms may be added to or subtracted from this License except as described by the License itself. No other terms or conditions may be applied to any Open Game Content distributed using this License.
+
+ 3. Offer and Acceptance: By Using the Open Game Content You indicate Your acceptance of the terms of this License.
+
+ 4. Grant and Consideration: In consideration for agreeing to use this License, the Contributors grant You a perpetual, worldwide, royalty-free, non-exclusive license with the exact terms of this License to Use, the Open Game Content.
+
+ 5. Representation of Authority to Contribute: If You are contributing original material as Open Game Content, You represent that Your Contributions are Your original creation and/or You have sufficient rights to grant the rights conveyed by this License.
+
+ 6. Notice of License Copyright: You must update the COPYRIGHT NOTICE portion of this License to include the exact text of the COPYRIGHT NOTICE of any Open Game Content You are copying, modifying or distributing, and You must add the title, the copyright date, and the copyright holder name to the COPYRIGHT NOTICE of any original Open Game Content you Distribute.
+
+ 7. Use of Product Identity: You agree not to Use any Product Identity, including as an indication as to compatibility, except as expressly licensed in another, independent Agreement with the owner of each element of that Product Identity. You agree not to indicate compatibility or co-adaptability with any Trademark or Registered Trademark in conjunction with a work containing Open Game Content except as expressly licensed in another, independent Agreement with the owner of such Trademark or Registered Trademark. The use of any Product Identity in Open Game Content does not constitute a challenge to the ownership of that Product Identity. The owner of any Product Identity used in Open Game Content shall retain all rights, title and interest in and to that Product Identity.
+
+ 8. Identification: If you distribute Open Game Content You must clearly indicate which portions of the work that you are distributing are Open Game Content.
+
+ 9. Updating the License: Wizards or its designated Agents may publish updated versions of this License. You may use any authorized version of this License to copy, modify and distribute any Open Game Content originally distributed under any version of this License.
+
+ 10. Copy of this License: You MUST include a copy of this License with every copy of the Open Game Content You distribute.
+
+ 11. Use of Contributor Credits: You may not market or advertise the Open Game Content using the name of any Contributor unless You have written permission from the Contributor to do so.
+
+ 12. Inability to Comply: If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Open Game Content due to statute, judicial order, or governmental regulation then You may not Use any Open Game Material so affected.
+
+ 13. Termination: This License will terminate automatically if You fail to comply with all terms herein and fail to cure such breach within 30 days of becoming aware of the breach. All sublicenses shall survive the termination of this License.
+
+ 14. Reformation: If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable.
+
+ 15. COPYRIGHT NOTICE
+
+ Open Game License v 1.0a, © 2000, Wizards of the Coast, Inc.; .
+
+ System Reference Document, © 2000, Wizards of the Coast, Inc.; Authors: Jonathan Tweet, Monte Cook, Skip Williams, based on material by E. Gary Gygax and Dave Arneson.
+
+ System Reference Document, © 2000, Wizards of the Coast, Inc; Authors: Jonathan Tweet, Monte Cook, Skip Williams, based on material by E. Gary Gygax and Dave Arneson.
+
+ Pathfinder RPG Bestiary, © 2009, Paizo Publishing, LLC; Author: Jason Bulmahn, based on material by Jonathan Tweet, Monte Cook, and Skip Williams.
+
+ Pathfinder Roleplaying Game Bestiary 6, © 2017, Paizo Inc.; Authors: Robert Brookes, Benjamin Bruck, John Compton, Paris Crenshaw, Adam Daigle, Crystal Frasier, James Jacobs, Thurston Hillman, Tim Hitchcock, Brandon Hodge, Jason Keeley, Isabelle Lee, Jason Nelson, Tim Nightengale, F. Wesley Schneider, David Schwartz, Mark Seifter, Todd Stewart, Josh Vogt, and Linda Zayas-Palmer.
+
+ Pathfinder Roleplaying Game Core Rulebook, © 2009, Paizo Publishing, LLC; Author: Jason Bulmahn, based on material by Jonathan Tweet, Monte Cook, and Skip Williams.
+
+ Pathfinder Roleplaying Game Core Rulebook, © 2011, Paizo Publishing, LLC; Author: Jason Bulmahn, based on material by Jonathan Tweet, Monte Cook, and Skip Williams.
+
+ Pathfinder Roleplaying Game Monster Codex, © 2014, Paizo Inc.; Authors: Dennis Baker, Jesse Benner, Logan Bonner, Jason Bulmahn, Ross Byers, John Compton, Robert N. Emerson, Jonathan H. Keith, Dale C. McCoy, Jr., Mark Moreland, Tom Phillips, Stephen Radney-MacFarland, Sean K Reynolds, Thomas M. Reid, Patrick Renie, Mark Seifter, Tork Shaw, Neil Spicer, Owen K.C. Stephens, and Russ Taylor.
+
+ The Book of Experimental Might, © 2008, Monte J. Cook; All rights reserved.
+
+ The Book of Fiends, © 2003, Green Ronin Publishing; Authors: Aaron Loeb, Erik Mona, Chris Pramas, Robert J. Schwalb.
+
+ Tome of Horrors III, © 2005, Necromancer Games, Inc; Author: Scott Greene, with Casey Christofferson, Erica Balsley, Kevin Baase, Lance Hawvermale, Travis Hawvermale, Ian S. Johnston, Patrick Lawinger, Nathan Paul, Clark Peterson, Greg Ragland, Robert Schwalb and Bill Webb.
+
+ Tome of Horrors, © 2002, Necromancer Games, Inc.; Authors: Scott Greene, with Clark Peterson, Erica Balsley, Kevin Baase, Casey Christofferson, Lance Hawvermale, Travis Hawvermale, Patrick Lawinger, and Bill Webb; Based on original content from TSR.
+
+ Basidirond from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Author: Scott Greene, based on original material by Gary Gygax.
+
+ Brownie from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Gary Gygax.
+
+ Cave Fisher from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Author: Scott Greene, based on original material by Lawrence Schick.
+
+ Crystal Ooze from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Author: Scott Greene, based on original material by Gary Gygax.
+
+ Daemon, Ceustodaemon (Guardian Daemon) from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on.
+
+ Daemon, Derghodaemon from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Gary Gygax.
+
+ Daemon, Hydrodaemon from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Gary Gygax.
+
+ Daemon, Piscodaemon from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Gary Gygax.
+
+ Dark Creeper from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Author: Scott Greene, based on original material by Rik Shepard.
+
+ Dark Stalker from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Author: Scott Greene, based on original material by Simon Muth.
+
+ Demon, Shadow from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Neville White.
+
+ Dracolisk from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Author: Scott Greene, based on original material by Gary Gygax.
+
+ Flind and Flindbar from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by J.D. Morris.
+
+ Froghemoth from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Author: Scott Greene, based on original material by Gary Gygax.
+
+ Giant Slug from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Author: Scott Greene, based on original material by Gary Gygax.
+
+ Giant, Wood from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Wizards of the Coast.
+
+ Grippli from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Gary Gygax.
+
+ Gryph from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Peter Brown.
+
+ Huecuva from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Underworld Oracle.
+
+ Ice Golem from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Author: Scott Greene.
+
+ Iron Cobra from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Philip Masters.
+
+ Iron Cobra from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Author: Scott Greene, based on original material by Philip Masters.
+
+ Jubilex from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Gary Gygax.
+
+ Mite from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Author: Scott Greene, based on original material by Ian Livingstone and Mark Barnes.
+
+ Nabasu Demon from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Author: Scott Greene, based on original material by Gary Gygax.
+
+ Pazuzu from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Gary Gygax.
+
+ Russet Mold from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Gary Gygax.
+
+ Russet Mold from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Author: Scott Greene, based on original material by Gary Gygax.
+
+ Scarecrow from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Roger Musson.
+
+ Shadow Demon from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Author: Scott Greene, based on original material by Neville White.
+
+ Slithering Tracker from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Gary Gygax.
+
+ Thessalhydra from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Gary Gygax and Wizards of the Coast.
+
+ Troll, Ice from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Russell Cole.
+
+ Vegepygmy from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Gary Gygax.
+
+ Vegepygmy from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Author: Scott Greene, based on original material by Gary Gygax.
+
+ Wood Golem from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Authors: Scott Greene and Patrick Lawinger.
+
+ Yellow Musk Creeper from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Author: Scott Greene, based on original material by Albie Fiore.
+
+ Yellow Musk Zombie from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Author: Scott Greene, based on original material by Albie Fiore.
+
+ Yeti from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Gary Gygax.
+
+ Yeti from the Tome of Horrors, © 2002, Necromancer Games, Inc.; Author: Scott Greene, based on original material by Gary Gygax.
+
+ Zombie, Juju from the Tome of Horrors Complete, © 2011, Necromancer Games, Inc., published and distributed by Frog God Games; Author: Scott Greene, based on original material by Gary Gygax.
+
+ PFSB, Copyright 2017, /*No associated company*/; Author: Frozen Mustelid and contributors (see CONTRIBUTORS file), based on material by Dennis Baker, Jesse Benner, Ross Beyers, Logan Bonner, Robert Brookes, Benjamin Bruck, Jason Bulmahn, John Compton, Paris Crenshaw, Adam Daigle, Robert Emerson, Crystal Frasier, James Jacobs, Thurston Hillman, Tim Hitchcock, Brandon Hodge, Jason Keeley, Isabelle Lee, Jason Nelson, Tim Nightengale, Tom Phillips, Stephen Radney-MacFarland, Thomas M. Reid, F. Wesley Schneider, Robert Schwalb, David Schwartz, Mark Seifter, Todd Stewart, Russ Taylor, Josh Vogt, and Linda Zayas-Palmer.
+
+
diff --git a/README.txt b/README.txt
index 00b6f5f..994acf3 100644
--- a/README.txt
+++ b/README.txt
@@ -1,42 +1,43 @@
-I hope you find PFSB useful. It needs a lot of work right now, but even in this
-primitive state it should be useful. I fully expect that nearly every line of
-code I have written in this version will have be replaced in a subsequent
-version.
-
-I wanted to release this as GPL code, but due to the nature of PFRPG I felt it
-was legally necessary for me to release this as OGL instead. I won't come after
-you for incorporating my code into a GPL or similarly copyleft program, as long
-as you return the favor if I ask. That said, I can't guarantee that another
-company that the terms of the OGL won't allow me to name won't come after you.
-
-Additional contributors, wittingly or otherwise, will be listed in CONTRIB.txt.
-
-To build:
-
-1. Ignore the config script. It just runs true.
-2. Run make.
-3. Run make install.
-
-
-To use:
-
-1. Open a terminal in the desired output folder.
-
-2. Invoke the program from the command line. --version prints the version and
- license information. --help prints help text and a warning to not use $ in
- user input. --ogl prints information on Open Game Content and Product
- Identity. Any other switch will cause the program to error out.
-
-3. Follow the prompts. You'll get an HTML file at the end. The HTML template and
- the CSS sheet are stored in a subdirectory of /usr/share/pfsb/.
-
-4. Open the HTML file and read/print your monster. Or put it on a laptop and
- take it to game night/your FLGS. Or print it to a PDF and put it on your
- phone.
-
-
-Also note that make uninstall intentionally leaves the CSS file where it is, so that existing files will still display correctly. To remove everything, run both "make uninstall" and "rm /usr/share/pfsb".
-
-
-As written, this version of the program will only funtion on *nix systems, but
-it should be trivial to make it behave on Windows. Follow development at $GITHUB/SRCFORGE LINK HERE
+A PFRPG Monster Stat Block generator
+
+I hope you find PFSB useful. It needs a lot of work right now, but even in this
+primitive state it should be useful.
+
+I wanted to release this as GPL code, but due to the nature of PFRPG I felt it
+was legally necessary for me to release this as OGL instead. I won't come after
+you for incorporating my code into a GPL or similarly copyleft program, as long
+as you return the favor if I ask. That said, I can't guarantee that someone else
+won't come after you.
+
+Additional contributors, wittingly or otherwise, will be listed in CONTRIBUTORS.
+
+To build:
+
+1. Ignore the config script. It just runs true.
+2. Run make.
+3. Run make install.
+
+
+To use:
+
+1. Open a terminal in the desired output folder.
+
+2. Invoke the program from the command line. --version prints the version and
+ license information. --help prints help text and a warning to not use $ in
+ user input. --ogl prints information on Open Game Content and Product
+ Identity. Any other switch will cause the program to error out.
+
+3. Follow the prompts. You'll get an HTML file at the end. The HTML template and
+ the CSS sheet are stored in a subdirectory of /usr/share/pfsb/.
+
+4. Open the HTML file and read/print your monster. Or put it on a laptop and
+ take it to game night/your FLGS. Or print it to a PDF and put it on your
+ phone.
+
+
+Also note that make uninstall intentionally leaves the CSS file where it is, so that existing files will still display correctly. To remove everything, run `make total-uninstall`.
+
+
+As written, this version of the program will only function on *nix systems, but
+it should be trivial to make it behave on Windows. Follow development at
+https://github.com/frozenMustelid/pfsb
diff --git a/changelog.txt b/changelog.txt
index 6336c6e..f29b3d8 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,2 +1,51 @@
-v00.001
- *Initial release
+Legend
+ +Is added/improved functionality
+ -Is a regression
+ *Is a change that is neutral or invisible as far as the user is concerned
+ ~Is a bugfix
+
+v00.002
+
+ +Added a total uninstall option to the Makefile
+ +All non-source text files now use Windows-style line endings.
+ +Corrected a few instances of incorrect spacing
+ +If you tell the program you took the Improved Initiative feat, it will add
+ that feat to the feat list
+ +If you tell the program you took the Agile Maneuvers feat, it will add
+ that feat to the feat list
+ +Special abilities are prettier now.
+ +Most text now properly spaced for a standard 80x24 terminal window
+ +XP now includes commas
+ +You can now add DR to your monsters
+ +You can now add SR to your monsters
+ +You can now add weaknesses to your monsters
+
+ *Added contributor agreement
+ *Added/updated various other contributing-related documents
+ *Attempted to remove all instances of spaces as indention in souce files
+ *Attempted to remove tabs on otherwise empty lines
+ *HTML/CSS comments of finished monsters list template and program version
+ *HTML/CSS comments of template list template version
+ *Makefile now slightly quieter.
+ *Now accepting submissions. Read CONTRIBUTING for more info.
+ *Rearranged internals to be more maintainable
+ *Updated Known Issues.txt
+ *Updated OGL copyright notice to include an "and contributors" line.
+ --version text updated with similar information.
+ *Updated README.txt
+ *Upgraded Makefile to play nice with multi-file compilation. It's also more
+ granular.
+
+ ~Fixed an issue that prevented users from creating very small constructs
+ ~Fixed an issue where high-CR monsters could have incorrect XP on systems
+ that use a 32-bit int
+ ~Fixed an issue with HP calculation
+ ~Fixed a typo in DEPENDENCIES.txt
+ ~Low (>10) ability scores no longer produce modifiers 1 higher than they
+ should
+ ~The program will no longer produce empty parentheses next to the the
+ creature type if there are no subtypes.
+
+
+v00.001
+ *Initial release
diff --git a/html/pfrpg.css b/html/pfrpg.css
index 6572d46..4f9f536 100644
--- a/html/pfrpg.css
+++ b/html/pfrpg.css
@@ -1,33 +1,37 @@
-p {
- font-family:sans-serif;
-}
-
-.headericon {
- float:right;
-}
-
-.itemheader {
- font-weight:bold;
-}
-
-.sectionheader {
- font-family:sans-serif;
- border-top-style:solid;
- border-top-width:thin;
- border-bottom-style:solid;
- border-bottom-width:thin;
-}
-
-#monheader {
- color:white;
- background:black;
- text-align:left;
-}
-
-#cr {
- float:right;
-}
-
-#xp {
- font-weight:bold;
-}
+/* Monster made using PFSB. Find it at https://github.com/frozenMustelid/pfsb
+ *
+ * CSS Stylesheet version 0.2. */
+
+p {
+ font-family:sans-serif;
+}
+
+.headericon {
+ float:right;
+}
+
+.itemheader {
+ font-weight:bold;
+}
+
+.sectionheader {
+ font-family:sans-serif;
+ border-top-style:solid;
+ border-top-width:thin;
+ border-bottom-style:solid;
+ border-bottom-width:thin;
+}
+
+#monheader {
+ color:white;
+ background:black;
+ text-align:left;
+}
+
+#cr {
+ float:right;
+}
+
+#xp {
+ font-weight:bold;
+}
diff --git a/html/pfrpg.html b/html/pfrpg.html
index ed18986..e58bbbd 100644
--- a/html/pfrpg.html
+++ b/html/pfrpg.html
@@ -1,71 +1,80 @@
-
-
-
- $PF_MON
-
-
-
-
-
-
- XP $XP
- $ALIGN $SIZE $CREATTY ($SUBTYPE)
- $INIT; $SENSES
- $AURA;
-
-
-
-
- $REGAC, $TOUCHAC, $FLATFOO $ACBREAK
- $HP ($HPBREAK)
- $FORTSAV, $REFSAVE, $WILLSAV
- $DEFABLE; $IMMUNE
-
-
-
-
-
- $SPEED
- $MELEE
- $RANGED
- $SPACE; $REACH
- $SPECATK
-
-
-
-
-
- $STR, $DEX, $CON, $INT, $WIS, $CHA
- $BAB; $CMB ($SPECCMB); $CMD ($SPECCMD)
- $FEATLIS
- $SKILIST
- $LANGLIS
- $SQLIST
-
-
-
-
- $ENVLIST
- $GRPLIST
- $LOOTLST
-
-
-
-
- $SPECABL
-
-
-
-
-
-
-
+
+
+
+
+
+ $PF_MON
+
+
+
+
+
+
+ XP $XP
+ $ALIGN $SIZE $CREATTY $SUBTYPE
+ $INIT; $SENSES
+ $AURA;
+
+
+
+
+ $REGAC, $TOUCHAC, $FLATFOO $ACBREAK
+ $HP ($HPBREAK)
+ $FORTSAV, $REFSAVE, $WILLSAV
+ $DEFABLE; $IMMUNE
+ $RESIST; $WEAK
+ $DR; $SR
+
+
+
+
+
+
+
+ $SPEED
+ $MELEE
+ $RANGED
+ $SPACE; $REACH
+ $SPECATK
+
+
+
+
+
+ $STR, $DEX, $CON, $INT, $WIS, $CHA
+ $BAB; $CMB ($SPECCMB); $CMD ($SPECCMD)
+ $FEATLIS
+ $SKILIST
+ $LANGLIS
+ $SQLIST
+
+
+
+
+ $ENVLIST
+ $GRPLIST
+ $LOOTLST
+
+
+
+
+ $SPECABL
+
+
+
+
+
+
+
diff --git a/src/commonFunctions.hpp b/src/commonFunctions.hpp
new file mode 100644
index 0000000..a1e07b2
--- /dev/null
+++ b/src/commonFunctions.hpp
@@ -0,0 +1,39 @@
+#ifndef COMMON_FUNCTIONS_HPP
+#define COMMON_FUNCTIONS_HPP
+
+#include
+#include
+
+using namespace std;
+
+void clearBuffer() {
+
+ cin.clear();
+
+ cin.ignore(numeric_limits::max(),'\n');
+
+ cin.clear();
+
+ return;
+}
+
+string stringToLower(string toLower) {
+
+ int x = 0;
+
+ while(toLower[x] != '\0') {
+ toLower[x] = tolower(toLower[x]);
+ x++;
+ }
+
+ return toLower;
+
+}
+
+//Trivial wrapper for floor. Used because of an enum conflict with cmath and Size::HUGE
+/*int intFloor(double value) {
+
+ return static_cast(floor(value));
+}*/
+
+#endif
\ No newline at end of file
diff --git a/src/errors.hpp b/src/errors.hpp
new file mode 100644
index 0000000..8ed2608
--- /dev/null
+++ b/src/errors.hpp
@@ -0,0 +1,9 @@
+#ifndef ERRORS_HPP
+#define ERRORS_HPP
+
+#define _ERR_UNRECOGNIZED_CLI_OPTION 1
+#define _ERR_INVALID_SWITCH 2
+#define _ERR_INVALID_SIZE 3
+#define _ERR_INVALID_CREATURE_TYPE 4
+
+#endif
\ No newline at end of file
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 0000000..efa5ea4
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,640 @@
+#include "errors.hpp"
+#include "pfMon.hpp"
+#include "platform.hpp"
+#include "version.hpp"
+
+#include
+#include
+#include
+#include
+
+/* And for some reason, GCC no longer requires me to include cstdlib to call
+ * exit. Wat.
+ */
+
+using namespace std;
+
+void printHelp();
+void printOgl();
+void printVersion();
+
+void selectGame(int argc, char**argv);
+void doPf();
+
+void insertIntoFile(fstream& monster, const char* toInsert, long beforeDollar, int keyLength, const char* baseFileName);
+long findDollar(fstream& monster, long currentPos);
+void findAndReplace(fstream& outfile, const char* replacement, const char* searchKey, const char* fileName);
+
+int main(int argc, char** argv) {
+
+ if (argc == 1) {
+ doPf();
+
+ #ifdef _DEBUG
+ cout << "Exited doProgram()" << endl;
+ #endif
+
+ exit(0);
+ } else if (argc == 2) {
+/******************************************************************************/
+#if (_PLATFAM == __unix)
+ if (!strcmp("--help",argv[1])) {
+#endif
+#if (_PLATFAM == __WIN32)
+ if (!strcmp("/?",argv[1])) {
+#endif
+/******************************************************************************/
+ printHelp();
+ exit(0);
+ }
+/******************************************************************************/
+#if (_PLATFAM == __unix)
+ if (!strcmp("--ogl",argv[1])) {
+#endif
+#if (_PLATFAM == __WIN32)
+ if (!strcmp("/o",argv[1])) {
+#endif
+/******************************************************************************/
+ printOgl();
+ exit(0);
+ }
+/******************************************************************************/
+//Compatibility note: No #ifs on --version because I don't know what's idiomatic for Windows
+/******************************************************************************/
+ if (!strcmp("--version",argv[1])) {
+ printVersion();
+ exit(0);
+ }
+
+ selectGame(argc,argv);
+ } else {
+ cout << "Unrecognized option!" << endl;
+ exit(_ERR_UNRECOGNIZED_CLI_OPTION);
+ }
+
+ return 0;
+}
+
+void selectGame(int argc, char** argv) {
+
+ if (!strcmp("pf",argv[1])) {
+ doPf();
+ exit(0);
+ }
+
+ cout << "Unrecognized option!" << endl;
+ exit(_ERR_UNRECOGNIZED_CLI_OPTION);
+}
+
+void doPf() {
+
+ string tempStorage;
+
+
+ PfMon monster;
+
+
+/*****************************************************************************\
+ * MONSTER *
+\*****************************************************************************/
+
+
+ #ifdef _DEBUG
+ cout << "Monster name: " << monster.monName << endl;
+ cout << "File path: " << monster.fileName << endl;
+ #endif
+
+ monster.crAndXp();
+ monster.fetchAlignment();
+ monster.fetchSize();
+ monster.fetchCreatureType();
+ monster.fetchSubtypes();
+ monster.fetchAura();
+
+ monster.fetchHd();
+ monster.calculateBab();
+ monster.fetchAbilities();
+ monster.determineInitiative();
+ monster.fetchSenses();
+ monster.calculateHp();
+ monster.determineAC();
+ monster.determineSaves();
+ monster.fetchDefensiveAbilities();
+ monster.determineImmunities();
+ monster.fetchResistances();
+ monster.fetchWeaknesses();
+ monster.fetchDr();
+ monster.fetchSr();
+
+ monster.fetchSpeed();
+ monster.fetchMeleeAtk();
+ monster.fetchRangedAtk();
+ monster.calculateSpace();
+ monster.determineReach();
+ monster.fetchSpecialAttacks();
+
+ monster.determineCombatManeuvers();
+ monster.fetchFeats();
+ monster.fetchSkills();
+ monster.fetchLanguages();
+ monster.fetchSpecialQualities();
+
+ monster.fetchEnvironment();
+ monster.fetchOrganization();
+ monster.fetchTreasure();
+
+ monster.fetchSpecialAbilities(); //*/
+
+ #ifndef _DEBUG
+ monster.prepareWrite();
+ #else
+ cout << monster.prepareWrite();
+ #endif
+
+
+/*****************************************************************************\
+ * WRITE FILE *
+\*****************************************************************************/
+
+ findAndReplace(monster.file,_VERSION,u8"PROGVER",monster.fileName);
+
+ tempStorage = "PFSB - ";
+ tempStorage += monster.monName;
+ findAndReplace(monster.file,tempStorage.c_str(),u8"PF_MON",monster.fileName);
+ #ifdef _DEBUG
+ cout << "Monster header written" << endl;
+ #endif
+
+ findAndReplace(monster.file,monster.monName.c_str(),u8"MONNAME",monster.fileName);
+ #ifdef _DEBUG
+ cout << "Monname written" << endl;
+ #endif
+
+ findAndReplace(monster.file,monster.cr,u8"CR",monster.fileName);
+ #ifdef _DEBUG
+ cout << "CR written" << endl;
+ #endif
+ findAndReplace(monster.file,(monster.xp.c_str()),u8"XP",monster.fileName);
+ #ifdef _DEBUG
+ cout << "XP written" << endl;
+ cout << endl << endl << endl << "Value (c++-string): " << monster.xp << endl << endl << endl;
+ cout << endl << endl << endl << "Value (c - string): " << monster.xp.c_str() << endl << endl << endl;
+ #endif
+ findAndReplace(monster.file,monster.alignment,u8"ALIGN",monster.fileName);
+ #ifdef _DEBUG
+ cout << "Alignment written" << endl;
+ #endif
+ findAndReplace(monster.file,monster.creatSizeWords.c_str(),u8"SIZE",monster.fileName);
+ #ifdef _DEBUG
+ cout << "Size written: " << monster.creatSizeWords << endl;
+ #endif
+
+ findAndReplace(monster.file,monster.creatTypeWords.c_str(),u8"CREATTY",monster.fileName);
+ findAndReplace(monster.file,monster.creatTypeWords.c_str(),u8"CREATTY",monster.fileName);
+
+ findAndReplace(monster.file,monster.subtypes.c_str(),u8"SUBTYPE",monster.fileName);
+ findAndReplace(monster.file,to_string(monster.initiative).c_str(),u8"INIT",monster.fileName);
+ findAndReplace(monster.file,monster.senses.c_str(),u8"SENSES",monster.fileName);
+ findAndReplace(monster.file,monster.aura.c_str(),u8"AURA",monster.fileName);
+
+
+
+
+ findAndReplace(monster.file,to_string(monster.ac.totalAc).c_str(),u8"REGAC",monster.fileName);
+ tempStorage = "touch ";
+ findAndReplace(monster.file,(tempStorage + to_string(monster.ac.touchAc)).c_str(),u8"TOUCHAC",monster.fileName);
+ tempStorage = "flat-footed ";
+ findAndReplace(monster.file,(tempStorage + to_string(monster.ac.flatFootAc)).c_str(),u8"FLATFOO",monster.fileName);
+ findAndReplace(monster.file,monster.ac.breakdown.c_str(),u8"ACBREAK",monster.fileName);
+
+ findAndReplace(monster.file,to_string(monster.hp).c_str(),u8"HP",monster.fileName);
+ findAndReplace(monster.file,monster.hpBreakdown.c_str(),u8"HPBREAK",monster.fileName);
+
+ findAndReplace(monster.file,to_string(monster.saves.fort).c_str(),u8"FORTSAV",monster.fileName);
+ findAndReplace(monster.file,to_string(monster.saves.reflex).c_str(),u8"REFSAVE",monster.fileName);
+ findAndReplace(monster.file,to_string(monster.saves.will).c_str(),u8"WILLSAV",monster.fileName);
+
+ findAndReplace(monster.file,monster.defensiveAbilities.c_str(),u8"DEFABLE",monster.fileName);
+ findAndReplace(monster.file,monster.immunities.c_str(),u8"IMMUNE",monster.fileName);
+ findAndReplace(monster.file,monster.resistances.c_str(),u8"RESIST",monster.fileName);
+ findAndReplace(monster.file,monster.weaknesses.c_str(),u8"WEAK",monster.fileName);
+
+ findAndReplace(monster.file,monster.dr.c_str(),u8"DR",monster.fileName);
+ findAndReplace(monster.file,monster.sr.c_str(),u8"SR",monster.fileName);
+
+
+
+
+ findAndReplace(monster.file,monster.speed.c_str(),u8"SPEED",monster.fileName);
+
+ findAndReplace(monster.file,monster.meleeAtk.c_str(),u8"MELEE",monster.fileName);
+ findAndReplace(monster.file,monster.rangedAtk.c_str(),u8"RANGED",monster.fileName);
+
+ findAndReplace(monster.file,monster.space.c_str(),u8"SPACE",monster.fileName);
+ findAndReplace(monster.file,monster.reach.c_str(),u8"REACH",monster.fileName);
+
+ findAndReplace(monster.file,monster.specAtk.c_str(),u8"SPECATK",monster.fileName);
+
+
+
+
+ findAndReplace(monster.file,monster.abilities.displayStr.c_str(),u8"STR",monster.fileName);
+ findAndReplace(monster.file,monster.abilities.displayDex.c_str(),u8"DEX",monster.fileName);
+ findAndReplace(monster.file,monster.abilities.displayCon.c_str(),u8"CON",monster.fileName);
+ findAndReplace(monster.file,monster.abilities.displayInt.c_str(),u8"INT",monster.fileName);
+ findAndReplace(monster.file,monster.abilities.displayWis.c_str(),u8"WIS",monster.fileName);
+ findAndReplace(monster.file,monster.abilities.displayCha.c_str(),u8"CHA",monster.fileName);
+
+ findAndReplace(monster.file,to_string(monster.bab).c_str(),u8"BAB",monster.fileName);
+
+ findAndReplace(monster.file,to_string(monster.maneuvers.cmb).c_str(),u8"CMB",monster.fileName);
+ findAndReplace(monster.file,to_string(monster.maneuvers.cmd).c_str(),u8"CMD",monster.fileName);
+ findAndReplace(monster.file,monster.maneuvers.specialCmb.c_str(),u8"SPECCMB",monster.fileName);
+ findAndReplace(monster.file,monster.maneuvers.specialCmd.c_str(),u8"SPECCMD",monster.fileName);
+
+ findAndReplace(monster.file,monster.featList.c_str(),u8"FEATLIS",monster.fileName);
+ findAndReplace(monster.file,monster.skillList.c_str(),u8"SKILIST",monster.fileName);
+ findAndReplace(monster.file,monster.languageList.c_str(),u8"LANGLIS",monster.fileName);
+
+ findAndReplace(monster.file,monster.specialQualities.c_str(),u8"SQLIST",monster.fileName);
+
+
+
+
+ findAndReplace(monster.file,monster.environmentList.c_str(),u8"ENVLIST",monster.fileName);
+ findAndReplace(monster.file,monster.groupList.c_str(),u8"GRPLIST",monster.fileName);
+ findAndReplace(monster.file,monster.lootList.c_str(),u8"LOOTLST",monster.fileName);
+
+
+
+
+ findAndReplace(monster.file,monster.specialAbilities.c_str(),u8"SPECABL",monster.fileName);
+
+
+
+ monster.file.close();
+
+ #ifdef _DEBUG
+ cout << "Monster closed" << endl;
+ #endif
+
+
+ //cout << monName << endl << cr << endl << displayCr << endl << xp << endl << creatSizeWords << endl << creatSize << endl << creatType << endl << subtypes << endl;
+
+}
+
+
+void findAndReplace(fstream& outfile, const char* replacement, const char* searchKey, const char* fileName) {
+
+ /* I am at a total loss to explain why this code doesn't fucking work if
+ * you give it a search key longer than 8 characters (including the null
+ * terminator). If you have even the slightest fucking clue, please tell
+ * me why. I'd love to know. I could have gotten this first version of the
+ * code out nearly a month earlier if I hadn't been trying to figure out
+ * why this piece of shit doesn't work. The reason this segment of the
+ * code is such a giant fucking mess is because I couldn't figure out what
+ * the fuck is wrong with it.
+ *
+ * Seriously, if you know, please share with the class. I'd love to know.
+ * It's probably something stupid, but at least then I'd know how I
+ * managed to FUBAR this so hard. I'd honestly be impressed with myself if
+ * I wasn't so pissed off.
+ */
+
+ fstream part1;
+ fstream part2;
+ long currentPos = 0;
+ const int keyLength = strlen(searchKey);
+ char current;
+ bool located = false;
+
+ char* potentialMatch = new char[keyLength];
+
+ outfile.seekp(0L, ios::beg);
+ outfile.seekg(0L, ios::beg);
+
+ /*#ifdef _DEBUG
+ cout << "find-replace entered" << endl;
+ #endif*/
+
+ do {
+ //outfile.seekg(0L, ios::beg);
+ currentPos = findDollar(outfile, currentPos);
+
+ current = outfile.peek();
+
+ /*#ifdef _DEBUG
+ cout << "Initial character found: " << *//*static_cast(current)*//* current<
+
+using namespace std;
+
+/* I'd prefer it if all enums are referred to by specifying the enum it came
+ * from. I think namespace is probably the wrong term for this, and something
+ * about "scoped" seems like it could be somehow confusing. Whatever the term
+ * for referring to enums as EnumName::ENUM_VALUE instead of just ENUM_VALUE is
+ * what I mean to type.
+ */
+
+enum CreatureType {ABERRATION = 1, ANIMAL, CONSTRUCT, DRAGON, FEY, HUMANOID, MAGICAL_BEAST, MONSTROUS_HUMANOID, OOZE, OUTSIDER, PLANT, UNDEAD, VERMIN};
+enum Size {FINE = 1, DIMINUTIVE, TINY, SMALL, MEDIUM, LARGE, HUGE, GARGANTUAN, COLOSSAL};
+
+struct Abilities{
+
+ int str;
+ int strMod;
+ string displayStr;
+
+ int dex;
+ int dexMod;
+ string displayDex;
+
+ int con;
+ int conMod;
+ string displayCon;
+
+ int intelligence;
+ int intMod;
+ string displayInt;
+
+ int wis;
+ int wisMod;
+ string displayWis;
+
+ int cha;
+ int chaMod;
+ string displayCha;
+};
+
+struct AC {
+
+ int armor = 0;
+ int shield = 0;
+ int dex = 0;
+ int natural = 0;
+ int dodge = 0;
+ int deflection = 0;
+ int sacred = 0;
+ int profane = 0;
+ int insight = 0;
+ int luck = 0;
+ int racial = 0;
+ int size = 0;
+ int trait = 0;
+ int untyped = 0;
+
+ int totalAc = 10;
+ int flatFootAc;
+ int touchAc;
+
+ string breakdown;
+
+};
+
+struct HitDice{
+ int d4 = 0;
+ int d6 = 0;
+ int d8 = 0;
+ int d10 = 0;
+ int d12 = 0;
+
+ int totalHd;
+ int racialHdCount;
+ int bonus = 0; //Intended for flat bonuses, like a construct's size bonus. Bonus HP from CON or CHA (if undead) are calculated as needed.
+
+};
+
+struct Maneuvers{
+ bool agileManuevers = false;
+
+ int cmb;
+ int cmd;
+
+ string specialCmb;
+ string specialCmd;
+};
+
+struct Saves{
+ bool goodFort = false;
+ bool goodReflex = false;
+ bool goodWill = false;
+
+ int fort;
+ int reflex;
+ int will;
+
+};
+
+
+
+#endif
\ No newline at end of file
diff --git a/src/pfMon.cpp b/src/pfMon.cpp
new file mode 100644
index 0000000..03db293
--- /dev/null
+++ b/src/pfMon.cpp
@@ -0,0 +1,1768 @@
+#include "commonFunctions.hpp"
+#include "errors.hpp"
+#include "pfMon.hpp"
+#include "platform.hpp"
+
+#include
+#undef HUGE
+//Something in cmath causes a conflict with Size::HUGE
+#include
+#include
+#include
+#include
+
+using namespace std;
+
+/******************************************************************************\
+ * HEADER *
+\******************************************************************************/
+
+long PfMon::crAndXp() {
+
+ long xp;
+
+ double crAsDouble;
+ int crAsInt;
+
+ cout << "CR (decimal 2 places if less than 1): ";
+ cin >> crAsDouble;
+ //TODO Add reprompt for invalid selection
+
+ //TODO Determine formula and use that instead of using a lookup table
+
+ crAsInt = static_cast(crAsDouble);
+
+ if (crAsDouble == .13) {
+ xp = 50L;
+ cr[0] = '1';
+ cr[1] = '/';
+ cr[2] = '8';
+
+ this->xp = xp;
+ return xp;
+ }
+ if (crAsDouble == .17) {
+ xp = 65L;
+ cr[0] = '1';
+ cr[1] = '/';
+ cr[2] = '6';
+
+ this->xp = xp;
+ return xp;
+ }
+ if (crAsDouble == .25) {
+ xp = 100L;
+ cr[0] = '1';
+ cr[1] = '/';
+ cr[2] = '4';
+
+ this->xp = xp;
+ return xp;
+ }
+ if (crAsDouble == .33) {
+ xp = 135L;
+ cr[0] = '1';
+ cr[1] = '/';
+ cr[2] = '3';
+
+ this->xp = xp;
+ return xp;
+ }
+ if (crAsDouble == .50) {
+ xp = 200L;
+ cr[0] = '1';
+ cr[1] = '/';
+ cr[2] = '2';
+
+ this->xp = xp;
+ return xp;
+ }
+
+ switch (crAsInt) {
+
+ case 1: {
+ xp = 400L;
+ cr[0] = '1';
+
+ break;
+ }
+ case 2: {
+ xp = 600L;
+ cr[0] = '2';
+
+ break;
+ }
+ case 3: {
+ xp = 800L;
+ cr[0] = '3';
+
+ break;
+ }
+ case 4: {
+ xp = 1'200L;
+ cr[0] = '4';
+
+ break;
+ }
+ case 5: {
+ xp = 1'600L;
+ cr[0] = '5';
+
+ break;
+ }
+ case 6: {
+ xp = 2'400L;
+ cr[0] = '6';
+
+ break;
+ }
+ case 7: {
+ xp = 3'200L;
+ cr[0] = '7';
+
+ break;
+ }
+ case 8: {
+ xp = 4'800L;
+ cr[0] = '8';
+
+ break;
+ }
+ case 9: {
+ xp = 6'400L;
+ cr[0] = '9';
+
+ break;
+ }
+ case 10: {
+ xp = 9'600L;
+ cr[0] = '1';
+ cr[1] = '0';
+
+ break;
+ }
+ case 11: {
+ xp = 12'800L;
+ cr[0] = '1';
+ cr[1] = '1';
+
+ break;
+ }
+ case 12: {
+ xp = 19'200L;
+ cr[0] = '1';
+ cr[1] = '2';
+
+ break;
+ }
+ case 13: {
+ xp = 25'600L;
+ cr[0] = '1';
+ cr[1] = '3';
+
+ break;
+ }
+ case 14: {
+ xp = 38'400L;
+ cr[0] = '1';
+ cr[1] = '4';
+
+ break;
+ }
+ case 15: {
+ xp = 51'200L;
+ cr[0] = '1';
+ cr[1] = '5';
+
+ break;
+ }
+ case 16: {
+ xp = 76'800L;
+ cr[0] = '1';
+ cr[1] = '6';
+
+ break;
+ }
+ case 17: {
+ xp = 102'000L;
+ cr[0] = '1';
+ cr[1] = '7';
+
+ break;
+ }
+ case 18: {
+ xp = 153'600L;
+ cr[0] = '1';
+ cr[1] = '8';
+
+ break;
+ }
+ case 19: {
+ xp = 204'800L;
+ cr[0] = '1';
+ cr[1] = '9';
+
+ break;
+ }
+ case 20: {
+ xp = 307'200L;
+ cr[0] = '2';
+ cr[1] = '0';
+
+ break;
+ }
+ case 21: {
+ xp = 409'600L;
+ cr[0] = '2';
+ cr[1] = '1';
+
+ break;
+ }
+ case 22: {
+ xp = 614'400L;
+ cr[0] = '2';
+ cr[1] = '2';
+
+ break;
+ }
+ case 23: {
+ xp = 819'200L;
+ cr[0] = '2';
+ cr[1] = '3';
+
+ break;
+ }
+ case 24: {
+ xp = 1'228'800L;
+ cr[0] = '2';
+ cr[1] = '4';
+
+ break;
+ }
+ case 25: {
+ xp = 1'638'400L;
+ cr[0] = '2';
+ cr[1] = '5';
+
+ break;
+ }
+ case 26: {
+ xp = 2'457'600L;
+ cr[0] = '2';
+ cr[1] = '6';
+
+ break;
+ }
+ case 27: {
+ xp = 3'276'800L;
+ cr[0] = '2';
+ cr[1] = '7';
+
+ break;
+ }
+ case 28: {
+ xp = 4'915'200L;
+ cr[0] = '2';
+ cr[1] = '8';
+
+ break;
+ }
+ case 29: {
+ xp = 6'553'600L;
+ cr[0] = '2';
+ cr[1] = '9';
+
+ break;
+ }
+ case 30: {
+ xp = 9'830'400L;
+ cr[0] = '3';
+ cr[1] = '0';
+
+ break;
+ }
+ default: {
+
+ xp = 0;
+ cr[0] = '0';
+ cr[1] = '\0';
+ cr[2] = '\0';
+
+ cout << "CR not within parameters, defaulting to 0" << endl;
+
+ break;
+ }
+
+ }
+
+ formatXp(xp);
+ return xp;
+}
+
+char* PfMon::fetchAlignment() {
+
+ clearBuffer();
+ cout << "Alignment (two-letter): ";
+ cin.getline(alignment,3,'\n');
+
+ alignment[0] = toupper(alignment[0]);
+ alignment[1] = toupper(alignment[1]);
+ alignment[2] = '\0';
+
+ return alignment;
+}
+
+string PfMon::fetchSize() {
+
+ cout << "Size: ";
+ //cin.getline(creatSizeWords,1023,'\n');
+ getline(cin, creatSizeWords);
+ creatSizeWords = stringToLower(creatSizeWords);
+ creatSizeWords[0] = toupper(creatSizeWords[0]);
+
+ switch (creatSizeWords[0]) {
+ case 'F': {
+ creatSize = Size::FINE;
+ break;
+ }
+ case 'D': {
+ creatSize = Size::DIMINUTIVE;
+ break;
+ }
+ case 'T': {
+ creatSize = Size::TINY;
+ break;
+ }
+ case 'S': {
+ creatSize = Size::SMALL;
+ break;
+ }
+ case 'M': {
+ creatSize = Size::MEDIUM;
+ break;
+ }
+ case 'L': {
+ creatSize = Size::LARGE;
+ break;
+ }
+ case 'H': {
+ creatSize = Size::HUGE;
+ break;
+ }
+ case 'G': {
+ creatSize = Size::GARGANTUAN;
+ break;
+ }
+ case 'C': {
+ creatSize = Size::COLOSSAL;
+ break;
+ }
+ default: {
+ cout << "Invalid creature size found, exiting program" << endl;
+ exit(_ERR_INVALID_SIZE);
+ }
+ }
+
+ return creatSizeWords;
+}
+
+string PfMon::fetchCreatureType() {
+
+ cout << "Creature Type: ";
+ getline(cin, creatTypeWords);
+
+ creatTypeWords = stringToLower(creatTypeWords);
+
+ determineCreatureType();
+
+ return creatTypeWords;
+}
+
+string PfMon::fetchSubtypes() {
+
+ string holding;
+
+ cout << "Subtypes (Just hit Enter if none): ";
+ getline(cin,holding);
+
+ if (!(holding.empty())) {
+ subtypes = '(';
+ subtypes += holding;
+ subtypes += ')';
+ }
+
+ return subtypes;
+}
+
+int PfMon::determineInitiative() {
+
+ int initBonus = abilities.dexMod;
+ char choice;
+
+ cout << "Improved initiative feat? (yes/no)" << endl;
+ cin >> choice;
+
+ clearBuffer();
+
+ if (choice == 'y' || choice == 'Y') {
+ initBonus += 4;
+ featList += "Improved Initiative, ";
+ }
+
+ initiative = initBonus;
+ return initBonus;
+
+}
+
+string PfMon::fetchSenses() {
+
+ int maxPerception = 3 + abilities.wisMod + hd.totalHd;
+
+ cout << "Type what should appear next to the Senses line in the general info section. The max Perception possible, with only \
+Wis mod + skill ranks + class skill bonus is " << maxPerception << "." << endl;
+ getline(cin,senses);
+
+ return senses;
+}
+
+string PfMon::fetchAura() {
+
+ cout << "List the monster's aura/auras here: ";
+ getline(cin,aura);
+
+ return aura;
+}
+
+string PfMon::formatXp(long xp) {
+
+ string xpString = to_string(xp);
+ string xpReversed = "";
+
+ for (unsigned int counter = 0; counter <= xpString.length(); counter++) {
+ /* Unsigned to avoid comparing signed int to unsigned int, which is the type
+ * returned by string::length(). Unlikely to cause issues, but all code in
+ * a release build must build without errors or warnings.
+ */
+ xpReversed += xpString[xpString.length() - counter];
+
+ if ((((counter) % 3) == 0) && (counter != 0)) {
+ xpReversed += ',';
+ }
+ }
+
+ xpString = "";
+
+ for (unsigned int counter = 0; counter <= xpReversed.length(); counter++) {
+ xpString += xpReversed[xpReversed.length() - counter];
+ }
+
+ xpString[0] = ' ';
+
+ if (xpString[1] == ',') { //This does produce extra spaces, but it doesnt look like browsers render these.
+ xpString[1] = ' ';
+ }
+
+ this->xp = xpString;
+ return xpString;
+}
+
+CreatureType PfMon::determineCreatureType() {
+
+ if (creatTypeWords == "aberration" || creatTypeWords == "abberration" || creatTypeWords == "abberation") {
+ creatType = CreatureType::ABERRATION;
+ return CreatureType::ABERRATION;
+ }
+ if (creatTypeWords == "animal") {
+ creatType = CreatureType::ANIMAL;
+ return CreatureType::ANIMAL;
+ }
+ if (creatTypeWords == "construct") {
+ creatType = CreatureType::CONSTRUCT;
+ return CreatureType::CONSTRUCT;
+ }
+ if (creatTypeWords == "dragon") {
+ creatType = CreatureType::DRAGON;
+ return CreatureType::DRAGON;
+ }
+ if (creatTypeWords == "fey") {
+ creatType = CreatureType::FEY;
+ return CreatureType::FEY;
+ }
+ if (creatTypeWords == "humanoid") {
+ creatType = CreatureType::HUMANOID;
+ return CreatureType::HUMANOID;
+ }
+ if (creatTypeWords == "magical beast") {
+ creatType = CreatureType::MAGICAL_BEAST;
+ return CreatureType::MAGICAL_BEAST;
+ }
+ if (creatTypeWords == "monstrous humanoid") {
+ creatType = CreatureType::MONSTROUS_HUMANOID;
+ return CreatureType::MONSTROUS_HUMANOID;
+ }
+ if (creatTypeWords == "ooze") {
+ creatType = CreatureType::OOZE;
+ return CreatureType::OOZE;
+ }
+ if (creatTypeWords == "outsider") {
+ creatType = CreatureType::OUTSIDER;
+ return CreatureType::OUTSIDER;
+ }
+ if (creatTypeWords == "plant") {
+ creatType = CreatureType::PLANT;
+ return CreatureType::PLANT;
+ }
+ if (creatTypeWords == "vermin") {
+ creatType = CreatureType::VERMIN;
+ return CreatureType::VERMIN;
+ }
+ if (creatTypeWords == "undead") {
+ creatType = CreatureType::UNDEAD;
+ return CreatureType::UNDEAD;
+ }
+
+ cout << "Creature type not known." << endl;
+ cout << "Ability to use homebrew creature types will be in a future version of this program. Exiting program." << endl;
+ exit(0);
+
+ return static_cast(NULL);
+}
+
+/******************************************************************************\
+ * DEFENSE *
+\******************************************************************************/
+
+AC PfMon::determineAC() {
+
+ ac.totalAc = 10;
+
+ cout << "Indicate total bonuses here. For example, if your creature has +5 Plate, give 14 as the armor bonus." << endl;
+
+ cout << "Armor bonus (0 if n/a): ";
+ cin >> ac.armor;
+
+ clearBuffer();
+ cout << "Shield bonus: ";
+ cin >> ac.shield;
+
+ clearBuffer();
+ cout << "Natural armor: ";
+ cin >> ac.natural;
+
+ clearBuffer();
+ cout << "Deflection bonus: ";
+ cin >> ac.deflection;
+
+ clearBuffer();
+ cout << "Dodge bonus: ";
+ cin >> ac.dodge;
+
+ clearBuffer();
+ cout << "Sacred: ";
+ cin >> ac.sacred;
+
+ clearBuffer();
+ cout << "Profane: ";
+ cin >> ac.profane;
+
+ clearBuffer();
+ cout << "Insight: ";
+ cin >> ac.insight;
+
+ clearBuffer();
+ cout << "Luck: ";
+ cin >> ac.luck;
+
+ clearBuffer();
+ cout << "Racial: ";
+ cin >> ac.racial;
+
+ clearBuffer();//
+ cout << "Untyped: ";
+ cin >> ac.untyped;
+
+ ac.dex = abilities.dexMod;
+
+ switch (creatSize) {
+ case Size::FINE: {
+ ac.size = 8;
+ break;
+ }
+ case Size::DIMINUTIVE: {
+ ac.size = 4;
+ break;
+ }
+ case Size::TINY: {
+ ac.size = 2;
+ break;
+ }
+ case Size::SMALL: {
+ ac.size = 1;
+ break;
+ }
+ case Size::MEDIUM: {
+ ac.size = 0;
+ break;
+ }
+ case Size::LARGE: {
+ ac.size = -1;
+ break;
+ }
+ case Size::HUGE: {
+ ac.size = -2;
+ break;
+ }
+ case Size::GARGANTUAN: {
+ ac.size = -4;
+ break;
+ }
+ case Size::COLOSSAL: {
+ ac.size = -8;
+ break;
+ }
+ default: {
+ cout << "Invalid creature size, exiting program." << endl;
+ exit(_ERR_INVALID_SIZE);
+ }
+
+ }
+
+ ac.totalAc += (ac.armor + ac.deflection + ac.dex + ac.dodge + ac.insight + ac.luck + ac.natural + ac.profane + ac.racial + ac.sacred + ac.shield + ac.size + ac.trait + ac.untyped);
+
+ ac.touchAc = ac.totalAc - (ac.armor + ac.shield + ac.natural);
+
+ ac.flatFootAc = ac.totalAc - (ac.dex + ac.dodge);
+
+ /*#ifdef _DEBUG
+ cout << "After calculations" << endl;
+ cout << "Armor bonus (0 if n/a): " << to_string(ac.armor) << endl;
+ cout << "Shield bonus: " << to_string(ac.shield) << endl;
+ cout << "Natural armor: " << to_string(ac.natural) << endl;
+ cout << "Deflection bonus: " << to_string(ac.deflection) << endl;
+ cout << "Dodge bonus: " << to_string(ac.dodge) << endl;
+ cout << "Sacred: " << to_string(ac.sacred) << endl;
+ cout << "Profane: " << to_string(ac.profane) << endl;
+ cout << "Insight: " << to_string(ac.insight) << endl;
+ cout << "Luck: " << to_string(ac.luck) << endl;
+ cout << "Racial: " << to_string(ac.racial) << endl;
+ cout << "Untyped: " << to_string(ac.untyped) << endl;
+
+ cout << "Dex bonus: " << to_string(ac.dex) << endl;
+ cout << "Size bonus: " << to_string(ac.size) << endl;
+
+ cout << "Total AC: " << to_string(ac.totalAc) << endl;
+ cout << "Flat-footed AC: " << to_string(ac.flatFootAc) << endl;
+ cout << "Touch AC: " << to_string(ac.touchAc) << endl;
+
+ cout << "AC Breakdown: " << ac.breakdown << endl;
+ #endif*/
+
+ ac.breakdown = calculateAcBreakdown();
+
+ /*#ifdef _DEBUG
+ cout << "After AC breakdown calculated" << endl;
+ cout << "Armor bonus (0 if n/a): " << to_string(ac.armor) << endl;
+ cout << "Shield bonus: " << to_string(ac.shield) << endl;
+ cout << "Natural armor: " << to_string(ac.natural) << endl;
+ cout << "Deflection bonus: " << to_string(ac.deflection) << endl;
+ cout << "Dodge bonus: " << to_string(ac.dodge) << endl;
+ cout << "Sacred: " << to_string(ac.sacred) << endl;
+ cout << "Profane: " << to_string(ac.profane) << endl;
+ cout << "Insight: " << to_string(ac.insight) << endl;
+ cout << "Luck: " << to_string(ac.luck) << endl;
+ cout << "Racial: " << to_string(ac.racial) << endl;
+ cout << "Untyped: " << to_string(ac.untyped) << endl;
+
+ cout << "Dex bonus: " << to_string(ac.dex) << endl;
+ cout << "Size bonus: " << to_string(ac.size) << endl;
+
+ cout << "Total AC: " << to_string(ac.totalAc) << endl;
+ cout << "Flat-footed AC: " << to_string(ac.flatFootAc) << endl;
+ cout << "Touch AC: " << to_string(ac.touchAc) << endl;
+
+ cout << "AC Breakdown: " << ac.breakdown << endl;
+ #endif*/
+
+ clearBuffer();
+
+ return ac;
+
+}
+
+HitDice PfMon::fetchHd() {
+
+ int racialHd;
+
+ cout << "How many racial HD does this creature have? ";
+ cin >> racialHd;
+ hd.totalHd = racialHd;
+ hd.racialHdCount = hd.totalHd;
+
+ cout << "Class levels for monsters will come in a later version." << endl;
+
+ switch (creatType) {
+ case CreatureType::ABERRATION: {
+ hd.d8 = racialHd;
+ break;
+ }
+ case CreatureType::ANIMAL: {
+ hd.d8 = racialHd;
+ break;
+ }
+ case CreatureType::CONSTRUCT: {
+ hd.d10 = racialHd;
+ switch (creatSize) {
+ case Size::FINE:
+ case Size::DIMINUTIVE:
+ case Size::TINY: {
+ break;
+ }
+ case Size::SMALL: {
+ hd.bonus = 10;
+ break;
+ }
+ case Size::MEDIUM: {
+ hd.bonus = 20;
+ break;
+ }
+ case Size::LARGE: {
+ hd.bonus = 30;
+ break;
+ }
+ case Size::HUGE: {
+ hd.bonus = 40;
+ break;
+ }
+ case Size::GARGANTUAN: {
+ hd.bonus = 60;
+ break;
+ }
+ case Size::COLOSSAL: {
+ hd.bonus = 80;
+ break;
+ }
+ default: {
+ cout << "Invalid creature size found, exiting program" << endl;
+ exit(_ERR_INVALID_SIZE);
+ }
+ }
+ break;
+ }
+ case CreatureType::DRAGON: {
+ hd.d12 = racialHd;
+ break;
+ }
+ case CreatureType::FEY: {
+ hd.d6 = racialHd;
+ break;
+ }
+ case CreatureType::HUMANOID: {
+ hd.d8 = racialHd;
+ break;
+ }
+ case CreatureType::MAGICAL_BEAST: {
+ hd.d10 = racialHd;
+ break;
+ }
+ case CreatureType::MONSTROUS_HUMANOID: {
+ hd.d10 = racialHd;
+ break;
+ }
+ case CreatureType::OOZE: {
+ hd.d8 = racialHd;
+ break;
+ }
+ case CreatureType::OUTSIDER: {
+ hd.d10 = racialHd;
+ break;
+ }
+ case CreatureType::PLANT: {
+ hd.d8 = racialHd;
+ break;
+ }
+ case CreatureType::UNDEAD: {
+ hd.d8 = racialHd;
+ break;
+ }
+ case CreatureType::VERMIN: {
+ hd.d8 = racialHd;
+ break;
+ }
+ default: {
+ cout << "Homebrew creature types will come later. Exiting program." << endl;
+ exit(_ERR_INVALID_CREATURE_TYPE);
+ }
+ }
+
+ return hd;
+}
+
+int PfMon::calculateHp() {
+
+ double hdAverage = 0.0;
+ hp = 0;
+
+ /* Note that even when monsters have multiple HD sources and two or more
+ * sources of hit dice that average to ?.5 each, each source of HD is
+ * rounded down before being added. For proof, math out the Serpentfolk
+ * Bone Prophet on MC204.
+ */
+
+ hdAverage += (2.5 * hd.d4);
+ hdAverage += (3.5 * hd.d6);
+ hdAverage += (4.5 * hd.d8);
+ hdAverage += (5.5 * hd.d10);
+ hdAverage += (6.5 * hd.d12);
+
+ hp = hd.bonus + static_cast(hdAverage) + (abilities.conMod * hd.totalHd);
+
+
+ /*#ifdef _DEBUG
+ cout << endl << "determineHP()" << endl << endl;
+ cout << "HP total: " << hp << endl;
+ cout << "HD average: " << hdAverage << endl;
+ cout << "HD average (casted to int): " << static_cast(hdAverage) << endl;
+ cout << "Con mod: " << abilities.conMod << endl;
+ cout << "Con bonus HP: " << (abilities.conMod * hd.totalHd) << endl;
+ cout << "Special bonus HP: " << hd.bonus << endl;
+ cout << "Total HD: " << hd.totalHd << endl;
+ cout << "Total d4: " << hd.d4 << endl;
+ cout << "Total d6: " << hd.d6 << endl;
+ cout << "Total d8: " << hd.d8 << endl;
+ cout << "Total d10: " << hd.d10 << endl;
+ cout << "Total d12: " << hd.d12 << endl;
+ #endif*/
+
+ calculateHpBreakdown();
+
+ return hp;
+}
+
+string PfMon::fetchDefensiveAbilities() {
+
+ cout << "List the monster's defensive abilities here: ";
+ getline(cin,defensiveAbilities);
+
+ return defensiveAbilities;
+}
+
+string PfMon::determineImmunities() {
+
+ string typeImmunities;
+
+ switch (creatType) {
+ case CreatureType::CONSTRUCT: {
+ typeImmunities = "construct traits, ";
+ break;
+ }
+ case CreatureType::PLANT: {
+ typeImmunities = "plant traits, ";
+ break;
+ }
+ case CreatureType::UNDEAD: {
+ typeImmunities = "undead traits, ";
+ break;
+ }
+ default: {
+ //doNothing();
+ }
+ }
+
+ cout << "List the monster's immunities here: ";
+ getline(cin,immunities);
+
+ return immunities;
+}
+
+string PfMon::fetchResistances() {
+
+ cout << "List the monster's resistances here: ";
+ getline(cin,resistances);
+
+ return resistances;
+}
+
+string PfMon::fetchWeaknesses() {
+
+ cout << "List the monster's weaknesses here: ";
+ getline(cin,weaknesses);
+
+ return weaknesses;
+}
+
+string PfMon::fetchDr() {
+
+ cout << "List the monster's DR here: ";
+ getline(cin,dr);
+
+ return dr;
+}
+
+string PfMon::fetchSr() {
+
+ cout << "List the monster's SR here: ";
+ getline(cin,sr);
+
+ return sr;
+}
+
+Saves PfMon::determineSaves() {
+
+ char choice;
+
+ switch (creatType) {
+ case CreatureType::ABERRATION: {
+ saves.goodWill = true;
+ break;
+ }
+ case CreatureType::ANIMAL: {
+ saves.goodFort = true;
+ saves.goodReflex = true;
+ break;
+ }
+ case CreatureType::CONSTRUCT: {
+ break;
+ }
+ case CreatureType::DRAGON: {
+ saves.goodFort = true;
+ saves.goodReflex = true;
+ saves.goodWill = true;
+ break;
+ }
+ case CreatureType::FEY: {
+ saves.goodFort = true;
+ break;
+ }
+ case CreatureType::HUMANOID: {
+ cout << "Humanoid creatures get 1 good save." << endl;
+ cout << "\t[1] Fortitude" << endl;
+ cout << "\t[2] Reflex" << endl;
+ cout << "\t[3] Will" << endl;
+
+ cin >> choice;
+ clearBuffer();
+
+ switch(choice) {
+ case '1': {
+ saves.goodFort = true;
+ break;
+ }
+ case '2': {
+ saves.goodReflex = true;
+ break;
+ }
+ case '3': {
+ saves.goodWill = true;
+ break;
+ }
+ default: {
+ cout << "I have no idea what you just typed. Defaulting to all poor saves." << endl;
+ }
+ }
+ break;
+ }
+ case CreatureType::MAGICAL_BEAST: {
+ saves.goodFort = true;
+ saves.goodReflex = true;
+ break;
+ }
+ case CreatureType::MONSTROUS_HUMANOID: {
+ saves.goodReflex = true;
+ saves.goodWill = true;
+ break;
+ }
+ case CreatureType::OOZE: {
+ break;
+ }
+ case CreatureType::OUTSIDER: {
+ cout << "Outsider creatures get 2 good saves." << endl;
+ cout << "\t[1] Fortitude + Reflex" << endl;
+ cout << "\t[2] Fortitude + Will" << endl;
+ cout << "\t[3] Reflex + Will" << endl;
+
+ cin >> choice;
+ clearBuffer();
+
+ switch(choice) {
+ case '1': {
+ saves.goodFort = true;
+ saves.goodReflex = true;
+ break;
+ }
+ case '2': {
+ saves.goodFort = true;
+ saves.goodWill = true;
+ break;
+ }
+ case '3': {
+ saves.goodReflex = true;
+ saves.goodWill = true;
+ break;
+ }
+ default: {
+ cout << "I have no idea what you just typed. Defaulting to all poor saves." << endl;
+ }
+ }
+ break;
+ }
+ case CreatureType::PLANT: {
+ saves.goodFort = true;
+ break;
+ }
+ case CreatureType::UNDEAD: {
+ saves.goodWill = true;
+ break;
+ }
+ case CreatureType::VERMIN: {
+ saves.goodFort = true;
+ break;
+ }
+ default: {
+ cout << "Homebrew creature types will come later. Exiting program." << endl;
+ exit(_ERR_INVALID_CREATURE_TYPE);
+ }
+
+ }
+
+ if (saves.goodFort) {
+ saves.fort = static_cast(0.5 * hd.totalHd) + 2;
+ //cout << "Base Fort: " << saves.fort << endl;
+ } else {
+ saves.fort = static_cast((1.0 / 3) * hd.totalHd);
+ //cout << "Base Fort: " << saves.fort << endl;
+ }
+
+ if (saves.goodReflex) {
+ saves.reflex = static_cast(0.5 * hd.totalHd) + 2;
+ //cout << "Base Ref: " << saves.reflex << endl;
+ } else {
+ saves.reflex = static_cast((1.0 / 3) * hd.totalHd);
+ //cout << "Base Ref: " << saves.reflex << endl;
+ }
+
+ if (saves.goodWill) {
+ saves.will = static_cast(0.5 * hd.totalHd) + 2;
+ //cout << "Base Will: " << saves.will << endl;
+ } else {
+ saves.will = static_cast((1.0 / 3) * hd.totalHd);
+ //cout << "Base Will: " << saves.will << endl;
+ }
+
+ saves.fort += abilities.conMod;
+ saves.reflex += abilities.dexMod;
+ saves.will += abilities.wisMod;
+
+ #ifdef _DEBUG
+ cout << "Good fort? " << saves.goodFort << endl;
+ cout << "Good reflex? " << saves.goodReflex << endl;
+ cout << "Good will? " << saves.goodWill << endl;
+
+ cout << "CON mod: " << abilities.conMod << endl;
+ cout << "DEX mod: " << abilities.dexMod << endl;
+ cout << "WIS mod: " << abilities.wisMod << endl;
+ #endif
+
+ return saves;
+}
+
+string PfMon::calculateAcBreakdown() {
+
+ ac.breakdown = "(";
+
+ if (ac.armor != 0) {
+ ac.breakdown += to_string(ac.armor);
+ ac.breakdown += " armor, ";
+ }
+ if (ac.shield != 0) {
+ ac.breakdown += to_string(ac.shield);
+ ac.breakdown += " shield, ";
+ }
+ if (ac.natural != 0) {
+ ac.breakdown += to_string(ac.natural);
+ ac.breakdown += " natural, ";
+ }
+ if (ac.dex != 0) {
+ ac.breakdown += to_string(ac.dex);
+ ac.breakdown += " Dex, ";
+ }
+ if (ac.dodge != 0) {
+ ac.breakdown += to_string(ac.dodge);
+ ac.breakdown += " dodge, ";
+ }
+ if (ac.deflection != 0) {
+ ac.breakdown += to_string(ac.deflection);
+ ac.breakdown += " deflection, ";
+ }
+ if (ac.insight != 0) {
+ ac.breakdown += to_string(ac.insight);
+ ac.breakdown += " insight, ";
+ }
+ if (ac.luck != 0) {
+ ac.breakdown += to_string(ac.luck);
+ ac.breakdown += " luck, ";
+ }
+ if (ac.sacred != 0) {
+ ac.breakdown += to_string(ac.sacred);
+ ac.breakdown += " sacred, ";
+ }
+ if (ac.profane != 0) {
+ ac.breakdown += to_string(ac.profane);
+ ac.breakdown += " profane, ";
+ }
+ if (ac.racial != 0) {
+ ac.breakdown += to_string(ac.racial);
+ ac.breakdown += " racial, ";
+ }
+ if (ac.size != 0) {
+ ac.breakdown += to_string(ac.size);
+ ac.breakdown += " size";
+ }
+
+ ac.breakdown += ")";
+ return ac.breakdown;
+}
+
+string PfMon::calculateHpBreakdown() {
+
+ int hdTypes = 0;
+
+ if (hd.d4) {
+ hdTypes += 1;
+ }
+ if (hd.d6) {
+ hdTypes += 1;
+ }
+ if (hd.d8) {
+ hdTypes += 1;
+ }
+ if (hd.d10) {
+ hdTypes += 1;
+ }
+ if (hd.d12) {
+ hdTypes += 1;
+ }
+
+ if (hdTypes > 1) {
+ hpBreakdown = to_string(hd.totalHd);
+ hpBreakdown += "HD; ";
+ }
+
+
+ if (hd.d4) {
+ hpBreakdown += to_string(hd.d4) + "d4 + ";
+ }
+ if (hd.d6) {
+ hpBreakdown += to_string(hd.d6) + "d6 + ";
+ }
+ if (hd.d8) {
+ hpBreakdown += to_string(hd.d8) + "d8 + ";
+ }
+ if (hd.d10) {
+ hpBreakdown += to_string(hd.d10) + "d10 + ";
+ }
+ if (hd.d12) {
+ hpBreakdown += to_string(hd.d12) + "d12 + ";
+ }
+
+ if (creatType == CreatureType::UNDEAD) {
+ hpBreakdown += to_string(abilities.chaMod * hd.totalHd + hd.bonus);
+ } else if (creatType == CreatureType::CONSTRUCT) {
+ hpBreakdown += to_string(hd.bonus);
+ } else {
+ hpBreakdown += to_string(abilities.conMod * hd.totalHd + hd.bonus);
+ }
+
+ return hpBreakdown;
+}
+
+/******************************************************************************\
+ * OFFENSE *
+\******************************************************************************/
+
+string PfMon::fetchSpeed() {
+
+ cout << "List speed here (including alt speeds, like fly or swim): " << endl;
+ getline(cin,speed);
+
+ return speed;
+
+}
+
+string PfMon::fetchMeleeAtk() {
+
+ int strAtkBonus = bab + abilities.strMod;
+ int finesseAtkBonus = bab + abilities.dexMod;
+
+ cout << "Type what you want to appear next to the Melee Attack line." << endl;
+ cout << "The STR-based attack bonus is " << strAtkBonus << ", and the DEX-based attack bonus (if applicable) is " << finesseAtkBonus << endl;
+
+ getline(cin,meleeAtk);
+
+ return meleeAtk;
+}
+
+string PfMon::fetchRangedAtk() {
+
+ int thrownAtkBonus = bab + abilities.strMod;
+ int bowAtkBonus = bab + abilities.dexMod;
+
+ cout << "Type what you want to appear next to the Ranged Attack line." << endl;
+ cout << "The STR-based attack bonus is " << thrownAtkBonus << ", and the DEX-based attack bonus is " << bowAtkBonus << endl;
+
+ getline(cin,rangedAtk);
+
+ return rangedAtk;
+}
+
+string PfMon::calculateSpace() {
+
+ switch (creatSize) {
+ case Size::FINE: {
+ space = "1/2 ft.";
+ break;
+ }
+ case Size::DIMINUTIVE: {
+ space = "1 ft.";
+ break;
+ }
+ case Size::TINY: {
+ space = "2-1/2 ft.";
+ break;
+ }
+ case Size::SMALL: {
+ space = "5 ft.";
+ break;
+ }
+ case Size::MEDIUM: {
+ space = "5 ft.";
+ break;
+ }
+ case Size::LARGE: {
+ space = "10 ft.";
+ break;
+ }
+ case Size::HUGE: {
+ space = "15 ft.";
+ break;
+ }
+ case Size::GARGANTUAN: {
+ space = "20 ft.";
+ break;
+ }
+ case Size::COLOSSAL: {
+ space = "30 ft.";
+ break;
+ }
+ default: {
+ cout << "Invalid creature size found, exiting program" << endl;
+ exit(_ERR_INVALID_SIZE);
+ }
+ }
+
+ return space;
+}
+
+string PfMon::determineReach() {
+
+ string isTall;
+
+ switch (creatSize) {
+ case Size::FINE: {
+ reach = "0 ft.";
+ break;
+ }
+ case Size::DIMINUTIVE: {
+ reach = "0 ft.";
+ break;
+ }
+ case Size::TINY: {
+ reach = "0 ft.";
+ break;
+ }
+ case Size::SMALL: {
+ reach = "5 ft.";
+ break;
+ }
+ case Size::MEDIUM: {
+ reach = "5 ft.";
+ break;
+ }
+ case Size::LARGE: {
+ cout << "Is the creature tall or long? ";
+ cin >> isTall;
+
+ if (tolower(isTall[0]) == 't') {
+ reach = "10 ft.";
+ } else {
+ reach = "5 ft.";
+ }
+ break;
+ }
+ case Size::HUGE: {
+ cout << "Is the creature tall or long? ";
+ cin >> isTall;
+
+ if (tolower(isTall[0]) == 't') {
+ reach = "15 ft.";
+ } else {
+ reach = "10 ft.";
+ }
+ break;
+ }
+ case Size::GARGANTUAN: {
+ cout << "Is the creature tall or long? ";
+ cin >> isTall;
+
+ if (tolower(isTall[0]) == 't') {
+ reach = "20 ft.";
+ } else {
+ reach = "15 ft.";
+ }
+ break;
+ }
+ case Size::COLOSSAL: {
+ cout << "Is the creature tall or long? ";
+ cin >> isTall;
+
+ if (tolower(isTall[0]) == 't') {
+ reach = "30 ft.";
+ } else {
+ reach = "20 ft.";
+ }
+ break;
+ }
+ default: {
+ cout << "Invalid creature size found, exiting program" << endl;
+ exit(_ERR_INVALID_SIZE);
+ }
+ }
+
+ return reach;
+}
+
+string PfMon::fetchSpecialAttacks() {
+
+ cout << "List the monster's special attacks here: ";
+ getline(cin,specAtk);
+
+ return specAtk;
+}
+
+/******************************************************************************\
+ * STATISTICS *
+\******************************************************************************/
+
+Abilities PfMon::fetchAbilities() {
+
+ cout << "Give the scores, not the modifiers." << endl;
+
+ cout << "STR: ";
+ cin >> abilities.str;
+ abilities.strMod = static_cast(floor((abilities.str - 10) / 2.0));
+ abilities.displayStr = to_string(abilities.str);
+
+
+ clearBuffer();
+ cout << "DEX: ";
+ cin >> abilities.dex;
+ abilities.dexMod = static_cast(floor((abilities.dex - 10) / 2.0));
+ abilities.displayDex = to_string(abilities.dex);
+
+ if ((creatType != CreatureType::UNDEAD) && (creatType != CreatureType::CONSTRUCT)) {
+ cout << "CON: ";
+ cin >> abilities.con;
+ abilities.conMod = static_cast(floor((abilities.con - 10) / 2.0));
+ abilities.displayCon = to_string(abilities.con);
+ /*cout << "determineBaseStats CON score (assign): " << abilities.con << endl;
+ cout << "determineBaseStats CON mod: (assign)" << abilities.conMod << endl;*/
+ } else {
+ abilities.con = INT_MIN;
+ abilities.displayCon = "—"; //Em-dash (U+2014) for extra width
+ }
+
+
+ if ((creatType != CreatureType::CONSTRUCT) && (creatType != CreatureType::OOZE)) {
+ clearBuffer();
+ cout << "INT: ";
+ cin >> abilities.intelligence;
+ abilities.intMod = static_cast(floor((abilities.intelligence - 10) / 2.0));
+ abilities.displayInt = to_string(abilities.intelligence);
+ } else {
+ abilities.intelligence = INT_MIN;
+ abilities.intMod = INT_MIN;
+ abilities.displayInt = "—"; //Em-dash (U+2014) for extra width
+ }
+
+ clearBuffer();
+ cout << "WIS: ";
+ cin >> abilities.wis;
+ abilities.wisMod = static_cast(floor((abilities.wis - 10) / 2.0));
+ abilities.displayWis = to_string(abilities.wis);
+
+ clearBuffer();
+ cout << "CHA: ";
+ cin >> abilities.cha;
+ abilities.chaMod = static_cast(floor((abilities.cha - 10) / 2.0));
+ abilities.displayCha = to_string(abilities.cha);
+
+
+ if (creatType == CreatureType::UNDEAD) {
+ abilities.conMod = abilities.chaMod;
+ }
+ if (creatType == CreatureType::CONSTRUCT) {
+ abilities.conMod = 0;
+ }
+
+#ifdef _DEBUG
+ cout << "Abilities: " << endl;
+
+ cout << "STR score: " << abilities.str << endl;
+ cout << "STR mod: " << abilities.strMod << endl;
+ cout << "Display STR: " << abilities.displayStr << endl << endl;
+
+ cout << "DEX score: " << abilities.dex << endl;
+ cout << "DEX mod: " << abilities.dexMod << endl;
+ cout << "Display DEX: " << abilities.displayDex << endl << endl;
+
+ cout << "CON score: " << abilities.con << endl;
+ cout << "STR CON: " << abilities.conMod << endl;
+ cout << "Display CON: " << abilities.displayCon << endl << endl;
+
+ cout << "INT score: " << abilities.intelligence << endl;
+ cout << "INT mod: " << abilities.intMod << endl;
+ cout << "Display INT: " << abilities.displayInt << endl << endl;
+
+ cout << "WIS score: " << abilities.wis << endl;
+ cout << "WIS mod: " << abilities.wisMod << endl;
+ cout << "Display WIS: " << abilities.displayWis << endl << endl;
+
+ cout << "CHA score: " << abilities.cha << endl;
+ cout << "CHA mod: " << abilities.chaMod << endl;
+ cout << "Display CHA: " << abilities.displayCha << endl << endl;
+#endif
+
+ return abilities;
+}
+
+int PfMon::calculateBab() {
+
+ switch (creatType) {
+ case CreatureType::ABERRATION: {
+ bab = static_cast(3.0 / 4 * hd.totalHd);
+ break;
+ }
+ case CreatureType::ANIMAL: {
+ bab = static_cast(3.0 / 4 * hd.totalHd);
+ break;
+ }
+ case CreatureType::CONSTRUCT: {
+ bab = hd.totalHd;
+ break;
+ }
+ case CreatureType::DRAGON: {
+ bab = hd.totalHd;
+ break;
+ }
+ case CreatureType::FEY: {
+ bab = static_cast(1.0 / 4 * hd.totalHd);
+ break;
+ }
+ case CreatureType::HUMANOID: {
+ bab = static_cast(3.0 / 4 * hd.totalHd);
+ break;
+ }
+ case CreatureType::MAGICAL_BEAST: {
+ bab = hd.totalHd;
+ break;
+ }
+ case CreatureType::MONSTROUS_HUMANOID: {
+ bab = hd.totalHd;
+ break;
+ }
+ case CreatureType::OOZE: {
+ bab = static_cast(3.0 / 4 * hd.totalHd);
+ break;
+ }
+ case CreatureType::OUTSIDER: {
+ bab = hd.totalHd;
+ break;
+ }
+ case CreatureType::PLANT: {
+ bab = static_cast(3.0 / 4 * hd.totalHd);
+ break;
+ }
+ case CreatureType::UNDEAD: {
+ bab = static_cast(3.0 / 4 * hd.totalHd);
+ break;
+ }
+ case CreatureType::VERMIN: {
+ bab = static_cast(3.0 / 4 * hd.totalHd);
+ break;
+ }
+ default: {
+ cout << "Homebrew creature types will come later. Exiting program." << endl;
+ exit(_ERR_INVALID_CREATURE_TYPE);
+ }
+
+ }
+
+ return bab;
+}
+
+Maneuvers PfMon::determineCombatManeuvers() {
+
+ char choice;
+
+ maneuvers.cmb = bab;
+ maneuvers.cmd = 10 + bab + abilities.strMod + abilities.dexMod;
+
+ cout << "Agile Maneuvers feat? (yes/no)" << endl;
+ cin >> choice;
+
+ clearBuffer();
+
+ if (choice == 'y' || choice == 'Y') {
+ maneuvers.agileManuevers = true;
+ featList += "Agile Maneuvers, ";
+ }
+
+ if ((!(maneuvers.agileManuevers)) || (abilities.strMod > abilities.dexMod)) {
+ maneuvers.cmb += abilities.strMod;
+ } else {
+ maneuvers.cmb += abilities.dexMod;
+ }
+
+ cout << "Special CMB? (current normal CMB is " << maneuvers.cmb << ')' << endl;
+ getline(cin,maneuvers.specialCmb);
+
+ cout << "Special CMD? (current normal CMD is " << maneuvers.cmd << ')' << endl;
+ getline(cin, maneuvers.specialCmd);
+
+ return maneuvers;
+}
+
+string PfMon::fetchFeats() {
+
+ int hdFeats = (1 + hd.totalHd) / 2;
+ string featsAddendum;
+
+ cout << "List the feats here. Based on HD, you should have " << hdFeats << " feats." << endl;
+ getline(cin,featsAddendum);
+
+ featList += featsAddendum;
+
+ return featList;
+}
+
+string PfMon::fetchSkills() {
+
+ int maxSkillPoints = INT_MIN;
+
+ switch (creatType) {
+ case CreatureType::ABERRATION: {
+ maxSkillPoints = (4 + abilities.intMod) * hd.totalHd;
+ break;
+ }
+ case CreatureType::ANIMAL: {
+ maxSkillPoints = (2 + abilities.intMod) * hd.totalHd;
+ break;
+ }
+ case CreatureType::CONSTRUCT: {
+ maxSkillPoints = (2 + abilities.intMod) * hd.totalHd;
+ break;
+ }
+ case CreatureType::DRAGON: {
+ maxSkillPoints = (6 + abilities.intMod) * hd.totalHd;
+ break;
+ }
+ case CreatureType::FEY: {
+ maxSkillPoints = (6 + abilities.intMod) * hd.totalHd;
+ break;
+ }
+ case CreatureType::HUMANOID: {
+ maxSkillPoints = (2 + abilities.intMod) * hd.totalHd;
+ break;
+ }
+ case CreatureType::MAGICAL_BEAST: {
+ maxSkillPoints = (2 + abilities.intMod) * hd.totalHd;
+ break;
+ }
+ case CreatureType::MONSTROUS_HUMANOID: {
+ maxSkillPoints = (4 + abilities.intMod) * hd.totalHd;
+ break;
+ }
+ case CreatureType::OOZE: {
+ maxSkillPoints = (2 + abilities.intMod) * hd.totalHd;
+ break;
+ }
+ case CreatureType::OUTSIDER: {
+ maxSkillPoints = (6 + abilities.intMod) * hd.totalHd;
+ break;
+ }
+ case CreatureType::PLANT: {
+ maxSkillPoints = (2 + abilities.intMod) * hd.totalHd;
+ break;
+ }
+ case CreatureType::UNDEAD: {
+ maxSkillPoints = (4 + abilities.intMod) * hd.totalHd;
+ break;
+ }
+ case CreatureType::VERMIN: {
+ maxSkillPoints = (2 + abilities.intMod) * hd.totalHd;
+ break;
+ }
+ default: {
+ cout << "Homebrew creature types will come later. Exiting program." << endl;
+ exit(_ERR_INVALID_CREATURE_TYPE);
+ }
+
+ }
+
+ int strSkills = 3 + abilities.strMod + hd.totalHd;
+ int dexSkills = 3 + abilities.dexMod + hd.totalHd;
+ int conSkills = 3 + abilities.conMod + hd.totalHd;
+ int intSkills = 3 + abilities.intMod + hd.totalHd;
+ int wisSkills = 3 + abilities.wisMod + hd.totalHd;
+ int chaSkills = 3 + abilities.chaMod + hd.totalHd;
+
+ cout << "Enter the creature's skills. The highest possible will be listed below, assuming that the creature gains that skill as a class skill." << endl;
+ cout << "\tSTR: " << strSkills << endl;
+ cout << "\tDEX: " << dexSkills << endl;
+ cout << "\tCON: " << conSkills << endl;
+ cout << "\tINT: " << intSkills << endl;
+ cout << "\tWIS: " << wisSkills << endl;
+ cout << "\tCHA: " << chaSkills << endl;
+
+ cout << "Total skill ranks available (not yet enforced by program logic): " << maxSkillPoints << endl;
+ getline(cin, skillList);
+
+ return skillList;
+}
+
+string PfMon::fetchLanguages() {
+
+ cout << "List the monster's languages here: ";
+ getline(cin,languageList);
+
+ return languageList;
+}
+
+string PfMon::fetchSpecialQualities() {
+
+ cout << "List the monster's special qualities here: ";
+ getline(cin,specialQualities);
+
+ return specialQualities;
+
+}
+
+/******************************************************************************\
+ * ECOLOGY *
+\******************************************************************************/
+
+string PfMon::fetchEnvironment() {
+
+ cout << "List the monster's environments here: ";
+ getline(cin,environmentList);
+
+ return environmentList;
+}
+
+string PfMon::fetchOrganization() {
+
+ cout << "List the monster's grouping habits here: ";
+ getline(cin,groupList);
+
+ return groupList;
+}
+
+string PfMon::fetchTreasure() {
+
+ cout << "List treasure here: " << endl;
+ getline(cin,lootList);
+
+ return lootList;
+}
+
+/******************************************************************************\
+ * SPECIAL ABILITIES *
+\******************************************************************************/
+
+string PfMon::fetchSpecialAbilities() {
+
+ string abilityName;
+ string abilityText;
+ string choice;
+
+ cout << "Does this monster have any special abilities not accounted for by the universal monster rules? ";
+ getline(cin,choice);
+
+ while (choice[0] == 'y' || choice[0] == 'Y') {
+
+ specialAbilities += "";
+
+ cout << "Ability text: ";
+ getline(cin,abilityText);
+ specialAbilities += abilityText;
+
+ specialAbilities += "
\n";
+
+ cout << "Continue? ";
+ getline(cin,choice);
+ }
+
+ return specialAbilities;
+}
+
+/******************************************************************************\
+ * SYSTEM *
+\******************************************************************************/
+
+PfMon::PfMon() {
+
+ string temp;
+
+ cout << "Monster Name: ";
+ getline(cin, monName);
+
+ temp = monName;
+
+ fileName = strcat(strcat(strcat(getenv("PWD"),_DIR_SEPARATOR_STRING),temp.c_str()),".html");
+ //Compatibility note: I have no idea if $PWD exists on Windows
+
+ cr [0] = '\0';
+ cr [1] = '\0';
+ cr [2] = '\0';
+ cr [3] = '\0';
+
+ alignment[0] = '\0';
+ alignment[1] = '\0';
+ alignment[2] = '\0';
+
+ senses = "";
+
+ specialAbilities = "";
+}
+
+PfMon::~PfMon() {
+}
+
+string PfMon::prepareWrite() {
+
+ ifstream monTemplate;
+
+ monTemplate.open(_PFRPG_TEMPLATE, ios::in);
+ file.open(fileName, ios::in | ios::out | ios::trunc);
+
+ file << monTemplate.rdbuf();
+
+ monTemplate.close();
+
+ return "Ready for write!";
+}
diff --git a/src/pfMon.hpp b/src/pfMon.hpp
new file mode 100644
index 0000000..50d8d11
--- /dev/null
+++ b/src/pfMon.hpp
@@ -0,0 +1,175 @@
+#ifndef PFMON_HPP
+#define PFMON_HPP
+
+#include "pfMeta.hpp"
+
+#include
+
+class PfMon
+{
+public:
+
+/* Note: I have to declare most of the data members as public, or else the find-
+ * replace function won't work. I'd end up being forced to write trivial getters
+ * for nearly every data member. So just make sure that only this class itself
+ * (and even then only the functions that should change that specific member's
+ * values) ever modifies any data members. Or else we're gonna have big trouble
+ * when/if I update this program to support non-PF games.
+ *
+ * Also note that these functions always return a value. Assigning this value to
+ * the appropriate data member is redundant. The value is returned for
+ * a) Debugging purposes, b) In case it becomes necessary in the future to
+ * use this value for something else, and c) Style reasons. I believe that void
+ * functions should be used sparingly, as they can obfuscate the purpose of a
+ * function. Add in the obfuscation caused by method calls that don't take
+ * arguments like a function does, and things can get confusing quickly.
+ *
+ * On the function naming scheme: "fetch" functions focus almost exclusively on
+ * user input. "calculate" functions do not contain any user input elements at
+ * all. "determine" functions feature both, but does enough to not warrant a
+ * fetch label.
+ */
+/******************************************************************************\
+ * HEADER *
+\******************************************************************************/
+
+ string monName;
+ char cr[4];
+ string xp;
+ char alignment[3];
+ Size creatSize;
+ string creatSizeWords;
+ CreatureType creatType;
+ string creatTypeWords;
+ string subtypes;
+ int initiative;
+ string senses;
+ string aura;
+
+ long crAndXp(); //Returns the XP value
+ char* fetchAlignment();
+ string fetchSize();
+ string fetchCreatureType();
+ string fetchSubtypes();
+ int determineInitiative();
+ string fetchSenses();
+ string fetchAura();
+
+
+/******************************************************************************\
+ * DEFENSE *
+\******************************************************************************/
+
+ AC ac;
+ HitDice hd;
+ int hp;
+ string hpBreakdown;
+ Saves saves;
+ string defensiveAbilities;
+ string immunities;
+ string resistances;
+ string weaknesses;
+ string dr;
+ string sr;
+
+ AC determineAC();
+ HitDice fetchHd();
+ int calculateHp();
+ Saves determineSaves();
+ string fetchDefensiveAbilities();
+ string determineImmunities();
+ string fetchResistances();
+ string fetchWeaknesses();
+ string fetchDr();
+ string fetchSr();
+
+
+/******************************************************************************\
+ * OFFENSE *
+\******************************************************************************/
+
+ string speed;
+ string meleeAtk;
+ string rangedAtk;
+ string space;
+ string reach;
+ string specAtk;
+
+ string fetchSpeed();
+ string fetchMeleeAtk();
+ string fetchRangedAtk();
+ string calculateSpace();
+ string determineReach();
+ string fetchSpecialAttacks();
+
+
+/******************************************************************************\
+ * STATISTICS *
+\******************************************************************************/
+
+ Abilities abilities;
+ int bab;
+ Maneuvers maneuvers;
+ string featList;
+ string skillList;
+ string languageList;
+ string specialQualities;
+
+ Abilities fetchAbilities();
+ int calculateBab();
+ Maneuvers determineCombatManeuvers();
+ string fetchFeats();
+ string fetchSkills();
+ string fetchLanguages();
+ string fetchSpecialQualities();
+
+/******************************************************************************\
+ * ECOLOGY *
+\******************************************************************************/
+
+ string environmentList;
+ string groupList;
+ string lootList;
+
+ string fetchEnvironment();
+ string fetchOrganization();
+ string fetchTreasure();
+
+/******************************************************************************\
+ * SPECIAL ABILITIES *
+\******************************************************************************/
+
+ string specialAbilities;
+
+ string fetchSpecialAbilities();
+
+/******************************************************************************\
+ * SYSTEM *
+\******************************************************************************/
+
+ const char* fileName;
+ fstream file;
+
+ PfMon();
+ ~PfMon();
+ string prepareWrite();
+
+
+private:
+
+/******************************************************************************\
+ * HEADER *
+\******************************************************************************/
+
+ string formatXp(long xp);
+ CreatureType determineCreatureType();
+
+/******************************************************************************\
+ * DEFENSE *
+\******************************************************************************/
+
+ string calculateAcBreakdown();
+ string calculateHpBreakdown();
+};
+
+#endif // PFMON_HPP
diff --git a/src/pfsb.cpp b/src/pfsb.cpp
deleted file mode 100644
index 44e1f6d..0000000
--- a/src/pfsb.cpp
+++ /dev/null
@@ -1,2368 +0,0 @@
-#include "platform.hpp"
-#include "version.hpp"
-
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-#include
-#include
-
-#define _ERR_UNRECOGNIZED_CLI_OPTION 1
-#define _ERR_INVALID_SWITCH 2
-#define _ERR_INVALID_SIZE 3
-#define _ERR_INVALID_CREATURE_TYPE 4
-
-/* Why is including cstdlib to call exit necessary now?
- * It's never been necessary before. I could write a program
- * that compiled on both Windows and Linux and nobody ever had
- * an issue with it.
- */
-
-using namespace std;
-
-struct AC {
-
- int armor = 0;
- int shield = 0;
- int dex = 0;
- int natural = 0;
- int dodge = 0;
- int deflection = 0;
- int sacred = 0;
- int profane = 0;
- int insight = 0;
- int luck = 0;
- int racial = 0;
- int size = 0;
- int trait = 0;
- int untyped = 0;
-
- int totalAc = 10;
- int flatFootAc;
- int touchAc;
-
- string breakdown;
-
-};
-
-struct BaseStats{
-
- int str;
- int strMod;
- string displayStr;
-
- int dex;
- int dexMod;
- string displayDex;
-
- int con;
- int conMod;
- string displayCon;
-
- int intelligence;
- int intMod;
- string displayInt;
-
- int wis;
- int wisMod;
- string displayWis;
-
- int cha;
- int chaMod;
- string displayCha;
-};
-
-struct HitDice{
- int d4 = 0;
- int d6 = 0;
- int d8 = 0;
- int d10 = 0;
- int d12 = 0;
-
- int totalHd;
- int bonus = 0; //Intended for flat bonuses, like a construct's size bonus. Bonus HP from CON or CHA (if undead) are calculated as needed.
-
-};
-
-struct Saves{
- bool goodFort = false;
- bool goodReflex = false;
- bool goodWill = false;
-
- int fort;
- int reflex;
- int will;
-
-};
-
-struct Maneuvers{
- bool agileManuevers = false;
-
- int cmb;
- int cmd;
-
- string specialCmb;
- string specialCmd;
-};
-
-enum CreatureType {ABERRATION = 1, ANIMAL, CONSTRUCT, DRAGON, FEY, HUMANOID, MAGICAL_BEAST, MONSTROUS_HUMANOID, OOZE, OUTSIDER, PLANT, UNDEAD, VERMIN};
-
-void printVersion();
-void doProgram();
-long crToXp(double cr, char* displayCr);
-void insertIntoFile(fstream& monster, const char* toInsert, long beforeDollar, int keyLength, const char* baseFileName);
-long findDollar(fstream& monster, long currentPos);
-AC determineAC(const int dex, char size);
-string determineAcBreakdown(AC ac);
-CreatureType determineCreatureType(string creatureType);
-BaseStats determineBaseStats(const CreatureType);
-Saves determineSaves(const HitDice hd, const BaseStats abilities, const CreatureType);
-HitDice determineHD(const CreatureType, char creatSize);
-int determineHP(const HitDice hd, const BaseStats abilities, const CreatureType);
-string determineHpBreakdown(const HitDice hd, const BaseStats abilities, const CreatureType);
-int determineBab(const HitDice hd, const CreatureType);
-int determineInitiative(const int dexMod);
-Maneuvers determineCombatManeuvers(const BaseStats abilities, const int bab);
-string determineMeleeAtk(const int bab, const BaseStats abilities);
-string determineRangedAtk(const int bab, const BaseStats abilities);
-string determineFeats(const HitDice hd);
-string determineEnvironment();
-string determineSpace(const char creatSize);
-string determineReach(const char creatSize);
-string determineSkills(const BaseStats abilities, const HitDice hd, const CreatureType);
-string determineLanguages();
-string determineDefensiveAbilities();
-string determineSpecialQualities();
-string determineSpecialAttacks();
-string determineImmunities(const CreatureType);
-string determineSenses(const BaseStats abilities,const HitDice hd);
-string determineAura();
-string determineOrganization();
-string determineTreasure();
-string determineSpeed();
-string determineSpecAbilities();
-void clearBuffer();
-string stringToLower(string);
-
-
-void findAndReplace(fstream& outfile, const char* replacement, const char* searchKey, const char* fileName);
-
-int main(int argc, char** argv) {
- if (argc == 2 && !strcmp("--version",argv[1])) {
- printVersion();
-
- exit(0);
- }
- if (argc == 2 && !strcmp("--help",argv[1])) {
- cout << "Run the program with no arguments to use it. Use --version to print version info. Use --ogl to print OGL-mandated information about product identity \
-and open game content. Use --help to print this text." << endl;
- cout << "Note: It is strongly advised that no user inputs contain dollar signs."<< endl << endl;
-
- exit(0);
- }
-
- if (argc == 1) {
- doProgram();
-
- #ifdef _DEBUG
- cout << "Exited doProgram()" << endl;
- #endif
-
- exit(0);
- }
-
- if (argc == 2 && !strcmp("--ogl",argv[1])) {
- cout << endl << "Product Identity: The following items are hereby identified as Product Identity, as defined in the Open Game License version 1.0a, Section 1(e), \
-and are not Open Content: All trademarks, registered trademarks, proper names (characters, deities, etc.), dialogue, plots, storylines, locations, characters, artwork, \
-and trade dress. (Elements that have previously been designated as Open Game Content or are in the public domain are not included in this declaration.)" << endl << endl;
-
- cout << "Open Content: Except for material designated as Product Identity (see above), the game mechanics of this game product are Open Game Content, as \
-defined in the Open Gaming License version 1.0a Section 1(d). No portion of this work other than the material designated as Open Game Content may be reproduced in any form \
-without written permission." << endl << endl;
-
- exit(0);
- }
-
-
- cout << "Unrecognized option!" << endl;
-
- exit(_ERR_UNRECOGNIZED_CLI_OPTION);
-
-
- return 0;
-}
-
-void doProgram() {
- ifstream pfTemplate;
- fstream monster;
-
- string tempStorage;
-
- const char* fileName;
- string monName;
- double cr;
- char displayCr[4] = "\0\0\0";
- long xp;
- string displayXp;
- char alignment[3] = "\0\0";
- string creatTypeWords;
- CreatureType creatType;
- string subtypes;
- string creatSizeWords;
- char creatSize;
- int initiative;
- AC ac;
- Saves saves;
- HitDice hd;
- int hp;
- string hpBreakdown;
- int bab;
- Maneuvers maneuvers;
- string meleeAtk;
- string rangedAtk;
- string featList;
- string envList;
- string space;
- string reach;
- string skills;
- string langList;
- string sqList;
- string specAtk;
- string defenseAbilities;
- string immunities;
- string senses;
- string aura;
- string organization;
- string treasure;
- string speed;
- string specAbilities;
-
-
-
- BaseStats abilities;
-
-
-
-/*****************************************************************************\
- * MONSTER *
-\*****************************************************************************/
-
- cout << "Monster Name: ";
- getline(cin, monName);
-
- cout << "CR (decimal 2 places if less than 1): ";
- cin >> cr;
- //TODO Add reprompt for invalid selection
-
- xp = crToXp(cr, displayCr);
-
- displayXp = to_string(xp);
-
-
- clearBuffer();
- cout << "Alignment (two-letter): ";
- cin.getline(alignment,3,'\n');
-
- alignment[0] = toupper(alignment[0]);
- alignment[1] = toupper(alignment[1]);
-
-
- cout << "Size: ";
- //cin.getline(creatSizeWords,1023,'\n');
- getline(cin, creatSizeWords);
- creatSizeWords = stringToLower(creatSizeWords);
- creatSizeWords[0] = toupper(creatSizeWords[0]);
- creatSize = creatSizeWords[0];
-
- cout << "Creature Type: ";
- getline(cin, creatTypeWords);
-
- creatTypeWords = stringToLower(creatTypeWords);
-
- creatType = determineCreatureType(creatTypeWords);
-
-
- cout << "Subtypes (Just hit Enter if none): ";
- getline(cin,subtypes);
-
- aura = determineAura();
-
- /*cout << "Aura (type \"null\" if n/a): ";
- getline(cin,subtypes); //*/
-
-
- abilities = determineBaseStats(creatType);
- ac = determineAC(abilities.dexMod,creatSize);
- hd = determineHD(creatType,creatSize);
- hp = determineHP(hd,abilities,creatType);
- hpBreakdown = determineHpBreakdown(hd,abilities,creatType);
- saves = determineSaves(hd,abilities,creatType);
- bab = determineBab(hd,creatType);
- maneuvers = determineCombatManeuvers(abilities,bab);
- initiative = determineInitiative(abilities.dexMod);
- meleeAtk = determineMeleeAtk(bab,abilities);
- rangedAtk = determineRangedAtk(bab,abilities);
- featList = determineFeats(hd);
- senses = determineSenses(abilities,hd);
- envList = determineEnvironment();
- skills = determineSkills(abilities,hd,creatType);
- space = determineSpace(creatSize);
- reach = determineReach(creatSize);
- langList = determineLanguages();
- sqList = determineSpecialQualities();
- specAtk = determineSpecialAttacks();
- defenseAbilities = determineDefensiveAbilities();
- immunities = determineImmunities(creatType);
- organization = determineOrganization();
- treasure = determineTreasure();
- speed = determineSpeed();
- specAbilities = determineSpecAbilities();
-
-
-/*****************************************************************************\
- * WRITE FILE *
-\*****************************************************************************/
-
- clearBuffer();
-
- pfTemplate.open(_PFRPG_TEMPLATE, ios::in);
-
- fileName = strcat(strcat(strcat(getenv("PWD"),"/"),monName.c_str()),".html");
-
- monster.open(fileName, ios::in | ios::out | ios::trunc);
- //TODO Add file rename scheme similar to wget
-
- monster << pfTemplate.rdbuf();
-
- pfTemplate.close();
-
- findAndReplace(monster,monName.c_str(),u8"MONNAME",fileName);
- #ifdef _DEBUG
- cout << "Monname written" << endl;
- #endif
- tempStorage = "PFSB - ";
- tempStorage += monName;
- findAndReplace(monster,tempStorage.c_str(),u8"PF_MON",fileName);
- #ifdef _DEBUG
- cout << "Monster header written" << endl;
- #endif
- findAndReplace(monster,displayCr,u8"CR",fileName);
- #ifdef _DEBUG
- cout << "CR written" << endl;
- #endif
- findAndReplace(monster,displayXp.c_str(),u8"XP",fileName);
- #ifdef _DEBUG
- cout << "XP written" << endl;
- #endif
- findAndReplace(monster,alignment,u8"ALIGN",fileName);
- #ifdef _DEBUG
- cout << "Alignment written" << endl;
- #endif
- findAndReplace(monster,creatSizeWords.c_str(),u8"SIZE",fileName);
- #ifdef _DEBUG
- cout << "Size written: " << creatSizeWords << endl;
- #endif
- findAndReplace(monster,creatTypeWords.c_str(),u8"CREATTY",fileName);
- findAndReplace(monster,creatTypeWords.c_str(),u8"CREATTY",fileName);
- findAndReplace(monster,subtypes.c_str(),u8"SUBTYPE",fileName);
-
- findAndReplace(monster,abilities.displayStr.c_str(),u8"STR",fileName);
- findAndReplace(monster,abilities.displayDex.c_str(),u8"DEX",fileName);
- findAndReplace(monster,abilities.displayCon.c_str(),u8"CON",fileName);
- findAndReplace(monster,abilities.displayInt.c_str(),u8"INT",fileName);
- findAndReplace(monster,abilities.displayWis.c_str(),u8"WIS",fileName);
- findAndReplace(monster,abilities.displayCha.c_str(),u8"CHA",fileName);//*/
-
- /*#ifdef _DEBUG
- cout << "Before insert" << endl;
- cout << "Armor bonus (0 if n/a): " << to_string(ac.armor) << endl;
- cout << "Shield bonus: " << to_string(ac.shield) << endl;
- cout << "Natural armor: " << to_string(ac.natural) << endl;
- cout << "Deflection bonus: " << to_string(ac.deflection) << endl;
- cout << "Dodge bonus: " << to_string(ac.dodge) << endl;
- cout << "Sacred: " << to_string(ac.sacred) << endl;
- cout << "Profane: " << to_string(ac.profane) << endl;
- cout << "Insight: " << to_string(ac.insight) << endl;
- cout << "Luck: " << to_string(ac.luck) << endl;
- cout << "Racial: " << to_string(ac.racial) << endl;
- cout << "Untyped: " << to_string(ac.untyped) << endl;
-
- cout << "Dex bonus: " << to_string(ac.dex) << endl;
- cout << "Size bonus: " << to_string(ac.size) << endl;
-
- cout << "Total AC: " << to_string(ac.totalAc) << endl;
- cout << "Flat-footed AC: " << to_string(ac.flatFootAc) << endl;
- cout << "Touch AC: " << to_string(ac.touchAc) << endl;
-
- cout << "AC Breakdown: " << ac.breakdown << endl;
- #endif*/
-
- findAndReplace(monster,to_string(ac.totalAc).c_str(),u8"REGAC",fileName);
- tempStorage = "touch ";
- findAndReplace(monster,(tempStorage + to_string(ac.touchAc)).c_str(),u8"TOUCHAC",fileName);
- tempStorage = "flat-footed ";
- findAndReplace(monster,(tempStorage + to_string(ac.flatFootAc)).c_str(),u8"FLATFOO",fileName);
- findAndReplace(monster,ac.breakdown.c_str(),u8"ACBREAK",fileName);
-
- findAndReplace(monster,to_string(hp).c_str(),u8"HP",fileName);
- findAndReplace(monster,hpBreakdown.c_str(),u8"HPBREAK",fileName);
-
- findAndReplace(monster,to_string(saves.fort).c_str(),u8"FORTSAV",fileName);
- findAndReplace(monster,to_string(saves.reflex).c_str(),u8"REFSAVE",fileName);
- findAndReplace(monster,to_string(saves.will).c_str(),u8"WILLSAV",fileName);
-
- findAndReplace(monster,to_string(bab).c_str(),u8"BAB",fileName);
- findAndReplace(monster,to_string(initiative).c_str(),u8"INIT",fileName);
-
- findAndReplace(monster,to_string(maneuvers.cmb).c_str(),u8"CMB",fileName);
- findAndReplace(monster,to_string(maneuvers.cmd).c_str(),u8"CMD",fileName);
- findAndReplace(monster,maneuvers.specialCmb.c_str(),u8"SPECCMB",fileName);
- findAndReplace(monster,maneuvers.specialCmd.c_str(),u8"SPECCMD",fileName);
-
- findAndReplace(monster,meleeAtk.c_str(),u8"MELEE",fileName);
- findAndReplace(monster,rangedAtk.c_str(),u8"RANGED",fileName);
- findAndReplace(monster,specAtk.c_str(),u8"SPECATK",fileName);
-
- findAndReplace(monster,space.c_str(),u8"SPACE",fileName);
- findAndReplace(monster,reach.c_str(),u8"REACH",fileName);
-
- findAndReplace(monster,featList.c_str(),u8"FEATLIS",fileName);
- findAndReplace(monster,skills.c_str(),u8"SKILIST",fileName);
- findAndReplace(monster,envList.c_str(),u8"ENVLIST",fileName);
- findAndReplace(monster,organization.c_str(),u8"GRPLIST",fileName);
-
- findAndReplace(monster,langList.c_str(),u8"LANGLIS",fileName);
- findAndReplace(monster,sqList.c_str(),u8"SQLIST",fileName);
-
- findAndReplace(monster,defenseAbilities.c_str(),u8"DEFABLE",fileName);
- findAndReplace(monster,immunities.c_str(),u8"IMMUNE",fileName);
-
- findAndReplace(monster,senses.c_str(),u8"SENSES",fileName);
- findAndReplace(monster,aura.c_str(),u8"AURA",fileName);
-
- findAndReplace(monster,treasure.c_str(),u8"LOOTLST",fileName);
- findAndReplace(monster,speed.c_str(),u8"SPEED",fileName);
-
- findAndReplace(monster,specAbilities.c_str(),u8"SPECABL",fileName);
-
-
-
- monster.close();
-
- #ifdef _DEBUG
- cout << "Monster closed" << endl;
- #endif
-
-
- //cout << monName << endl << cr << endl << displayCr << endl << xp << endl << creatSizeWords << endl << creatSize << endl << creatType << endl << subtypes << endl;
-
-}
-
-string determineSpecAbilities() {
-
- string specAbilities;
-
- cout << "List special abilities here: " << endl;
- getline(cin,specAbilities);
-
- return specAbilities;
-}
-
-string determineTreasure() {
-
- string treasure;
-
- cout << "List treasure here: " << endl;
- getline(cin,treasure);
-
- return treasure;
-}
-
-string determineSpeed() {
-
- string speed;
-
- cout << "List speed here (including alt speeds, like fly or swim): " << endl;
- getline(cin,speed);
-
- return speed;
-
-}
-
-string determineMeleeAtk(const int bab, const BaseStats abilities) {
-
- int strAtkBonus = bab + abilities.strMod;
- int finesseAtkBonus = bab + abilities.dexMod;
-
- string meleeAtk;
-
- cout << "Type what you want to appear next to the Melee Attack line." << endl;
- cout << "The STR-based attack bonus is " << strAtkBonus << ", and the DEX-based attack bonus (if applicable) is " << finesseAtkBonus << endl;
-
- getline(cin,meleeAtk);
-
- return meleeAtk;
-}
-
-string determineRangedAtk(const int bab, const BaseStats abilities) {
-
- int thrownAtkBonus = bab + abilities.strMod;
- int bowAtkBonus = bab + abilities.dexMod;
-
- string rangedAtk;
-
- cout << "Type what you want to appear next to the Ranged Attack line." << endl;
- cout << "The STR-based attack bonus is " << thrownAtkBonus << ", and the DEX-based attack bonus is " << bowAtkBonus << endl;
-
- getline(cin,rangedAtk);
-
- return rangedAtk;
-}
-
-string featList(const HitDice hd) {
-
- int hdFeats = (1 + hd.totalHd) / 2;
- string feats;
-
- cout << "List the feats here. Based on HD, you should have " << hdFeats << " feats.";
- getline(cin,feats);
-
- return feats;
-}
-
-string determineEnvironment() {
-
- string environments;
-
- cout << "List the monster's environments here: ";
- getline(cin,environments);
-
- return environments;
-}
-
-string determineSkills(const BaseStats abilities, const HitDice hd, const CreatureType creatType) {
-
- string skills;
- int maxSkillPoints = INT_MIN;
-
- switch (creatType) {
- case CreatureType::ABERRATION: {
- maxSkillPoints = (4 + abilities.intMod) * hd.totalHd;
- break;
- }
- case CreatureType::ANIMAL: {
- maxSkillPoints = (2 + abilities.intMod) * hd.totalHd;
- break;
- }
- case CreatureType::CONSTRUCT: {
- maxSkillPoints = (2 + abilities.intMod) * hd.totalHd;
- break;
- }
- case CreatureType::DRAGON: {
- maxSkillPoints = (6 + abilities.intMod) * hd.totalHd;
- break;
- }
- case CreatureType::FEY: {
- maxSkillPoints = (6 + abilities.intMod) * hd.totalHd;
- break;
- }
- case CreatureType::HUMANOID: {
- maxSkillPoints = (2 + abilities.intMod) * hd.totalHd;
- break;
- }
- case CreatureType::MAGICAL_BEAST: {
- maxSkillPoints = (2 + abilities.intMod) * hd.totalHd;
- break;
- }
- case CreatureType::MONSTROUS_HUMANOID: {
- maxSkillPoints = (4 + abilities.intMod) * hd.totalHd;
- break;
- }
- case CreatureType::OOZE: {
- maxSkillPoints = (2 + abilities.intMod) * hd.totalHd;
- break;
- }
- case CreatureType::OUTSIDER: {
- maxSkillPoints = (6 + abilities.intMod) * hd.totalHd;
- break;
- }
- case CreatureType::PLANT: {
- maxSkillPoints = (2 + abilities.intMod) * hd.totalHd;
- break;
- }
- case CreatureType::UNDEAD: {
- maxSkillPoints = (4 + abilities.intMod) * hd.totalHd;
- break;
- }
- case CreatureType::VERMIN: {
- maxSkillPoints = (2 + abilities.intMod) * hd.totalHd;
- break;
- }
- default: {
- cout << "Homebrew creature types will come later. Exiting program." << endl;
- exit(_ERR_INVALID_CREATURE_TYPE);
- }
-
- }
-
- int strSkills = 3 + abilities.strMod + hd.totalHd;
- int dexSkills = 3 + abilities.dexMod + hd.totalHd;
- int conSkills = 3 + abilities.conMod + hd.totalHd;
- int intSkills = 3 + abilities.intMod + hd.totalHd;
- int wisSkills = 3 + abilities.wisMod + hd.totalHd;
- int chaSkills = 3 + abilities.chaMod + hd.totalHd;
-
- cout << "Enter the creature's skills. The highest possible will be listed below, assuming that the creature gains that skill as a class skill." << endl;
- cout << "\tSTR: " << strSkills << endl;
- cout << "\tDEX: " << dexSkills << endl;
- cout << "\tCON: " << conSkills << endl;
- cout << "\tINT: " << intSkills << endl;
- cout << "\tWIS: " << wisSkills << endl;
- cout << "\tCHA: " << chaSkills << endl;
-
- cout << "Total skill ranks available (not yet enforced by program logic): " << maxSkillPoints << endl;
-
- getline(cin, skills);
-
- return skills;
-}
-
-string determineSpace(const char creatSize) {
-
- string space;
-
- switch (creatSize) {
- case 'f':
- case 'F': {
- space = "1/2 ft.";
- break;
- }
- case 'd':
- case 'D': {
- space = "1 ft.";
- break;
- }
- case 't':
- case 'T': {
- space = "2-1/2 ft.";
- break;
- }
- case 's':
- case 'S': {
- space = "5 ft.";
- break;
- }
- case 'm':
- case 'M': {
- space = "5 ft.";
- break;
- }
- case 'l':
- case 'L': {
- space = "10 ft.";
- break;
- }
- case 'h':
- case 'H': {
- space = "15 ft.";
- break;
- }
- case 'g':
- case 'G': {
- space = "20 ft.";
- break;
- }
- case 'c':
- case 'C': {
- space = "30 ft.";
- break;
- }
- default: {
- cout << "Invalid creature size found, exiting program" << endl;
- exit(_ERR_INVALID_SIZE);
- }
- }
-
- return space;
-}
-
-string determineSpecialQualities() {
-
- string sq;
-
- cout << "List the monster's special qualities here: ";
- getline(cin,sq);
-
- return sq;
-
-}
-
-string determineSpecialAttacks() {
-
- string specAtk;
-
- cout << "List the monster's special attacks here: ";
- getline(cin,specAtk);
-
- return specAtk;
-}
-
-string determineDefensiveAbilities() {
-
- string defAbilities;
-
- cout << "List the monster's defensive abilities here: ";
- getline(cin,defAbilities);
-
- return defAbilities;
-}
-
-string determineImmunities(const CreatureType creatType) {
-
- string specAtk;
- string typeImmunities;
-
- switch (creatType) {
- case CreatureType::CONSTRUCT: {
- typeImmunities = "construct traits, ";
- break;
- }
- case CreatureType::PLANT: {
- typeImmunities = "plant traits, ";
- break;
- }
- case CreatureType::UNDEAD: {
- typeImmunities = "undead traits, ";
- break;
- }
- default: {
- //doNothing();
- }
- }
-
- cout << "List the monster's immunities here: ";
- getline(cin,specAtk);
-
- return specAtk;
-}
-
-string determineLanguages() {
-
- string languages;
-
- clearBuffer();
- cout << "List the monster's languages here: ";
- getline(cin,languages);
-
- return languages;
-}
-
-string determineSenses(const BaseStats abilities, const HitDice hd) {
-
- string senses;
- int maxPerception = 3 + abilities.wisMod + hd.totalHd;
-
- cout << "Type what should appear next to the Senses line in the general info section. The max Perception possible, with only Wis mod + skill ranks + class skill bonus is " << maxPerception << ".";
- getline(cin,senses);
-
- return senses;
-}
-
-string determineAura() {
-
- string aura;
-
- cout << "List the monster's aura/auras here: ";
- getline(cin,aura);
-
- return aura;
-}
-
-string determineOrganization() {
-
- string organization;
-
- cout << "List the monster's grouping habits here: ";
- getline(cin,organization);
-
- return organization;
-}
-
-string determineReach(const char creatSize) {
-
- string isTall;
- string reach;
-
- switch (creatSize) {
- case 'f':
- case 'F': {
- reach = "0 ft.";
- break;
- }
- case 'd':
- case 'D': {
- reach = "0 ft.";
- break;
- }
- case 't':
- case 'T': {
- reach = "0 ft.";
- break;
- }
- case 's':
- case 'S': {
- reach = "5 ft.";
- break;
- }
- case 'm':
- case 'M': {
- reach = "5 ft.";
- break;
- }
- case 'l':
- case 'L': {
- cout << "Is the creature tall or long? ";
- cin >> isTall;
-
- if (tolower(isTall[0]) == 't') {
- reach = "10 ft.";
- } else {
- reach = "5 ft.";
- }
- break;
- }
- case 'h':
- case 'H': {
- cout << "Is the creature tall or long?";
- cin >> isTall;
-
- if (tolower(isTall[0]) == 't') {
- reach = "15 ft.";
- } else {
- reach = "10 ft.";
- }
- break;
- }
- case 'g':
- case 'G': {
- cout << "Is the creature tall or long?";
- cin >> isTall;
-
- if (tolower(isTall[0]) == 't') {
- reach = "20 ft.";
- } else {
- reach = "15 ft.";
- }
- break;
- }
- case 'c':
- case 'C': {
- cout << "Is the creature tall or long?";
- cin >> isTall;
-
- if (tolower(isTall[0]) == 't') {
- reach = "30 ft.";
- } else {
- reach = "20 ft.";
- }
- break;
- }
- default: {
- cout << "Invalid creature size found, exiting program" << endl;
- exit(_ERR_INVALID_SIZE);
- }
- }
-
- return reach;
-}
-
-string determineFeats(const HitDice hd) {
-
- string feats;
- int featCount = (hd.totalHd + 1 ) / 2;
-
- cout << "By hit dice, the creature should have " << featCount << " feats. Mindless creatures, like oozes, may not have feats.";
-
- cout << "List the monster's feats here (nothing here is autofilled or used in any calculations): ";
- getline(cin,feats);
-
- return feats;
-}
-
-Maneuvers determineCombatManeuvers(const BaseStats abilities, const int bab) {
-
- Maneuvers maneuvers;
- char choice;
-
- maneuvers.cmb = bab;
- maneuvers.cmd = 10 + bab + abilities.strMod + abilities.dexMod;
-
- cout << "Agile Maneuvers feat? (yes/no)" << endl;
- cin >> choice;
-
- clearBuffer();
-
- cout << "Note: Feat-based calculations will be in a later build. In the feats line, you will have to re-indicate that the Agile Maneuvers feat was taken." << endl;
-
- if (choice == 'y' || choice == 'Y') {
- maneuvers.agileManuevers = true;
- }
-
- if ((!(maneuvers.agileManuevers)) || (abilities.strMod > abilities.dexMod)) {
- maneuvers.cmb += abilities.strMod;
- } else {
- maneuvers.cmb += abilities.dexMod;
- }
-
- cout << "Special CMB? (current normal CMB is " << maneuvers.cmb << ')' << endl;
- getline(cin,maneuvers.specialCmb);
-
- cout << "Special CMD? (current normal CMD is " << maneuvers.cmd << ')' << endl;
- getline(cin, maneuvers.specialCmd);
-
- clearBuffer();
-
- return maneuvers;
-}
-
-int determineInitiative(const int dexMod) {
-
- int initBonus = dexMod;
- char choice;
-
- cout << "Improved initiative feat? (yes/no)" << endl;
- cin >> choice;
-
- clearBuffer();
-
- cout << "Note: Feat-based calculations will be in a later build. In the feats line, you will have to re-indicate that the Improved Initiative feat was taken." << endl;
-
- if (choice == 'y' || choice == 'Y') {
- initBonus += 4;
- }
-
- return initBonus;
-
-}
-
-int determineBab(const HitDice hd, const CreatureType creatType) {
- int bab;
-
- switch (creatType) {
- case CreatureType::ABERRATION: {
- bab = static_cast(3.0 / 4 * hd.totalHd);
- break;
- }
- case CreatureType::ANIMAL: {
- bab = static_cast(3.0 / 4 * hd.totalHd);
- break;
- }
- case CreatureType::CONSTRUCT: {
- bab = hd.totalHd;
- break;
- }
- case CreatureType::DRAGON: {
- bab = hd.totalHd;
- break;
- }
- case CreatureType::FEY: {
- bab = static_cast(1.0 / 4 * hd.totalHd);
- break;
- }
- case CreatureType::HUMANOID: {
- bab = static_cast(3.0 / 4 * hd.totalHd);
- break;
- }
- case CreatureType::MAGICAL_BEAST: {
- bab = hd.totalHd;
- break;
- }
- case CreatureType::MONSTROUS_HUMANOID: {
- bab = hd.totalHd;
- break;
- }
- case CreatureType::OOZE: {
- bab = static_cast(3.0 / 4 * hd.totalHd);
- break;
- }
- case CreatureType::OUTSIDER: {
- bab = hd.totalHd;
- break;
- }
- case CreatureType::PLANT: {
- bab = static_cast(3.0 / 4 * hd.totalHd);
- break;
- }
- case CreatureType::UNDEAD: {
- bab = static_cast(3.0 / 4 * hd.totalHd);
- break;
- }
- case CreatureType::VERMIN: {
- bab = static_cast(3.0 / 4 * hd.totalHd);
- break;
- }
- default: {
- cout << "Homebrew creature types will come later. Exiting program." << endl;
- exit(_ERR_INVALID_CREATURE_TYPE);
- }
-
- }
-
- return bab;
-}
-
-string determineHpBreakdown(const HitDice hd, const BaseStats abilities, const CreatureType creatType) {
-
- int hdTypes = 0;
- string hdBreakdown;
-
-
- if (hd.d4) {
- hdTypes += 1;
- }
- if (hd.d6) {
- hdTypes += 1;
- }
- if (hd.d8) {
- hdTypes += 1;
- }
- if (hd.d10) {
- hdTypes += 1;
- }
- if (hd.d12) {
- hdTypes += 1;
- }
-
- if (hdTypes > 1) {
- hdBreakdown = to_string(hd.totalHd);
- hdBreakdown += "HD; ";
- }
-
-
- if (hd.d4) {
- hdBreakdown += to_string(hd.d4) + "d4 + ";
- }
- if (hd.d6) {
- hdBreakdown += to_string(hd.d6) + "d6 + ";
- }
- if (hd.d8) {
- hdBreakdown += to_string(hd.d8) + "d8 + ";
- }
- if (hd.d10) {
- hdBreakdown += to_string(hd.d10) + "d10 + ";
- }
- if (hd.d12) {
- hdBreakdown += to_string(hd.d12) + "d12 + ";
- }
-
- if (creatType == CreatureType::UNDEAD) {
- hdBreakdown += to_string(abilities.chaMod * hd.totalHd + hd.bonus);
- } else if (creatType == CreatureType::CONSTRUCT) {
- hdBreakdown += to_string(hd.bonus);
- } else {
- hdBreakdown += to_string(abilities.conMod * hd.totalHd + hd.bonus);
- }
-
- return hdBreakdown;
-}
-
-int determineHP(const HitDice hd, const BaseStats abilities, const CreatureType creatType) {
-
- double hdAverage = 0.0;
- int hp = 0;
-
- /* Note that even when monsters have multiple HD sources and two or more
- * sources of hit dice that average to ?.5 each, each source of HD is
- * rounded down before being added. For proof, math out the Serpentfolk
- * Bone Prophet on MC204.
- */
-
- /*#ifdef _DEBUG
- hdAverage += floor(2.5 * hd.d4);
- cout << "HD average (after d4)" << hdAverage << endl;
- hdAverage += floor(3.5 * hd.d6);
- cout << "HD average (after d6)" << hdAverage << endl;
- hdAverage += floor(4.5 * hd.d8);
- cout << "HD average (after d8)" << hdAverage << endl;
- hdAverage += floor(5.5 * hd.d10);
- cout << "HD average (after d10)" << hdAverage << endl;
- hdAverage += floor(6.5 * hd.d12);
- cout << "HD average (after d12)" << hdAverage << endl;
- #endif */
-
-
- hp = hd.bonus + static_cast(hdAverage) + (abilities.conMod * hd.totalHd);
-
- if (creatType == CreatureType::UNDEAD) {
- hp += abilities.chaMod * hd.totalHd;
- } else if (creatType != CreatureType::CONSTRUCT) {
- hp += abilities.conMod * hd.totalHd;
- }
-
- /*#ifdef _DEBUG
- cout << endl << "determineHP()" << endl << endl;
- cout << "HP total: " << hp << endl;
- cout << "HD average: " << hdAverage << endl;
- cout << "HD average (casted to int): " << static_cast(hdAverage) << endl;
- cout << "Con mod: " << abilities.conMod << endl;
- cout << "Con bonus HP: " << (abilities.conMod * hd.totalHd) << endl;
- cout << "Special bonus HP: " << hd.bonus << endl;
- cout << "Total HD: " << hd.totalHd << endl;
- cout << "Total d4: " << hd.d4 << endl;
- cout << "Total d6: " << hd.d6 << endl;
- cout << "Total d8: " << hd.d8 << endl;
- cout << "Total d10: " << hd.d10 << endl;
- cout << "Total d12: " << hd.d12 << endl;
- #endif*/
-
- return hp;
-}
-
-HitDice determineHD(const CreatureType creatType, const char creatSize) {
-
- HitDice hd;
- int hdCount;
-
- clearBuffer();
-
- cout << "How many racial HD does this creature have? ";
- cin >> hdCount;
- hd.totalHd = hdCount;
-
- cout << "Class levels for monsters will come in a later version." << endl;
-
- switch (creatType) {
- case CreatureType::ABERRATION: {
- hd.d8 = hdCount;
- break;
- }
- case CreatureType::ANIMAL: {
- hd.d8 = hdCount;
- break;
- }
- case CreatureType::CONSTRUCT: {
- hd.d10 = hdCount;
- switch (creatSize) {
- case 's':
- case 'S': {
- hd.bonus = 10;
- break;
- }
- case 'm':
- case 'M': {
- hd.bonus = 20;
- break;
- }
- case 'l':
- case 'L': {
- hd.bonus = 30;
- break;
- }
- case 'h':
- case 'H': {
- hd.bonus = 40;
- break;
- }
- case 'g':
- case 'G': {
- hd.bonus = 60;
- break;
- }
- case 'c':
- case 'C': {
- hd.bonus = 80;
- break;
- }
- default: {
- cout << "Invalid creature size found, exiting program" << endl;
- exit(_ERR_INVALID_SIZE);
- }
- }
- break;
- }
- case CreatureType::DRAGON: {
- hd.d12 = hdCount;
- break;
- }
- case CreatureType::FEY: {
- hd.d6 = hdCount;
- break;
- }
- case CreatureType::HUMANOID: {
- hd.d8 = hdCount;
- break;
- }
- case CreatureType::MAGICAL_BEAST: {
- hd.d10 = hdCount;
- break;
- }
- case CreatureType::MONSTROUS_HUMANOID: {
- hd.d10 = hdCount;
- break;
- }
- case CreatureType::OOZE: {
- hd.d8 = hdCount;
- break;
- }
- case CreatureType::OUTSIDER: {
- hd.d10 = hdCount;
- break;
- }
- case CreatureType::PLANT: {
- hd.d8 = hdCount;
- break;
- }
- case CreatureType::UNDEAD: {
- hd.d8 = hdCount;
- break;
- }
- case CreatureType::VERMIN: {
- hd.d8 = hdCount;
- break;
- }
- default: {
- cout << "Homebrew creature types will come later. Exiting program." << endl;
- exit(_ERR_INVALID_CREATURE_TYPE);
- }
-
- }
-
- /*#ifdef _DEBUG
- cout << "determineHD()" << endl;
- cout << "Total HD: " << hd.totalHd << endl;
- cout << "Total d4: " << hd.d4 << endl;
- cout << "Total d6: " << hd.d6 << endl;
- cout << "Total d8: " << hd.d8 << endl;
- cout << "Total d10: " << hd.d10 << endl;
- cout << "Total d12: " << hd.d12 << endl;
- #endif*/
-
- return hd;
-
-}
-
-Saves determineSaves(const HitDice hd, const BaseStats abilities, const CreatureType creatType) {
-
- Saves saves;
- char choice;
-
- switch (creatType) {
- case CreatureType::ABERRATION: {
- saves.goodWill = true;
- break;
- }
- case CreatureType::ANIMAL: {
- saves.goodFort = true;
- saves.goodReflex = true;
- break;
- }
- case CreatureType::CONSTRUCT: {
- break;
- }
- case CreatureType::DRAGON: {
- saves.goodFort = true;
- saves.goodReflex = true;
- saves.goodWill = true;
- break;
- }
- case CreatureType::FEY: {
- saves.goodFort = true;
- break;
- }
- case CreatureType::HUMANOID: {
- cout << "Humanoid creatures get 1 good save." << endl;
- cout << "\t[1] Fortitude" << endl;
- cout << "\t[2] Reflex" << endl;
- cout << "\t[3] Will" << endl;
-
- cin >> choice;
-
- switch(choice) {
- case '1': {
- saves.goodFort = true;
- break;
- }
- case '2': {
- saves.goodReflex = true;
- break;
- }
- case '3': {
- saves.goodWill = true;
- break;
- }
- default: {
- cout << "I have no idea what you just typed. Defaulting to all poor saves." << endl;
- }
- }
- break;
- }
- case CreatureType::MAGICAL_BEAST: {
- saves.goodFort = true;
- saves.goodReflex = true;
- break;
- }
- case CreatureType::MONSTROUS_HUMANOID: {
- saves.goodReflex = true;
- saves.goodWill = true;
- break;
- }
- case CreatureType::OOZE: {
- break;
- }
- case CreatureType::OUTSIDER: {
- cout << "Outsider creatures get 2 good saves." << endl;
- cout << "\t[1] Fortitude + Reflex" << endl;
- cout << "\t[2] Fortitude + Will" << endl;
- cout << "\t[3] Reflex + Will" << endl;
-
- cin >> choice;
-
- switch(choice) {
- case '1': {
- saves.goodFort = true;
- saves.goodReflex = true;
- break;
- }
- case '2': {
- saves.goodFort = true;
- saves.goodWill = true;
- break;
- }
- case '3': {
- saves.goodReflex = true;
- saves.goodWill = true;
- break;
- }
- default: {
- cout << "I have no idea what you just typed. Defaulting to all poor saves." << endl;
- }
- }
- break;
- }
- case CreatureType::PLANT: {
- saves.goodFort = true;
- break;
- }
- case CreatureType::UNDEAD: {
- saves.goodWill = true;
- break;
- }
- case CreatureType::VERMIN: {
- saves.goodFort = true;
- break;
- }
- default: {
- cout << "Homebrew creature types will come later. Exiting program." << endl;
- exit(_ERR_INVALID_CREATURE_TYPE);
- }
-
- }
-
- if (saves.goodFort) {
- saves.fort = static_cast(0.5 * hd.totalHd) + 2;
- //cout << "Base Fort: " << saves.fort << endl;
- } else {
- saves.fort = static_cast((1.0 / 3) * hd.totalHd);
- //cout << "Base Fort: " << saves.fort << endl;
- }
-
- if (saves.goodReflex) {
- saves.reflex = static_cast(0.5 * hd.totalHd) + 2;
- //cout << "Base Ref: " << saves.reflex << endl;
- } else {
- saves.reflex = static_cast((1.0 / 3) * hd.totalHd);
- //cout << "Base Ref: " << saves.reflex << endl;
- }
-
- if (saves.goodWill) {
- saves.will = static_cast(0.5 * hd.totalHd) + 2;
- //cout << "Base Will: " << saves.will << endl;
- } else {
- saves.will = static_cast((1.0 / 3) * hd.totalHd);
- //cout << "Base Will: " << saves.will << endl;
- }
-
- saves.fort += abilities.conMod;
- saves.reflex += abilities.dexMod;
- saves.will += abilities.wisMod;
-
- #ifdef _DEBUG
- cout << "Good fort? " << saves.goodFort << endl;
- cout << "Good reflex? " << saves.goodReflex << endl;
- cout << "Good will? " << saves.goodWill << endl;
-
- cout << "CON mod: " << abilities.conMod << endl;
- cout << "DEX mod: " << abilities.dexMod << endl;
- cout << "WIS mod: " << abilities.wisMod << endl;
- #endif
-
- return saves;
-}
-
-CreatureType determineCreatureType(string creatType) {
-
- if (creatType == "aberration" || creatType == "abberration" || creatType == "abberation") {
- return CreatureType::ABERRATION;
- }
- if (creatType == "animal") {
- return CreatureType::ANIMAL;
- }
- if (creatType == "construct") {
- return CreatureType::CONSTRUCT;
- }
- if (creatType == "dragon") {
- return CreatureType::DRAGON;
- }
- if (creatType == "fey") {
- return CreatureType::FEY;
- }
- if (creatType == "humanoid") {
- return CreatureType::HUMANOID;
- }
- if (creatType == "magical beast") {
- return CreatureType::MAGICAL_BEAST;
- }
- if (creatType == "monstrous humanoid") {
- return CreatureType::MONSTROUS_HUMANOID;
- }
- if (creatType == "ooze") {
- return CreatureType::OOZE;
- }
- if (creatType == "outsider") {
- return CreatureType::OUTSIDER;
- }
- if (creatType == "plant") {
- return CreatureType::PLANT;
- }
- if (creatType == "vermin") {
- return CreatureType::VERMIN;
- }
- if (creatType == "undead") {
- return CreatureType::UNDEAD;
- }
-
- cout << "Creature type not known." << endl;
- cout << "Ability to use homebrew creature types will be in a future version of this program. Exiting program." << endl;
- exit(0);
-
- return static_cast(NULL);
-}
-
-BaseStats determineBaseStats(const CreatureType creatType) {
-
- BaseStats abilities;
-
- cout << "Give the scores, not the modifiers." << endl;
-
- cout << "STR: ";
- cin >> abilities.str;
- abilities.strMod = (abilities.str - 10) / 2;
- abilities.displayStr = to_string(abilities.str);
-
-
- clearBuffer();
- cout << "DEX: ";
- cin >> abilities.dex;
- abilities.dexMod = (abilities.dex - 10) / 2;
- abilities.displayDex = to_string(abilities.dex);
-
- if ((creatType != CreatureType::UNDEAD) && (creatType != CreatureType::CONSTRUCT)) {
- cout << "CON: ";
- cin >> abilities.con;
- abilities.conMod = (abilities.con - 10) / 2;
- abilities.displayCon = to_string(abilities.con);
- /*cout << "determineBaseStats CON score (assign): " << abilities.con << endl;
- cout << "determineBaseStats CON mod: (assign)" << abilities.conMod << endl;*/
- } else {
- abilities.con = INT_MIN;
- abilities.displayCon = "—"; //Em-dash (U+2014) for extra width
- }
-
-
- if ((creatType != CreatureType::CONSTRUCT) && (creatType != CreatureType::OOZE)) {
- clearBuffer();
- cout << "INT: ";
- cin >> abilities.intelligence;
- abilities.intMod = (abilities.intelligence - 10) / 2;
- abilities.displayInt = to_string(abilities.intelligence);
- } else {
- abilities.intelligence = INT_MIN;
- abilities.intMod = INT_MIN;
- abilities.displayInt = "—"; //Em-dash (U+2014) for extra width
- }
-
- clearBuffer();
- cout << "WIS: ";
- cin >> abilities.wis;
- abilities.wisMod = (abilities.wis - 10) / 2;
- abilities.displayWis = to_string(abilities.wis);
-
- clearBuffer();
- cout << "CHA: ";
- cin >> abilities.cha;
- abilities.chaMod = (abilities.cha - 10) / 2;
- abilities.displayCha = to_string(abilities.cha);
-
-
- if (creatType == CreatureType::UNDEAD) {
- abilities.conMod = abilities.chaMod;
- }
- if (creatType == CreatureType::CONSTRUCT) {
- abilities.conMod = 0;
- }
-
-
- return abilities;
-}
-
-AC determineAC(const int dexMod, char size) {
-
- AC ac;
-
- ac.totalAc = 10;
-
- clearBuffer();
- cout << "Indicate total bonuses here. For example, if your creature has +5 Plate, give 14 as the armor bonus." << endl;
-
- cout << "Armor bonus (0 if n/a): ";
- cin >> ac.armor;
-
- clearBuffer();
- cout << "Shield bonus: ";
- cin >> ac.shield;
-
- clearBuffer();
- cout << "Natural armor: ";
- cin >> ac.natural;
-
- clearBuffer();
- cout << "Deflection bonus: ";
- cin >> ac.deflection;
-
- clearBuffer();
- cout << "Dodge bonus: ";
- cin >> ac.dodge;
-
- clearBuffer();
- cout << "Sacred: ";
- cin >> ac.sacred;
-
- clearBuffer();
- cout << "Profane: ";
- cin >> ac.profane;
-
- clearBuffer();
- cout << "Insight: ";
- cin >> ac.insight;
-
- clearBuffer();
- cout << "Luck: ";
- cin >> ac.luck;
-
- clearBuffer();
- cout << "Racial: ";
- cin >> ac.racial;
-
- clearBuffer();//
- cout << "Untyped: ";
- cin >> ac.untyped;
-
- ac.dex = dexMod;
-
- switch (size) {
- case 'f':
- case 'F': {
- ac.size = 8;
- break;
- }
- case 'd':
- case 'D': {
- ac.size = 4;
- break;
- }
- case 't':
- case 'T': {
- ac.size = 2;
- break;
- }
- case 's':
- case 'S': {
- ac.size = 1;
- break;
- }
- case 'm':
- case 'M': {
- ac.size = 0;
- break;
- }
- case 'l':
- case 'L': {
- ac.size = -1;
- break;
- }
- case 'h':
- case 'H': {
- ac.size = -2;
- break;
- }
- case 'g':
- case 'G': {
- ac.size = -4;
- break;
- }
- case 'c':
- case 'C': {
- ac.size = -8;
- break;
- }
- default: {
- cout << "Invalid creature size, exiting program." << endl;
- exit(_ERR_INVALID_SIZE);
- }
-
- }
-
- /*#ifdef _DEBUG
- cout << "Before calculation" << endl;
- cout << "Armor bonus (0 if n/a): " << to_string(ac.armor) << endl;
- cout << "Shield bonus: " << to_string(ac.shield) << endl;
- cout << "Natural armor: " << to_string(ac.natural) << endl;
- cout << "Deflection bonus: " << to_string(ac.deflection) << endl;
- cout << "Dodge bonus: " << to_string(ac.dodge) << endl;
- cout << "Sacred: " << to_string(ac.sacred) << endl;
- cout << "Profane: " << to_string(ac.profane) << endl;
- cout << "Insight: " << to_string(ac.insight) << endl;
- cout << "Luck: " << to_string(ac.luck) << endl;
- cout << "Racial: " << to_string(ac.racial) << endl;
- cout << "Untyped: " << to_string(ac.untyped) << endl;
-
- cout << "Dex bonus: " << to_string(ac.dex) << endl;
- cout << "Size bonus: " << to_string(ac.size) << endl;
-
- cout << "Total AC: " << to_string(ac.totalAc) << endl;
- cout << "Flat-footed AC: " << to_string(ac.flatFootAc) << endl;
- cout << "Touch AC: " << to_string(ac.touchAc) << endl;
-
- cout << "AC Breakdown: " << ac.breakdown << endl;
- #endif*/
-
- ac.totalAc += (ac.armor + ac.deflection + ac.dex + ac.dodge + ac.insight + ac.luck + ac.natural + ac.profane + ac.racial + ac.sacred + ac.shield + ac.size + ac.trait + ac.untyped);
-
- ac.touchAc = ac.totalAc - (ac.armor + ac.shield + ac.natural);
-
- ac.flatFootAc = ac.totalAc - (ac.dex + ac.dodge);
-
- /*#ifdef _DEBUG
- cout << "After calculations" << endl;
- cout << "Armor bonus (0 if n/a): " << to_string(ac.armor) << endl;
- cout << "Shield bonus: " << to_string(ac.shield) << endl;
- cout << "Natural armor: " << to_string(ac.natural) << endl;
- cout << "Deflection bonus: " << to_string(ac.deflection) << endl;
- cout << "Dodge bonus: " << to_string(ac.dodge) << endl;
- cout << "Sacred: " << to_string(ac.sacred) << endl;
- cout << "Profane: " << to_string(ac.profane) << endl;
- cout << "Insight: " << to_string(ac.insight) << endl;
- cout << "Luck: " << to_string(ac.luck) << endl;
- cout << "Racial: " << to_string(ac.racial) << endl;
- cout << "Untyped: " << to_string(ac.untyped) << endl;
-
- cout << "Dex bonus: " << to_string(ac.dex) << endl;
- cout << "Size bonus: " << to_string(ac.size) << endl;
-
- cout << "Total AC: " << to_string(ac.totalAc) << endl;
- cout << "Flat-footed AC: " << to_string(ac.flatFootAc) << endl;
- cout << "Touch AC: " << to_string(ac.touchAc) << endl;
-
- cout << "AC Breakdown: " << ac.breakdown << endl;
- #endif*/
-
- ac.breakdown = determineAcBreakdown(ac);
-
- /*#ifdef _DEBUG
- cout << "After AC breakdown calculated" << endl;
- cout << "Armor bonus (0 if n/a): " << to_string(ac.armor) << endl;
- cout << "Shield bonus: " << to_string(ac.shield) << endl;
- cout << "Natural armor: " << to_string(ac.natural) << endl;
- cout << "Deflection bonus: " << to_string(ac.deflection) << endl;
- cout << "Dodge bonus: " << to_string(ac.dodge) << endl;
- cout << "Sacred: " << to_string(ac.sacred) << endl;
- cout << "Profane: " << to_string(ac.profane) << endl;
- cout << "Insight: " << to_string(ac.insight) << endl;
- cout << "Luck: " << to_string(ac.luck) << endl;
- cout << "Racial: " << to_string(ac.racial) << endl;
- cout << "Untyped: " << to_string(ac.untyped) << endl;
-
- cout << "Dex bonus: " << to_string(ac.dex) << endl;
- cout << "Size bonus: " << to_string(ac.size) << endl;
-
- cout << "Total AC: " << to_string(ac.totalAc) << endl;
- cout << "Flat-footed AC: " << to_string(ac.flatFootAc) << endl;
- cout << "Touch AC: " << to_string(ac.touchAc) << endl;
-
- cout << "AC Breakdown: " << ac.breakdown << endl;
- #endif*/
-
- return ac;
-
-}
-
-string determineAcBreakdown(AC ac) {
-
- string acBreakdown = "(";
-
- if (ac.armor != 0) {
- acBreakdown += to_string(ac.armor);
- acBreakdown += " armor, ";
- }
- if (ac.shield != 0) {
- acBreakdown += to_string(ac.shield);
- acBreakdown += " shield, ";
- }
- if (ac.natural != 0) {
- acBreakdown += to_string(ac.natural);
- acBreakdown += " natural, ";
- }
- if (ac.dex != 0) {
- acBreakdown += to_string(ac.dex);
- acBreakdown += " Dex, ";
- }
- if (ac.dodge != 0) {
- acBreakdown += to_string(ac.dodge);
- acBreakdown += " dodge, ";
- }
- if (ac.deflection != 0) {
- acBreakdown += to_string(ac.deflection);
- acBreakdown += " deflection, ";
- }
- if (ac.insight != 0) {
- acBreakdown += to_string(ac.insight);
- acBreakdown += " insight, ";
- }
- if (ac.luck != 0) {
- acBreakdown += to_string(ac.luck);
- acBreakdown += " luck, ";
- }
- if (ac.sacred != 0) {
- acBreakdown += to_string(ac.sacred);
- acBreakdown += " sacred, ";
- }
- if (ac.profane != 0) {
- acBreakdown += to_string(ac.profane);
- acBreakdown += " profane, ";
- }
- if (ac.racial != 0) {
- acBreakdown += to_string(ac.racial);
- acBreakdown += " racial, ";
- }
- if (ac.size != 0) {
- acBreakdown += to_string(ac.size);
- acBreakdown += " size";
- }
-
- acBreakdown += ")";
- return acBreakdown;
-}
-
-
-void findAndReplace(fstream& outfile, const char* replacement, const char* searchKey, const char* fileName) {
-
- /* I am at a total loss to explain why this code doesn't fucking work if
- * you give it a search key longer than 8 characters (including the null
- * terminator). If you have even the slightest fucking clue, please tell
- * me why. I'd love to know. I could have gotten this first version of the
- * code out nearly a month earlier if I hadn't been trying to figure out
- * why this piece of shit doesn't work. The reason this segment of the
- * code is such a giant fucking mess is because I couldn't figure out what
- * the fuck is wrong with it.
- *
- * Seriously, if you know, please share with the class. I'd love to know.
- * It's probably something stupid, but at least then I'd know how I
- * managed to FUBAR this so hard. I'd honestly be impressed with myself if
- * I wasn't so pissed off.
- */
-
- fstream part1;
- fstream part2;
- long currentPos = 0;
- const int keyLength = strlen(searchKey);
- char current;
- bool located = false;
-
- char* potentialMatch = new char[keyLength];
-
- outfile.seekp(0L, ios::beg);
- outfile.seekg(0L, ios::beg);
-
- /*#ifdef _DEBUG
- cout << "find-replace entered" << endl;
- #endif*/
-
- do {
- //outfile.seekg(0L, ios::beg);
- currentPos = findDollar(outfile, currentPos);
-
- current = outfile.peek();
-
- /*#ifdef _DEBUG
- cout << "Initial character found: " << *//*static_cast(current)*//* current<(cr);
-
- if (cr == .13) {
- xp = 50;
- displayCR[0] = '1';
- displayCR[1] = '/';
- displayCR[2] = '8';
-
- return xp;
- }
- if (cr == .17) {
- xp = 65;
- displayCR[0] = '1';
- displayCR[1] = '/';
- displayCR[2] = '6';
-
- return xp;
- }
- if (cr == .25) {
- xp = 100;
- displayCR[0] = '1';
- displayCR[1] = '/';
- displayCR[2] = '4';
-
- return xp;
- }
- if (cr == .33) {
- xp = 135;
- displayCR[0] = '1';
- displayCR[1] = '/';
- displayCR[2] = '3';
-
- return xp;
- }
- if (cr == .50) {
- xp = 200;
- displayCR[0] = '1';
- displayCR[1] = '/';
- displayCR[2] = '2';
-
- return xp;
- }
-
- switch (workingCR) {
-
- case 1: {
- xp = 400;
- displayCR[0] = '1';
-
- break;
- }
- case 2: {
- xp = 600;
- displayCR[0] = '2';
-
- break;
- }
- case 3: {
- xp = 800;
- displayCR[0] = '3';
-
- break;
- }
- case 4: {
- xp = 1200;
- displayCR[0] = '4';
-
- break;
- }
- case 5: {
- xp = 1600;
- displayCR[0] = '5';
-
- break;
- }
- case 6: {
- xp = 2400;
- displayCR[0] = '6';
-
- break;
- }
- case 7: {
- xp = 3200;
- displayCR[0] = '7';
-
- break;
- }
- case 8: {
- xp = 4800;
- displayCR[0] = '8';
-
- break;
- }
- case 9: {
- xp = 6400;
- displayCR[0] = '9';
-
- break;
- }
- case 10: {
- xp = 9600;
- displayCR[0] = '1';
- displayCR[1] = '0';
-
- break;
- }
- case 11: {
- xp = 12'800;
- displayCR[0] = '1';
- displayCR[1] = '1';
-
- break;
- }
- case 12: {
- xp = 19'200;
- displayCR[0] = '1';
- displayCR[1] = '2';
-
- break;
- }
- case 13: {
- xp = 25'600;
- displayCR[0] = '1';
- displayCR[1] = '3';
-
- break;
- }
- case 14: {
- xp = 38'400;
- displayCR[0] = '1';
- displayCR[1] = '4';
-
- break;
- }
- case 15: {
- xp = 51'200;
- displayCR[0] = '1';
- displayCR[1] = '5';
-
- break;
- }
- case 16: {
- xp = 76'800;
- displayCR[0] = '1';
- displayCR[1] = '6';
-
- break;
- }
- case 17: {
- xp = 102'000;
- displayCR[0] = '1';
- displayCR[1] = '7';
-
- break;
- }
- case 18: {
- xp = 153'600;
- displayCR[0] = '1';
- displayCR[1] = '8';
-
- break;
- }
- case 19: {
- xp = 204'800;
- displayCR[0] = '1';
- displayCR[1] = '9';
-
- break;
- }
- case 20: {
- xp = 307'200;
- displayCR[0] = '2';
- displayCR[1] = '0';
-
- break;
- }
- case 21: {
- xp = 409'600;
- displayCR[0] = '2';
- displayCR[1] = '1';
-
- break;
- }
- case 22: {
- xp = 614'400;
- displayCR[0] = '2';
- displayCR[1] = '2';
-
- break;
- }
- case 23: {
- xp = 819'200;
- displayCR[0] = '2';
- displayCR[1] = '3';
-
- break;
- }
- case 24: {
- xp = 1'228'800;
- displayCR[0] = '2';
- displayCR[1] = '4';
-
- break;
- }
- case 25: {
- xp = 1'638'400;
- displayCR[0] = '2';
- displayCR[1] = '5';
-
- break;
- }
- case 26: {
- xp = 2'457'600;
- displayCR[0] = '2';
- displayCR[1] = '6';
-
- break;
- }
- case 27: {
- xp = 3'276'800;
- displayCR[0] = '2';
- displayCR[1] = '7';
-
- break;
- }
- case 28: {
- xp = 4'915'200;
- displayCR[0] = '2';
- displayCR[1] = '8';
-
- break;
- }
- case 29: {
- xp = 6'553'600;
- displayCR[0] = '2';
- displayCR[1] = '9';
-
- break;
- }
- case 30: {
- xp = 9'830'400;
- displayCR[0] = '3';
- displayCR[1] = '0';
-
- break;
- }
- default: {
- xp = 0;
- displayCR[0] = '0';
- displayCR[1] = '\0';
- displayCR[2] = '\0';
-
- cout << "CR not within parameters, defaulting to 0" << endl;
-
- break;
- }
-
- }
-
- return xp;
-}
-
-
-void printVersion() {
- cout << "PFSB, a PF RPG monster Stat Block generator" << endl << endl;
- #ifndef _DEBUG
- cout << "PFSB version " << _VERSION << endl;
- #endif
- #ifdef _DEBUG
- cout << "PFSB debug version " << _VERSION << ", by Frozen Mustelid" << endl;
- #endif
- cout << "Distributed under the Open Gaming License v1.0a. A copy of the OGL should have been included with this program, as indicated in section 10 of OGLv1.0a." << endl;
- cout << endl << "Copyright (C) 2017 Frozen Mustelid"<< endl;
-}
-
-void clearBuffer() {
-
- cin.clear();
-
- cin.ignore(numeric_limits::max(),'\n');
-
- cin.clear();
-
- return;
-}
-
-string stringToLower(string toLower) {
-
- int x = 0;
-
- while(toLower[x] != '\0') {
- toLower[x] = tolower(toLower[x]);
- x++;
- }
-
- return toLower;
-
-}
-/*switch (creatSize) {
- case 'f':
- case 'F': {
- break;
- }
- case 'd':
- case 'D': {
- break;
- }
- case 't':
- case 'T': {
- break;
- }
- case 's':
- case 'S': {
- break;
- }
- case 'm':
- case 'M': {
- break;
- }
- case 'l':
- case 'L': {
- break;
- }
- case 'h':
- case 'H': {
- break;
- }
- case 'g':
- case 'G': {
- break;
- }
- case 'c':
- case 'C': {
- break;
- }
- default: {
- cout << "Invalid creature size found, exiting program" << endl;
- exit(_ERR_INVALID_SIZE);
- }
-} //*/
-
-
-/*switch (creatType) {
- case CreatureType::ABERRATION: {
- break;
- }
- case CreatureType::ANIMAL: {
- break;
- }
- case CreatureType::CONSTRUCT: {
- break;
- }
- case CreatureType::DRAGON: {
- break;
- }
- case CreatureType::FEY: {
- break;
- }
- case CreatureType::HUMANOID: {
- break;
- }
- case CreatureType::MAGICAL_BEAST: {
- break;
- }
- case CreatureType::MONSTROUS_HUMANOID: {
- break;
- }
- case CreatureType::OOZE: {
- break;
- }
- case CreatureType::OUTSIDER: {
- break;
- }
- case CreatureType::PLANT: {
- break;
- }
- case CreatureType::UNDEAD: {
- break;
- }
- case CreatureType::VERMIN: {
- break;
- }
- default: {
- cout << "Homebrew creature types will come later. Exiting program." << endl;
- exit(_ERR_INVALID_CREATURE_TYPE);
- }
-
- }//*/
\ No newline at end of file
diff --git a/src/platform.hpp b/src/platform.hpp
index 26b6109..ae87007 100644
--- a/src/platform.hpp
+++ b/src/platform.hpp
@@ -4,8 +4,24 @@
#define _PLATFAM __unix
#define _PLATFORM __linux
-#if (_PLATFAM == __unix)
+//_PLATFORM currently unused
+
+#ifdef __unix
#define _PFRPG_TEMPLATE "/usr/share/pfsb/pfrpg/html/pfrpg.html"
+ #define _DIR_SEPARATOR_CHAR '/'
+ //Pretty sure the char is unused
+ #define _DIR_SEPARATOR_STRING "/"
#endif
-#endif
+/*#ifdef (_WIN32)
+
+ * Commented out because unfinished
+ * Also because I didn't bother to look up the Windows preprocessor test
+ * Also because I don't like to release untested code
+
+ #define _PFRPG_TEMPLATE "/usr/share/pfsb/pfrpg/html/pfrpg.html"
+ #define _DIR_SEPARATOR_CHAR '\'
+ #define _DIR_SEPARATOR_STRING "\"
+#endif*/
+
+#endif
\ No newline at end of file
diff --git a/src/version.hpp b/src/version.hpp
index 266c538..b31a3af 100644
--- a/src/version.hpp
+++ b/src/version.hpp
@@ -3,8 +3,7 @@
//#define _DEBUG
-#define _VERSION 0.1
+#define _VERSION "0.2"
#endif
-