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
  • Ignore whitespace
 
 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
  • Ignore whitespace
     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 ###