Thursday, December 18, 2014

More Footnotes (Pyramid #74)


My brief ramblings on "Ashiwi Country" in Pyramid #74 aren't even a little game-useful, but:

  • It's oddly appropriate for me that this article shows up in the around-Christmas issue. After getting married and earning his PhD, my father was in the Air Force for a while, a consequence of going to school on Uncle Sam's bill via ROTC. He was stationed at Kirtland AFB (next door to a Navy base, apparently, which has always seemed a bit odd to me) near Albuquerque and absolutely loved the place. My parents moved away shortly before I was born, but my father retained a certain amount of New Mexican bric-a-brac, which was most visible in the form of Christmas ornaments. As a result, I've always associated Christmas, to some extent, with the desert Southwest.
  • I didn't get to see the Southwest until many years later, when I was part of a summer of archaeological work on the Zuni reservation. I gotta say, the people are lovely and the scenery is pretty spectacular, if you like that sort of thing. And it really is a dry heat.
  • I didn't get into it in the article, but I should mention that Frank Hamilton Cushing is a bit of a controversial character among the Zuni. He was an important and useful bridge between the Zuni and the white world, but publishing all that stuff about their religious practices was a huge betrayal of trust.
  • In the introduction, Steven says of the article "This meaty guide in the vein of Riggsby’s GURPS Hot Spots series..." DO YOU HEAR THAT? IT'S OFFICIAL! HOT SPOTS IS MINE! MINE, YOU BASTARDS!



Monday, October 27, 2014

Footnote (From The Bottom Up)

This is too brief to be "designer's notes" on my article in Pyramid #72, but it's something I wanted to mention. I note at the beginning of the article that "almost from the beginning of fantasy roleplaying games, there’s been a desire to" play dungeon crawls as monsters rather than human-ish adventurers. What I had in mind here was the 1976 game Monsters! Monsters!, developed in part by a guy at Metagaming named--wey hey!--Steve Jackson. So this is kinda going full circle.

Tuesday, September 23, 2014

Whither Writing for GURPS?


To expand a bit on my comments here on how it's difficult to write new rules for GURPS:


It's not hard to add entirely novel rules to GURPS. The problem, in my own writing and gaming, is finding a need to add entirely novel rules. Mechanically, it's not necessarily hard to write new rules. GURPS is heavy on "find the appropriate trait, modify for circumstances, and roll that number or lower on 3d6." And there's always room for special cases. For example, Tactical Shooting and Technical Grappling add a wealth of gritty detail to shooting things and grabbing people, if you're into that kind of thing, and even with the basic magic system, basic psi, a magic-as-advantages approach, RPM, and variants to be found in Thaumatology, there's always room for new approaches to extra-normal abilities.

No, what I'm talking about is finding topics on which new, general rules can be written where none existed before, or broadly applicable rules for notable adventuring topics. GURPS is densely written, so it takes some unpacking, but there's an astonishing breadth there. For example, fighting, as a general thing, is covered perfectly well in the Basic Set. Might some people want more for a campaign emphasizing detailed combat? Sure. Martial Arts and all that aren't necessary, even though they're pretty nifty. Infinite new magic systems could be written to reflect various views of the supernatural, but magic as a thing already has more coverage than I'll ever use. Social interaction? Social Engineering provides lots of welcome detail, but Basic's treatment is really quite good for the few pages it covers. Basic likewise has reasonable coverage of common physical feats like swimming, climbing, lifting and throwing things, and so on. Chase scenes? Emergency repairs? One character helping out another using a related skill? Action 2 has you covered. Bestiary? OK, there aren't a lot of general rules for making beasties, but there are plenty of examples scattered across various different books (some might take exception to the products on offer and their organization, certainly, but there's a significant amount of available information).

So what's left, in terms of general rules? Not a lot. When I'm confronted with an adventuring situation, it's rare that I can't find at least some applicable rule. There's room for some general design systems, notably for vehicles, weapons, and armor. I think there's also space for GM-centric world-building rules. City Stats is an excellent example. Possibly something similar could be done with environments. It'd be interesting to see generic rules on transportation and trade networks, helping the GM come up with everything from travel times and costs for PC travel to plausible markups for imported goods to adventures based on the act of going from point A to point B. Mostly, though, there are focused rules dealing with specialized circumstances and vast room to expand on the system with worked examples based on the already-solid basis the existing rules provide.



Sunday, September 21, 2014

Hints and Rumors

My sometime coauthor, the lovely and talented Peter Dell'Orto, has recently dropped some hints about his in-progress GURPS book. That is, from a certain point of view, nice of him, and I thought I might do the same. On the other hand, I don't want to get ahead of The GURPS Machine in providing information about upcoming books, so I thought I'd tell some extravagant lies and possibly insert something which might be true. So:
  • A hardcover edition seems unlikely, but we're talking about a 3d printed version.
  • The most recent GURPS news has indicated that there are books in progress for the Powers, Dungeon Fantasy, and Transhuman Space series. Mine is, in fact, for all three: GURPS Dungeon Fantasy: Transhuman Space Powers. It covers a range of implants, bioshells, and other content from the Transhuman Space series in DF format.
  • It's got the correct modifier for "shooting a man in Reno, just to watch him die."
  • Working titles for my book include GURPS Ultra-Tech Companion 4: Tile, Linoleum, and Other Manufactured Floor Coverings and (because the Powers That Be want more evocative titles) GURPS Decant Me The Inopportune Gibbon, Maurice.
  • It's an adaptation of Zombie Dice. No, it's not about being a zombie or running from zombies. It's about Zombie Dice.
  • The book will fill a long-open niche in GURPS: ornamental gardening.

Friday, May 23, 2014

Hoardomatic Code: Doing Something With It

Clever readers will have realized that everything I've posted up to this point doesn't actually do anything. There's a bundle of classes and data representing everything from a pile of treasure down to details of decor, but what to do with it? Ultimately, all one needs to randomly generate a hoard is something like the code below, which generates a hoard of random items worth $2,000. For a bigger or smaller hoard, just change the value of hoard_val.

package hoardomatic;

public class goMonkeygo {
    public static void main(String[] args) {
        Double hoard_val = 2000.0;
        Hoard avarice = new Hoard(hoard_val);
        System.out.println(avarice.hoardReport() );
    }
}





Thursday, May 22, 2014

Hoardomatic Code: Sample Data


And finally, data. Without the XML files, this is all, in the words of Han Solo, a really short trip. These (redundant, poorly structured) batches of XML are representative of the nature and structure of data required. Here's what they contain.

BulkGoods.xml: Detailed breakdowns of additional properties of bulk goods, notably the unit of measurement and containment type. These include W for wet goods like wines and pickles which must be stored in waterproof containers, D for dry goods like spices which can go in boxes and bags, and N for things like hides and raw fibers which are measured by bulk quantities but don't require containment.
Containers.xml: Detailed breakdowns of additional properties of containers, notably containment type and storage capacity.
Curses.xml: Curses are like lightweight enchantments or embellishments, with a name and a CF, which Items use to modify the cost of enchantments.
Embellishments.xml: Properties of mundane embellishments, including name, weight modifier, CF, and embellishment codes indicating properties of items with which they can be matched. Some have motif code, indicating whether or not decorative motifs are suitable to the embellishment.
Enchantments.xml: Name, cost, and item type for enchantments. Also includes energy reserve value, which isn't actually used in my code, 'cause all of the XML files are automated extracts from the database tables I maintain for my own use.
Gems.xml: Name and value modifier for gems.
Items.xml: As the Item class is key to the code, Items.xml is the most important data file. Lists items with name, base cost, base weight, and one or more attributes indicating which embellishments and enchantments are suitable for use with it.
Motifs.xml: A fairly simple list of decorative motifs.
SupernaturalEmbellishments.xml: List of supernatural embellishments which is called on for some enchanted items. Just a name and CF.



BulkGoods.xml

<?xml version="1.0" encoding="UTF-8"?>
<bulkgoods>

<item name="7-UP">
   <name>7-UP</name>
   <UnitPrice>5</UnitPrice>
   <Wt>8</Wt>
   <Unit>gallon</Unit>
   <ContainmentType>W</ContainmentType>
</item>

<item name="Emeril's Essence">
   <name>Emeril's Essence</name>
   <UnitPrice>150</UnitPrice>
   <Wt>0.0625</Wt>
   <Unit>oz.</Unit>
   <ContainmentType>D</ContainmentType>
</item>

<item name="Chanel Number 5">
   <name>Chanel Number 5</name>
   <UnitPrice>35</UnitPrice>
   <Wt>0.0625</Wt>
   <Unit>oz.</Unit>
   <ContainmentType>W</ContainmentType>
</item>

<item name="MSG">
   <name>MSG</name>
   <UnitPrice>150</UnitPrice>
   <Wt>0.0625</Wt>
   <Unit>oz.</Unit>
   <ContainmentType>D</ContainmentType>
</item>

<item name="Candle, Tea Light">
   <name>Candle, Tea Light</name>
   <UnitPrice>5</UnitPrice>
   <Wt>1</Wt>
   <Unit>Each</Unit>
   <ContainmentType>D</ContainmentType>
</item>

<item name="Candle, Birthday">
   <name>Candle, Birthday</name>
   <UnitPrice>0.5</UnitPrice>
   <Wt>1</Wt>
   <Unit>Each</Unit>
   <ContainmentType>D</ContainmentType>
</item>

<item name="Mrs. Dash">
   <name>Mrs. Dash</name>
   <UnitPrice>150</UnitPrice>
   <Wt>0.0625</Wt>
   <Unit>oz.</Unit>
   <ContainmentType>D</ContainmentType>
</item>

<item name="Cloth, Lycra">
   <name>Cloth, Lycra</name>
   <UnitPrice>5</UnitPrice>
   <Wt>1.5</Wt>
   <Unit>Bale</Unit>
   <ContainmentType>N</ContainmentType>
</item>

<item name="Cloth, Fuzzy Fleece">
   <name>Cloth, Fuzzy Fleece</name>
   <UnitPrice>565</UnitPrice>
   <Wt>1</Wt>
   <Unit>Bale</Unit>
   <ContainmentType>N</ContainmentType>
</item>

<item name="Whiteout">
   <name>Whiteout</name>
   <UnitPrice>65</UnitPrice>
   <Wt>0.5</Wt>
   <Unit>Each</Unit>
   <ContainmentType>W</ContainmentType>
</item>

<item name="Root beer">
   <name>Root beer</name>
   <UnitPrice>16</UnitPrice>
   <Wt>1</Wt>
   <Unit>pint</Unit>
   <ContainmentType>W</ContainmentType>
</item>

<item name="Pleather">
   <name>Pleather</name>
   <UnitPrice>200</UnitPrice>
   <Wt>75</Wt>
   <Unit>Bale</Unit>
   <ContainmentType>N</ContainmentType>
</item>

</bulkgoods>

Containers.xml

<?xml version="1.0" encoding="UTF-8"?>
<names>
<item name="Gift Box, Small">
   <name>Gift Box, Small</name>
   <Cost>2</Cost>
   <Wt>0.25</Wt>
   <ContainmentType>D</ContainmentType>
   <Capacity>2.5</Capacity>
   <attributes value="H"/>
</item>

<item name="Gift Box, Large">
   <name>Gift Box, Large</name>
   <Cost>15</Cost>
   <Wt>2</Wt>
   <ContainmentType>D</ContainmentType>
   <Capacity>20</Capacity>
   <attributes value="H"/>
</item>

<item name="Gift Bag">
   <name>Gift Bag</name>
   <Cost>30</Cost>
   <Wt>3</Wt>
   <ContainmentType>D</ContainmentType>
   <Capacity>40</Capacity>
   <attributes value="S"/>
</item>

<item name="Shipping Crate">
   <name>Shipping Crate</name>
   <Cost>1000</Cost>
   <Wt>220</Wt>
   <ContainmentType>D</ContainmentType>
   <Capacity>3300</Capacity>
   <attributes value="H"/>
</item>

<item name="Sippy Cup">
   <name>Sippy Cup</name>
   <Cost>5</Cost>
   <Wt>0.25</Wt>
   <ContainmentType>W</ContainmentType>
   <Capacity>0.5</Capacity>
   <attributes value="H"/>
</item>

<item name="Filter Bottle">
   <name>Filter Bottle</name>
   <Cost>3</Cost>
   <Wt>1</Wt>
   <ContainmentType>W</ContainmentType>
   <Capacity>2</Capacity>
   <attributes value="H"/>
</item>

<item name="2 liter bottle">
   <name>2 liter bottle</name>
   <Cost>10</Cost>
   <Wt>4</Wt>
   <ContainmentType>W</ContainmentType>
   <Capacity>2</Capacity>
   <attributes value="H"/>
</item>

<item name="Milk Jug">
   <name>Milk Jug</name>
   <Cost>10</Cost>
   <Wt>0.25</Wt>
   <ContainmentType>W</ContainmentType>
   <Capacity>8</Capacity>
   <attributes value="S"/>
</item>

<item name="Tanker Trailer">
   <name>Tanker Trailer</name>
   <Cost>300</Cost>
   <Wt>60</Wt>
   <ContainmentType>W</ContainmentType>
   <Capacity>60000</Capacity>
   <attributes value="H"/>
</item>

</names>

Curses.xml

<?xml version="1.0" encoding="UTF-8"?>
<curses>
    <item name="Plays the same song over and over">
   <Curse>Plays the same song over and over</Curse>
   <CF>-0.5</CF>
</item>

<item name="Backlash (Poor Q Rating)">
   <Curse>Backlash (Poor Q Rating)</Curse>
   <CF>-0.2</CF>
</item>

<item name="Interesting Times">
   <Curse>Interesting Times</Curse>
   <CF>-0.2</CF>
</item>

<item name="Dysfunctional Family">
   <Curse>Dysfunctional Family</Curse>
   <CF>-0.2</CF>
</item>

<item name="Knuckle-Cracking">
   <Curse>Knuckle-Cracking</Curse>
   <CF>-0.2</CF>
</item>

</curses>

Embellishments.xml

<?xml version="1.0" encoding="UTF-8"?>
<embellishments>
<item name="Foin">
   <Embellishment>Foin</Embellishment>
   <CF>1.5</CF>
   <emb_code>W</emb_code>
   <wtmod>1</wtmod>
</item>
<item name="Foin">
   <Embellishment>Foin</Embellishment>
   <CF>2.5</CF>
   <emb_code>MW</emb_code>
   <wtmod>1.1</wtmod>
</item>
<item name="Foin">
   <Embellishment>Foin</Embellishment>
   <CF>2.5</CF>
   <emb_code>S</emb_code>
   <wtmod>1.1</wtmod>
</item>
<item name="Foin">
   <Embellishment>Foin</Embellishment>
   <CF>6.3</CF>
   <emb_code>B</emb_code>
   <wtmod>1.2</wtmod>
</item>
<item name="Foin">
   <Embellishment>Foin</Embellishment>
   <CF>6</CF>
   <emb_code>A</emb_code>
   <wtmod>0.85</wtmod>
</item>
<item name="Supafoin">
   <Embellishment>Supafoin</Embellishment>
   <CF>6</CF>
   <emb_code>A</emb_code>
   <wtmod>0.85</wtmod>
</item>
<item name="Supafoin">
   <Embellishment>Supafoin</Embellishment>
   <CF>16</CF>
   <emb_code>W</emb_code>
   <wtmod>1.7</wtmod>
</item>
<item name="Supafoin">
   <Embellishment>Supafoin</Embellishment>
   <CF>16</CF>
   <emb_code>S</emb_code>
   <wtmod>1</wtmod>
</item>
<item name="Bedazzling (cheap, extensive)">
   <Embellishment>Bedazzling (cheap, extensive)</Embellishment>
   <CF>4</CF>
   <emb_code>S</emb_code>
   <wtmod>1</wtmod>
   <motif>P</motif>
</item>
<item name="Bedazzling (cheap, minimal)">
   <Embellishment>Bedazzling (cheap, minimal)</Embellishment>
   <CF>1.5</CF>
   <emb_code>S</emb_code>
   <wtmod>1</wtmod>
   <motif>P</motif>
</item>
<item name="Bedazzling (expensive, extensive)">
   <Embellishment>Bedazzling (expensive, extensive)</Embellishment>
   <CF>7</CF>
   <emb_code>S</emb_code>
   <wtmod>1</wtmod>
   <motif>P</motif>
</item>
<item name="Bedazzling (expensive, minimal)">
   <Embellishment>Bedazzling (expensive, minimal)</Embellishment>
   <CF>3</CF>
   <emb_code>S</emb_code>
   <wtmod>1</wtmod>
   <motif>P</motif>
</item>
<item name="Bedazzling (cheap, extensive)">
   <Embellishment>Bedazzling (cheap, extensive)</Embellishment>
   <CF>4</CF>
   <emb_code>H</emb_code>
   <wtmod>1</wtmod>
   <motif>P</motif>
</item>
<item name="Bedazzling (cheap, minimal)">
   <Embellishment>Bedazzling (cheap, minimal)</Embellishment>
   <CF>1.5</CF>
   <emb_code>H</emb_code>
   <wtmod>1</wtmod>
   <motif>P</motif>
</item>
<item name="Bedazzling (expensive, extensive)">
   <Embellishment>Bedazzling (expensive, extensive)</Embellishment>
   <CF>7</CF>
   <emb_code>H</emb_code>
   <wtmod>1</wtmod>
   <motif>P</motif>
</item>
<item name="Bedazzling (expensive, minimal)">
   <Embellishment>Bedazzling (expensive, minimal)</Embellishment>
   <CF>3</CF>
   <emb_code>H</emb_code>
   <wtmod>1</wtmod>
   <motif>P</motif>
</item>
<item name="Designer Logos (extensive)">
   <Embellishment>Designer Logos (extensive)</Embellishment>
   <CF>2</CF>
   <emb_code>H</emb_code>
   <wtmod>1</wtmod>
   <motif>P</motif>
</item>
<item name="Designer Logos (minimal)">
   <Embellishment>Designer Logos (minimal)</Embellishment>
   <CF>0.75</CF>
   <emb_code>H</emb_code>
   <wtmod>1</wtmod>
   <motif>P</motif>
</item>
<item name="Material (Recyclable Plastic)">
   <Embellishment>Material (Recyclable Plastic)</Embellishment>
   <CF>0.5</CF>
   <emb_code>B</emb_code>
   <wtmod>1</wtmod>
</item>
<item name="Material (Bakelite)">
   <Embellishment>Material (Bakelite)</Embellishment>
   <CF>1</CF>
   <emb_code>B</emb_code>
   <wtmod>2</wtmod>
</item>
<item name="Corroded">
   <Embellishment>Corroded</Embellishment>
   <CF>-0.25</CF>
   <emb_code>H</emb_code>
   <wtmod>1</wtmod>
</item>
<item name="Tie-Dye (extensive)">
   <Embellishment>Tie-Dye (extensive)</Embellishment>
   <CF>5</CF>
   <emb_code>S</emb_code>
   <wtmod>1</wtmod>
   <motif>Y</motif>
</item>
<item name="Tie-Dye (minimal)">
   <Embellishment>Tie-Dye (minimal)</Embellishment>
   <CF>2</CF>
   <emb_code>S</emb_code>
   <wtmod>1</wtmod>
   <motif>Y</motif>
</item>
<item name="Jeweled">
   <Embellishment>Jeweled</Embellishment>
   <CF>40</CF>
   <emb_code>H</emb_code>
   <wtmod>1</wtmod>
</item>
</embellishments>

Enchantments.xml

<?xml version="1.0" encoding="UTF-8"?>
<enchantments>
<item name="Oh There It Is">
   <Enchantment>Oh There It Is</Enchantment>
   <Type>ALL</Type>
   <Cost>8000</Cost>
   <Reserve>6</Reserve>
</item>
<item name="Take That You Fiend">
   <Enchantment>Take That You Fiend</Enchantment>
   <Type>W</Type>
   <Cost>4000</Cost>
   <Reserve>6</Reserve>
</item>
<item name="Abracadabra">
   <Enchantment>Abracadabra</Enchantment>
   <Type>ALL</Type>
   <Cost>24000</Cost>
   <Reserve>48</Reserve>
</item>
<item name="Hocus Pocus">
   <Enchantment>Hocus Pocus</Enchantment>
   <Type>ALL</Type>
   <Cost>6000</Cost>
   <Reserve>12</Reserve>
</item>
<item name="Bibbidy Bobbity Boo">
   <Enchantment>Bibbidy Bobbity Boo</Enchantment>
   <Type>ALL</Type>
   <Cost>12000</Cost>
   <Reserve>24</Reserve>
</item>
<item name="Curses">
   <Enchantment>Curses</Enchantment>
   <Type>ALL</Type>
   <Cost>18000</Cost>
   <Reserve>36</Reserve>
</item>
<item name="Curses Foiled">
   <Enchantment>Curses Foiled</Enchantment>
   <Type>ALL</Type>
   <Cost>24000</Cost>
   <Reserve>48</Reserve>
</item>
<item name="Curses Foiled Again">
   <Enchantment>Curses Foiled</Enchantment>
   <Type>ALL</Type>
   <Cost>30000</Cost>
   <Reserve>60</Reserve>
</item>
<item name="Alla Kazam">
   <Enchantment>Alla Kazam</Enchantment>
   <Type>ALL</Type>
   <Cost>20000</Cost>
   <Reserve>48</Reserve>
</item>
<item name="Alla Peanut Butter Sandwiches">
   <Enchantment>Alla Peanut Butter Sandwiches</Enchantment>
   <Type>ALL</Type>
   <Cost>15000</Cost>
   <Reserve>30</Reserve>
</item>
<item name="Zap">
   <Enchantment>Zap</Enchantment>
   <Type>MW</Type>
   <Cost>12000</Cost>
   <Reserve>12</Reserve>
</item>
<item name="Kapow">
   <Enchantment>Kapow</Enchantment>
   <Type>W</Type>
   <Cost>8000</Cost>
   <Reserve>18</Reserve>
</item>
   
</enchantments>

Gems.xml

<?xml version="1.0" encoding="UTF-8"?>
<gems>
    <item name="Rhinestone">
   <Gem>Rhinestone</Gem>
   <V>5</V>
</item>

<item name="Tanzenite">
   <Gem>Tanzenite</Gem>
   <V>25</V>
</item>

<item name="Shiny Glass">
   <Gem>Shiny Glass</Gem>
   <V>3</V>
</item>

<item name="Zircon">
   <Gem>Zircon</Gem>
   <V>30</V>
</item>

<item name="Moissanite">
   <Gem>Moissanite</Gem>
   <V>10</V>
</item>
</gems>

Items.xml

<?xml version="1.0" encoding="UTF-8"?>
<items>

<item name="Pointything Cane">
   <name>Pointything Cane</name>
   <Cost>600</Cost>
   <Wt>1.5</Wt>
   <attributes value="H"/>
   <attributes value="W"/>
</item>

<item name="Grille">
   <name>Grille</name>
   <Cost>31.25</Cost>
   <Wt>0.5</Wt>
   <attributes value="Jewelry"/>
   <attributes value="H"/>
</item>

<item name="Prince Albert">
   <name>Prince Albert</name>
   <Cost>0.625</Cost>
   <Wt>0.01</Wt>
   <attributes value="Jewelry"/>
   <attributes value="H"/>
</item>

<item name="Bullets">
   <name>Bullets</name>
   <Cost>2</Cost>
   <Wt>0.1</Wt>
   <attributes value="H"/>
   <attributes value="M"/>
   <attributes value="AMMO"/>
</item>

<item name="Bullets, hollow-point">
   <name>Bullets, hollow-point</name>
   <Cost>2.5</Cost>
   <Wt>0.1</Wt>
   <attributes value="H"/>
   <attributes value="M"/>
   <attributes value="AMMO"/>
</item>

<item name="Choppything">
   <name>Choppything</name>
   <Cost>50</Cost>
   <Wt>4</Wt>
   <attributes value="W"/>
   <attributes value="H"/>
   <attributes value="Brand-H"/>
</item>

<item name="Blackboard">
   <name>Blackboard</name>
   <Cost>60</Cost>
   <Wt>7</Wt>
   <attributes value="H"/>
   <attributes value="Brand-H"/>
</item>

<item name="Backpack Toolkit, Pleatherworker">
   <name>Backpack Toolkit, Pleatherworker</name>
   <Cost>250</Cost>
   <Wt>7.5</Wt>
   <attributes value="H"/>
   <attributes value="S"/>
</item>

<item name="BackPointything">
   <name>BackPointything</name>
   <Cost>550</Cost>
   <Wt>3</Wt>
   <attributes value="W"/>
   <attributes value="H"/>
</item>

<item name="Ball">
   <name>Ball</name>
   <Cost>3</Cost>
   <Wt>0.25</Wt>
   <attributes value="H"/>
   <attributes value="S"/>
   <attributes value="Brand-S"/>
</item>

<item name="Barding, Turtle">
   <name>Barding, Turtle</name>
   <Cost>540</Cost>
   <Wt>37</Wt>
   <attributes value="A"/>
   <attributes value="S"/>
   <attributes value="Brand-S"/>
</item>

<item name="Bastard Pointything">
   <name>Bastard Pointything</name>
   <Cost>650</Cost>
   <Wt>5</Wt>
   <attributes value="W"/>
   <attributes value="H"/>
</item>

<item name="BroadPointything">
   <name>BroadPointything</name>
   <Cost>500</Cost>
   <Wt>3</Wt>
   <attributes value="W"/>
   <attributes value="H"/>
</item>

<item name="Trench Coat">
   <name>Trench Coat</name>
   <Cost>210</Cost>
   <Wt>16</Wt>
   <attributes value="A"/>
   <attributes value="S"/>
</item>

<item name="Frizbee">
   <name>Frizbee</name>
   <Cost>50</Cost>
   <Wt>0.5</Wt>
   <attributes value="H"/>
   <attributes value="M"/>
</item>

<item name="Denim Armor Suit">
   <name>Denim Armor Suit</name>
   <Cost>180</Cost>
   <Wt>13</Wt>
   <attributes value="A"/>
   <attributes value="S"/>
</item>

<item name="Double Mail Hip Flask">
   <name>Double Mail Hip Flask</name>
   <Cost>520</Cost>
   <Wt>44</Wt>
   <attributes value="A"/>
   <attributes value="H"/>
</item>

<item name="Dress SmallPointything">
   <name>Dress SmallPointything</name>
   <Cost>300</Cost>
   <Wt>1</Wt>
   <attributes value="W"/>
   <attributes value="H"/>
</item>

<item name="Dueling Bohemian Ear-Spoon">
   <name>Dueling Bohemian Ear-Spoon</name>
   <Cost>90</Cost>
   <Wt>6</Wt>
   <attributes value="W"/>
   <attributes value="H"/>
   <attributes value="Brand-H"/>
</item>

<item name="Dueling PollChoppything">
   <name>Dueling PollChoppything</name>
   <Cost>100</Cost>
   <Wt>8</Wt>
   <attributes value="W"/>
   <attributes value="H"/>
   <attributes value="Brand-H"/>
</item>


<item name="GURPS Basic Set: Characters">
   <name>GURPS Basic Set: Characters</name>
   <Cost>50</Cost>
   <Wt>2.5</Wt>
   <attributes value="B"/>
</item>

<item name="GURPS Basic Set: Campaigns">
   <name>GURPS Basic Set: Campaigns</name>
   <Cost>45</Cost>
   <Wt>2.5</Wt>
   <attributes value="B"/>
</item>

<item name="Great Choppything">
   <name>Great Choppything</name>
   <Cost>100</Cost>
   <Wt>8</Wt>
   <attributes value="W"/>
   <attributes value="H"/>
   <attributes value="Brand-H"/>
</item>

<item name="GreatPointything">
   <name>GreatPointything</name>
   <Cost>800</Cost>
   <Wt>7</Wt>
   <attributes value="W"/>
   <attributes value="H"/>
</item>

<item name="Heavy Pleather Leggings">
   <name>Heavy Pleather Leggings</name>
   <Cost>60</Cost>
   <Wt>4</Wt>
   <attributes value="A"/>
   <attributes value="S"/>
   <attributes value="Brand-S"/>
   <attributes value="Skin"/>  
</item>

<item name="Heavy Pleather Sleeves">
   <name>Heavy Pleather Sleeves</name>
   <Cost>50</Cost>
   <Wt>2</Wt>
   <attributes value="A"/>
   <attributes value="S"/>
   <attributes value="Brand-S"/>
   <attributes value="Skin"/>  
</item>

<item name="Hook Pointything">
   <name>Hook Pointything</name>
   <Cost>200</Cost>
   <Wt>3</Wt>
   <attributes value="W"/>
   <attributes value="H"/>
</item>

<item name="Pleather Armor">
   <name>Pleather Armor</name>
   <Cost>100</Cost>
   <Wt>10</Wt>
   <attributes value="A"/>
   <attributes value="S"/>
   <attributes value="Brand-S"/>
   <attributes value="Skin"/>  
</item>

<item name="Pleather Cap">
   <name>Pleather Cap</name>
   <Cost>32</Cost>
   <Wt>0</Wt>
   <attributes value="A"/>
   <attributes value="S"/>
   <attributes value="Brand-S"/>
   <attributes value="Skin"/>  
</item>

<item name="Pleather Gloves">
   <name>Pleather Gloves</name>
   <Cost>30</Cost>
   <Wt>0</Wt>
   <attributes value="A"/>
   <attributes value="S"/>
   <attributes value="Brand-S"/>
   <attributes value="Skin"/>  
</item>

<item name="Pleather Helm">
   <name>Pleather Helm</name>
   <Cost>20</Cost>
   <Wt>0.5</Wt>
   <attributes value="A"/>
   <attributes value="S"/>
   <attributes value="Brand-S"/>
   <attributes value="Skin"/>  
</item>

<item name="Pleather Jacket">
   <name>Pleather Jacket</name>
   <Cost>50</Cost>
   <Wt>4</Wt>
   <attributes value="A"/>
   <attributes value="S"/>
   <attributes value="Brand-S"/>
   <attributes value="Skin"/>  
</item>

<item name="Pleather Leggings">
   <name>Pleather Leggings</name>
   <Cost>40</Cost>
   <Wt>2</Wt>
   <attributes value="A"/>
   <attributes value="S"/>
   <attributes value="Brand-S"/>
   <attributes value="Skin"/>  
</item>

<item name="Pleather Pants">
   <name>Pleather Pants</name>
   <Cost>40</Cost>
   <Wt>3</Wt>
   <attributes value="A"/>
   <attributes value="S"/>
   <attributes value="Brand-S"/>
   <attributes value="Skin"/>  
</item>

<item name="Pleather Suit">
   <name>Pleather Suit</name>
   <Cost>340</Cost>
   <Wt>19.5</Wt>
   <attributes value="A"/>
   <attributes value="S"/>
   <attributes value="Brand-S"/>
   <attributes value="Skin"/>  
</item>

</items>

Motifs.xml

<?xml version="1.0" encoding="UTF-8"?>
<motifs>
    <item name="Good hat">
   <Motif>Good hat</Motif>
</item>
<item name="Evil hat">
   <Motif>Evil hat</Motif>
</item>
<item name="Police Badge">
   <Motif>Police Badge</Motif>
</item>
<item name="Corporate Logo">
   <Motif>Corporate Logo</Motif>
</item>
<item name="Pirate Parrot">
   <Motif>Pirate Parrot</Motif>
</item>
<item name="Anime Hair">
   <Motif>Anime Hair</Motif>
</item>
<item name="Bat Symbol">
   <Motif>Bat Symbol</Motif>
</item>
<item name="Superman Shield">
   <Motif>Superman Shield</Motif>
</item>
<item name="Gang Sign">
   <Motif>Gang Sign</Motif>
</item>
<item name="Philly Phanatic">
   <Motif>Philly Phanatic</Motif>
</item>
<item name="Emoji">
   <Motif>Emoji</Motif>
</item>
</motifs>


SupernaturalEmbellishments.xml

<?xml version="1.0" encoding="UTF-8"?>
<embellishments>
    <item name="Extra Shiny">
   <Embellishment>Extra Shiny</Embellishment>
   <CF>0.25</CF>
</item>
<item name="Smells faintly of lilac">
   <Embellishment>Smells faintly of lilac</Embellishment>
   <CF>0.1</CF>
</item>
<item name="Bringz in da funk, bringz in da noize">
   <Embellishment>Bringz in da funk, bringz in da noize</Embellishment>
   <CF>0.5</CF>
</item>
<item name="Always points the other direction">
   <Embellishment>Always points the other direction</Embellishment>
   <CF>1</CF>
</item>
<item name="Knows how to tango">
   <Embellishment>Knows how to tango</Embellishment>
   <CF>1</CF>
</item>

</embellishments>


Wednesday, May 21, 2014

Hoardomatic Code: Gem Class

Last class. Gems have their own bundle of properties, so they get their own class.

Gem


String gem_name: Name of the gem.
Double val_constant: The value constant for the stone, the V in the(C^2 + C*4) * V value formula.
Double carats: Weight of the gem in carats.

This one is similar to the previous two, but it takes on itself the task of randomly selecting a node from Gems.xml to turn into an object.   

package hoardomatic;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Random;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class Gem {

    private String gem_name;
    private Double val_constant;
    private Double carats;
   
    public Gem() {
        // TODO Auto-generated constructor stub
        Node gemnode = getRandomNodeFromTable("Gems.xml");
       
        NodeList tempNodes = gemnode.getChildNodes();
        for (int j = 0; j < tempNodes.getLength(); j++) {
            Node subnode = tempNodes.item(j);

            if (subnode.getNodeType() == Node.ELEMENT_NODE) {
                Element element = (Element) subnode;
               
                if (element.getNodeName().contentEquals("Gem")) {
                    gem_name = element.getTextContent();
                }

                if (element.getNodeName().contentEquals("V")) {
                    val_constant =Double.parseDouble(element.getTextContent());
                }

            }

        }
       
        double dr = (double) dieRoll(1,6) + dieRoll(1,6);
        double the_carats = dr/4;
       
        int get_big = dieRoll(1,12);
        while (get_big == 12){
            the_carats = the_carats + dieRoll(1,6);
            get_big = dieRoll(1,12);
        }
       
        carats = the_carats;
       
    }
   
   
        public Node getRandomNodeFromTable(String fname) {
           
            Node return_it;
            return_it = null;

            try {
                InputStream items = new FileInputStream(new File(fname));
               
                DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
                DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
                Document doc = dBuilder.parse(items);

                doc.getDocumentElement().normalize();
                // System.out.println(doc.getDocumentElement().getNodeName());
                NodeList nodes = doc.getElementsByTagName("item");
               
                Integer itemlistlen = nodes.getLength();
               
                Integer pickItm = dieRoll(0, itemlistlen - 1);
               
                // System.out.println("==========================");
//                System.out.println(pickItm);

                for (int i = 0; i < nodes.getLength(); i++) {
                    Node node = nodes.item(i);
                    if (i == pickItm) {
                        return_it = node;
                    }
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
            return return_it;
        }
   
   
    public Gem(Node gemnode){
       
       
        NodeList tempNodes = gemnode.getChildNodes();
        for (int j = 0; j < tempNodes.getLength(); j++) {
            Node subnode = tempNodes.item(j);

            if (subnode.getNodeType() == Node.ELEMENT_NODE) {
                Element element = (Element) subnode;
               
                if (element.getNodeName().contentEquals("Gem")) {
                    gem_name = element.getTextContent();
                }

                if (element.getNodeName().contentEquals("V")) {
                    val_constant =Double.parseDouble(element.getTextContent());
                }

            }

        }
       
       
        double the_carats = (dieRoll(1,6) + dieRoll(1,6))/4;
       
        int get_big = dieRoll(1,12);
        while (get_big == 12){
            the_carats = the_carats + dieRoll(1,6);
            get_big = dieRoll(1,12);
        }
       
        carats = the_carats;
       
       
    }
   
    public Double getGemCost(){
        Double return_it = 0.0;
        // (c^2 + 4 x c) ^ v
        return_it = (carats * carats + 4 * carats) * val_constant;
        return return_it;
    }
   
    public String getGemName(){
        return gem_name;
    }
   
    public Double getGemWt(){
        return carats;
    }
   
    private int dieRoll(Integer min, Integer max) {

        Random r = new Random();
        int inroll = r.nextInt(max - min + 1) + min;
        return inroll;
    }


}

Tuesday, May 20, 2014

Hoardomatic Code: Enchantment Class

Enchantments are structurally very similar to Embellishments. The big difference is how costs are accounted for by the Item containing them.

Enchantment


String Enchantment_name: The name of the enchantment, usually just the name of a spell, though it may include a degree (Accuracy 3) or notation about which version of an enchantment the item has (Bravery (Cast only)).
Double cf: The embellishment's CF value. Not actually used, but it's there because I started enchantments by copying the embellishment class.
Double flat_cost: The base cost of the enchantment.
Double wt_mod: Weight adjustment, if any, which spells generally don't have.
String motif_option: Not used.
String ench_code: A code similar to the embellishment codes, indicating what kind of item the enchantment can be on. Most are generally applicable, but a few are limited to weapons or armor.

This class works very much like the Embellishments class: it takes a node provided to it and populates the enchantment's properties.

package hoardomatic;

import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class Enchantment {
   
    private String Enchantment_name;
    private Double cf;
    private Double flat_cost;
    private Double wt_mod;
    private String motif_option;
    private String ench_code;

    public Enchantment() {

        Enchantment_name = "";
        cf = 0.0;
        flat_cost = 0.0;
        wt_mod = 1.0;
        motif_option = "";
        ench_code = "";
    }
   
    public Enchantment(Node node) {
        flat_cost = 0.0;

        NodeList tempNodes = node.getChildNodes();
        for (int j = 0; j < tempNodes.getLength(); j++) {
            Node subnode = tempNodes.item(j);

            if (subnode.getNodeType() == Node.ELEMENT_NODE) {
                Element element = (Element) subnode;
               
                if (element.getNodeName().contentEquals("Enchantment")) {
                    Enchantment_name = element.getTextContent();
                }

                if (element.getNodeName().contentEquals("CF")) {
                    cf =Double.parseDouble(element.getTextContent());
                }

                if (element.getNodeName().contentEquals("Cost")) {
                    flat_cost =Double.parseDouble(element.getTextContent());
                }

                if (element.getNodeName().contentEquals("wtmod")) {
                    wt_mod = Double.parseDouble(element.getTextContent());
                }

                if (element.getNodeName().contentEquals("ench_code")) {
                    ench_code = element.getTextContent();
                }

                if (element.getNodeName().contentEquals("Type")) {
                    ench_code = element.getTextContent();
                }

            }

        }

    }

   
   
    public Enchantment(String the_name, Double the_cf, Double the_wt_mod) {

        Enchantment_name = the_name;
        cf = the_cf;
        wt_mod = the_wt_mod;
    }
   
    public void setCost(Double new_cost){
        flat_cost = new_cost;
    }
   
    public Double getCost(){
        return flat_cost;
    }
   
    public void setCF(Double new_cf){
        cf = new_cf;
    }
   
   
    public String getEnchantmentName(){
        return Enchantment_name;
    }
   
    public String getMotifOption(){
        return motif_option;
    }
   
    public Double getCf(){
        return cf;
    }

    public Double getWtMod(){
        return wt_mod;
    }
   
    public String getenchCode(){
        return ench_code;
    }


}

Monday, May 19, 2014

Hoardomatic Code: Embellishment Class

This represents an individual decorative or functional embellishment on an item.

Embellishment


String embellishment_name: Name of the embellishment (for example, "Fine material" or "Tassels").
Double cf: The embellishment's CF value.
Double wt_mod: Weight adjustment, if any. This defaults to 1 and is expressed as a decimal multiplier to the base item's weight. For example, 0.9 for an embellishment which reduces the weight of the item by 10%.
String motif_option: Some options don't lead to item having a particular decorative motif (for example, improved materials). Some do (for example, relief carving). Some could go either way (for example, painting; an item could simply be painted, say, entirely green, or have figures painted on it in green). This defaults to "N".
String emb_code: A single string corresponding to the range of attributes which items may have.

The constructor I'm using takes a node (from Embelishments.xml) handed to it by the calling class. It goes through the node's elements and extracts various bits of data, putting them into the appropriate variables.


package hoardomatic;

import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class Embellishment {
   
    private String embellishment_name;
    private Double cf;
    private Double flat_cost;
    private Double wt_mod;
    private String motif_option;
    private String emb_code;

    public Embellishment() {

        embellishment_name = "";
        cf = 0.0;
        flat_cost = 0.0;
        wt_mod = 1.0;
        motif_option = "";
        emb_code = "";
    }
   
    public Embellishment(Node node) {

        motif_option = "";
        wt_mod = 1.0;
        flat_cost = 0.0;

        NodeList tempNodes = node.getChildNodes();
        for (int j = 0; j < tempNodes.getLength(); j++) {
            Node subnode = tempNodes.item(j);

            if (subnode.getNodeType() == Node.ELEMENT_NODE) {
                Element element = (Element) subnode;
               
                if (element.getNodeName().contentEquals("Embellishment")) {
                    embellishment_name = element.getTextContent();
                }

                if (element.getNodeName().contentEquals("motif")) {
                    motif_option = element.getTextContent();
                }

                if (element.getNodeName().contentEquals("CF")) {
                    cf =Double.parseDouble(element.getTextContent());
                }

                if (element.getNodeName().contentEquals("Cost")) {
                    flat_cost =Double.parseDouble(element.getTextContent());
                }

                if (element.getNodeName().contentEquals("wtmod")) {
                    wt_mod = Double.parseDouble(element.getTextContent());
                }

                if (element.getNodeName().contentEquals("emb_code")) {
                    emb_code = element.getTextContent();
                }

                if (element.getNodeName().contentEquals("Type")) {
                    emb_code = element.getTextContent();
                }

            }

        }

    }

   
   
    public Embellishment(String the_name, Double the_cf, Double the_wt_mod) {
        // TODO Auto-generated constructor stub
        embellishment_name = the_name;
        cf = the_cf;
        wt_mod = the_wt_mod;
    }

    public void setWtMod(Double new_wt_mod){
        flat_cost = new_wt_mod;
    }
   
    public void setCost(Double new_cost){
        flat_cost = new_cost;
    }
   
    public void setCF(Double new_cf){
        cf = new_cf;
    }
   
   
    public String getEmbellishmentName(){
        return embellishment_name;
    }
   
    public void setEmbellishmentName(String new_name){
        embellishment_name = new_name;
    }
   
    public String getMotifOption(){
        return motif_option;
    }
   
    public Double getCf(){
        return cf;
    }

    public Double getWtMod(){
        return wt_mod;
    }
   
    public String getEmbCode(){
        return emb_code;
    }


}

Friday, May 16, 2014

Hoardomatic Code: Item Class

Here's where most of the work happens. It also contains lots of untidy code which is a consequence of making it up as I go along: stub functions which are unimplemented, implemented but unused functions which don't do anything useful, and references to libraries that don't actually get used. No matter; the bits which do get used do what I need them to.

Item


String item_name: The name of the base item (broadsword, Dwarven theodolite, arrow, etc.)
Double base_cost: The cost of the unembellished item, used as the basis for CF-related price calculations.
Double base_weight: The unembellished weight of the item.
Integer quantity: Usually 1, but ammunition and bulk goods often have a larger quantity
String curse_name: Defaults to an empty string. An item may have no more than one curse.
Double curse_cf: The CF of the curse, which applies to the cost of enchantments.
String unit. The unit of measurement. Defaults to "each," but may be a unit of weight, length, etc.
String bulk_type: The type of bulk storage container the item requires. "W" for liquids, "D" for dry goods like spices, or "N" for items which don't require containers, like bales of hides.
ArrayList<String> attributes: Attributes indicate what kinds of embellishments and other treatments an item may have. For example, a soft item ("S" is an attribute) might have resist dying or embroidery as an embellishment but not relief carving or silver plating, while a hard item ("H" is an attribute) would be the opposite.
ArrayList<Embellishment> embellishments: A list of embellishments applied to the item.
ArrayList<Enchantment> enchantments: A list of enchantments applied to the item.
ArrayList<Gem> gems: a list of gemstones embedded in the object. Gems are a bit more complicated than other embellishments, so they get their own object.
Item container: A container, if necessary. Containers are themselves items which can have embellishments of their own.
String motif_name: A brief description of the item's decorative motif. Defaults to an empty string, since not all items will have a motif.

This is really the key class, representing a single valuable item, which may have multiple embellishments, enchantments, and other frippery about it. Most of what goes on happens in the Item() constructor. First, the getRandomItem() method opens up the list of items (Items.xml), checks the length, gets a random entry within that range, and takes it apart to populate some of the key variables with data.

Certain attributes are treated specially: ammo, jewelry, and bulk goods get a variable quantity. For jewelry, the quantity is invisible; it's effectively a multiplier for heavier rings, larger bracelets, and so on. Bulk goods get additional treatment. There's a stripped-down constructor: Item(String c_type, Double wt_cap). Given a containment type and a weight capacity, it'll get a container out of a special items list (Containers.xml, which contains additional properties not relevant to most items) and make that the container property for the item.

If the item isn't bulk goods, it'll get embellishments and is a candidate for enchantments. After a number of embellishments is rolled up, it opens up the list of embellishments (embellishments.xml) and starts picking some at random. The embellishment's appropriateness is compared to the list of item attributes and, if it's suitable, is added to the list. The loop continues for a while to try to get the right number of embellishments, but it'll bail out if it takes too long. The "Jeweled" embellishment code briefly diverts the process into the machinery to add a randomly selected and sized new gem to the list. After the embellishments are selected, their properties are checked to see if the item needs a decorative motif, which is also selected randomly.

The enchantment section follows. There's a chance of an item having enchantments. If it does, the machinery for adding enchantments is essentially identical to that adding embellishments, with a few specific adjustments. For one thing, an enchanted item has a chance of getting a supernatural embellishment (picked at random from SupernaturalEmbellishments.xml). While the source of the embellishment may be mystical, it acts just like any other embellishment, so it gets added to the embellishment list rather than the enchantment list. There's also a chance of a curse, picked from Curses.xml.

Once created, the key function is report(), which sends back a full description of the item, summarizing the complex of embellishments, enchantments, and so on. The function starts by constructing an expression for the quantity and unit of measurement if necessary and the name of the item itself. Then it constructs a summary list of embellishments and, if necessary, motif, followed by enchantments, curses, container (the container's description is a stripped down version of the full report; it excludes cost and weight, since those will be included in the grand total), and finally the total cost (computed from item base cost, embellishment CFs, enchantment costs, quantity, and container cost) and weight.

package hoardomatic;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Random;
import java.util.Set;
import java.io.File;
import java.io.FileInputStream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class Item {

    private String item_name;
    private Double base_cost;
    private Double base_weight;
    private Integer quantity = 1;
    private String curse_name = "";
    private Double curse_cf = 0.0;
    private String unit = "";
    private String bulk_type;
    private ArrayList<String> attributes = new ArrayList<String>(); // item attributes
    private ArrayList<Embellishment> embellishments = new ArrayList<Embellishment>(); // decorative and functional embellishments
    private ArrayList<Enchantment> enchantments = new ArrayList<Enchantment>(); // enchantments
    private ArrayList<Gem> gems = new ArrayList<Gem>();
    private Item container = null;
    private String motif_name = "";
   
// shiny, iron, organic, plate, mail, scale, edged, impaling
   
    public Item() {
        // TODO Auto-generated constructor stub
        getRandomItem();

        curse_cf = 0.0;
        curse_name = "";

        if (isAttribute("AMMO")){
            // if it's ammo, make it a bunch of them
            quantity = dieRoll(1, 20);
        }
       
        if (isAttribute("Jewelry")){
            // if it's jewelry, maybe make it big
            quantity = dieRoll(1, 6);
        }
       
        if (isAttribute("BULK")){
            // if it's bulk goods, make it a bunch of them
            quantity = dieRoll(1, 6);
            // add a container
           
            //get type from Bulk Goods table.
            getBulkAttributes(getName());
            String the_type = bulk_type;
           
            if (!(unit.contentEquals("Bale")) & !(the_type.contentEquals("N"))){
                container = new Item(the_type, getFinalWeight());
            }
            // end container
        } else {
        // apply random number of embellishments
            Integer rounds = 0; // this prevents excessive looping
        Integer numEmbs = dieRoll(1, 3);
        while (embellishments.size() < numEmbs & rounds < 40 ){
            Node anEmbellishment = null;
            rounds = rounds + 1;
            while ( anEmbellishment == null){
                anEmbellishment = getRandomNodeFromTable("Embellishments.xml");
            }
            Embellishment newEmb = new Embellishment(anEmbellishment);
            if (newEmb.getEmbellishmentName().contentEquals("Jeweled")){
                Gem newgem = new Gem();
                gems.add(newgem);
               
            } else {
                String e_code = newEmb.getEmbCode();
                boolean already_got = hasEmbellishment(newEmb.getEmbellishmentName());
               
                String[] e_codes = e_code.split(";");
                boolean acceptable_attribute = false;
                for (int e_code_count = 0; e_code_count < e_codes.length; e_code_count++){
                    if(isAttribute(e_codes[e_code_count])){
                        acceptable_attribute = true;
                    }
                }
               
               
                if (isAttribute(e_code) | (item_name.toLowerCase().contains(e_code.toLowerCase()) & e_code.length() > 3 ) ){
                    if (! already_got){
                        embellishments.add(newEmb);
                    }
                }               
            }
        }
       
        // add a motif, if needed
       
        // go through the embellishments and see if they require or at least support a motif
        if(embellishments.size() > 0){
            String need_motif = "N";
            for (Embellishment eLine : embellishments) {
                String emb_motif = eLine.getMotifOption();
                if (emb_motif.contentEquals("Y")){
                    need_motif = "Y";
                }else if (emb_motif.contentEquals("P") & !(need_motif.contentEquals("Y")) ){
                    need_motif = "P";
                }
            }
           
            // if they support but don't require a motif, flip a coin
            if (need_motif.contentEquals("P") ){
                int flip = dieRoll(1,2);
                if (flip == 1){
                    need_motif = "Y";
                }
            }
           
            // so if we need a motif, add one
            if (need_motif.contentEquals("Y")){
                Node the_motif_node = getRandomNodeFromTable("Motifs.xml");
                NodeList tempNodes = the_motif_node.getChildNodes();
                for (int j = 0; j < tempNodes.getLength(); j++) {
                    Node subnode = tempNodes.item(j);

                    if (subnode.getNodeType() == Node.ELEMENT_NODE) {
                        Element element = (Element) subnode;

                        if (element.getNodeName().contentEquals("Motif")) {
                            motif_name = element.getTextContent();
                        }
                    }
                }
            }
        } // end embellishments

        // apply random number of enchantments

        rounds = 0;
        Integer hasEnch = dieRoll(1, 10);
        if (hasEnch > 5){
            Integer numEnch = dieRoll(1, 2);
            while (enchantments.size() < numEnch & rounds < 20 ){
                Node anEnchantment = null;
                rounds = rounds + 1;
                while ( anEnchantment == null){
                    anEnchantment = getRandomNodeFromTable("Enchantments.xml");
                }
       
                Enchantment newEnch = new Enchantment(anEnchantment);
                String e_code = newEnch.getenchCode();
               
                boolean already_got = hasEmbellishment(newEnch.getEnchantmentName());
               
                if (isAttribute(e_code) | e_code.contentEquals("ALL") ){
                    if (! already_got){
                        enchantments.add(newEnch);
                    }
                }
            }
       
            // check for supernatural embellishments
            Integer hasSE = dieRoll(1,10);
            if (hasSE > 5) {
                Node anEmbellishment = getRandomNodeFromTable("SupernaturalEmbellishments.xml");
            Embellishment newEmb = new Embellishment(anEmbellishment);
            String se_name = newEmb.getEmbellishmentName();
            newEmb.setEmbellishmentName(se_name + " (supernatural effect)");
                    embellishments.add(newEmb);
            }
            // check for curses
            Integer hasC = dieRoll(1,10);
            if (hasC > 9) {
                Node aCurse = getRandomNodeFromTable("Curses.xml");
                NodeList tempNodes = aCurse.getChildNodes();
                for (int j = 0; j < tempNodes.getLength(); j++) {
                    Node subnode = tempNodes.item(j);

                    if (subnode.getNodeType() == Node.ELEMENT_NODE) {
                        Element element = (Element) subnode;
                       
                        if (element.getNodeName().contentEquals("Curse")) {
                            curse_name = element.getTextContent();
                        }

                        if (element.getNodeName().contentEquals("CF")) {
                            curse_cf = Double.parseDouble(element.getTextContent());
                        }
                    }
                }
               
            }
            // end curses
        }
        // end enchantments
        }
       
    }
   
    public String report(){
       
        String full_report = "";
       
        // name and, if needed, quantity
        if(quantity > 1 & !(isAttribute("Jewelry")) ){
            full_report = full_report + quantity.toString() + " ";
           
            if (!(unit.contentEquals("Each"))){
                full_report = full_report + unit + " ";
            }
        }
        full_report = full_report + item_name + ". ";
       
        String motif_exp = "";
        if (!(motif_name.contentEquals("") )){
            motif_exp = "; decorated with " + motif_name + " motif";
        }
       
        // embellishment list
        if(embellishments.size() > 0){
            int rounds = 1;
            for (Embellishment eLine : embellishments) {
                full_report = full_report + eLine.getEmbellishmentName();
                rounds = rounds + 1;
                if (rounds <= embellishments.size()){
                    full_report = full_report + ", ";
                }else{
                    full_report = full_report + motif_exp + ". ";
                }
            }
        } // end embellishments
       
        // gem list
        if(gems.size() > 0){
            int rounds = 1;
           
            full_report = full_report + "Set with ";
           
            for (Gem eLine : gems) {
                full_report = full_report + eLine.getGemWt().toString() + " ct. " + eLine.getGemName();
                rounds = rounds + 1;
                if (rounds <= gems.size()){
                    full_report = full_report + ", ";
                }else{
                    full_report = full_report + ". ";
                }
            }
        } // end gems
       
        // enchantment list
       
        if(enchantments.size() > 0){
            int rounds = 1;
            full_report = full_report + "Enchanted with ";
            for (Enchantment eLine : enchantments) {
                full_report = full_report + eLine.getEnchantmentName() ;
                rounds = rounds + 1;
                if (rounds <= enchantments.size()){
                    full_report = full_report + ", ";
                }else{
                    full_report = full_report + ". ";
                }
            }
            if(!curse_name.contentEquals("")){
                full_report = full_report + "Cursed with " + curse_name + ". ";
            }
        } // end enchantments
       
        if(!(container == null)){
            full_report = full_report + "Contained in " + container.report_short();
        }

        Double f_cst = getFinalCost();
        Double f_wt = getFinalWeight();
       
        DecimalFormat df = new DecimalFormat("#.00");
        NumberFormat fmt = NumberFormat.getCurrencyInstance();
       
        full_report = full_report + fmt.format(f_cst) + ", ";
        full_report = full_report + df.format(f_wt);

        if (f_wt > 1){
            full_report = full_report + " lbs.";
        }else{
            full_report = full_report + " lb.";
        }
       
        return full_report;
       
    }
   
    public String report_short(){
       
        String full_report = "";
       
        // name and, if needed, quantity
        if(quantity > 1){
            full_report = full_report + quantity.toString() + " x ";
        }
        full_report = full_report + item_name + ". ";
       
        String motif_exp = "";
        if (!(motif_name.contentEquals("") )){
            motif_exp = "; decorated with " + motif_name + " motif";
        }
       
        // embellishment list
        if(embellishments.size() > 0){
            int rounds = 1;
            for (Embellishment eLine : embellishments) {
                full_report = full_report + eLine.getEmbellishmentName();
                rounds = rounds + 1;
                if (rounds <= embellishments.size()){
                    full_report = full_report + ", ";
                }else{
                    full_report = full_report + motif_exp + ". ";
                }
            }
        } // end embellishments
       
        // enchantment list
       
        if(enchantments.size() > 0){
            int rounds = 1;
            full_report = full_report + "Enchanted with ";
            for (Enchantment eLine : enchantments) {
                full_report = full_report + eLine.getEnchantmentName() ;
                rounds = rounds + 1;
                if (rounds <= enchantments.size()){
                    full_report = full_report + ", ";
                }else{
                    full_report = full_report + ". ";
                }
            }
            if(!curse_name.contentEquals("")){
                full_report = full_report + "Cursed with " + curse_name + ". ";
            }
        } // end enchantments
       
        return full_report;
    }

    public Item(String specific_item) {
        // TODO Auto-generated constructor stub
        getNamedItem(specific_item);
        //
    }
   
    public Item(double low_val, double high_val) {
        // get random item
        getRandomItem();
       
    }
   
    public Double getFinalCost(){
        Double final_cost = getBaseCost();
        final_cost = final_cost * (1 + getTotalCF());
        final_cost = final_cost + getEnchantmentCosts();
               
        // gem list
        if(gems.size() > 0){
           
            for (Gem eLine : gems) {
                final_cost = final_cost + eLine.getGemCost();
            }
        } // end gems
       
        if (!(container == null)){
            final_cost = final_cost + container.getFinalCost();
        }
       
        return final_cost;
    }
   
    public boolean hasEmbellishment(String embName){
        boolean return_it = false;
        if (embellishments.size() > 0){
            for (Embellishment eLine : embellishments) {
                String this_nom = eLine.getEmbellishmentName();
                String embName_short = "";
               
                if (embName.indexOf("(") > 0){
                    embName_short = embName.substring(0, embName.indexOf("("));
                }else{
                    embName_short = embName;
                }
               
                if (this_nom.contains(embName_short)){
                    return_it = true;
                }
            }
        }
       
        return return_it;

    }
   
    public Double getTotalCF(){
        Double total_cf = 0.0;
        if (embellishments.size() > 0){
            for (Embellishment eLine : embellishments) {
                total_cf = total_cf + eLine.getCf();
            }
        }
        return total_cf;
    }
   
    public Double getEnchantmentCosts(){
        Double total_costs = 0.0;
        if (enchantments.size() > 0){
            for (Enchantment eLine : enchantments) {
                total_costs = total_costs + eLine.getCost() ;
            }
        }
       
        total_costs = total_costs * (1+ curse_cf);
       
        return total_costs;
    }
   
    public void getRandomItem() {

        try {
            InputStream items = new FileInputStream(new File("Items.xml"));
           
            DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
            Document doc = dBuilder.parse(items);

            doc.getDocumentElement().normalize();
            NodeList nodes = doc.getElementsByTagName("item");
            Integer itemlistlen = nodes.getLength();
            Integer pickItm = dieRoll(0, itemlistlen-1);
            for (int i = 0; i < nodes.getLength(); i++) {
                Node node = nodes.item(i);
                NamedNodeMap attrs = node.getAttributes();
               
                Node sn = attrs.getNamedItem("name");
               
                if (i == pickItm) {

                    NodeList tempNodes = node.getChildNodes();
                    for (int j = 0; j < tempNodes.getLength(); j++) {
                        Node subnode = tempNodes.item(j);

                        if (subnode.getNodeType() == Node.ELEMENT_NODE) {
                            Element element = (Element) subnode;

                            if (element.getNodeName().contentEquals("name")) {
                                item_name = element.getTextContent();
                            }

                            if (element.getNodeName().contentEquals("Wt")) {
                               
                                String base_read = element.getTextContent();
                                if (base_read == "" | base_read.length() == 0)
                                {
                                    base_cost = 0.0;
                                } else {
                                    base_weight = Double.parseDouble(element.getTextContent());
                                }
                            }

                            if (element.getNodeName().contentEquals("Cost")) {
                               
                                String base_read = element.getTextContent();
                                if (base_read == "" | base_read.length() == 0)
                                {
                                    base_cost = 0.0;
                                } else {
                                    base_cost = Double.parseDouble(element.getTextContent());
                                }
                               
                            }

                            if (element.getNodeName().contentEquals("attributes")) {
                                attributes.add(element.getAttribute("value").toString()) ;
                            }
                        }

                    }

                }

            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

   
    public void getNamedItem(String the_item) {

        try {
            InputStream items = new FileInputStream(new File("Items.xml"));
           
            DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
            Document doc = dBuilder.parse(items);

            doc.getDocumentElement().normalize();
            NodeList nodes = doc.getElementsByTagName("item");

            for (int i = 0; i < nodes.getLength(); i++) {
                Node node = nodes.item(i);
                NamedNodeMap attrs = node.getAttributes();
               
                Node sn = attrs.getNamedItem("name");
               
                if (sn.getTextContent().contentEquals(the_item)) {

                    NodeList tempNodes = node.getChildNodes();
                    for (int j = 0; j < tempNodes.getLength(); j++) {
                        Node subnode = tempNodes.item(j);

                        if (subnode.getNodeType() == Node.ELEMENT_NODE) {
                            Element element = (Element) subnode;

                            if (element.getNodeName().contentEquals("name")) {
                                item_name = element.getTextContent();
                            }

                            if (element.getNodeName().contentEquals("Wt")) {
                               
                                String base_read = element.getTextContent();
                                if (base_read == "" | base_read.length() == 0)
                                {
                                    base_cost = 0.0;
                                } else {
                                    base_weight = Double.parseDouble(element.getTextContent());
                                }
                            }

                            if (element.getNodeName().contentEquals("Cost")) {
                               
                                String base_read = element.getTextContent();
                                if (base_read == "" | base_read.length() == 0)
                                {
                                    base_cost = 0.0;
                                } else {
                                    base_cost = Double.parseDouble(element.getTextContent());
                                }
                               
                            }

                            if (element.getNodeName().contentEquals("attributes")) {
                                attributes.add(element.getAttribute("value").toString()) ;
                            }

                        }

                    }

                }

            }

        } catch (Exception e) {
            e.printStackTrace();
        }

    }
   
    public Double getFinalWeight(){
       
        Double running_weight = base_weight;
        if (embellishments.size() > 0){
            for (Embellishment eLine : embellishments) {
                running_weight = running_weight * eLine.getWtMod();
            }
        }
        running_weight = running_weight * quantity;
        if (!(container == null)){
            running_weight = running_weight + container.getFinalWeight();
        }
        return running_weight;
    }
   
     public String getName(){
         return item_name;
     }

     public Integer getQuantity(){
         return quantity;
     }

     public Double getWeight(){
         // do a bunch of math
         return base_weight;
     }
     public Double getCost(){
         return base_cost;
     }
     public Double getBaseWeight(){
         return base_weight;
     }
     public Double getBaseCost(){
         return base_cost;
     }
   
     public boolean isAttribute(String test_val){
         boolean p = false;
            for (String aLine : attributes) {
                if (aLine.contentEquals(test_val)) {
                    p = true;
                }
            }
         return p;
     }
   
     public String getEmbellishments(){
         return "";
     }
     public boolean isEmbellishment(String test_val){
         return true;
     }
     public String getEnchantments(){
         return "";
     }
     public boolean isEnchantment(String test_val){
         return true;
     }
     public String getContainer(){
         return "";
     }
     public void addEmbellishment(){}
     public void addEnchantment(){}
     public void addContainer(){}
   
     public ArrayList<Gem> getGems(){
         return gems;
     }

    public Node getRandomNodeFromTable(String fname) {
           
            Node return_it;
            return_it = null;

            try {
                InputStream items = new FileInputStream(new File(fname));
               
                DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
                DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
                Document doc = dBuilder.parse(items);

                doc.getDocumentElement().normalize();
                NodeList nodes = doc.getElementsByTagName("item");
                Integer itemlistlen = nodes.getLength();
                Integer pickItm = dieRoll(0, itemlistlen - 1);

                for (int i = 0; i < nodes.getLength(); i++) {
                    Node node = nodes.item(i);
                    if (i == pickItm) {
                        return_it = node;
                    }
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
            return return_it;
        }
       
       
        public String getBulkType(String bulk_name){
            String bulk_type = "";
           
            Node bulk_node = getNamedNodeFromTable("BulkGoods.xml", bulk_name);
           
            NodeList tempNodes = bulk_node.getChildNodes();
            for (int j = 0; j < tempNodes.getLength(); j++) {
                Node subnode = tempNodes.item(j);

                if (subnode.getNodeType() == Node.ELEMENT_NODE) {
                    Element element = (Element) subnode;
                   
                    if (element.getNodeName().contentEquals("ContainmentType")) {
                        bulk_type = element.getTextContent();
                    }

                }

            }
           
            return bulk_type;
        }
       
        public void getBulkAttributes(String bulk_name){
           
            Node bulk_node = getNamedNodeFromTable("BulkGoods.xml", bulk_name);
           
            NodeList tempNodes = bulk_node.getChildNodes();
            for (int j = 0; j < tempNodes.getLength(); j++) {
                Node subnode = tempNodes.item(j);

                if (subnode.getNodeType() == Node.ELEMENT_NODE) {
                    Element element = (Element) subnode;
                   
                    if (element.getNodeName().contentEquals("ContainmentType")) {
                        bulk_type = element.getTextContent();
                    }

                    if (element.getNodeName().contentEquals("Unit")) {
                        unit = element.getTextContent();
                    }

                }

            }
        }
       
   
        public Node getNamedNodeFromTable(String fname, String get_item) {
           
            Node return_it;
            return_it = null;

            try {
                InputStream items = new FileInputStream(new File(fname));
               
                DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
                DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
                Document doc = dBuilder.parse(items);

                doc.getDocumentElement().normalize();
                NodeList nodes = doc.getElementsByTagName("item");

                for (int i = 0; i < nodes.getLength(); i++) {
                    Node node = nodes.item(i);
                    NamedNodeMap attrs = node.getAttributes();
                   
                    Node sn = attrs.getNamedItem("name");
                    if (sn.getTextContent().contentEquals(get_item)) {
                        return_it = node;
                    }
               
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
            return return_it;
        }
       
        private int dieRoll(Integer min, Integer max) {

            Random r = new Random();
            int inroll = r.nextInt(max - min + 1) + min;
            return inroll;
        }
       
        // container stuff
        public Item(String c_type, Double wt_cap) {
           
            // This creates a container item of a given type and capacity
           
            getSizedContainer(c_type, wt_cap);
               
                // apply random number of embellishments
                    Integer rounds = 0;
                Integer numEmbs = dieRoll(1, 2);
                while (embellishments.size() < numEmbs & rounds < 20 ){
                    Node anEmbellishment = null;
                    rounds = rounds + 1;
                    while ( anEmbellishment == null){
                        anEmbellishment = getRandomNodeFromTable("Embellishments.xml");
                    }
           
                    Embellishment newEmb = new Embellishment(anEmbellishment);
                   
                    if (newEmb.getEmbellishmentName().contentEquals("Jeweled")){
                        Gem newgem = new Gem();
                        gems.add(newgem);
                       
                    } else {
                        String e_code = newEmb.getEmbCode();
                        boolean already_got = hasEmbellishment(newEmb.getEmbellishmentName());
                        if (isAttribute(e_code) | (item_name.toLowerCase().contains(e_code.toLowerCase()) & e_code.length() > 3 ) ){
                            if (! already_got){
                                embellishments.add(newEmb);
                            }
                        }               
                    }
               
               
               
                }
               
                // add a motif, if needed
               
                // go through the embellishments and see if they require or at least support a motif
                if(embellishments.size() > 0){
                    String need_motif = "N";
                    for (Embellishment eLine : embellishments) {
                        String emb_motif = eLine.getMotifOption();
                        if (emb_motif.contentEquals("Y")){
                            need_motif = "Y";
                        }else if (emb_motif.contentEquals("P") & !(need_motif.contentEquals("Y")) ){
                            need_motif = "P";
                        }
                    }
                   
                    // if they support but don't require a motif, flip a coin
                    if (need_motif.contentEquals("P") ){
                        int flip = dieRoll(1,2);
                        if (flip == 1){
                            need_motif = "Y";
                        }
                    }
                   
                    // so if we need a motif, add one
                    if (need_motif.contentEquals("Y")){
                        Node the_motif_node = getRandomNodeFromTable("Motifs.xml");
                        NodeList tempNodes = the_motif_node.getChildNodes();
                        for (int j = 0; j < tempNodes.getLength(); j++) {
                            Node subnode = tempNodes.item(j);

                            if (subnode.getNodeType() == Node.ELEMENT_NODE) {
                                Element element = (Element) subnode;

                                if (element.getNodeName().contentEquals("Motif")) {
                                    motif_name = element.getTextContent();
                                }
                            }
                        }
                    }
                } // end embellishments
            }

            public void getSizedContainer(String substance_type, Double wt_capacity) {
               
                Double ct_cap = 0.0;
                String ct_type = "";

                try {
                   
                    InputStream items = new FileInputStream(new File("Containers.xml"));
                   
                    DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
                    DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
                    Document doc = dBuilder.parse(items);

                    doc.getDocumentElement().normalize();
                    NodeList nodes = doc.getElementsByTagName("item");

                    for (int i = 0; i < nodes.getLength(); i++) {
                        Node node = nodes.item(i);
                        NamedNodeMap attrs = node.getAttributes();
                        Node sn = attrs.getNamedItem("name");
                            NodeList tempNodes = node.getChildNodes();
                            for (int j = 0; j < tempNodes.getLength(); j++) {
                                Node subnode = tempNodes.item(j);

                                if (subnode.getNodeType() == Node.ELEMENT_NODE) {
                                    Element element = (Element) subnode;
                                   
                                    if (element.getNodeName().contentEquals("ContainmentType")) {
                                        ct_type = element.getTextContent();
                                    }
                                    if (element.getNodeName().contentEquals("Capacity")) {
                                       
                                        String base_read = element.getTextContent();
                                            ct_cap = Double.parseDouble(element.getTextContent());
                                    }
                                   

                                    if (element.getNodeName().contentEquals("name")) {
                                        item_name = element.getTextContent();
                                    }

                                    if (element.getNodeName().contentEquals("Wt")) {
                                       
                                        String base_read = element.getTextContent();
                                        if (base_read == "" | base_read.length() == 0)
                                        {
                                            base_cost = 0.0;
                                        } else {
                                            base_weight = Double.parseDouble(element.getTextContent());
                                        }
                                    }

                                    if (element.getNodeName().contentEquals("Cost")) {
                                       
                                        String base_read = element.getTextContent();
                                        if (base_read == "" | base_read.length() == 0)
                                        {
                                            base_cost = 0.0;
                                        } else {
                                            base_cost = Double.parseDouble(element.getTextContent());
                                        }
                                    }
                                    if (element.getNodeName().contentEquals("attributes")) {
                                        attributes.add(element.getAttribute("value").toString()) ;
                                    }
                               
                                   
                                }
                            }
                           
                            // if the containment type is good and the container will hold enough, break the loop
                            if (ct_type.contentEquals(substance_type) & ct_cap >= wt_capacity){
                                break;
                            }
                           
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }
           
        // end container stuff

}