From c0c4478e4a5748feb30c0923103d90a9ecc23063 Mon Sep 17 00:00:00 2001
From: Simon Cross <hodgestar@gmail.com>
Date: Sat, 5 Mar 2016 19:01:57 +0200
Subject: [PATCH] Refactor cat.

---
 koperkapel/roaches.py      | 46 +++++++++++++++++++
 koperkapel/scenes/base.py  |  8 ----
 koperkapel/scenes/level.py | 14 +++---
 koperkapel/world.py        | 91 ++++++++++++--------------------------
 4 files changed, 84 insertions(+), 75 deletions(-)

diff --git a/koperkapel/roaches.py b/koperkapel/roaches.py
index c844b52..da362a3 100644
--- a/koperkapel/roaches.py
+++ b/koperkapel/roaches.py
@@ -5,6 +5,32 @@ from pygame.constants import BLEND_RGBA_MULT
 from .actors.animsurf import AnimatedSurfActor
 from .serums import roach_serum_color
 
+NAMES = [
+    "roupert",
+    "roachel",
+    "roeginald",
+    "roichard",
+    "rory",
+    "roalph",
+    "roabia",
+    "roafi",
+    "roaman",
+    "roemus",
+    "roadley",
+    "roanaell",
+    "roashwan",
+    "roashid",
+    "roaphael",
+    "roenfield",
+    "roani",
+    "roaya",
+    "roaza",
+    "robekka",
+    "rogan",
+    "roiana",
+    "roberta",
+]
+
 
 def roach_by_name(world, roach_name):
     roaches = [r for r in world.roaches if r.name == roach_name]
@@ -13,6 +39,26 @@ def roach_by_name(world, roach_name):
     return roaches[0]
 
 
+def next_roach_name(world):
+    roach_names = [x['name'] for x in world.roaches]
+    for cand in NAMES:
+        if cand not in roach_names:
+            return cand
+
+
+def build_roach(world, name=None, health=5, **kw):
+    if name is None:
+        name = next_roach_name(world)
+    if name is None:
+        return
+    roach = {
+        "name": name,
+        "health": health,
+    }
+    roach.update(kw)
+    return roach
+
+
 class WorldRoach(object):
     """A roach proxy with no properties for display on the game level."""
 
diff --git a/koperkapel/scenes/base.py b/koperkapel/scenes/base.py
index 1499803..623eb42 100644
--- a/koperkapel/scenes/base.py
+++ b/koperkapel/scenes/base.py
@@ -43,9 +43,6 @@ class Engine:
     def move_screen(self, offset):
         self._scene.move_screen(offset)
 
-    def add_roach(self):
-        self._world.add_new_roach()
-
     @apply_events
     def update(self, dt):
         return self._scene.update(self._world.proxy(), self, dt)
@@ -110,11 +107,6 @@ class MoveViewportEvent(Event):
 
     ENGINE_METHOD = "move_screen"
 
-class AddRoachEvent(Event):
-    """ Change to a new scene. """
-
-    ENGINE_METHOD = "add_roach"
-
 
 class Layer:
     """ A single layer of actors. """
diff --git a/koperkapel/scenes/level.py b/koperkapel/scenes/level.py
index 6a8e232..31e1269 100644
--- a/koperkapel/scenes/level.py
+++ b/koperkapel/scenes/level.py
@@ -4,10 +4,10 @@ from pgzero.constants import keys
 from pygame import Surface
 import pygame.locals as pgl
 from ..loaders.levelloader import levels
-from .base import Scene, ChangeSceneEvent, MoveViewportEvent, AddRoachEvent, defer_to_update
+from .base import Scene, ChangeSceneEvent, MoveViewportEvent, defer_to_update
 from ..constants import TILE_SIZE, WIDTH, HEIGHT
+from ..roaches import build_roach
 from ..vehicles.base import Vehicle
-from ..roaches import RoachFactory, WorldRoach
 
 
 class BaseLevelScene(Scene):
@@ -111,7 +111,7 @@ class GameLevelScene(BaseLevelScene):
 
     def _set_pos(self, x, y):
         self._player_pos = (x, y)
-        #print('At ', (x, y))
+        # print('At ', (x, y))
 
     def _can_move(self, x, y):
         if self._mode == 'walk':
@@ -134,6 +134,11 @@ class GameLevelScene(BaseLevelScene):
         self._roaches.add(self._avatar)
         self._set_angle(self._angle)
 
+    @defer_to_update
+    def _add_roach(self, world):
+        world.roaches.append(build_roach(world))
+        self._vehicle_changed()
+
     @defer_to_update
     def _change_vehicle(self, world):
         vehicle = Vehicle.random()
@@ -187,8 +192,7 @@ class GameLevelScene(BaseLevelScene):
             elif self._level.is_on_friend(x, y):
                 friend = self._level.remove_friend(x, y)
                 self._friends.remove(friend)
-                self._vehicle_changed()
-                return [AddRoachEvent()]
+                self._add_roach()
             return
         elif key == keys.X:
             # Fire
diff --git a/koperkapel/world.py b/koperkapel/world.py
index 7115194..5a22b04 100644
--- a/koperkapel/world.py
+++ b/koperkapel/world.py
@@ -1,33 +1,7 @@
 """ World and player state. """
 
 from .scenes.base import WorldEvent
-
-
-NAMES =[
-        "roupert",
-        "roachel",
-        "roeginald",
-        "roichard",
-        "rory",
-        "roalph",
-        "roabia",
-        "roafi",
-        "roaman",
-        "roemus",
-        "roadley",
-        "roanaell",
-        "roashwan",
-        "roashid",
-        "roaphael",
-        "roenfield",
-        "roani",
-        "roaya",
-        "roaza",
-        "robekka",
-        "rogan",
-        "roiana",
-        "roberta",
-       ]
+from .roaches import build_roach
 
 
 class World:
@@ -47,10 +21,7 @@ class World:
     def _build_initial_state(self):
         state = {}
         state["roaches"] = [
-            #self._build_roach("roachel", smart=True),
-            #self._build_roach("roeginald", strong=True),
-            #self._build_roach("roichard", fast=True),
-            self._build_roach("roupert"),
+            build_roach(self, "roupert"),
         ]
         state["serums"] = [
             "smart", "strong", "fast",
@@ -72,26 +43,27 @@ class World:
         }
         return state
 
-    def _build_roach(self, name, health=5, **kw):
-        roach = {
-            "name": name,
-            "health": health,
-        }
-        roach.update(kw)
-        return roach
+    def _get_obj(self, name):
+        parts = name.split(".")
+        obj = self._state
+        for p in parts[:-1]:
+            if isinstance(obj, dict):
+                obj = obj[p]
+            elif isinstance(obj, list):
+                obj = obj[int(p)]
+            else:
+                raise KeyError("%r not found in world" % (name,))
+        return obj, parts[-1]
 
     def _apply_set(self, action, updates):
         for name, value in updates.items():
-            parts = name.split(".")
-            obj = self._state
-            for p in parts[:-1]:
-                if isinstance(obj, dict):
-                    obj = obj[p]
-                elif isinstance(obj, list):
-                    obj = obj[int(p)]
-                else:
-                    raise KeyError("%r not found in world" % (name,))
-            obj[parts[-1]] = value
+            obj, key = self._get_obj(name)
+            obj[key] = value
+
+    def _apply_append(self, action, updates):
+        for name, value in updates.items():
+            obj, key = self._get_obj(name)
+            obj.append(value)
 
     def _apply_reset(self, action):
         self._state = self._build_initial_state()
@@ -102,14 +74,6 @@ class World:
     def proxy(self):
         return WorldDictProxy(self._state)
 
-    def add_new_roach(self):
-        roach_names = [x['name'] for x in self.roaches]
-        for cand in NAMES:
-            if cand not in roach_names:
-                roach = self._build_roach(cand)
-                self._state['roaches'].append(roach)
-                break
-
     def apply_event(self, action, *args, **kw):
         handler = getattr(self, "_apply_%s" % (action,))
         return handler(action, *args, **kw)
@@ -142,8 +106,8 @@ class WorldBaseProxy:
             "_events": _events,
         })
 
-    def _record_change(self, fullname, value):
-        self._events.append(WorldEvent("set", {
+    def _record_change(self, fullname, value, action="set"):
+        self._events.append(WorldEvent(action, {
             fullname: value
         }))
 
@@ -155,10 +119,6 @@ class WorldBaseProxy:
 class WorldDictProxy(WorldBaseProxy):
     """ World dictionary proxy that records changes and produces events. """
 
-    def items(self):
-        return (
-            (k, _maybe_subproxy(self, k, v)) for k, v in self._state.items())
-
     def __setattr__(self, name, value):
         self._top._record_change("%s%s" % (self._prefix, name), value)
 
@@ -173,6 +133,10 @@ class WorldDictProxy(WorldBaseProxy):
     def __getitem__(self, name):
         return self.__getattr__(name)
 
+    def items(self):
+        return (
+            (k, _maybe_subproxy(self, k, v)) for k, v in self._state.items())
+
 
 class WorldListProxy(WorldBaseProxy):
     """ World list proxy that records changes and produces events. """
@@ -188,3 +152,6 @@ class WorldListProxy(WorldBaseProxy):
 
     def __bool__(self):
         return bool(self._state)
+
+    def append(self, value):
+        self._top._record_change(self._prefix, value, action="append")
-- 
2.34.1