Mercurial > parpg-source
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