PageRenderTime 43ms CodeModel.GetById 1ms RepoModel.GetById 3ms app.codeStats 0ms

/code/screens/base.py

https://gitlab.com/matt81093/endgame-singularity
Python | 340 lines | 311 code | 13 blank | 16 comment | 9 complexity | 990cfa78e90488fdc0e8a25266ee529c MD5 | raw file
  1. #file: base_screen.py
  2. #Copyright (C) 2005,2006,2007,2008 Evil Mr Henry, Phil Bordelon, Brian Reid,
  3. # and FunnyMan3595
  4. #This file is part of Endgame: Singularity.
  5. #Endgame: Singularity is free software; you can redistribute it and/or modify
  6. #it under the terms of the GNU General Public License as published by
  7. #the Free Software Foundation; either version 2 of the License, or
  8. #(at your option) any later version.
  9. #Endgame: Singularity is distributed in the hope that it will be useful,
  10. #but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. #GNU General Public License for more details.
  13. #You should have received a copy of the GNU General Public License
  14. #along with Endgame: Singularity; if not, write to the Free Software
  15. #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  16. #This file contains the screen to display the base screen.
  17. import locale
  18. import pygame
  19. import code.g as g
  20. import code.graphics.g as gg
  21. from code.graphics import constants, widget, dialog, text, button
  22. state_colors = dict(
  23. active = gg.colors["green"],
  24. sleep = gg.colors["yellow"],
  25. overclocked = gg.colors["orange"],
  26. suicide = gg.colors["red"],
  27. stasis = gg.colors["gray"],
  28. entering_stasis = gg.colors["gray"],
  29. leaving_stasis = gg.colors["gray"],
  30. )
  31. class BuildDialog(dialog.ChoiceDescriptionDialog):
  32. type = widget.causes_rebuild("_type")
  33. def __init__(self, parent, pos=(0, 0), size=(-1, -1),
  34. anchor=constants.TOP_LEFT, *args, **kwargs):
  35. super(BuildDialog, self).__init__(parent, pos, size, anchor, *args,
  36. **kwargs)
  37. self.type = None
  38. self.desc_func = self.on_change
  39. def show(self):
  40. self.list = []
  41. self.key_list = []
  42. item_list = g.items.values()
  43. item_list.sort()
  44. item_list.reverse()
  45. for item in item_list:
  46. if item.item_type == self.type and item.available() \
  47. and self.parent.base.location.id in item.buildable:
  48. self.list.append(item.name)
  49. self.key_list.append(item)
  50. current = self.parent.get_current(self.type)
  51. if current is None:
  52. self.default = None
  53. else:
  54. self.default = self.parent.get_current(self.type).type.id
  55. self.needs_rebuild = True
  56. return super(BuildDialog, self).show()
  57. def on_change(self, description_pane, item):
  58. if item is not None:
  59. text.Text(description_pane, (0, 0), (-1, -1), text=item.get_info(),
  60. background_color=gg.colors["dark_blue"],
  61. align=constants.LEFT, valign=constants.TOP,
  62. borders=constants.ALL)
  63. else:
  64. text.Text(description_pane, (0, 0), (-1, -1), text="",
  65. background_color=gg.colors["dark_blue"],
  66. align=constants.LEFT, valign=constants.TOP,
  67. borders=constants.ALL)
  68. class ItemPane(widget.BorderedWidget):
  69. type = widget.causes_rebuild("_type")
  70. def __init__(self, parent, pos, size=(.48, .06), anchor=constants.TOP_LEFT,
  71. type=None, **kwargs):
  72. kwargs.setdefault("background_color", gg.colors["dark_blue"])
  73. super(ItemPane, self).__init__(parent, pos, size, anchor=anchor, **kwargs)
  74. if type is None:
  75. for type in g.item_types:
  76. if type.id == 'cpu':
  77. break
  78. self.type = type
  79. self.name_panel = text.Text(self, (0,0), (.35, .03),
  80. anchor=constants.TOP_LEFT,
  81. align=constants.LEFT,
  82. background_color=self.background_color,
  83. bold=True)
  84. self.build_panel = text.Text(self, (0,.03), (.35, .03),
  85. anchor=constants.TOP_LEFT,
  86. align=constants.LEFT,
  87. background_color=self.background_color,
  88. text="", bold=True)
  89. self.change_button = button.FunctionButton(
  90. self, (.36,.01), (.12, .04),
  91. anchor=constants.TOP_LEFT,
  92. text="%s (&%s)" % (_("CHANGE"), self.type.hotkey.upper()),
  93. force_underline=len(_("CHANGE")) + 2,
  94. autohotkey=True,
  95. function=self.parent.parent.build_item,
  96. kwargs={'type': self.type.id},
  97. )
  98. class BaseScreen(dialog.Dialog):
  99. base = widget.causes_rebuild("_base")
  100. def __init__(self, *args, **kwargs):
  101. if len(args) < 3:
  102. kwargs.setdefault("size", (.75, .5))
  103. base = kwargs.pop("base", None)
  104. super(BaseScreen, self).__init__(*args, **kwargs)
  105. self.base = base
  106. self.build_dialog = BuildDialog(self)
  107. self.count_dialog = dialog.TextEntryDialog(self)
  108. self.header = widget.Widget(self, (0,0), (-1, .08),
  109. anchor=constants.TOP_LEFT)
  110. self.name_display = text.Text(self.header, (-.5,0), (-1, -.5),
  111. anchor=constants.TOP_CENTER,
  112. borders=constants.ALL,
  113. border_color=gg.colors["dark_blue"],
  114. background_color=gg.colors["black"],
  115. shrink_factor=.85, bold=True)
  116. self.next_base_button = \
  117. button.FunctionButton(self.name_display, (-1, 0), (.03, -1),
  118. anchor=constants.TOP_RIGHT,
  119. text=">", hotkey=">",
  120. function=self.switch_base,
  121. kwargs={"forwards": True})
  122. self.add_key_handler(pygame.K_RIGHT, self.next_base_button.activate_with_sound)
  123. self.prev_base_button = \
  124. button.FunctionButton(self.name_display, (0, 0), (.03, -1),
  125. anchor=constants.TOP_LEFT,
  126. text="<", hotkey="<",
  127. function=self.switch_base,
  128. kwargs={"forwards": False})
  129. self.add_key_handler(pygame.K_LEFT, self.prev_base_button.activate_with_sound)
  130. self.state_display = text.Text(self.header, (-.5,-.5), (-1, -.5),
  131. anchor=constants.TOP_CENTER,
  132. borders=(constants.LEFT,constants.RIGHT,
  133. constants.BOTTOM),
  134. border_color=gg.colors["dark_blue"],
  135. background_color=gg.colors["black"],
  136. shrink_factor=.8, bold=True)
  137. self.back_button = \
  138. button.ExitDialogButton(self, (-.5,-1),
  139. anchor = constants.BOTTOM_CENTER,
  140. text=_("&BACK"), autohotkey=True)
  141. self.detect_frame = text.Text(self, (-1, .09), (.21, .33),
  142. anchor=constants.TOP_RIGHT,
  143. background_color=gg.colors["dark_blue"],
  144. borders=constants.ALL,
  145. bold=True,
  146. align=constants.LEFT,
  147. valign=constants.TOP)
  148. self.contents_frame = \
  149. widget.BorderedWidget(self, (0, .09), (.50, .33),
  150. anchor=constants.TOP_LEFT,
  151. background_color=gg.colors["dark_blue"],
  152. borders=range(6))
  153. for i, type in enumerate(g.item_types):
  154. setattr(self,
  155. type.id + "_pane",
  156. ItemPane(self.contents_frame, (.01, .01+.08*i), type=type))
  157. def get_current(self, type):
  158. if type == "cpu":
  159. target = self.base.cpus
  160. else:
  161. index = ["reactor", "network", "security"].index(type)
  162. target = self.base.extra_items[index]
  163. if target is not None:
  164. return target
  165. def set_current(self, type, item_type):
  166. if type == "cpu":
  167. space_left = self.base.type.size
  168. # If there are any existing CPUs of this type, warn that they will
  169. # be taken offline until construction finishes.
  170. matches = self.base.cpus is not None \
  171. and self.base.cpus.type == item_type
  172. if matches:
  173. space_left -= self.base.cpus.count
  174. if self.base.cpus.done:
  175. yn = dialog.YesNoDialog(self, pos=(-.5,-.5), size=(-.5,-1),
  176. anchor=constants.MID_CENTER,
  177. text=g.strings["will_lose_cpus"])
  178. go_ahead = dialog.call_dialog(yn, self)
  179. yn.remove_hooks()
  180. if not go_ahead:
  181. return
  182. text = g.strings["num_cpu_prompt"] % (item_type.name, space_left)
  183. self.count_dialog.text = text
  184. self.count_dialog.default_text = locale.format("%d", space_left)
  185. can_exit = False
  186. while not can_exit:
  187. result = dialog.call_dialog(self.count_dialog, self)
  188. if not result:
  189. can_exit = True
  190. else:
  191. try:
  192. count = locale.atoi(result)
  193. if count > space_left:
  194. count = space_left
  195. elif count <= 0:
  196. return
  197. new_cpus = g.item.Item(item_type, base=self.base,
  198. count=count)
  199. if matches:
  200. self.base.cpus += new_cpus
  201. else:
  202. self.base.cpus = new_cpus
  203. self.base.check_power()
  204. can_exit = True
  205. except ValueError:
  206. md = dialog.MessageDialog(self, pos=(-.5, -.5),
  207. size=(-.5, -1),
  208. anchor=constants.MID_CENTER,
  209. text=g.strings["nan"])
  210. dialog.call_dialog(md, self)
  211. md.remove_hooks()
  212. else:
  213. index = ["reactor", "network", "security"].index(type)
  214. if self.base.extra_items[index] is None \
  215. or self.base.extra_items[index].type != item_type:
  216. self.base.extra_items[index] = \
  217. g.item.Item(item_type, base=self.base)
  218. self.base.check_power()
  219. self.base.recalc_cpu()
  220. def build_item(self, type):
  221. self.build_dialog.type = type
  222. result = dialog.call_dialog(self.build_dialog, self)
  223. if 0 <= result < len(self.build_dialog.key_list):
  224. item_type = self.build_dialog.key_list[result]
  225. self.set_current(type, item_type)
  226. self.needs_rebuild = True
  227. self.parent.parent.needs_rebuild = True
  228. def switch_base(self, forwards):
  229. self.base = self.base.next_base(forwards)
  230. self.needs_rebuild = True
  231. def show(self):
  232. self.needs_rebuild = True
  233. return super(BaseScreen, self).show()
  234. def rebuild(self):
  235. # Cannot use self.base.type.name directly because it may contain a
  236. # different language than current
  237. self.name_display.text="%s (%s)" % (self.base.name,
  238. g.base_type[self.base.type.id].name)
  239. discovery_template = \
  240. _("Detection chance:").upper() + "\n" + \
  241. _("NEWS").title() + u":\xA0%s\n" + \
  242. _("SCIENCE").title() + u":\xA0%s\n" + \
  243. _("COVERT").title() + u":\xA0%s\n" + \
  244. _("PUBLIC").title() + u":\xA0%s"
  245. self.state_display.color = state_colors[self.base.power_state]
  246. self.state_display.text = self.base.power_state_name
  247. mutable = not self.base.type.force_cpu
  248. for item in g.item_types:
  249. pane = getattr(self, item.id + "_pane")
  250. pane.change_button.visible = mutable
  251. current = self.get_current(item.id)
  252. if current is None:
  253. current_name = _("None")
  254. current_build = ""
  255. else:
  256. current_name = g.items[current.id].name
  257. if current.done:
  258. current_build = ""
  259. else:
  260. current_build = _("Completion in %s.") % \
  261. g.to_time(current.cost_left[2])
  262. pane.name_panel.text = "%s: %s" % (item.label,
  263. current_name)
  264. pane.build_panel.text = current_build
  265. count = ""
  266. if self.base.type.size > 1:
  267. current = getattr(self.base.cpus, "count", 0)
  268. size = self.base.type.size
  269. if size == current:
  270. count = _("x%d (max)") % current
  271. elif current == 0:
  272. count = _("(room for %d)") % size
  273. else:
  274. #Translators: current and maximum number of CPUs in a base
  275. count = _("x{CURRENT:d} (max {SIZE:d})",
  276. CURRENT=current, SIZE=size)
  277. self.cpu_pane.name_panel.text += " " + count
  278. # Detection chance display. If Socioanalytics hasn't been researched,
  279. # you get nothing; if it has, but not Advanced Socioanalytics, you get
  280. # an inaccurate value.
  281. if not g.techs["Socioanalytics"].done:
  282. self.detect_frame.text = g.strings["detect_chance_unknown_base"]
  283. else:
  284. accurate = g.techs["Advanced Socioanalytics"].done
  285. chance = self.base.get_detect_chance(accurate)
  286. def get_chance(group):
  287. return g.to_percent(chance.get(group, 0))
  288. self.detect_frame.text = discovery_template % \
  289. (get_chance("news"), get_chance("science"),
  290. get_chance("covert"), get_chance("public"))
  291. super(BaseScreen, self).rebuild()