changeset 655:c77d6aa29bee pyntnclick

Some code to kinda demonstrate the ever so cunning state handling plan
author Neil Muller <neil@dip.sun.ac.za>
date Sun, 12 Feb 2012 13:56:59 +0200
parents 335db68e0db4
children 59d16b45ee4c
files gamelib/main.py gamelib/scenes/bridge.py gamelib/scenes/game_widgets.py gamelib/ss_state.py pyntnclick/main.py pyntnclick/state.py
diffstat 6 files changed, 75 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/gamelib/main.py	Sun Feb 12 13:11:53 2012 +0200
+++ b/gamelib/main.py	Sun Feb 12 13:56:59 2012 +0200
@@ -2,6 +2,7 @@
 
 from menu import MenuScreen
 from endscreen import EndScreen
+from ss_state import SSState
 
 from pyntnclick.main import GameDescription
 
@@ -16,6 +17,9 @@
             }
     START_SCREEN = 'menu'
 
+    def __init__(self):
+        super(SuspendedSentence, self).__init__(SSState)
+
 
 def main():
     ss = SuspendedSentence()
--- a/gamelib/scenes/bridge.py	Sun Feb 12 13:11:53 2012 +0200
+++ b/gamelib/scenes/bridge.py	Sun Feb 12 13:56:59 2012 +0200
@@ -330,19 +330,21 @@
             return "The sign reads 'Warning: Authorized Techinicians Only'."
 
     def interact_without(self):
-        if self.scene.get_data('ai status') == 'online':
+        ai_status = self.state.get_jim_state()
+        if ai_status == 'online':
             return self.interact_default(None)
         elif self.scene.get_data('ai panel') == 'closed':
             return Result("You are unable to open the panel with your"
                           " bare hands.")
         elif self.scene.get_data('ai panel') == 'open':
             self.scene.set_data('ai panel', 'broken')
-            self.scene.set_data('ai status', 'dead')
+            self.state.break_ai()
             self.set_interact('broken')
             return Result("You unplug various important-looking wires.")
 
     def interact_with_machete(self, item):
-        if self.scene.get_data('ai status') == 'online':
+        ai_status = self.state.get_jim_state()
+        if ai_status == 'online':
             return self.interact_default(item)
         elif self.scene.get_data('ai panel') == 'closed':
             self.scene.set_data('ai panel', 'open')
@@ -350,13 +352,13 @@
             return Result("Using the machete, you lever the panel off.")
         elif self.scene.get_data('ai panel') == 'open':
             self.scene.set_data('ai panel', 'broken')
-            self.scene.set_data('ai status', 'dead')
+            self.state.break_ai()
             self.set_interact('broken')
             return Result("You smash various delicate components with"
                           " the machete.")
 
     def interact_default(self, item):
-        if self.scene.get_data('ai status') == 'online':
+        if self.state.get_jim_state() == 'online':
             return (Result('You feel a shock from the panel.'),
                     make_jim_dialog("Prisoner %s. Please step away from the"
                                     " panel. You are not an authorized"
--- a/gamelib/scenes/game_widgets.py	Sun Feb 12 13:11:53 2012 +0200
+++ b/gamelib/scenes/game_widgets.py	Sun Feb 12 13:56:59 2012 +0200
@@ -28,9 +28,9 @@
         return self.interact_without()
 
 
-def make_jim_dialog(mesg, state):
+def make_jim_dialog(mesg, game):
     "Utility helper function"
-    if state.scenes['bridge'].get_data('ai status') == 'online':
+    if game.data.get_jim_state() == 'online':
         return Result(mesg, style='JIM')
     else:
         return None
@@ -45,7 +45,7 @@
     }
 
     def get_description(self):
-        status = self.game.scenes['bridge'].get_data('ai status')
+        status = self.state.get_jim_state()
         if status == 'online':
             return "A security camera watches over the room"
         elif status == 'looping':
@@ -55,19 +55,19 @@
             return "The security camera is powered down"
 
     def is_interactive(self, tool=None):
-        return self.game.scenes['bridge'].get_data('ai status') == 'online'
+        return self.state.get_jim_state() == 'online'
 
     def interact_with_escher_poster(self, item):
         # Order matters here, because of helper function
-        if self.game.scenes['bridge'].get_data('ai status') == 'online':
+        if self.state.get_jim_state() == 'online':
             ai_response = make_jim_dialog("3D scene reconstruction failed."
                     " Critical error. Entering emergency shutdown.",
                     self.game)
-            self.game.scenes['bridge'].set_data('ai status', 'looping')
+            self.game.data.loop_ai()
             return ai_response
 
     def animate(self):
-        ai_status = self.game.scenes['bridge'].get_data('ai status')
+        ai_status = self.state.get_jim_state()
         if ai_status != self.get_data('status'):
             self.set_data('status', ai_status)
             self.set_interact(ai_status)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gamelib/ss_state.py	Sun Feb 12 13:56:59 2012 +0200
@@ -0,0 +1,16 @@
+"""The Custom state object for Suspended Sentence"""
+
+from pyntnclick.state import GameState
+
+class SSState(GameState):
+
+   def get_jim_state(self):
+       """Check JIM's health"""
+       return self['bridge']['ai status']
+
+   def loop_ai(self):
+       """Make JIM loopy"""
+       self['bridge']['ai status'] = 'looping'
+
+   def break_ai(self):
+       self['bridge']['ai status'] = 'dead'
--- a/pyntnclick/main.py	Sun Feb 12 13:11:53 2012 +0200
+++ b/pyntnclick/main.py	Sun Feb 12 13:56:59 2012 +0200
@@ -49,7 +49,7 @@
     # resource module
     RESOURCE_MODULE = "Resources"
 
-    def __init__(self):
+    def __init__(self, custom_data_cls=None):
         if self.INITIAL_SCENE is None:
             raise GameDescriptionError("A game must have an initial scene.")
         if not self.SCENE_LIST:
@@ -62,6 +62,7 @@
         self._scene_list = self.SCENE_LIST
         self._resource_module = self.RESOURCE_MODULE
         self._debug_rects = False
+        self._custom_data_cls = custom_data_cls
         self._screens = self.SCREENS.copy()
         self._screens['game'] = GameScreen
         self.resource = Resources(self._resource_module)
@@ -72,6 +73,8 @@
     def initial_state(self):
         """Create a copy of the initial game state."""
         initial_state = state.Game(self)
+        if self._custom_data_cls:
+            initial_state.set_custom_data(self._custom_data_cls())
         initial_state.set_debug_rects(self._debug_rects)
         for scene in self._scene_list:
             initial_state.load_scenes(scene)
--- a/pyntnclick/state.py	Sun Feb 12 13:11:53 2012 +0200
+++ b/pyntnclick/state.py	Sun Feb 12 13:56:59 2012 +0200
@@ -7,7 +7,7 @@
 from pygame.color import Color
 
 
-def frame_rect(surface, color, rect, thick = 1):
+def frame_rect(surface, color, rect, thick=1):
     # FIXME: Stolen from albow
     surface.fill(color, (rect.left, rect.top, rect.width, thick))
     surface.fill(color, (rect.left, rect.bottom - thick, rect.width, thick))
@@ -59,6 +59,35 @@
                     res.process(scene_widget)
 
 
+class GameState(dict):
+    """This holds the serializable game state.
+
+       Games wanting to do fancier stuff with the state should
+       sub-class this and feed the subclass into
+       GameDescription via the custom_data parameter."""
+
+    def get_all_gizmo_data(self, state_key):
+        """Get all state for a gizmo - returns a dict"""
+        return self[state_key]
+
+    def get_data(self, state_key, data_key):
+        """Get a single entry"""
+        return self[state_key].get(data_key, None)
+
+    def set_data(self, state_key, data_key, value):
+        """Set a single value"""
+        self[state_key][data_key] = value
+
+    def initialize_state(self, state_key, initial_data):
+        """Initialize a gizmo entry"""
+        if state_key not in self:
+            self[state_key] = {}
+            if initial_data:
+                # deep copy of INITIAL_DATA allows lists, sets and
+                # other mutable types to safely be used in INITIAL_DATA
+                self[state_key].update(copy.deepcopy(initial_data))
+
+
 class Game(object):
     """Complete game state.
 
@@ -81,7 +110,7 @@
         # currently selected tool (item)
         self.tool = None
         # Global game data
-        self.data = {}
+        self.data = GameState()
         # current scene
         self.current_scene = None
         # current detail view
@@ -97,6 +126,9 @@
         # debug rects
         self.debug_rects = False
 
+    def set_custom_data(self, data_object):
+        self.data = data_object
+
     def set_debug_rects(self, value=True):
         self.debug_rects = value
 
@@ -251,21 +283,15 @@
     def set_state(self, state):
         """Set the state object and initialize if needed"""
         self.state = state
-        if self.state_key not in self.state:
-            self.state[self.state_key] = {}
-            if self.INITIAL_DATA:
-                # deep copy of INITIAL_DATA allows lists, sets and
-                # other mutable types to safely be used in INITIAL_DATA
-                self.state[self.state_key].update(
-                        copy.deepcopy(self.INITIAL_DATA))
+        self.state.initialize_state(self.state_key, self.INITIAL_DATA)
 
     def set_data(self, key, value):
         if self.state:
-            self.state[self.state_key][key] = value
+            self.state.set_data(self.state_key, key, value)
 
     def get_data(self, key):
         if self.state:
-            return self.state[self.state_key].get(key, None)
+            return self.state.get_data(self.state_key, key)
 
 
 class Scene(StatefulGizmo):