A character/one-shot generator for KOBOLDS IN SPACE!
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900
  1. import random as r
  2. import argparse
  3. import sys
  4. import adversaries
  5. import gear
  6. 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"]
  7. mid = beg + ["l","x","n","r", "k"]
  8. def binarize(num, l=None):
  9. r = bin(num)[2:]
  10. try:
  11. l = int(l)
  12. except:
  13. l = None
  14. if l != None:
  15. while len(r) < l:
  16. r = "0" + r
  17. return r
  18. def gen_name(length=None, minimum=3):
  19. maximum = 8
  20. if length == None:
  21. lgt = r.randint(minimum,maximum)
  22. else:
  23. if length > maximum:
  24. length = maximum
  25. print("Maximum name length is 8.")
  26. lgt = length
  27. morae = []
  28. while len("".join(morae)) < lgt:
  29. if len(morae) == 0:
  30. mora = r.choice(beg)
  31. morae.append(mora[0].upper() + mora[1:])
  32. else:
  33. mora = r.choice(mid)
  34. if morae[-1] == mora:
  35. mora = r.choice(mid)
  36. morae.append(mora)
  37. fname = "".join(morae)[:lgt]
  38. return fname
  39. class Plot:
  40. loc1 = ["friendly","hostile","derelict","airless","poison-filled/covered","overgrown","looted","burning","frozen","haunted","infested"]
  41. loc2 = ["asteroid","moon","space station","spaceship","ringworld","Dyson sphere","planet","Space Whale","pocket of folded space","time vortex","battlefield"]
  42. miss = ["explore","loot everything not bolted down too securely","find the last group of kobolds who came here","find a rumored secret weapon","find a way to break someone else's secret weapon","claim this place in the name of the Kobold Empire","make friends","rediscover lost technology","find lost magical items","find and defeat a powerful enemy"]
  43. prob = adversaries.prob
  44. def __init__(self, loc_desc=None, locIndex=None, battlefield=None, location=None, missIndex=None, oops=None, mission=None, probIndex=None, problem=None, probName=None, secProblem=None, thirdProblem=None):
  45. self.loc_desc = loc_desc if loc_desc != None else Plot.loc1[r.randint(0, len(Plot.loc1)-1)]
  46. self.locIndex = int(locIndex) if locIndex != None else r.randint(0, len(Plot.loc2)-1)
  47. self.battlefield = int(battlefield) if battlefield != None else 0
  48. self.location = location if location != None else Plot.loc2[self.locIndex]
  49. if locIndex == None and Plot.loc2[self.locIndex] == "battlefield":
  50. self.battlefield = 1
  51. self.locIndex = r.randint(0, len(Plot.loc2)-2)
  52. elif locIndex == None and self.locIndex != len(Plot.loc2) - 1:
  53. if self.location == "":
  54. self.location = Plot.loc2[self.locIndex]
  55. if self.location[0].lower() in ["a","e","i","o","u"]:
  56. self.locart = 1
  57. else:
  58. self.locart = 0
  59. self.missIndex = missIndex if missIndex != None else r.randint(0, len(Plot.miss)-1)
  60. self.oops = int(oops) if oops != None else r.randint(1,12)
  61. if self.oops != 1:
  62. self.oops = 0
  63. self.mission = Plot.miss[self.missIndex]
  64. self.probIndex = probIndex if probIndex != None else r.randint(0, len(Plot.prob)-1)
  65. self.problem = Plot.prob[self.probIndex]
  66. if type(self.problem["name"]) != str:
  67. self.problem["name"] = self.problem["name"]()
  68. if type(self.problem["note"]) != str:
  69. self.problem["note"] = self.problem["note"]()
  70. if self.problem["hasMinion"]:
  71. if secProblem != None:
  72. try:
  73. self.secProblem = Plot.prob[secProblem]
  74. except:
  75. self.secProblem = Plot.prob[r.choice(self.problem["potentialMinions"])]
  76. else:
  77. self.secProblem = Plot.prob[r.choice(self.problem["potentialMinions"])]
  78. if type(self.secProblem["name"]) != str:
  79. self.secProblem["name"] = self.secProblem["name"]()
  80. if type(self.secProblem["note"]) != str:
  81. self.secProblem["note"] = self.secProblem["note"]()
  82. self.fullProblem = ""
  83. if self.problem["needsName"]:
  84. self.problem["givenname"] = probName if probName != None else gen_name()
  85. self.fullProblem += self.problem["givenname"] + ", "
  86. if not self.problem["isPlural"] and self.problem["name"].split(" ")[0] not in ["Old", "Pev"]:
  87. if self.problem["name"][0].lower() in ["a","e","i","o","u"]:
  88. self.fullProblem += "an "
  89. else:
  90. self.fullProblem += "a "
  91. self.fullProblem += self.problem["name"]
  92. if self.problem["hasMinion"]:
  93. self.fullProblem += " and their minion, "
  94. # if self.secProblem["name"][0].lower() in ["a","e","i","o","u"]:
  95. # self.fullProblem += "an "
  96. # else:
  97. # self.fullProblem += "a "
  98. self.fullProblem += self.secProblem["name"]
  99. class Character:
  100. # Remember to update gen_gadget() when you add gadgets
  101. GADGETS = gear.gadgets
  102. # Remember to update gen_career() when you add careers
  103. CAREERS = [ {"id": 0, "name": "Soldier/Guard"},
  104. {"id": 1, "name": "Pilot"},
  105. {"id": 2, "name": "Medic"},
  106. {"id": 3, "name": "Mechanic"},
  107. {"id": 4, "name": "Politician"},
  108. {"id": 5, "name": "Spellcaster"},
  109. {"id": 6, "name": "Performer"},
  110. {"id": 7, "name": "Historian"},
  111. {"id": 8, "name": "Spy"},
  112. {"id": 9, "name": "Cook"},
  113. {"id": 10, "name": "Cartographer"},
  114. {"id": 11, "name": "Inventor"},
  115. {"id": 12, "name": "Merchant"},
  116. {"id": 13, "name": "Bard"},
  117. {"id": 14, "name": "Sorcerer"},
  118. {"id": 15, "name": "Cleric"},
  119. {"id": 16, "name": "Rogue"},
  120. {"id": 17, "name": "Ranger"},
  121. {"id": 18, "name": "Barbarian"},
  122. {"id": 19, "name": "Artificer"},
  123. {"id": 20, "name": "Druid"},
  124. {"id": 21, "name": "Wizard"},
  125. {"id": 22, "name": "Fighter"},
  126. {"id": 23, "name": "Monk"},
  127. {"id": 24, "name": "Paladin"},
  128. {"id": 25, "name": "Warlock"},
  129. {"id": 26, "name": "Blood Hunter"}
  130. ]
  131. def __init__(self, name=None, career=None, stats=None, gadget=None):
  132. self.name = name if name != None else ""
  133. if career == None:
  134. self.career = ""
  135. elif isinstance(career, str):
  136. self.career = career
  137. elif isinstance(career, int) and career in range(27):
  138. self.career = [x for x in Character.CAREERS if x["id"] == career][0]
  139. else:
  140. self.career = ""
  141. self.stats = stats if stats != None else []
  142. if gadget == None:
  143. self.gadget = ""
  144. elif isinstance(gadget, str) or isinstance(gadget, dict):
  145. self.gadget = gadget
  146. if type(self.gadget["description"]) != str:
  147. self.gadget["description"] = self.gadget["description"]()
  148. else:
  149. self.gadget = next((x for x in Character.GADGETS if x["id"] == gadget), "")
  150. if type(self.gadget["description"]) != str:
  151. self.gadget["description"] = self.gadget["description"]()
  152. self.generate()
  153. def generate(self):
  154. if self.name == "" or self.name == None:
  155. self.gen_name()
  156. if self.stats == [] or self.stats == None:
  157. self.gen_stats()
  158. if self.career == "" or self.career == None:
  159. self.gen_career()
  160. if self.gadget == "" or self.gadget == None:
  161. self.gen_gadget()
  162. def gen_name(self):
  163. self.name = gen_name()
  164. def gen_stats(self, n=12):
  165. if n < 0:
  166. print("Too few stat points!")
  167. return [0,0,0,0]
  168. stats = [0,0,0,0]
  169. points = n
  170. slots = [0,1,2,3]
  171. for _ in range(points):
  172. tgl = False
  173. while tgl == False:
  174. slt = r.choice(slots)
  175. if slt <= 1:
  176. if stats[slt] == 6: continue
  177. if stats[slt] == 5 and r.randint(0,1) != 1: continue
  178. else:
  179. if stats[slt] == 6: continue
  180. if stats[slt] > 2 and r.randint(0,stats[slt]-2) != 1: continue
  181. stats[slt] += 1
  182. tgl = True
  183. stats[3] = stats[3] + 1
  184. if stats[3] > 6:
  185. stats[3] = 6
  186. stats[2] = stats[2] + 1
  187. if stats[2] > 6:
  188. stats[2] = 6
  189. self.stats = stats
  190. def gen_career(self):
  191. cid = r.randint(0,26)
  192. self.career = next((x for x in Character.CAREERS if x["id"] == cid), "")
  193. def gen_gadget(self):
  194. gid = r.randint(0,36)
  195. self.gadget = [x for x in Character.GADGETS if x["id"] == gid][0]
  196. if type(self.gadget["description"]) != str:
  197. self.gadget["description"] = self.gadget["description"]()
  198. def print_name(self, html=False):
  199. if isinstance(self.career, str):
  200. cname = self.career
  201. cid = next((x for x in Character.CAREERS if x["name"] == cname), "")
  202. else:
  203. cname = self.career["name"]
  204. c = dict(next((x for x in Character.CAREERS if x["name"] == cname), ""))
  205. cid = c["id"]
  206. if html:
  207. charText = f"<h4>Name: {self.name} (Kobold {cname})</h4>"
  208. else:
  209. charText = f"\nName: {self.name} (Kobold {cname} {cid})"
  210. print(charText)
  211. def print(self, html=False):
  212. if html:
  213. if isinstance(self.career, str):
  214. cname = self.career
  215. else:
  216. cname = self.career["name"]
  217. if isinstance(self.gadget, str):
  218. gdg = {"id": 127, "name": self.gadget, "description": "", "reusable": True}
  219. else:
  220. gdg = self.gadget
  221. out = (
  222. f"<div class='kobold'>\n"
  223. f" <span class='koboldid'>\n"
  224. f" <span class='koboldname'>{self.name}</span><br>\n"
  225. f" <span class='koboldcareer'>Kobold {cname}</span>\n"
  226. f" </span>\n"
  227. f" <br>\n"
  228. f" <span class='koboldstats'>\n"
  229. f" <ul>\n"
  230. f" <li>Order: {self.stats[0]}</li>\n"
  231. f" <li>Chaos: {self.stats[1]}</li>\n"
  232. f" <li>Brain: {self.stats[2]}</li>\n"
  233. f" <li>Body: {self.stats[3]}</li>\n"
  234. f" </ul>\n"
  235. f" </span>\n"
  236. f" <br>\n"
  237. f" <span class='koboldgadget'>\n"
  238. f" <span class='koboldgadgetname'>{gdg['name']}</span><br>\n"
  239. f" <span class='koboldgadgetdescription'>{gdg['description']}</span>\n"
  240. f" <span class='koboldgadgetreuse'>{'<br>Reusable' if gdg['reusable'] else ''}</span>\n"
  241. f" </span>"
  242. f"</div>\n"
  243. )
  244. print(out)
  245. else:
  246. self.print_name()
  247. print(f"Order: {self.stats[0]}")
  248. print(f"Chaos: {self.stats[1]}")
  249. print(f"Brain: {self.stats[2]}")
  250. print(f"Body: {self.stats[3]}")
  251. if isinstance(self.gadget, str):
  252. print(f"Gadget: {self.gadget}")
  253. else:
  254. print(f"Gadget: {self.gadget['name']} ({self.gadget['description']}{'- Reusable' if self.gadget['reusable'] else ''})")
  255. class Ship:
  256. NAME1 = ["Red","Orange","Yellow","Green","Blue","Violet","Dark","Light","Frenzied","Maniacal","Ancient"]
  257. NAME2 = ["Moon","Comet","Star","Saber","World-Eater","Dancer","Looter","Phlogiston","Fireball","Mecha","Raptor"]
  258. GQUAL = ["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"]
  259. BQUAL = ["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"]
  260. def __init__(self, name1=None, name2=None, gqual = None, bqual = None):
  261. self.name1 = name1 if name1 != None else r.choice(Ship.NAME1)
  262. self.name2 = name2 if name2 != None else r.choice(Ship.NAME2)
  263. self.gqual = gqual if gqual != None else r.choice(Ship.GQUAL)
  264. self.bqual = bqual if bqual != None else r.choice(Ship.BQUAL)
  265. self.fullname = f"{self.name1} {self.name2}"
  266. def print(self, html=False):
  267. if (html):
  268. shipText = f"<p>The <strong>{self.fullname}</strong> <span style='color: blue;'>{self.gqual}</span>, but <span style='color: red;'>{self.bqual}</span>.</p>\n"
  269. else:
  270. shipText = f"The {self.fullname} {self.gqual}, but {self.bqual}.\n"
  271. print(shipText)
  272. class Campaign:
  273. ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz?- "
  274. NAMELETS = ['a', 'e', 'i', 'o', 'u', 'b', 'y', 'd', 'f', 'g', 'k', 'm', 'n', 'p', 'r', 's', 't', 'w', 'z', 'l', 'x']
  275. def __init__(self, n=None, makeChars=True, fromPW=False, pw=None):
  276. if fromPW == True:
  277. self.ship = None
  278. self.params = None
  279. self.characters = None
  280. self.art = None
  281. self.masterpassword = pw
  282. self.decode_key(pw)
  283. else:
  284. self.masterpassword = None
  285. self.create_campaign(n, makeChars)
  286. def create_campaign(self, n=None, makeChars=True):
  287. n = 6 if n == None else n
  288. self.ship = Ship()
  289. self.params = Plot()
  290. self.params.problem["fullname"] = ""
  291. # if self.params.problem["id"] in [1,2]:
  292. # self.params.problem["fullname"] += " led by " + self.params.problem["name"]
  293. # if self.params.problem["id"] in [4,7,8,10]:
  294. # self.params.problem["fullname"] += " named " + self.params.problem["name"]
  295. if makeChars:
  296. self.characters = []
  297. for _ in range(n):
  298. c = Character()
  299. c.generate()
  300. self.characters.append(c)
  301. self.art = "an" if self.params.loc_desc[0] in ["a","e","i","o","u"] else "a"
  302. def generate_key(self):
  303. """
  304. "PACKTA CTICS! ------- ------- ------- ------- ------- ------- ------- ------- ------- ------- ------- -------" should generate the Season 3 Pack Tactics crew.
  305. "JUSTIN BAILEY" and "NARPAS SWORD" should do something too.
  306. Key is analphanumeric string generated from a bitfield
  307. Bitfield is:
  308. Location 1: 7 bits
  309. Location 2: 7 bits
  310. Location Battlefield: 1 bit
  311. Mission: 7 bits
  312. Oops: 1 bit
  313. Problem 1: 7 bits
  314. Problem 1 name: 40 bits
  315. Problem 2: 7 bits
  316. Ship name 1: 7 bits
  317. Ship name 2: 7 bits
  318. Ship gqual: 7 bits
  319. Ship bqual: 7 bits
  320. Character 1 name: 40 bits
  321. Character 1 career: 7 bits
  322. Character 1 order: 3 bits
  323. Character 1 chaos: 3 bits
  324. Character 1 body: 3 bits
  325. Character 1 brain: 3 bits
  326. Character 1 gadget: 7 bits
  327. Character 2 name: 40 bits
  328. Character 2 career: 7 bits
  329. Character 2 order: 3 bits
  330. Character 2 chaos: 3 bits
  331. Character 2 body: 3 bits
  332. Character 2 brain: 3 bits
  333. Character 2 gadget: 7 bits
  334. Character 3 name: 40 bits
  335. Character 3 career: 7 bits
  336. Character 3 order: 3 bits
  337. Character 3 chaos: 3 bits
  338. Character 3 body: 3 bits
  339. Character 3 brain: 3 bits
  340. Character 3 gadget: 7 bits
  341. Character 4 name: 40 bits
  342. Character 4 career: 7 bits
  343. Character 4 order: 3 bits
  344. Character 4 chaos: 3 bits
  345. Character 4 body: 3 bits
  346. Character 4 brain: 3 bits
  347. Character 4 gadget: 7 bits
  348. Character 5 name: 40 bits
  349. Character 5 career: 7 bits
  350. Character 5 order: 3 bits
  351. Character 5 chaos: 3 bits
  352. Character 5 body: 3 bits
  353. Character 5 brain: 3 bits
  354. Character 5 gadget: 7 bits
  355. Character 6 name: 40 bits
  356. Character 6 career: 7 bits
  357. Character 6 order: 3 bits
  358. Character 6 chaos: 3 bits
  359. Character 6 body: 3 bits
  360. Character 6 brain: 3 bits
  361. Character 6 gadget: 7 bits
  362. 104 for campaign
  363. 396 for characters
  364. total 501
  365. 11 remaining
  366. """
  367. self.key = ""
  368. l1 = bin(Plot.loc1.index(self.params.loc_desc))[2:]
  369. l2 = bin(Plot.loc2.index(self.params.location))[2:]
  370. lb = bin(self.params.battlefield)[2:]
  371. op = bin(self.params.oops)[2:]
  372. ms = bin(self.params.missIndex)[2:]
  373. pb1 = bin(self.params.probIndex)[2:]
  374. if self.params.problem["hasMinion"]:
  375. pb2 = bin(self.params.secProblem["id"])[2:]
  376. else:
  377. pb2 = bin(127)[2:]
  378. if self.params.problem["needsName"]:
  379. pbname = self.encode_name(self.params.problem["givenname"])
  380. else:
  381. pbname = self.encode_name("yyyyyyyy")
  382. n1 = bin(Ship.NAME1.index(self.ship.name1))[2:]
  383. n2 = bin(Ship.NAME2.index(self.ship.name2))[2:]
  384. gq = bin(Ship.GQUAL.index(self.ship.gqual))[2:]
  385. bq = bin(Ship.BQUAL.index(self.ship.bqual))[2:]
  386. chars = {}
  387. i = 0
  388. for chct in self.characters:
  389. chars[i] = {
  390. "name": self.encode_name(chct.name),
  391. "career": bin(chct.career["id"])[2:],
  392. "order": bin(chct.stats[0])[2:],
  393. "chaos": bin(chct.stats[1])[2:],
  394. "body": bin(chct.stats[2])[2:],
  395. "brains": bin(chct.stats[3])[2:],
  396. "gadget": bin(chct.gadget["id"])[2:]
  397. }
  398. i += 1
  399. l1 = lpad(l1, 7)
  400. l2 = lpad(l2, 7)
  401. ms = lpad(ms, 7)
  402. pb1 = lpad(pb1, 7)
  403. pb2 = lpad(pb2, 7)
  404. pbname = lpad(pbname, 40, "1")
  405. n1 = lpad(n1, 7)
  406. n2 = lpad(n2, 7)
  407. gq = lpad(gq, 7)
  408. bq = lpad(bq, 7)
  409. self.key += l1 + l2 + lb + ms + op + pb1 + pbname + pb2 + n1 + n2 + gq + bq
  410. for k,chct in chars.items():
  411. chct["name"] = lpad(chct["name"], 40, "1")
  412. chct["career"] = lpad(chct["career"], 7)
  413. chct["order"] = lpad(chct["order"],3)
  414. chct["chaos"] = lpad(chct["chaos"], 3)
  415. chct["body"] = lpad(chct["body"], 3)
  416. chct["brains"] = lpad(chct["brains"], 3)
  417. chct["gadget"] = lpad(chct["gadget"], 7)
  418. self.key += chct["name"] + chct["career"] + chct["order"] + chct["chaos"] + chct["body"] + chct["brains"] + chct["gadget"]
  419. while len(self.key) < 509:
  420. self.key = self.key + "0"
  421. letters = []
  422. letter = []
  423. for bit in self.key:
  424. letter.append(bit)
  425. if len(letter) == 6:
  426. letters.append(Campaign.ALPHABET[int("".join(letter),2)])
  427. letter = []
  428. words = []
  429. word = []
  430. for lt in letters:
  431. word.append(lt)
  432. if len(word) == 6:
  433. words.append("".join(word))
  434. word = []
  435. words.append("".join(word))
  436. self.password = " ".join(words)
  437. return self.password
  438. def decode_key(self, pw):
  439. densePwd = pw.replace(" ", "")
  440. aBuiltin = densePwd[:12].upper()
  441. builtins = ["PACKTACTICS!", "JUSTINBAILEY", "NARPASSWORD", "PACKTACTICS1", "PACKTACTICS2", "PACKTACTICS3", "OHNOITSPEV!!"]
  442. if len(densePwd) != 84 and (aBuiltin not in builtins):
  443. print("This password is not valid. If this is a password that this generator created, please email noelle@noelle.codes and let me know.")
  444. sys.exit(0)
  445. if aBuiltin in builtins:
  446. if aBuiltin == "PACKTACTICS!" or aBuiltin == "PACKTACTICS3":
  447. # Create a campaign featuring the Season 3 Pack Tactics crew.
  448. self.ship = Ship("Red", "Star", "is maneuverable & unarmored", "has a politician who thinks they're in charge of it")
  449. self.params = Plot()
  450. self.art = "an" if self.params.loc_desc[0] in ["a","e","i","o","u"] else "a"
  451. self.characters = []
  452. self.characters.append(Character("Niwri", 17, [3, 4, 4, 3], 123))
  453. self.characters.append(Character("Zax", 18, [1, 6, 2, 5], 124))
  454. self.characters.append(Character("Chroma", 19, [4, 2, 5, 3], 125))
  455. self.characters.append(Character("Zenosha", 20, [3, 4, 5, 2], 126))
  456. self.characters.append(Character("Snax", 21, [3, 4, 6, 1], 127))
  457. # self.print_params()
  458. # self.print_chars()
  459. return
  460. elif aBuiltin == "PACKTACTICS1":
  461. # Create a campaign featuring the Season 1 Pack Tactics crew.
  462. self.ship = Ship()
  463. self.params = Plot()
  464. self.art = "an" if self.params.loc_desc[0] in ["a","e","i","o","u"] else "a"
  465. self.characters = []
  466. self.characters.append(Character("Daldain", 13, [3, 4, 5, 2], 114))
  467. self.characters.append(Character("Gox", 18, [2, 5, 2, 5], 115))
  468. self.characters.append(Character("Zeeli", 14, [4, 2, 5, 3], 116))
  469. self.characters.append(Character("Sox", 16, [3, 3, 3, 3], 117))
  470. # self.print_params()
  471. # self.print_chars()
  472. return
  473. elif aBuiltin == "PACKTACTICS2":
  474. # Create a campaign featuring the Season 2 Pack Tactics crew.
  475. self.ship = Ship("Red", "Star", "is maneuverable & unarmored", "has a politician who thinks they're in charge of it")
  476. self.params = Plot()
  477. self.art = "an" if self.params.loc_desc[0] in ["a","e","i","o","u"] else "a"
  478. self.characters = []
  479. self.characters.append(Character("Daldain", 13, [4, 5, 5, 3], 118))
  480. self.characters.append(Character("Gox", 18, [2, 6, 4, 5], 119))
  481. self.characters.append(Character("Zeeli", 14, [3, 4, 6, 5], 120))
  482. self.characters.append(Character("Marwyse", 15, [4, 4, 6, 3], 121))
  483. self.characters.append(Character("Sox", 16, [5, 4, 4, 4], 122))
  484. # self.print_params()
  485. # self.print_chars()
  486. return
  487. elif aBuiltin == "JUSTINBAILEY":
  488. # Create a random campaign, but everyone's Gadget is a leotard that somehow is also an environment suit
  489. self.create_campaign(n=6, makeChars=True)
  490. for c in self.characters:
  491. c.gadget = {"id":127, "name": "The Bailey", "description": "A form-fitting leotard that somehow protects the wearer from all environmental effects except extreme heat - including vacuum and poison.", "reusable": True}
  492. # self.print_params()
  493. # self.print_chars()
  494. return
  495. elif aBuiltin == "NARPASSWORD":
  496. # Create a random campaign, but all the kobolds' stats are set to 6
  497. self.create_campaign(n=6, makeChars=True)
  498. for c in self.characters:
  499. c.stats = [6,6,6,6]
  500. # self.print_params()
  501. # self.print_chars()
  502. return
  503. elif aBuiltin == "OHNOITSPEV!!":
  504. # Create a random campaign, but the adversary is Pev
  505. self.create_campaign(n=6, makeChars=True)
  506. self.params = Plot(probIndex=121)
  507. return
  508. numPwd = []
  509. for c in densePwd:
  510. numPwd.append(Campaign.ALPHABET.index(c))
  511. bitPwd = [lpad(bin(x).replace("0b",""), 6) for x in numPwd]
  512. longBitPwd = []
  513. for word in bitPwd:
  514. longword = lpad(word,6)
  515. longBitPwd.append(longword)
  516. self.newBitfield = "".join(longBitPwd)
  517. while len(self.newBitfield) < 509:
  518. self.newBitfield += "0"
  519. outkey = {}
  520. # Location 1: 7 bits
  521. i,j = 0,7
  522. outkey["loc1"] = int(self.newBitfield[i:j], 2)
  523. # Location 2: 7 bits
  524. i,j = j,j+7
  525. outkey["loc2"] = int(self.newBitfield[i:j], 2)
  526. # Location Battlefield: 1 bit
  527. i,j = j,j+1
  528. outkey["battlefield"] = int(self.newBitfield[i:j], 2)
  529. # Mission: 7 bits
  530. i,j = j,j+7
  531. outkey["miss"] = int(self.newBitfield[i:j], 2)
  532. # Oops: 1 bit
  533. i,j = j,j+1
  534. outkey["oops"] = int(self.newBitfield[i:j], 2)
  535. # Problem 1: 7 bits
  536. i,j = j,j+7
  537. outkey["prob1"] = int(self.newBitfield[i:j], 2)
  538. # Problem 1 name: 40 bits
  539. i,j = j,j+40
  540. outkey["prob1name"] = self.newBitfield[i:j]
  541. # Problem 2: 7 bits
  542. i,j = j,j+7
  543. outkey["prob2"] = int(self.newBitfield[i:j], 2)
  544. # Ship name 1: 7 bits
  545. i,j = j,j+7
  546. outkey["sname1"] = int(self.newBitfield[i:j], 2)
  547. # Ship name 2: 7 bits
  548. i,j = j,j+7
  549. outkey["sname2"] = int(self.newBitfield[i:j], 2)
  550. # Ship gqual: 7 bits
  551. i,j = j,j+7
  552. outkey["gqual"] = int(self.newBitfield[i:j], 2)
  553. # Ship bqual: 7 bits
  554. i,j = j,j+7
  555. outkey["bqual"] = int(self.newBitfield[i:j], 2)
  556. # Character 1 name: 40 bits
  557. i,j = j,j+40
  558. outkey["char1name"] = self.newBitfield[i:j]
  559. # Character 1 career: 7 bits
  560. i,j = j,j+7
  561. outkey["char1career"] = int(self.newBitfield[i:j], 2)
  562. # Character 1 order: 3 bits
  563. i,j = j,j+3
  564. outkey["char1ord"] = int(self.newBitfield[i:j], 2)
  565. # Character 1 chaos: 3 bits
  566. i,j = j,j+3
  567. outkey["char1cha"] = int(self.newBitfield[i:j], 2)
  568. # Character 1 body: 3 bits
  569. i,j = j,j+3
  570. outkey["char1bod"] = int(self.newBitfield[i:j], 2)
  571. # Character 1 brain: 3 bits
  572. i,j = j,j+3
  573. outkey["char1bra"] = int(self.newBitfield[i:j], 2)
  574. # Character 1 gadget: 7 bits
  575. i,j = j,j+7
  576. outkey["char1gad"] = int(self.newBitfield[i:j], 2)
  577. # Character 2 name: 40 bits
  578. i,j = j,j+40
  579. outkey["char2name"] = self.newBitfield[i:j]
  580. # Character 2 career: 7 bits
  581. i,j = j,j+7
  582. outkey["char2career"] = int(self.newBitfield[i:j], 2)
  583. # Character 2 order: 3 bits
  584. i,j = j,j+3
  585. outkey["char2ord"] = int(self.newBitfield[i:j], 2)
  586. # Character 2 chaos: 3 bits
  587. i,j = j,j+3
  588. outkey["char2cha"] = int(self.newBitfield[i:j], 2)
  589. # Character 2 body: 3 bits
  590. i,j = j,j+3
  591. outkey["char2bod"] = int(self.newBitfield[i:j], 2)
  592. # Character 2 brain: 3 bits
  593. i,j = j,j+3
  594. outkey["char2bra"] = int(self.newBitfield[i:j], 2)
  595. # Character 2 gadget: 7 bits
  596. i,j = j,j+7
  597. outkey["char2gad"] = int(self.newBitfield[i:j], 2)
  598. # Character 3 name: 40 bits
  599. i,j = j,j+40
  600. outkey["char3name"] = self.newBitfield[i:j]
  601. # Character 3 career: 7 bits
  602. i,j = j,j+7
  603. outkey["char3career"] = int(self.newBitfield[i:j], 2)
  604. # Character 3 order: 3 bits
  605. i,j = j,j+3
  606. outkey["char3ord"] = int(self.newBitfield[i:j], 2)
  607. # Character 3 chaos: 3 bits
  608. i,j = j,j+3
  609. outkey["char3cha"] = int(self.newBitfield[i:j], 2)
  610. # Character 3 body: 3 bits
  611. i,j = j,j+3
  612. outkey["char3bod"] = int(self.newBitfield[i:j], 2)
  613. # Character 3 brain: 3 bits
  614. i,j = j,j+3
  615. outkey["char3bra"] = int(self.newBitfield[i:j], 2)
  616. # Character 3 gadget: 7 bits
  617. i,j = j,j+7
  618. outkey["char3gad"] = int(self.newBitfield[i:j], 2)
  619. # Character 4 name: 40 bits
  620. i,j = j,j+40
  621. outkey["char4name"] = self.newBitfield[i:j]
  622. # Character 4 career: 7 bits
  623. i,j = j,j+7
  624. outkey["char4career"] = int(self.newBitfield[i:j], 2)
  625. # Character 4 order: 3 bits
  626. i,j = j,j+3
  627. outkey["char4ord"] = int(self.newBitfield[i:j], 2)
  628. # Character 4 chaos: 3 bits
  629. i,j = j,j+3
  630. outkey["char4cha"] = int(self.newBitfield[i:j], 2)
  631. # Character 4 body: 3 bits
  632. i,j = j,j+3
  633. outkey["char4bod"] = int(self.newBitfield[i:j], 2)
  634. # Character 4 brain: 3 bits
  635. i,j = j,j+3
  636. outkey["char4bra"] = int(self.newBitfield[i:j], 2)
  637. # Character 4 gadget: 7 bits
  638. i,j = j,j+7
  639. outkey["char4gad"] = int(self.newBitfield[i:j], 2)
  640. # Character 5 name: 40 bits
  641. i,j = j,j+40
  642. outkey["char5name"] = self.newBitfield[i:j]
  643. # Character 5 career: 7 bits
  644. i,j = j,j+7
  645. outkey["char5career"] = int(self.newBitfield[i:j], 2)
  646. # Character 5 order: 3 bits
  647. i,j = j,j+3
  648. outkey["char5ord"] = int(self.newBitfield[i:j], 2)
  649. # Character 5 chaos: 3 bits
  650. i,j = j,j+3
  651. outkey["char5cha"] = int(self.newBitfield[i:j], 2)
  652. # Character 5 body: 3 bits
  653. i,j = j,j+3
  654. outkey["char5bod"] = int(self.newBitfield[i:j], 2)
  655. # Character 5 brain: 3 bits
  656. i,j = j,j+3
  657. outkey["char5bra"] = int(self.newBitfield[i:j], 2)
  658. # Character 5 gadget: 7 bits
  659. i,j = j,j+7
  660. outkey["char5gad"] = int(self.newBitfield[i:j], 2)
  661. # Character 6 name: 40 bits
  662. i,j = j,j+40
  663. outkey["char6name"] = self.newBitfield[i:j]
  664. # Character 6 career: 7 bits
  665. i,j = j,j+7
  666. outkey["char6career"] = int(self.newBitfield[i:j], 2)
  667. # Character 6 order: 3 bits
  668. i,j = j,j+3
  669. outkey["char6ord"] = int(self.newBitfield[i:j], 2)
  670. # Character 6 chaos: 3 bits
  671. i,j = j,j+3
  672. outkey["char6cha"] = int(self.newBitfield[i:j], 2)
  673. # Character 6 body: 3 bits
  674. i,j = j,j+3
  675. outkey["char6bod"] = int(self.newBitfield[i:j], 2)
  676. # Character 6 brain: 3 bits
  677. i,j = j,j+3
  678. outkey["char6bra"] = int(self.newBitfield[i:j], 2)
  679. # Character 6 gadget: 7 bits
  680. i,j = j,j+7
  681. outkey["char6gad"] = int(self.newBitfield[i:j], 2)
  682. self.ship = Ship(Ship.NAME1[outkey["sname1"]], Ship.NAME2[outkey["sname2"]], Ship.GQUAL[outkey["gqual"]], Ship.BQUAL[outkey["bqual"]])
  683. self.params = Plot(loc_desc=Plot.loc1[outkey["loc1"]], locIndex=outkey["loc2"], battlefield=outkey["battlefield"], location=None, missIndex=outkey["miss"], oops=outkey["oops"], mission=None, probIndex=outkey["prob1"], problem=None, probName=self.decode_name(outkey["prob1name"]), secProblem=outkey["prob2"], thirdProblem=None)
  684. self.art = "an" if self.params.loc_desc[0] in ["a","e","i","o","u"] else "a"
  685. self.characters = []
  686. for q in range(1,7):
  687. keys = [f"char{q}name", f"char{q}career", f"char{q}ord", f"char{q}cha", f"char{q}bod", f"char{q}bra", f"char{q}gad"]
  688. c = Character(name=self.decode_name(outkey[keys[0]]), career=outkey[keys[1]], stats=[outkey[keys[2]], outkey[keys[3]], outkey[keys[4]], outkey[keys[5]]], gadget=outkey[keys[6]])
  689. self.characters.append(c)
  690. #return self.newBitfield
  691. def encode_name(self, name):
  692. field = "".join([lpad(bin(Campaign.NAMELETS.index(c.lower()))[2:], 5) for c in name])
  693. return field
  694. def decode_name(self, field):
  695. i,j = 35,40
  696. name = ""
  697. for _ in range(8):
  698. k = int(field[i:j], 2)
  699. if k != 31:
  700. name = Campaign.NAMELETS[k] + name
  701. i,j = i-5, i
  702. name = name[0].upper() + name[1:]
  703. return name
  704. def print_params(self, endc=" ", html=False):
  705. st = ["Order:", "Chaos:", "Brains:", "Body:"]
  706. cst = ", ".join([" ".join(y) for y in list(zip(st, [str(x) for x in self.params.problem["stats"]]))])
  707. if self.params.oops == 1:
  708. oops = "...well, they weren't paying attention, so don't tell them, but they're supposed to "
  709. else:
  710. oops = ""
  711. mission = oops + self.params.mission
  712. # pl = "s" if self.params.problem["isPlural"] else ""
  713. note = self.params.problem["note"]
  714. secnote = ""
  715. if self.params.problem["hasMinion"]:
  716. secnote = "Their minion: " + self.params.secProblem["note"]
  717. bf = ""
  718. if self.params.battlefield:
  719. bf = "a battlefield on "
  720. lines = [
  721. f"The Kobolds of the {self.ship.fullname}",
  722. f"have been sent out to {bf}{self.art} {self.params.loc_desc} {self.params.location}!",
  723. f"in order to {mission}",
  724. f"but they're challenged by {self.params.fullProblem}!",
  725. f"{note} {secnote}",
  726. f"The stats of the {self.params.problem['shortname']}",
  727. f"{cst}"
  728. ]
  729. if self.params.problem["hasMinion"]:
  730. mst = ", ".join([" ".join(y) for y in list(zip(st, [str(x) for x in self.params.secProblem["stats"]]))])
  731. # spl = "s" if self.params.secProblem["isPlural"] else ""
  732. lines.append(f"The stats of the {self.params.secProblem['shortname']}")
  733. lines.append(f"{mst}")
  734. if html:
  735. secnote = ""
  736. if self.params.problem["hasMinion"]:
  737. secnote = "Their minion: " + self.params.secProblem["note"] + "<br>\n"
  738. out = (
  739. f"<div id='theship' class='firstrow'>\n"
  740. f" <span class='head'>The Ship</span>\n"
  741. f" <span id='shipname'>\n"
  742. f" The {self.ship.fullname}!\n"
  743. f" </span>\n"
  744. f" <br>\n"
  745. f" <span id='shipquality1'>\n"
  746. f" It {self.ship.gqual}...\n"
  747. f" </span><br>\n"
  748. f" <span id='shipquality2'>\n"
  749. f" But {self.ship.bqual}!\n"
  750. f" </span>\n"
  751. f"</div>\n"
  752. f"<div id='themission' class='firstrow'>\n"
  753. f" <span class='head'>The Mission</span>\n"
  754. f" <span id='missionloc'>\n"
  755. f" The kobolds have been sent to {bf}{self.art} {self.params.loc_desc} {self.params.location}\n"
  756. f" </span><br>\n"
  757. f" <span id='missiontarget'>\n"
  758. f" in order to {mission}!\n"
  759. f" </span>\n"
  760. f"</div>\n<br clear='all'>\n"
  761. f"<div id='theadversary' class='firstrow'>\n"
  762. f" <span class='head'>The Adversary</span>\n"
  763. f" They're challenged by <span id='advname'>{self.params.fullProblem}</span>!\n"
  764. f" <br>\n"
  765. f" <span id='problemnote'>\n"
  766. f" {self.params.problem['note']}<br>\n"
  767. f" {secnote}"
  768. f" </span>\n"
  769. f" <span id='problemstats'>\n"
  770. f" The stats of the {self.params.problem['shortname']}:<br>\n"
  771. f" {cst}\n"
  772. f" </span>\n"
  773. )
  774. if self.params.problem["hasMinion"]:
  775. out += (
  776. f" <br><span id='secprobstats'>\n"
  777. f" The stats of the {self.params.secProblem['shortname']}:<br>\n"
  778. f" {mst}\n"
  779. f" </span>\n"
  780. )
  781. out += f"</div>\n"
  782. print(out)
  783. print(f"<br clear='all'>\n")
  784. else:
  785. print(f"{lines[0]} {lines[1]} {lines[2]} -- {lines[3]}")
  786. print(f"{lines[4]}")
  787. print(f"{lines[5]}: {lines[6]}")
  788. if self.params.problem["hasMinion"]:
  789. print(f"- {lines[7]}: {lines[8]}")
  790. print()
  791. self.ship.print(html=html)
  792. def print_chars(self, html=False):
  793. if html:
  794. print(f"<div id='thekobolds'>\n")
  795. print(f"<span class='head'>The Kobolds</span>\n")
  796. else:
  797. print("The kobolds:")
  798. for k in self.characters:
  799. k.print(html=html)
  800. if html:
  801. print(f"</div>\n<br clear='all'>\n")
  802. def print_password(self, html=False):
  803. if self.masterpassword:
  804. pw = self.masterpassword
  805. else:
  806. pw = self.generate_key()
  807. if html:
  808. out = (
  809. f"<div id='passwordbox'>"
  810. f"<span class='passwordhead'>Permalink to this campaign:</span><br>"
  811. f"<span><a href='http://node.noelle.codes/kobold?pw={pw.replace(' ', '')}'>{pw.replace(' ', '')}</a></span><br><br>"
  812. f"<span class='passwordhead'><a href='http://node.noelle.codes/kobold'>Generate a new random campaign</a></span><br>"
  813. f"</div>"
  814. )
  815. print(out)
  816. print(f"<br clear='all'>\n")
  817. else:
  818. print(f"Password: {pw}")
  819. def decode(self, pwd):
  820. pass
  821. def lpad(s, n, c="0"):
  822. while len(s) < n:
  823. s = c + s
  824. return s
  825. def create_password():
  826. pw = []
  827. for _ in range(84):
  828. pw.append(r.choice(Campaign.ALPHABET))
  829. return "".join(pw)
  830. if __name__ == "__main__":
  831. parser = argparse.ArgumentParser()
  832. group = parser.add_mutually_exclusive_group()
  833. group.add_argument("-c", "--campaign", help="print a full campaign block with N kobolds (default 6)", nargs="?", const=6, type=int, metavar="N")
  834. group.add_argument("-k", "--kobolds", help="print N kobolds", type=int, nargs="?", const=1, default=1, metavar="N")
  835. group.add_argument("-n", "--names", help="print N kobolds without stat blocks", nargs="?", const=1, type=int, metavar="N")
  836. group.add_argument("-p", "--params", help="print only the parameters of a campaign", action="store_true")
  837. group.add_argument("-s", "--ship", help="print only the ship name and description", action="store_true")
  838. group.add_argument("-pw", "--password", help="print the campaign defined by the submitted password", type=str, nargs="?", const=1, metavar="PW")
  839. parser.add_argument("--html", help="print in HTML instead of plain text", action="store_true")
  840. args = parser.parse_args()
  841. html = True if args.html else False
  842. if args.password:
  843. cmp = Campaign(fromPW = True, pw=args.password)
  844. cmp.print_params(html=html)
  845. cmp.print_chars(html=html)
  846. cmp.print_password(html=html)
  847. elif args.campaign:
  848. #cmp = Campaign(fromPW = True, pw=create_password())
  849. cmp = Campaign(args.campaign)
  850. cmp.print_params(html=html)
  851. cmp.print_chars(html=html)
  852. cmp.print_password(html=html)
  853. elif args.params:
  854. cmp = Campaign(makeChars=False)
  855. cmp.print_params(html=html)
  856. elif args.ship:
  857. ship = Ship()
  858. ship.print(html=html)
  859. elif args.names:
  860. for _ in range(args.names):
  861. c = Character()
  862. c.generate()
  863. c.print_name(html=html)
  864. else:
  865. for _ in range(args.kobolds):
  866. c = Character()
  867. c.generate()
  868. c.print(html=html)