diff charactercreationcontroller.py @ 0:7a89ea5404b1

Initial commit of parpg-core.
author M. George Hansen <technopolitica@gmail.com>
date Sat, 14 May 2011 01:12:35 -0700
parents
children 06145a6ee387
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/charactercreationcontroller.py	Sat May 14 01:12:35 2011 -0700
@@ -0,0 +1,394 @@
+#   This file is part of PARPG.
+#
+#   PARPG is free software: you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as published by
+#   the Free Software Foundation, either version 3 of the License, or
+#   (at your option) any later version.
+#
+#   PARPG is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with PARPG.  If not, see <http://www.gnu.org/licenses/>.
+"""Provides the controller that defines the behaviour of the character creation
+   screen."""
+
+import characterstatistics as char_stats
+from serializers import XmlSerializer
+from controllerbase import ControllerBase
+from gamescenecontroller import GameSceneController
+from gamesceneview import GameSceneView
+from parpg.inventory import Inventory
+
+DEFAULT_STAT_VALUE = 50
+
+
+def getStatCost(offset):
+    """Gets and returns the cost to increase stat based on the offset"""
+    if offset < 0:
+        offset *= -1
+    if offset < 22:
+        return 1
+    elif offset < 29:
+        return 2
+    elif offset < 32:
+        return 3
+    elif offset < 35:
+        return 4
+    elif offset < 36:
+        return 5
+    elif offset < 38:
+        return 6
+    elif offset < 39:
+        return 7
+    elif offset < 40:
+        return 8
+    elif offset < 41:
+        return 9
+    else:
+        return 10    
+
+#TODO: Should be replaced with the real character class once its possible
+class SimpleCharacter(object):
+    """This is a simple class that is used to store the data during the
+    character creation"""
+    
+    def __init__(self, name, gender, origin, age, picture, traits,
+                 primary_stats, secondary_stats, inventory):
+        self.name = name
+        self.gender = gender
+        self.origin = origin
+        self.age = age
+        self.picture = picture
+        self.traits = traits
+        self.statistics = {}
+        for primary_stat in primary_stats:
+            short_name = primary_stat.short_name
+            self.statistics[short_name] = char_stats.PrimaryStatisticValue(
+                                                    primary_stat,
+                                                    self,
+                                                    DEFAULT_STAT_VALUE)
+            long_name = primary_stat.long_name
+            self.statistics[long_name] = char_stats.PrimaryStatisticValue(
+                                                    primary_stat,
+                                                    self,
+                                                    DEFAULT_STAT_VALUE)
+        for secondary_stat in secondary_stats:
+            name = secondary_stat.name            
+            self.statistics[name] = char_stats.SecondaryStatisticValue(
+                                                    secondary_stat,
+                                                    self)
+        self.inventory = inventory
+            
+class CharacterCreationController(ControllerBase):
+    """Controller defining the behaviour of the character creation screen."""
+    
+    #TODO: Change to actual values
+    MAX_TRAITS = 3
+    MIN_AGE = 16
+    MAX_AGE = 40
+    ORIGINS = {"None": None,}
+    GENDERS = ["Male", "Female",]
+    PICTURES = {"Male": ["None",], "Female": ["None",],}
+    TRAITS = {}
+    def __init__(self, engine, view, model, application):
+        """Construct a new L{CharacterCreationController} instance.
+           @param engine: Rendering engine used to display the associated view.
+           @type engine: L{fife.Engine}
+           @param view: View used to display the character creation screen.
+           @type view: L{ViewBase}
+           @param model: Model of the game state.
+           @type model: L{GameModel}
+           @param application: Application used to glue the various MVC
+               components together.
+           @type application: 
+               L{fife.extensions.basicapplication.ApplicationBase}"""
+        ControllerBase.__init__(self, engine, view, model, application)
+        self.view.start_new_game_callback = self.startNewGame
+        self.view.cancel_new_game_callback = self.cancelNewGame
+        self.view.show()
+        #TODO: Maybe this should not be hardcoded
+        stream = file("character_scripts/primary_stats.xml")        
+        prim_stats = XmlSerializer.deserialize(stream)
+        stream = file("character_scripts/secondary_stats.xml")        
+        sec_stats = XmlSerializer.deserialize(stream)
+        self.char_data = SimpleCharacter("",
+                                              self.GENDERS[0],
+                                              self.ORIGINS.keys()[0],
+                                              20,
+                                              self.PICTURES[self.GENDERS[0]][0],
+                                              [],
+                                              prim_stats,
+                                              sec_stats,
+                                              Inventory())
+        self._stat_points = 200
+  
+       
+    def startNewGame(self):
+        """Create the new character and start a new game.
+           @return: None"""
+        view = GameSceneView(self.engine, self.model)
+        controller = GameSceneController(self.engine, view, self.model,
+                                         self.application)
+        self.application.view = view
+        self.application.switchController(controller)
+        start_map = self.model.settings.parpg.Map
+        self.model.changeMap(start_map)
+    
+    def cancelNewGame(self):
+        """Exit the character creation view and return the to main menu.
+           @return: None"""
+        # KLUDGE Technomage 2010-12-24: This is to prevent a circular import
+        #     but a better fix needs to be thought up.
+        from mainmenucontroller import MainMenuController
+        from mainmenuview import MainMenuView
+        view = MainMenuView(self.engine, self.model)
+        controller = MainMenuController(self.engine, view, self.model,
+                                        self.application)
+        self.application.view = view
+        self.application.switchController(controller)
+    
+    def onStop(self):
+        """Called when the controller is removed from the list.
+           @return: None"""
+        self.view.hide()
+        
+    @property
+    def name(self):
+        """Returns the name of the character.
+        @return: Name of the character"""
+        return self.char_data.name
+    
+    @property
+    def age(self):
+        """Returns the age of the character.
+        @return: Age of the character"""
+        return self.char_data.age
+    
+    @property
+    def gender(self):
+        """Returns the gender of the character.
+        @return: Gender of the character"""
+        return self.char_data.gender
+    
+    @property
+    def origin(self):
+        """Returns the origin of the character.
+        @return: Origin of the character"""
+        return self.char_data.origin
+    
+    @property
+    def picture(self):
+        """Returns the ID of the current picture of the character."""
+        return self.char_data.picture
+    
+    def getStatPoints(self):
+        """Returns the remaining statistic points that can be distributed"""
+        return self._stat_points
+    
+    def increaseStatistic(self, statistic):
+        """Increases the given statistic by one.
+        @param statistic: Name of the statistic to increase
+        @type statistic: string""" 
+        if self.canIncreaseStatistic(statistic):
+            cost = self.getStatisticIncreaseCost(statistic)
+            if  cost <= self._stat_points:
+                self.char_data.statistics[statistic].value += 1
+                self._stat_points -= cost  
+
+    def getStatisticIncreaseCost(self, statistic):
+        """Calculate and return the cost to increase the statistic
+        @param statistic: Name of the statistic to increase
+        @type statistic: string
+        @return cost to increase the statistic"""
+        cur_value = self.char_data.statistics[statistic].value
+        new_value = cur_value + 1
+        offset =  new_value - DEFAULT_STAT_VALUE
+        return getStatCost(offset)
+    
+    def canIncreaseStatistic(self, statistic):
+        """Checks whether the given statistic can be increased or not.
+        @param statistic: Name of the statistic to check
+        @type statistic: string
+        @return: True if the statistic can be increased, False if not."""
+        stat = self.char_data.statistics[statistic].value
+        return stat < stat.statistic_type.maximum
+    
+    def decreaseStatistic(self, statistic):
+        """Decreases the given statistic by one.
+        @param statistic: Name of the statistic to decrease
+        @type statistic: string""" 
+        if self.canDecreaseStatistic(statistic):
+            gain = self.getStatisticDecreaseGain(statistic)
+            self.char_data.statistics[statistic].value -= 1
+            self._stat_points += gain  
+
+    def getStatisticDecreaseGain(self, statistic):
+        """Calculate and return the gain of decreasing the statistic
+        @param statistic: Name of the statistic to decrease
+        @type statistic: string
+        @return cost to decrease the statistic"""
+        cur_value = self.char_data.statistics[statistic].value
+        new_value = cur_value - 1
+        offset =  new_value - DEFAULT_STAT_VALUE
+        return getStatCost(offset)
+    
+    def canDecreaseStatistic(self, statistic):
+        """Checks whether the given statistic can be decreased or not.
+        @param statistic: Name of the statistic to check
+        @type statistic: string
+        @return: True if the statistic can be decreased, False if not."""
+        stat = self.char_data.statistics[statistic].value
+        return stat > stat.statistic_type.minimum
+    
+    def getStatisticValue(self, statistic):
+        """Returns the value of the given statistic.
+        @param statistic: Name of the primary or secondary statistic
+        @type statistic: string
+        @return: Value of the given statistic"""
+        return self.char_data.statistics[statistic]
+    
+    def areAllStatisticsValid(self):
+        """Checks if all statistics are inside the minimum/maximum values
+        @return True if all statistics are valid False if not"""
+        for stat in self.char_data.statistics.items():
+            if not (stat.value > stat.statistic_type.minumum and\
+                stat.value < stat.statistic_type.maximum):
+                return False
+        return True
+    
+    def setName(self, name):
+        """Sets the name of the character to the given value.
+        @param name: New name
+        @type name: string"""
+        self.char_data.name = name
+    
+    def isNameValid(self, name):
+        """Checks whether the name is valid.
+        @param name: Name to check
+        @type name: string
+        @return: True if the name is valid, False if not"""
+        if name:
+            return True
+        return False
+    
+    def changeOrigin(self, origin):
+        """Changes the origin of the character to the given value.
+        @param origin: New origin
+        @type origin: string"""
+        if self.isOriginValid(origin):
+            self.char_data.origin = origin
+            #TODO: Make changes according to origin
+   
+    def isOriginValid(self, origin):
+        """Checks whether the origin is valid.
+        @param origin: Origin to check
+        @type origin: string
+        @return: True if the origin is valid, False if not"""
+        return origin in self.ORIGINS
+    
+    def changeGender(self, gender):
+        """Changes the gender of the character to the given value.
+        @param gender: New gender
+        @param gender: string"""
+        if self.isGenderValid(gender):
+            self.char_data.gender = gender
+    
+    def isGenderValid(self, gender):
+        """Checks whether the gender is valid.
+        @param gender: Gender to check
+        @type gender: string?
+        @return: True if the origin is valid, False if not"""        
+        return gender in self.GENDERS
+    
+    def changeAge(self, age):
+        """Sets the age of the character to the given value.
+        @param age: New age
+        @type age: integer
+        """
+        if self.isAgeValid(age):
+            self.char_data.age = age
+    
+    def isAgeValid(self, age):
+        """Checks whether the age is valid.
+        @param age: Age to check
+        @type age: integer
+        @return: True if the origin is valid, False if not"""
+        return age >= self.MIN_AGE and age <= self.MAX_AGE 
+    
+    def setPicture(self, picture):
+        """Set picture of the character.
+        @param picture: ID of the new picture
+        @type picture: string"""
+        if self.isPictureValid(picture):
+            self.char_data.picture = picture
+    
+    def isPictureValid(self, picture):
+        """Checks whether the picture is valid.
+        @param picture: ID of the picture to check
+        @type picture: string
+        @return: True if the picture is valid, False if not"""
+        return picture in self.PICTURES[self.gender]
+    
+    def addTrait(self, trait):
+        """Adds a trait to the character.
+        @param trait: ID of the trait to add
+        @type trait: string"""
+        if self.canAddAnotherTrait() and self.isTraitValid(trait)\
+                and not self.hasTrait(trait):
+            self.char_data.traits.append(trait)                
+    
+    def canAddAnotherTrait(self):
+        """Checks whether another trait can be added.
+        @return: True if another trait can be added, False if not"""
+        return len(self.char_data.traits) < self.MAX_TRAITS
+    
+    def removeTrait(self, trait):
+        """Remove trait from character.
+        @param trait: ID of the trait to remove
+        @type trait: string"""
+        if self.hasTrait(trait):
+            self.char_data.traits.remove(trait)
+    
+    def hasTrait(self, trait):
+        """Checks whether the character has the trait.
+        @param trait: ID of the trait to check
+        @type trait: string
+        @return: True if the character has the trait, False if not"""
+        return trait in self.char_data.traits
+    
+    def isTraitValid(self, trait):
+        """Checks whether the trait is valid.
+        @param trait: ID of the trait to check
+        @type trait: string
+        @return: True if the trait is valid, False if not"""
+        return trait in self.TRAITS
+    
+    def areCurrentTraitsValid(self):
+        """Checks whether the characters traits are valid.
+        @return: True if the traits are valid, False if not"""
+        if len(self.char_data.traits) > self.MAX_TRAITS:
+            return False 
+        for trait in self.char_data.traits:
+            if not self.isTraitValid(trait):
+                return False
+        return True
+    
+    def isCharacterValid(self):
+        """Checks whether the character as a whole is valid.
+        @return: True if the character is valid, False if not"""
+        #Single checks can be disabled by putting a "#" in front of them
+        if True\
+        and self._stat_points >= 0\
+        and self.areAllStatisticsValid() \
+        and self.areCurrentTraitsValid() \
+        and self.isNameValid(self.name)\
+        and self.isPictureValid(self.picture)\
+        and self.isAgeValid(self.age)\
+        and self.isGenderValid(self.gender)\
+        and self.isOriginValid(self.origin)\
+        :
+            return True
+        return False