Mercurial > sypikslang
view gamelib/gamegui.py @ 228:3955d126ca26
merge
author | Rizmari Versfeld <rizziepit@gmail.com> |
---|---|
date | Sat, 12 May 2012 23:09:33 +0200 |
parents | f8c05e6dc0e2 |
children | 0478c8b44acf |
line wrap: on
line source
# -*- coding: utf-8 -*- # vim:fileencoding=utf-8 ai ts=4 sts=4 et sw=4 """Gui for the actual game""" from pygame import image try: import simplejson json = simplejson except ImportError: import json from gamelib.data import filepath from gamelib.game_base import get_save_filename from gamelib.gui_base import (Window, TextLabel, TextBox, font_small, font_medium, font_large) from gamelib.gui import BigButton, ImageDrawable, IconTextButton from gamelib.engine import PopWindow, AddWindow, GameOver from gamelib.constants import WIDTH, HEIGHT, FAILURE, SUCCESS, GAME_WIN, INFO from gamelib.gamestate import Game def _lookup_reputation(rep): """Turn reputation in a nice string""" if rep < 0: return 'Mindless Thug' if rep < 10: return 'Fringe Lunatic' if rep < 20: return 'Batty Experimenter' if rep < 50: return 'Demented Analyst' if rep < 70: return 'Deranged Researcher' if rep < 150: return 'Mad Scientist' return 'Major Threat to World Peace' class PopWindowButton(BigButton): def __init__(self, pos, text): super(PopWindowButton, self).__init__(pos, text) def on_click(self): PopWindow.post() class ExitGameButton(PopWindowButton): def __init__(self): super(ExitGameButton, self).__init__((WIDTH - 128, HEIGHT - 60), 'Exit') class EndTurnButton(BigButton): def __init__(self, parent): super(EndTurnButton, self).__init__(((WIDTH - 128), 0), 'End Turn') self.parent = parent def on_click(self): self.parent.end_turn() class NextTurnButton(PopWindowButton): def __init__(self): super(NextTurnButton, self).__init__(((WIDTH - 128), 0), 'Next Turn') class AssignButton(PopWindowButton): def __init__(self, parent): super(AssignButton, self).__init__(((WIDTH - 128), 0), 'Assign Scheme') self.parent = parent def on_click(self): self.parent.accept() super(AssignButton, self).on_click() class CancelButton(PopWindowButton): def __init__(self, parent): super(CancelButton, self).__init__(((WIDTH - 256), 0), 'Cancel Scheme') self.parent = parent def on_click(self): self.parent.cancel() super(CancelButton, self).on_click() class ResetButton(BigButton): def __init__(self, parent, text='Clear calendars'): super(ResetButton, self).__init__((10, 0), text) self.parent = parent def on_click(self): self.parent.do_reset() class SwitchWinButton(BigButton): def __init__(self, pos, text, new_window): super(SwitchWinButton, self).__init__(pos, text) self.new_window = new_window def on_click(self): PopWindow.post() # Refresh state self.new_window.update() AddWindow.post(self.new_window) class ScienceWidget(IconTextButton): def __init__(self, science, pos, parent): self.science = science self.points = 0 self.parent = parent super(ScienceWidget, self).__init__( pos, science.get_image_name(), self.make_text(), font_small) def make_text(self): #name = self.science.NAME[:4] name = self.science.NAME text = '%s: %d' % (name, self.science.points) if self.points > 0: text = "%s + %d" % (text, self.points) return text def on_click(self): if (self.parent.game.get_available_points() > 0 and self.science.can_spend(self.parent.game.lab, self.points + 1)): self.points += 1 self.parent.game.cur_allocation.append(self.science) self.parent.update_labels() self.set_text() def set_text(self): self.text = self.make_text() self._draw_text() def reset(self): while self.points > 0: self.parent.game.cur_allocation.remove(self.science) self.points -= 1 self.set_text() self.parent.update_labels() def restore_selection(self, old_selection): points = old_selection.count(self.science) # We skip things we cannot allocate enoigh points to (temp points # boosts, etc.) or we cannot currently develop further if (points != self.points and points <= self.parent.game.get_available_points() and self.science.can_spend(self.parent.game.lab, self.points + points)): self.points = points self.parent.game.cur_allocation.extend([self.science] * points) self.parent.update_labels() self.set_text() class MissionWidget(BigButton): WIDTH = 260 HEIGHT = 48 BG_IMAGE_NORMAL = image.load(filepath('images/science_normal.png')) BG_IMAGE_DOWN = image.load(filepath('images/science_down.png')) BG_IMAGE_SELECTED = image.load(filepath('images/mission_selected.png')) def __init__(self, mission, pos, parent): self.mission = mission self.parent = parent self.game = self.parent.game self.minions = 0 super(MissionWidget, self).__init__(pos, '%s' % mission.NAME, font_small) selected = ImageDrawable((0, 0, self.WIDTH, self.HEIGHT), self.BG_IMAGE_SELECTED) self.add_state('SELECTED', selected) self.mode = 'NORMAL' self.equipment = [] def on_mouse_cancel(self, pos): self.set_state(self.mode) def on_mouse_up(self, pos): self.set_state(self.mode) self.on_click() def select(self): if not self.selected(): self.game.minions -= self.minions self.mode = 'SELECTED' self.set_state(self.mode) self.parent.update_labels() def unselect(self): if self.selected(): # Unassign the minions from the main game self.game.minions += self.minions self.reset() def on_click(self): # select mission and equipment if self.game.minions > 0 or self.selected(): if not self.selected(): # Assign the minions automatically self.minions = self.mission.MINIONS_REQUIRED equip = EquipWindow(self.parent.screen, self.mission, self.game, self) AddWindow.post(equip) def selected(self): return self.mode == 'SELECTED' def get_equipment(self): return self.equipment def reset(self): for equip in self.equipment: # Release funds self.game.money += equip.COST self.minions = 0 self.parent.update() self.equipment = [] self.mode = 'NORMAL' self.set_state('NORMAL') class EquipWidget(BigButton): WIDTH = 260 HEIGHT = 48 BG_IMAGE_NORMAL = image.load(filepath('images/science_normal.png')) BG_IMAGE_DOWN = image.load(filepath('images/science_down.png')) BG_IMAGE_UNAVAILABLE = image.load(filepath('images/equip_grey.png')) def __init__(self, equip, pos, parent, copies, available): self.equip = equip self.parent = parent self.available = available super(EquipWidget, self).__init__(pos, '[%s : %d] - %d' % (equip.NAME, equip.points, equip.COST), font_small) if not self.available: unavailable = ImageDrawable((0, 0, self.WIDTH, self.HEIGHT), self.BG_IMAGE_UNAVAILABLE) # Override the default self.add_state('NORMAL', unavailable) self.set_state('NORMAL') self.equipment = [] def on_mouse_down(self, pos): if self.available: super(EquipWidget, self).on_mouse_down(pos) def on_mouse_up(self, pos): if self.available: super(EquipWidget, self).on_mouse_up(pos) def on_click(self): self.parent.buy(self.equip) class ValueLabel(TextLabel): def __init__(self, pos, description, width=300): self.description = description rect = (pos[0], pos[1], width, 20) super(ValueLabel, self).__init__(rect, '%s : 0' % description, font_medium, (255, 255, 0)) def set_value(self, value): self.text = '%s : %s' % (self.description, value) self._draw_text() class EquipWindow(Window): def __init__(self, screen, mission, game, parent): super(EquipWindow, self).__init__(screen) assign = AssignButton(self) self.add_child(assign) cancel = CancelButton(self) self.add_child(cancel) reset = ResetButton(self, 'Clear inventory') self.add_child(reset) title = TextBox((170, 10, 370, 50), "Choose equipment for %s" % mission.NAME, font_medium, (255, 255, 255)) self.add_child(title) self.description = TextBox((10, 60, 790, 20), mission.get_description(), font_medium, (255, 255, 255)) self.add_child(self.description) self.parent = parent self.game = game self.money = ValueLabel((10, self.description.rect.height + 58), 'Money') self.money.set_value(self.game.money) self.minions = ValueLabel((310, self.description.rect.height + 58), 'Minions Assigned') self.minions.set_value(self.parent.minions) self.add_child(self.money) self.add_child(self.minions) inventory = TextLabel((10, self.description.rect.height + 75, 400, 20), 'Current Inventory', font_medium, (255, 255, 255)) self.add_child(inventory) self._equip = [] self._inventory = [] self._update_widgets() def cancel(self): self.do_reset() self.parent.unselect() def accept(self): self.parent.select() def _update_widgets(self): self._make_inventory() self._make_equip_widgets() def _make_inventory(self): for widget in self._inventory: self.remove_child(widget) self._inventory = [] x = 10 y = 110 + self.description.rect.height # collections.Counter is python 2.7. This is a bother equipment = sorted(set(self.parent.equipment), key=lambda x: x.NAME) for equip in equipment: count = self.parent.equipment.count(equip) widget = TextLabel((x, y, 700, 15), '%s x %s' % (equip.NAME, count), font_medium, (255, 255, 128)) x += 270 if x > WIDTH: x = 10 y += 18 self._inventory.append(widget) self.add_child(widget) def _make_equip_widgets(self): for widget in self._equip: self.remove_child(widget) self._equip = [] x = 0 y = max(250, 130 + self.description.rect.height + 18 * len(self._inventory) // 3) available = self.game.get_available_equipment() for equip in sorted(self.game.get_all_equipment(), key=lambda x: x.NAME): copies = self.parent.equipment.count(equip) widget = EquipWidget(equip, (x, y), self, copies, equip in available) self._equip.append(widget) self.add_child(widget) x += widget.WIDTH + 10 if x >= WIDTH: x = 0 y += 45 def buy(self, equip): self.game.money -= equip.COST self.money.set_value(self.game.money) self.parent.equipment.append(equip) self.parent.parent.update() self._update_widgets() def do_reset(self): for equip in self.parent.equipment: # Release funds self.game.money += equip.COST self.money.set_value(self.game.money) self.parent.equipment = [] self.parent.parent.update() self._update_widgets() class ResultsWindow(Window): message_colours = { INFO: (60, 60, 255), FAILURE: (255, 60, 60), SUCCESS: (60, 255, 60), } def __init__(self, screen, messages, turn): super(ResultsWindow, self).__init__(screen) nextbut = NextTurnButton() self.add_child(nextbut) title = TextLabel((200, 20, 400, 50), "Results for turn %d" % turn, font_medium, (255, 255, 255)) self.add_child(title) self.is_game_over = False if not messages: results = TextBox((200, 200, 400, 50), "Nothing of interest happened", font_medium, (255, 255, 255)) self.add_child(results) else: y = 200 for mission, msg_type, msg, loot in messages: # FIXME: Better widgets if msg_type == GAME_WIN: self._make_win_screen(turn, msg) self.is_game_over = True break colour = self.message_colours.get(msg_type, (255, 255, 255)) if mission: msg = '%s: %s' % (mission, msg) y = self.display_message(y, msg, loot, colour) def display_message(self, y, msg, loot, colour, font=font_medium): text = TextBox((50, y, 700, 25), msg, font, colour) y += text.rect.height + 5 self.add_child(text) for kind, value in loot.items(): y += self.display_loot_item(y, kind, value) return y + 5 def display_loot_item(self, y, kind, value): if hasattr(value, 'NAME'): value = value.NAME msg = "* %s: %s" % (kind, value) text = TextBox((80, y, 670, 25), msg, font_medium, (255, 255, 60)) self.add_child(text) return text.rect.height + 5 def _make_win_screen(self, turn, msg): # Clear existing widgets, and turn this into a won screen for child in self.children[:]: self.remove_child(child) exitbut = ExitGameButton() self.add_child(exitbut) title = TextLabel((200, 20, 400, 50), "Results for turn %d" % turn, font_medium, (255, 255, 255)) self.add_child(title) won = TextBox((200, 200, 400, 50), "You've succeeded in your quest", font_large, (255, 255, 255)) self.add_child(won) won = TextBox((200, 250, 400, 50), msg, font_large, (255, 255, 255)) self.add_child(won) class GameStateWindow(Window): """Base class for windows that show a lot of game state info""" def __init__(self, screen, game): super(GameStateWindow, self).__init__(screen) self.game = game self.screen = screen exitbut = ExitGameButton() self.add_child(exitbut) end_turn = EndTurnButton(self) self.add_child(end_turn) reset = ResetButton(self) self.add_child(reset) self.points = ValueLabel((10, 60), 'Blue-Sky Research Projects') self.add_child(self.points) self.minions = ValueLabel((310, 60), 'Minions available') self.add_child(self.minions) self.money = ValueLabel((510, 60), 'Money') self.add_child(self.money) self.milestone = ValueLabel((10, 80), 'Currently taken over') self.add_child(self.milestone) self.reputation = ValueLabel((310, 80), 'Reputation') self.add_child(self.reputation) self.advice = ValueLabel((10, 100), 'Research advice', width=780) self.add_child(self.advice) def update_labels(self): self.points.set_value(self.game.get_available_points()) self.money.set_value(self.game.money) self.minions.set_value(self.game.minions) self.milestone.set_value(self.game.milestone) self.reputation.set_value(_lookup_reputation(self.game.reputation)) self.advice.set_value(self.game.advice) class ActivityWindow(GameStateWindow): def __init__(self, screen, lab, develop): super(ActivityWindow, self).__init__(screen, lab.game) self.lab = lab self.develop = develop labbut = SwitchWinButton((150, 0), 'SCIENCE!!', lab) self.add_child(labbut) devbut = SwitchWinButton((300, 0), 'Engineering', develop) self.add_child(devbut) self.update_labels() self._missions = [] self._make_widgets() def _make_widgets(self): for widget in self._missions: self.remove_child(widget) self._missions = [] x = 0 y = 130 for mission in sorted(self.game.get_available_missions(), key=lambda x: x.NAME): widget = MissionWidget(mission, (x, y), self) self._missions.append(widget) self.add_child(widget) x += widget.WIDTH + 10 if x >= WIDTH: x = 0 y += 55 def end_turn(self): # Drop back to the research screen PopWindow.post() self.lab.update() AddWindow.post(self.lab) self.lab.end_turn() def update_widgets(self): self._make_widgets() def update(self): self.update_labels() def do_reset(self): self.lab.reset() self.develop.reset() self.reset() def get_mission_list(self): selected_missions = [] for widget in self._missions: if widget.selected(): equipment = widget.get_equipment() mission = widget.mission selected_missions.append((mission, equipment)) return selected_missions def reset(self): for widget in self._missions: widget.unselect() widget.reset() self.update_labels() class DevelopmentWindow(GameStateWindow): """Window for handling schematics research""" def __init__(self, screen, lab): super(DevelopmentWindow, self).__init__(screen, lab.game) self.lab = lab labbut = SwitchWinButton((150, 0), 'SCIENCE!!', lab) self.add_child(labbut) self.activity = None self.update_labels() self._sciences = [] self._make_science_widgets() def set_activity_window(self, activity): # Oh, what tangled webs we weave if not self.activity: self.activity = activity actbut = SwitchWinButton((300, 0), 'Schemes', activity) self.add_child(actbut) def _make_science_widgets(self): for widget in self._sciences: self.remove_child(widget) self._sciences = [] x = 0 y = 130 for science in sorted(self.game.lab.science, key=lambda x: x.NAME): if science.SCIENCE_TYPE == 'schematic': widget = ScienceWidget(science, (x, y), self) self.add_child(widget) self._sciences.append(widget) x += widget.WIDTH + 10 if x >= WIDTH: x = 0 y += widget.HEIGHT def end_turn(self): # Drop back to the research screen PopWindow.post() self.lab.update() AddWindow.post(self.lab) self.lab.end_turn() def update_widgets(self): self._make_science_widgets() self.update_labels() def update(self): self.update_labels() def do_reset(self): self.reset() self.lab.reset() self.activity.reset() def reset(self): for widget in self._sciences: widget.reset() def restore_selection(self, old_selection): for widget in self._sciences: widget.restore_selection(old_selection) class LabWindow(GameStateWindow): """Window for the research lab""" def __init__(self, screen, game_dict): self.game = Game(game_dict) super(LabWindow, self).__init__(screen, self.game) self.autosave = get_save_filename() # Ensure we setup everything with the correct state set self.game.start_turn() self.develop = DevelopmentWindow(screen, self) self.activity = ActivityWindow(screen, self, self.develop) self.develop.set_activity_window(self.activity) devbut = SwitchWinButton((150, 0), 'Engineering', self.develop) self.add_child(devbut) actbut = SwitchWinButton((300, 0), 'Schemes', self.activity) self.add_child(actbut) self._sciences = [] # Setup for the first turn self.update_labels() self._make_science_widgets() def _make_science_widgets(self): # FIXME: Horrible hackery for widget in self._sciences: self.remove_child(widget) self._sciences = [] x = 0 y = 130 for science in sorted(self.game.lab.science, key=lambda x: x.NAME): if science.SCIENCE_TYPE == 'research': widget = ScienceWidget(science, (x, y), self) self.add_child(widget) self._sciences.append(widget) x += widget.WIDTH + 10 if x >= WIDTH: x = 0 y += widget.HEIGHT + 5 def update_widgets(self): self._make_science_widgets() def restore_selection(self, old_selection): for widget in self._sciences: widget.restore_selection(old_selection) def end_turn(self): self.game.cur_missions = self.activity.get_mission_list() old_allocation = self.game.cur_allocation messages = self.game.end_turn() results = ResultsWindow(self.screen, messages, self.game.turn) if results.is_game_over: PopWindow.post() GameOver.post(results) return self.save_game() self.game.start_turn() self.update_widgets() self.develop.update_widgets() self.activity.update_widgets() # restore previous allocation self.restore_selection(old_allocation) self.develop.restore_selection(old_allocation) self.update_labels() self.develop.update_labels() AddWindow.post(results) def save_game(self): game_data = self.game.save_data() if self.autosave: # Don't corrupt the savefile if json crashes data = json.dumps(game_data) savefile = open(self.autosave, 'w') savefile.write(data) savefile.close() def update(self): self.update_labels() def do_reset(self): self.reset() self.develop.reset() self.activity.reset() def reset(self): for widget in self._sciences: widget.reset()