view src/parpg/charactercreationcontroller.py @ 0:1fd2201f5c36

Initial commit of parpg-core.
author M. George Hansen <technopolitica@gmail.com>
date Sat, 14 May 2011 01:12:35 -0700
parents
children d60f1dab8469
line wrap: on
line source

#   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