1. Arne Babenhauserheide
  2. TextRPG

Commits

Arne Babenhauserheide  committed f4b665f

Split the Story and Char classes and extracted shared dialogs.

  • Participants
  • Parent commits b412e30
  • Branches default

Comments (0)

Files changed (2)

File anyrpg.py

View file
 
 It contains the basic functions any RPG scripting module which wants to be compatible with TextRPG stories needs to provide. 
 
+TODO: Add doctests, since it begins to become more complex... 
+
 Basic design principles for the scripting language: 
     
     - The action is character centered wherever possible and useful. 
 
 ### Classes ###
 
-class Char(ews_char):
+class UserInteraction(object): 
+    """Basic user interaction: Showing and asking things."""
+    def __init__(self, *args, **kwds): 
+        super(UserInteraction, self).__init__(*args, **kwds)
+        
+    def ask(self, question=None, *args, **kwds): 
+        """Ask a question.
+        
+        Note: Not console based implementations will want to overwrite this. """
+        if question is not None: 
+            return raw_input(_(question) + " ")
+    
+    def diag(self, data, localize=True, autoscroll=False, *args, **kwds):
+        if localize: 
+            text = _(data)
+        else: text = data 
+    
+        if not autoscroll: 
+            raw_input(text)
+        else: 
+            print text
+            sleep(len(text)*0.1)
+
+
+class Dialogs(object): 
+    def __init__(self, *args, **kwds): 
+        super(Dialogs, self).__init__(*args, **kwds)
+    
+    def get_experience(self, chars, amount=0):
+        """Show the experience the chars get and what they do with it."""
+        for i in self.get_experience_header(chars=chars, amount=amount).splitlines(): 
+            self.diag(i)
+            
+        # Info text for each char. 
+        for i in chars: 
+            text = i.incremental_upgrade(amount=1)
+            if text: 
+                for j in text.splitlines():
+                    self.diag(j)
+            
+        # Dialog footer. 
+        for i in self.get_experience_footer(chars, amount=amount).splitlines(): 
+            self.diag(i)
+    
+    def get_experience_header(self, chars, amount=0):
+        """@return: The header line of the experience dialog."""
+        scribble = ""
+        if len(chars) == 1: 
+            scribble += "\n***experience of " + chars[0].name + "***\n"
+            scribble += chars[0].name + " got " + str(amount) + " experience."
+        else: 
+            scribble += "\n***experience***\n"
+            scribble += "The characters got " + str(amount) + " experience."
+        return scribble
+    
+    def get_experience_footer(self, chars, amount=0):
+        """@return: The header line of the experience dialog."""
+        return "***/experience***\n"
+    
+    
+    def upgrade_info(self, char, amount): 
+        """Return the effects of improving teh char by the given amount of points/Strichen."""
+        upgrade = char.upgrade(amount)
+        item_name = " ".join(upgrade[0].items()[0][0])
+        old_value = upgrade[0].items()[0][1]["Zahlenwert"] 
+        new_value = upgrade[1].items()[0][1]["Zahlenwert"]
+        if old_value < new_value:
+            return char.name + " raised " + item_name + " from " + str(old_value) + " to " + str(new_value)
+        else: 
+            return False
+
+
+class Char(ews_char, UserInteraction, Dialogs):
     """A Char is any character in the game, those from the player as well as others."""
     def __init__(self, template=False, *args, **kwds): 
         """A Char is any character in the game, those from the player as well as others."""
     
     def get_exp(self, amount=0): 
         """Get experience and show it."""
-        return give_exp(self, amount)
+        self.get_experience([self], amount)
+    
+    def incremental_upgrade(self, amount=0): 
+        # Upgrade in single point steps, so the experience gets spread a bit. 
+        # if it's less than two points, use it completely. 
+        if amount <= 2: 
+            text = self.upgrade_info(self, amount)
+        # If it's more than one point, spread all but one in single points 
+        # and at last improve by 1 plus the remaining fraction. 
+        # With this, no value gets less than one point/Strich 
+        # (no use in spreading points which can only give an increase by pure chance). 
+        else: 
+            text = ""
+            for j in range(int(amount) -1): 
+                text += self.upgrade_info(self, 1)
+            text += self.upgrade_info(self, amount - int(amount) + 1)
+        return text
     
     def battle(self, other): 
         """Fight a deadly battle."""
-        return battle(self, other)
+        return self.fight_while_standing(self, other)
+    
+    def fight_while_standing(self, me, other):
+        '''Fight as long as both characters are still active.
+        
+        Plans: 
+            - TODO: Select fight styles, when you attack as well as when the other attacks. 
+        '''
+        
+        # Show the battle status of both chars. 
+        self.diag(self.battle_info(me, other))
+        
+        # Ask the player which action to take.        
+        self.diag(other.name + ' comes closer.')
+        won, injuries_self, injuries_other = self.select_battle_action(me, other)
+        
+        self.diag(self.battle_round_result(me, other, won, injuries_self, injuries_other))
+        
+        self.diag(self.battle_info(me, other))
+        
+        while me.active and other.active:
+            if won: # we won the last round, so we can attack again.
+                won, injuries_self, injuries_other = self.select_battle_action(me, other)
+            else: 
+                self.diag(other.name + " attacks you.")
+                won, injuries_self, injuries_other = self.select_battle_style(me, other, attacker=False)
+            
+            self.diag(self.battle_round_result(me, other, won, injuries_self, injuries_other))
+            
+            self.diag(self.battle_info(me, other))
+        
+        if me.active: 
+            return True # We won
+        else: 
+            return False # We lost
+    
+    def battle_info(self, me, other): 
+        """Status information about a battle.
+        
+        @return: Completely formatted String to display. """
+        return "\n" + me.battle_stats() + "\n\n" + other.battle_stats() + "\n"
+    
+    def battle_round_result(self, me, other, won, injuries_self, injuries_other): 
+        """Return the result of one round of battle. Called with the battle results (won, injuries_self, injuries_other)."""
+        if won: 
+            scribble = _("\nYou won this round.")
+            if injuries_other[2] != 0: 
+                scribble += "\n" + other.name + _(" took ") + str(injuries_other[2]) + _(" points of damage")
+                if injuries_other[0] == 1: 
+                    scribble += _(" and a deep wound.") # You never get more than one wound per enemy in one round in the 1d6 rpg. 
+                elif injuries_other[1] == 1: 
+                    scribble += _(" and a critical wound.") # You never get more than one wound per enemy in one round in the 1d6 rpg. 
+                else: scribble += "."
+        else: 
+            scribble = _("\nYou didn't win this round.")
+            if injuries_self[2] != 0: 
+                scribble += "\n" + _("and you took ") + str(injuries_self[2]) + _(" points of damage")
+                if injuries_self[0] == 1: 
+                    scribble += _(" and a deep wound.") # You never get more than one wound per enemy in one round in the 1d6 rpg. 
+                elif injuries_self[1] == 1: 
+                    scribble += _(" and a critical wound.") # You never get more than one wound per enemy in one round in the 1d6 rpg. 
+                else: scribble += "."
+        return scribble
+    
+    def select_battle_action(self, me, other, attack=True): 
+        """Ask which action to take in the battle.
+        
+        @param me: The player char. 
+        @type me: Char
+        @param other: The enemy. 
+        @type other: Char
+        
+        @param attack: Whether the character is the attacker. 
+        @type attack: True/False
+        
+        @return: The result of the battle (won or lost, wounds)."""
+        
+        if self.ask('Do you want to attack ' + other.name + '? (Yes, no) ').lower() in ['yes', 'y', '']: 
+            self.diag("You attack " + other.name + ".")
+            won, injuries_self, injuries_other = self.select_battle_style(me, other, attacker=True)
+        else:
+            self.diag("You don't attack, so you could do something else this round, if this was already implemented.")
+            won, injuries_self, injuries_other = False, [0, 0, 0], [0, 0, 0]
+        return won, injuries_self, injuries_other 
+        
+    def select_battle_style(self, me, other, attacker): 
+        """Select how to fight.
+        @param attacker: Is me the attacker? 
+        @type: Bool
+        """
+        styles_self = []
+        style = self.ask("How do you want to fight? (Usual, defensive, target head)")
+        if style.lower() in ["target head", "head", "h"]: 
+            styles_self.append("target head")
+        elif style.lower() in ["defensive", "d"]: 
+            styles_self.append("defensive")
+        # TODO: Use the style. 
+        if attacker: 
+            won, injuries_self, injuries_other = me.fight_round(other, styles_self=styles_self)
+        else: 
+            won, injuries_other, injuries_self = other.fight_round(me, styles_other=styles_self)
+            won = not won
+        return won, injuries_self, injuries_other
     
     def battle_stats(self):
         """@return: A string with battle status information, formatted as battle stats"""
     
 
 
-class Story(object): 
+class Story(UserInteraction, Dialogs): 
     """The main component for telling stories. Subclass this to change all behaviour."""
     def __init__(self, *args, **kwds): 
         """The main component for telling stories."""
-        pass
-
-    def ask(self, question=None, *args, **kwds): 
-        """Ask a question.
-        
-        Note: Not console based implementations will want to overwrite this. """
-        if question is not None: 
-            return raw_input(_(question) + " ")
+        super(Story, self).__init__(*args, **kwds)
     
     def story(self, data=None, *args, **kwds):
         """Tell a part of the story."""
             for i in data.split("\n"): 
                 self.diag(i, localize=False, *args, **kwds)
     
-    def diag(self, data, localize=True, autoscroll=False, *args, **kwds):
-        if localize: 
-            text = _(data)
-        else: text = data 
-    
-        if not autoscroll: 
-            raw_input(text)
-        else: 
-            print text
-            sleep(len(text)*0.1)
-    
     
     def save(self, chars=[], *args, **kwds):
             """Save the current state."""
                     i.save()
     
     
-    def battle(self, me, other, *args, **kwds): 
-        return self.fight_while_standing(me, other, *args, **kwds)
-    
-    def battle_info(self, me, other): 
-        """Status information about a battle.
-        
-        @return: Completely formatted String to display. """
-        return "\n" + me.battle_stats() + "\n\n" + other.battle_stats() + "\n"
-    
-    def fight_while_standing(self, me, other):
-        '''Fight as long as both characters are still active.
-        
-        Plans: 
-            - TODO: Select fight styles, when you attack as well as when the other attacks. 
-        '''
-        
-        # Show the battle status of both chars. 
-        self.diag(self.battle_info(me, other))
-        
-        # Ask the player which action to take.        
-        self.diag(other.name + ' comes closer.')
-        won, injuries_self, injuries_other = self.select_battle_action(me, other)
-        
-        self.diag(self.battle_round_result(me, other, won, injuries_self, injuries_other))
-        
-        self.diag(self.battle_info(me, other))
-        
-        while me.active and other.active:
-            if won: # we won the last round, so we can attack again.
-                won, injuries_self, injuries_other = self.select_battle_action(me, other)
-            else: 
-                self.diag(other.name + " attacks you.")
-                won, injuries_self, injuries_other = self.select_battle_style(me, other, attacker=False)
-            
-            self.diag(self.battle_round_result(me, other, won, injuries_self, injuries_other))
-            
-            self.diag(self.battle_info(me, other))
-        
-        if me.active: 
-            return True # We won
-        else: 
-            return False # We lost
-    
-    def battle_round_result(self, me, other, won, injuries_self, injuries_other): 
-        """Return the result of one round of battle. Called with the battle results (won, injuries_self, injuries_other)."""
-        if won: 
-            scribble = _("\nYou won this round.")
-            if injuries_other[2] != 0: 
-                scribble += "\n" + other.name + _(" took ") + str(injuries_other[2]) + _(" points of damage")
-                if injuries_other[0] == 1: 
-                    scribble += _(" and a deep wound.") # You never get more than one wound per enemy in one round in the 1d6 rpg. 
-                elif injuries_other[1] == 1: 
-                    scribble += _(" and a critical wound.") # You never get more than one wound per enemy in one round in the 1d6 rpg. 
-                else: scribble += "."
-        else: 
-            scribble = _("\nYou didn't win this round.")
-            if injuries_self[2] != 0: 
-                scribble += "\n" + _("and you took ") + str(injuries_self[2]) + _(" points of damage")
-                if injuries_self[0] == 1: 
-                    scribble += _(" and a deep wound.") # You never get more than one wound per enemy in one round in the 1d6 rpg. 
-                elif injuries_self[1] == 1: 
-                    scribble += _(" and a critical wound.") # You never get more than one wound per enemy in one round in the 1d6 rpg. 
-                else: scribble += "."
-        return scribble
-    
-    def select_battle_action(self, me, other, attack=True): 
-        """Ask which action to take in the battle.
-        
-        @param me: The player char. 
-        @type me: Char
-        @param other: The enemy. 
-        @type other: Char
-        
-        @param attack: Whether the character is the attacker. 
-        @type attack: True/False
-        
-        @return: The result of the battle (won or lost, wounds)."""
-        
-        if self.ask('Do you want to attack ' + other.name + '? (Yes, no) ').lower() in ['yes', 'y', '']: 
-            self.diag("You attack " + other.name + ".")
-            won, injuries_self, injuries_other = self.select_battle_style(me, other, attacker=True)
-        else:
-            self.diag("You don't attack, so you could do something else this round, if this was already implemented.")
-            won, injuries_self, injuries_other = False, [0, 0, 0], [0, 0, 0]
-        return won, injuries_self, injuries_other 
-        
-    def select_battle_style(self, me, other, attacker): 
-        """Select how to fight.
-        @param attacker: Is me the attacker? 
-        @type: Bool
-        """
-        styles_self = []
-        style = self.ask("How do you want to fight? (Usual, defensive, target head)")
-        if style.lower() in ["target head", "head", "h"]: 
-            styles_self.append("target head")
-        elif style.lower() in ["defensive", "d"]: 
-            styles_self.append("defensive")
-        # TODO: Use the style. 
-        if attacker: 
-            won, injuries_self, injuries_other = me.fight_round(other, styles_self=styles_self)
-        else: 
-            won, injuries_other, injuries_self = other.fight_round(me, styles_other=styles_self)
-            won = not won
-        return won, injuries_self, injuries_other
-    
-    
     def give_exp(self, char, amount=0): 
         """Give experience to only one char."""
         return self.get_experience([char], amount=amount)
-    
-    def get_experience(self, chars, amount=0):
-        """Show the experience the chars get and what they do with it."""
-        for i in self.get_experience_header(chars=chars, amount=amount).splitlines(): 
-            self.diag(i)
-            
-        # Info text for each char. 
-        for i in chars: 
-            # Upgrade in single point steps, so the experience gets spread a bit. 
-            # if it's less than two points, use it completely. 
-            if amount <= 2: 
-                text = self.upgrade_info(i, amount)
-                if text: 
-                    self.diag(text)
-            # If it's more than one point, spread all but one in single points 
-            # and at last improve by 1 plus the remaining fraction. 
-            # With this, no value gets less than one point/Strich 
-            # (no use in spreading points which can only give an increase by pure chance). 
-            else: 
-                for j in range(int(amount) -1): 
-                    text = self.upgrade_info(i, 1)
-                    if text: 
-                        self.diag(text)
-                text = self.upgrade_info(i, amount - int(amount) + 1)
-                if text: 
-                    self.diag(text)
-            
-        # Dialog footer. 
-        for i in self.get_experience_footer(chars, amount=amount).splitlines(): 
-            self.diag(i)
-    
-    def get_experience_header(self, chars, amount=0):
-        """@return: The header line of the experience dialog."""
-        scribble = ""
-        if len(chars) == 1: 
-            scribble += "\n***experience of " + chars[0].name + "***"
-            scribble += chars[0].name + " got " + str(amount) + " experience."
-        else: 
-            scribble += "\n***experience***"
-            scribble += "The characters got " + str(amount) + " experience."
-        return scribble
-    
-    def get_experience_footer(self, chars, amount=0):
-        """@return: The header line of the experience dialog."""
-        return "***/experience***\n"
-    
-    
-    def upgrade_info(self, char, amount): 
-        """Return the effects of improving teh char by the given amount of points/Strichen."""
-        upgrade = char.upgrade(amount)
-        item_name = " ".join(upgrade[0].items()[0][0])
-        old_value = upgrade[0].items()[0][1]["Zahlenwert"] 
-        new_value = upgrade[1].items()[0][1]["Zahlenwert"]
-        if old_value < new_value:
-            return char.name + " raised " + item_name + " from " + str(old_value) + " to " + str(new_value)
-        else: 
-            return False
 
 
 class MissingScriptingFunction(Exception): 
 
 def battle(me, other, *args, **kwds): 
     raise MissingScriptingFunction(ask, implementation = """def battle(me, other, *args, **kwds): 
-    return story_helper.battle(me, other, *args, **kwds)""")
+    return me.battle(other, *args, **kwds)""")
 
 
 

File textrpg.py

View file
     return story_helper.save(chars=chars, *args, **kwds)
 
 def battle(me, other, *args, **kwds): 
-    return story_helper.battle(me, other, *args, **kwds)
+    return me.battle(other, *args, **kwds)
 
 
 ### Test story lines ###