A generator for *Star Wars* interstellar shipping and passenger jobs, based loosely on the missions you can take on in *Escape Velocity* and *Endless Space*.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

swjg.py 7.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. import argparse
  2. import re
  3. from enum import Enum
  4. from string import Template
  5. from typing import NamedTuple
  6. from jinja2 import Environment, PackageLoader, select_autoescape
  7. # global variables
  8. env = Environment(loader=PackageLoader("swjg"), autoescape=select_autoescape)
  9. parser = argparse.ArgumentParser()
  10. # helper definitions
  11. def get_identifiers_from_template(template):
  12. # Template.get_identifiers() doesn't appear until Python 3.11
  13. # Don't substitute anything; get the raw string
  14. template_str = Template.safe_substitute()
  15. matches = re.findall("\$[a-z0-9_\.]+", template_str)
  16. matches = [x[1:] for x in matches]
  17. return matches
  18. class DangerLevel(Enum):
  19. NONE = 1
  20. LOW = 2
  21. MODERATE = 3
  22. HIGH = 4
  23. EXTREME = 5
  24. class DangerType(Enum):
  25. HOSTILE = 1
  26. AT_WAR = 2
  27. DESERT = 3
  28. JUNGLE = 4
  29. OCEAN = 5
  30. ATMOSPHERE = 6
  31. class Destination(NamedTuple):
  32. world: str
  33. system: str | None
  34. sector: str | None
  35. # Class definitions
  36. class Mission:
  37. output_destination_template = Template("to $world in the $system system")
  38. output_sector_template = Template(", part of the $sector,")
  39. value_template = Template("for $value credits")
  40. error_template = Template(
  41. "You haven't given this mission some of the values it needs. Please check: $potential_values"
  42. )
  43. def __init__(self, destination: Destination, value: int, *args, **kwargs):
  44. # destination: Destination (see above)
  45. self.world = destination.world if destination.world is not None else None
  46. self.system = (
  47. destination.system if destination.system is not None else destination.world
  48. )
  49. self.sector = destination.sector if destination.sector is not None else None
  50. self.value = value
  51. # for arg, val in kwargs.items():
  52. # setattr(self, arg, val)
  53. def assemble_templates(self) -> list:
  54. # Override this in children
  55. template_list = []
  56. if self.world != None:
  57. template_list.append(self.output_destination_template)
  58. if self.sector != None:
  59. template_list.append(self.output_sector_template)
  60. template_list.append(self.value_template)
  61. return template_list
  62. def get_current_values(self) -> dict:
  63. return {
  64. "world": self.world,
  65. "system": self.system,
  66. "sector": self.sector,
  67. "value": self.value,
  68. }
  69. def missing_values(self) -> list:
  70. object_vars = vars(self)
  71. missing = []
  72. for key, val in object_vars.items():
  73. if val == None:
  74. missing.append(key)
  75. return missing
  76. def assemble_text(self) -> str:
  77. missing_vals = self.missing_values()
  78. templates = self.assemble_templates()
  79. if (
  80. len(templates) == 0 or len(missing_vals) != 0
  81. ): # either both of these should be true or neither should
  82. raise ValueError(
  83. self.error_template.substitute(potential_values=missing_vals)
  84. )
  85. current_values = self.get_current_values()
  86. output = []
  87. for template in templates:
  88. output.append(template.substitute(**current_values))
  89. out_text = " ".join(output)
  90. out_text = out_text.replace(" ,", ",")
  91. out_text += "."
  92. return out_text
  93. class PassengerMission(Mission):
  94. output_initial_template = Template("Bring $number passengers")
  95. def __init__(self, number: int, *args, **kwargs):
  96. super(PassengerMission, self).__init__(*args, **kwargs)
  97. self.number = number
  98. def get_current_values(self) -> dict:
  99. base_dict = super(PassengerMission, self).get_current_values()
  100. new_dict = {"number": self.number}
  101. base_dict.update(new_dict)
  102. return base_dict
  103. def assemble_templates(self):
  104. template_list = [self.output_initial_template]
  105. template_list.extend(super(PassengerMission, self).assemble_templates())
  106. return template_list
  107. def assemble_text(self) -> str:
  108. return super(PassengerMission, self).assemble_text()
  109. class CargoMission(Mission):
  110. output_initial_template = Template("Deliver $tons tons of $item")
  111. output_time_timeplate = Template("in the next $time days")
  112. def __init__(self, tons: int, item: str, time: int, *args, **kwargs):
  113. # tons: integer
  114. # item: string (this will not be pluralized in the text)
  115. # time: integer (number of days the crew have to complete delivery)
  116. # value: integer (value in credits)
  117. super(CargoMission, self).__init__(*args, **kwargs)
  118. self.tons = tons
  119. self.item = item
  120. self.time = time
  121. def get_current_values(self) -> dict:
  122. base_dict = super(CargoMission, self).get_current_values()
  123. new_dict = {"tons": self.tons, "item": self.item, "time": self.time}
  124. base_dict.update(new_dict)
  125. return base_dict
  126. def assemble_templates(self):
  127. template_list = [self.output_initial_template]
  128. if self.time is not None:
  129. template_list.append(self.output_time_timeplate)
  130. template_list.extend(super(CargoMission, self).assemble_templates())
  131. return template_list
  132. def assemble_text(self) -> str:
  133. return super(CargoMission, self).assemble_text()
  134. class GroupMission(Mission):
  135. output_initial_template = Template("Bring $number members of a $group")
  136. def __init__(self, number: int, group: str, *args, **kwargs):
  137. super(GroupMission, self).__init__(*args, **kwargs)
  138. self.number = number
  139. self.group = group
  140. def get_current_values(self) -> dict:
  141. base_dict = super(GroupMission, self).get_current_values()
  142. new_dict = {"number": self.number, "group": self.group}
  143. base_dict.update(new_dict)
  144. return base_dict
  145. def assemble_templates(self):
  146. template_list = [self.output_initial_template]
  147. template_list.extend(super(GroupMission, self).assemble_templates())
  148. return template_list
  149. def assemble_text(self) -> str:
  150. return super(GroupMission, self).assemble_text()
  151. # the function that does the thing
  152. def main():
  153. import random
  154. mission_types = [PassengerMission, CargoMission, GroupMission]
  155. missions = []
  156. for _ in range(5):
  157. current_mission = random.choice(mission_types)
  158. destination = Destination("Alderaan", None, "Core Worlds")
  159. number = random.randint(1, 8)
  160. tons = random.randint(4, 12)
  161. item = random.choice(
  162. [
  163. "gravel",
  164. "computer chips",
  165. "spice",
  166. "live ysalamiri",
  167. "empty shipping containers",
  168. ]
  169. )
  170. time = random.randint(7, 31)
  171. value = random.randint(20, 120) * 1000
  172. group = None
  173. if current_mission == GroupMission:
  174. group = random.choice(["family", "performing troupe", "acrobatic troupe"])
  175. missions.append(
  176. current_mission(
  177. destination=destination,
  178. number=number,
  179. tons=tons,
  180. item=item,
  181. time=time,
  182. value=value,
  183. group=group,
  184. )
  185. )
  186. for mission in missions:
  187. # print(mission, mission.__dict__)
  188. print(mission.assemble_text())
  189. # Don't do anything if the module is loaded wholesale into something else
  190. if __name__ == "__main__":
  191. main()