A character/one-shot generator for KOBOLDS IN SPACE!
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

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)