import random as r import argparse beg = ["a","e","i","o","u","ba","be","bi","bo","bu","by","y","da","de","di","do","du","dy","fa","fi","fo","fe","fu","ga","ge","gi","go","gu","ka","ke","ki","ko","ku","ky","ma","me","mi","mo","mu","na","ne","ni","no","nu","pa","pe","pi","po","pu","ra","re","ri","ro","ru","ry","sa","se","si","so","su","ta","te","ti","to","tu","ty","wa","we","wi","wo","wy","za","ze","zi","zo","zu","zy"] mid = beg + ["l","x","n","r"] def gen_name(length=None, minimum=3, maximum=9): if length == None: lgt = r.randint(minimum,maximum) else: lgt = length morae = [] while len("".join(morae)) < lgt: if len(morae) == 0: mora = r.choice(beg) morae.append(mora[0].upper() + mora[1:]) else: mora = r.choice(mid) if morae[-1] == mora: mora = r.choice(mid) morae.append(mora) return "".join(morae) class Plot: loc1 = ["friendly","hostile","derelict","airless","poison-filled/covered","overgrown","looted","burning","frozen","haunted","infested"] loc2 = ["asteroid","moon","space station","spaceship","ringworld","Dyson sphere","planet","Space Whale","pocket of folded space","time vortex","Reroll"] miss = ["to explore","to loot everything not bolted down too securely","to find the last group of kobolds who came here","to find a rumored secret weapon","to find a way to break someone else's secret weapon","to claim this place in the name of the Kobold Empire","to make friends","to rediscover lost technology","to find lost magical items","to find and defeat a powerful enemy"] prob = [ { "id": 0, "name": "an Undead Sample Pack (swarm of zombies and skeletons)", "shortname": "undead", "stats": [0,5,2,6], }, { "id": 1, "name": "a rival band of kobolds", "shortname": "kobold rivals", "stats": [3,3,4,4], }, { "id": 2, "name": "a detachment from the Elf Armada", "shortname": "elves", "stats": [4,3,5,4], }, { "id": 3, "name": "refugees with parasites. Big parasites", "shortname": "refugees", "stats": [2,4,0,0], }, { "id": 4, "name": "an artificial intelligence bent on multi-world domination", "shortname": "AI", "stats": [4,1,6,3], }, { "id": 5, "name": "robot spiders", "shortname": "spiders", "stats": [3,3,2,4], }, { "id": 6, "name": "semi-intelligent metal eating slime", "shortname": "slime", "stats": [0,2,1,5], }, { "id": 7, "name": "a living asteroid that intends to follow the kobolds home like the largest puppy", "shortname": "asteroid", "stats": [2,3,1,6], }, { "id": 8, "name": "an old lich that wants everyone to stay off of their 'lawn'", "shortname": "lich", "stats": [5,2,6,3], }, { "id": 9, "name": "elder gods hailing from the dark spaces between the stars", "shortname": "gods", "stats": [6,6,6,6], }, { "id": 10, "name": "a Flying Brain Monster", "shortname": "Flying Brain Monster", "stats": [2,3,6,1], }, ] def __init__(self): self.loc_desc = Plot.loc1[r.randint(0, len(Plot.loc1)-1)] self.locIndex = r.randint(0, len(Plot.loc2)-1) if self.locIndex == len(Plot.loc2) - 1: self.location = "battlefield on " self.locIndex = r.randint(0, len(Plot.loc2)-2) self.mainLocation = Plot.loc2[self.locIndex] if self.mainLocation[0].lower() in ["a","e","i","o","u"]: self.location += "an " else: self.location += "a " self.location += self.mainLocation else: self.location = Plot.loc2[self.locIndex] self.missIndex = r.randint(0, len(Plot.miss)-1) self.oops = r.randint(1,12) self.mission = "" if self.oops == 12: self.mission += "...well, they weren't paying attention, so don't tell them, but they're supposed " self.mission += Plot.miss[r.randint(0, len(Plot.miss)-1)] + "!" self.probIndex = r.randint(0, len(Plot.prob)-1) self.problem = Plot.prob[self.probIndex] self.secProblem = None self.thirdProblem = None if self.problem["id"] in [1,2]: self.problem["name"] += " led by " + gen_name() if self.problem["id"] in [4,7,8,10]: self.problem["name"] += " named " + gen_name(minimum=5, maximum=12) if self.problem["id"] == 3: self.secProblem = {"name": "Parasites", "shortname": "parasites", "stats": [3,4,2,3]} if self.problem["id"] == 10: self.secProbIndex = r.randint(0, len(Plot.prob)-2) self.secProblem = Plot.prob[self.secProbIndex] if self.secProbIndex == 3: self.thirdProblem = {"name": "Parasites", "shortname": "parasites", "stats": [3,4,2,3]} self.fullProblem = self.problem["name"] if self.secProblem and self.secProblem["name"] != "Parasites": self.fullProblem += " and its minion, " + self.secProblem["name"] class Character: def __init__(self): self.name = "" self.career = "" self.stats = [] def generate(self): self.gen_name() self.gen_stats() self.gen_career() def gen_name(self): self.name = gen_name() def gen_stats(self, n=12): if n < 0: print("Too few stat points!") return [0,0,0,0] stats = [0,0,0,0] points = n slots = [0,1,2,3] for _ in range(points): tgl = False while tgl == False: slt = r.choice(slots) if slt <= 1: if stats[slt] == 6: continue if stats[slt] == 5 and r.randint(0,1) != 1: continue else: if stats[slt] == 6: continue if stats[slt] > 2 and r.randint(0,stats[slt]-2) != 1: continue stats[slt] += 1 tgl = True stats[3] = stats[3] + 1 if stats[3] > 6: stats[3] = 6 stats[2] = stats[2] + 1 if stats[2] > 6: stats[2] = 6 self.stats = stats def gen_career(self): self.career = r.choice(["Soldier/Guard","Pilot","Medic","Mechanic","Politician","Spellcaster","Performer","Historian","Spy","Cook","Cartographer","Inventor","Merchant"]) def print_name(self, html=False): if html: charText = f"

Name: {self.name} (Kobold {self.career})

" else: charText = f"\nName: {self.name} (Kobold {self.career})" print(charText) def print(self, html=False): if html: out = ( f"
\n" f" \n" f" {self.name}
\n" f" Kobold {self.career}\n" f"
\n" f"
\n" f" \n" f" \n" f" \n" f"
\n" ) print(out) else: self.print_name() print(f"Order: {self.stats[0]}") print(f"Chaos: {self.stats[1]}") print(f"Brain: {self.stats[2]}") print(f"Body: {self.stats[3]}") class Ship: def __init__(self): self.name1 = r.choice(["Red","Orange","Yellow","Green","Blue","Violet","Dark","Light","Frenzied","Maniacal","Ancient"]) self.name2 = r.choice(["Moon","Comet","Star","Saber","World-Eater","Dancer","Looter","Phlogiston","Fireball","Mecha","Raptor"]) self.gqual = r.choice(["is stealthy & unarmored","is speedy & unarmored","is maneuverable & unarmored","is always repairable","is self-repairing","is flamboyant & speedy","is slow & armored","is flamboyant & armored","is hard to maneuver & armored","has Too Many Weapons!","has a prototype hyperdrive"]) self.bqual = r.choice(["has an annoying AI","has inconveniently crossed circuits","has an unpredictable power source","drifts to the right","is haunted","was recently 'found' so the kobolds are unused to it","is too cold","has a constant odd smell","its interior design... changes","its water pressure shifts between slow drip and power wash","it leaves a visible smoke trail"]) self.fullname = f"{self.name1} {self.name2}" def print(self, html=False): if (html): shipText = f"

The {self.fullname} {self.gqual}, but {self.bqual}.

\n" else: shipText = f"The {self.fullname} {self.gqual}, but {self.bqual}.\n" print(shipText) class Campaign: def __init__(self, n=None, makeChars=True): n = 6 if n == None else n self.ship = Ship() self.params = Plot() if makeChars: self.characters = [] for _ in range(n): c = Character() c.generate() self.characters.append(c) self.art = "an" if self.params.loc_desc[0] in ["a","e","i","o","u"] else "a" def print_params(self, endc=" ", html=False): st = ["Order:", "Chaos:", "Brains:", "Body:"] cst = ", ".join([" ".join(y) for y in list(zip(st, [str(x) for x in self.params.problem["stats"]]))]) lines = [ f"The Kobolds of the {self.ship.fullname}", f"have been sent out to {self.art} {self.params.loc_desc} {self.params.location}", f"in order {self.params.mission}", f"but they're challenged by {self.params.fullProblem}!", f"The stats of the {self.params.problem['shortname']}", f"{cst}" ] if self.params.secProblem: mst = ", ".join([" ".join(y) for y in list(zip(st, [str(x) for x in self.params.secProblem["stats"]]))]) lines.append(f"The stats of the {self.params.secProblem['shortname']}") lines.append(f"{mst}") if self.params.thirdProblem: pst = ", ".join([" ".join(y) for y in list(zip(st, [str(x) for x in self.params.thirdProblem["stats"]]))]) lines.append(f"The stats of the {self.params.thirdProblem['shortname']}") lines.append("{pst}") if html: out = ( f"
\n" f" The Ship\n" f" \n" f" The {self.ship.fullname}!\n" f" \n" f"
\n" f" \n" f" It {self.ship.gqual}...\n" f"
\n" f" \n" f" But {self.ship.bqual}!\n" f" \n" f"
\n" f"
\n" f" The Mission\n" f" \n" f" The kobolds have been sent to {self.art} {self.params.loc_desc} {self.params.location}\n" f"
\n" f" \n" f" in order {self.params.mission}\n" f" \n" f"
\n" f"
\n" f" The Adversary\n" f" They're challenged by {self.params.fullProblem}!\n" f"
\n" f" \n" f" The stats of the {self.params.problem['shortname']}:
\n" f" {cst}\n" f"
\n" ) if self.params.secProblem: out += ( f"
\n" f" The stats of the {self.params.secProblem['shortname']}:
\n" f" {mst}\n" f"
\n" ) if self.params.thirdProblem: out += ( f"
\n" f" The stats of the {self.params.thirdProblem['shortname']}:
\n" f" {pst}\n" f"
\n" ) out += f"
\n" print(out) print(f"
\n") else: print(f"{lines[0]} {lines[1]} {lines[2]} -- {lines[3]}") print(f"{lines[4]}: {lines[5]}") if self.params.secProblem: print(f"- {lines[6]}: {lines[7]}") if self.params.thirdProblem: print(f"- - {lines[8]}: {lines[9]}") print() self.ship.print(html=html) def print_chars(self, html=False): if html: print(f"
\n") print(f"The Kobolds\n") else: print("The kobolds:") for k in self.characters: k.print(html=html) if html: print(f"
\n
\n") if __name__ == "__main__": parser = argparse.ArgumentParser() group = parser.add_mutually_exclusive_group() group.add_argument("-c", "--campaign", help="print a full campaign block with N kobolds (default 6)", nargs="?", const=6, type=int, metavar="N") group.add_argument("-k", "--kobolds", help="print N kobolds", type=int, nargs="?", const=1, default=1, metavar="N") group.add_argument("-n", "--names", help="print N kobolds without stat blocks", nargs="?", const=1, type=int, metavar="N") group.add_argument("-p", "--params", help="print only the parameters of a campaign", action="store_true") group.add_argument("-s", "--ship", help="print only the ship name and description", action="store_true") parser.add_argument("--html", help="print in HTML instead of plain text", action="store_true") args = parser.parse_args() # print(args) html = True if args.html else False if args.campaign: cmp = Campaign(args.campaign) cmp.print_params(html=html) cmp.print_chars(html=html) elif args.params: cmp = Campaign(makeChars=False) cmp.print_params(html=html) elif args.ship: ship = Ship() ship.print(html=html) elif args.names: for _ in range(args.names): c = Character() c.generate() c.print_name(html=html) else: for _ in range(args.kobolds): c = Character() c.generate() c.print(html=html)