view gamelib/gamestate.py @ 221:bf92c982996e

Don't crash when we run out of suggestions
author Neil Muller <drnlmuller@gmail.com>
date Sat, 12 May 2012 21:02:02 +0200
parents ed25c335fd67
children e4f9513c9dd0
line wrap: on
line source

# -*- coding: utf-8 -*-
# vim:fileencoding=utf-8 ai ts=4 sts=4 et sw=4

"""The actual game state object"""

from random import choice

from gamelib import missions, lab
from gamelib.game_base import get_subclasses
from gamelib.constants import M_VALS, INFO


class Game(object):

    def __init__(self, init_data=None):
        self.money = 1000
        self.milestone = "basement"
        # Will be updated on the next turn
        self.minions = 0
        self.points = 0
        self.reputation = 0
        self.turn = 0
        self.income = 0
        # Missions being attempted
        self.cur_missions = []
        # Science allocation for the current turn
        self.cur_allocation = []
        self.lab = None
        self.missions = []
        if init_data:
            self._load_data(init_data)
        else:
            self.lab = lab.Lab()
            # instantiate all the available missions
            for cls in get_subclasses(missions.Mission):
                self.missions.append(cls())

    def start_turn(self):
        # Make more flexible?
        self.points += 1 + M_VALS[self.milestone] * 2
        self.minions += 1 + M_VALS[self.milestone] * 2
        self.turn += 1
        self.money += self.income
        self.cur_missions = []
        self.cur_allocation = []
        # Suggest research
        basic_research, suggestions = self.lab.suggest_research()
        if basic_research:
            suggestions.append(None)
        try:
            self.advice = choice(suggestions)
        except IndexError:
            # We have an empty list of suggestions
            self.advice = None
        if self.advice is None:
            self.advice = ("Leave some researchers unassigned."
                           " They might turn up something cool.")
        else:
            self.advice = "Our work in %s is looking promising." % (
                self.advice.NAME,)

    def get_available_equipment(self):
        """Return a list of equipment we can produce and afford"""
        available = [x for x in self.lab.science
                     if x.SCIENCE_TYPE == 'schematic' and x.COST <= self.money]
        return available

    def get_all_equipment(self):
        """Return a list of equipment we could produce, regardless of cost"""
        equipment = [x for x in self.lab.science
                     if x.SCIENCE_TYPE == 'schematic']
        return equipment

    def get_available_missions(self):
        """Return a list of missions we can feasibly attempt"""
        available = [x for x in self.missions if x.can_attempt(self)]
        return available

    def get_available_points(self):
        return self.points - len(self.cur_allocation)

    def apply_mission_special(self, new_milestone=None, new_schematic=None,
            new_science=None, income=None):
        if new_milestone:
            self.milestone = new_milestone
        if new_schematic:
            self.lab.steal_science(new_schematic)
        if new_science:
            self.lab.steal_science(new_science)
        if income:
            self.income += income

    def end_turn(self):
        # Attempt the missions
        mission_results = []
        for mission, equipment in self.cur_missions:
            mission_results.append(mission.attempt_mission(equipment, self))
        if not self.cur_missions and self.reputation > 0:
            # If you're not doing stuff, you're not in the news
            self.reputation -= M_VALS[self.milestone]
        # Do the science
        self.points -= len(self.cur_allocation)
        if self.points < 0:
            raise RuntimeError('Spent too many points')
        # Process mission results
        messages = []
        for result in mission_results:
            result.apply(self)
            messages.append((result.outcome, result.text, result.loot))
        # Missions may give us science breakthroughs, so handle this after
        # mission results
        new_stuff = self.lab.spend_points(self.cur_allocation, self.points)
        self.points = 0
        self.minions = 0
        for science in new_stuff:
            # FIXME: Update UI better.
            messages.append((INFO, "SCIENCE breakthrough!", {
                        science.SCIENCE_TYPE: science}))
        return messages

    def save_data(self):
        """Serialize the game state into a dict"""
        data = {}
        data['money'] = self.money
        data['minions'] = self.minions
        data['reputation'] = self.reputation
        data['milestone'] = self.milestone
        data['points'] = self.points
        data['lab'] = self.lab.save_data()
        data['turn'] = self.turn
        data['income'] = self.income
        # Save mission state
        data['missions'] = {}
        for mission in self.missions:
            miss_name = type(mission).__name__
            data['missions'][miss_name] = mission.save_data()
        return data

    def _load_data(self, data):
        """Restore the game state"""
        self.money = data['money']
        self.minions = data['minions']
        self.reputation = data['reputation']
        self.points = data['points']
        self.turn = data['turn']
        self.income = data['income']
        self.milestone = data['milestone']
        self.lab = lab.Lab(data['lab'])
        for mis_class in missions.Mission.__subclasses__():
            miss_name = mis_class.__name__
            if miss_name in data['missions']:
                self.missions.append(mis_class(data['missions'][miss_name]))