source: branches/active/character_customization/game/parpg/charactercreationcontroller.py @ 777

Revision 777, 15.3 KB checked in by beliar, 9 years ago (diff)

Ticket #305: Patch by Beliar.

Following messages belong to [776]

fixes[s:trac, t:305]

RevLine 
[693]1#   This file is part of PARPG.
2#
3#   PARPG is free software: you can redistribute it and/or modify
4#   it under the terms of the GNU General Public License as published by
5#   the Free Software Foundation, either version 3 of the License, or
6#   (at your option) any later version.
7#
8#   PARPG is distributed in the hope that it will be useful,
9#   but WITHOUT ANY WARRANTY; without even the implied warranty of
10#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11#   GNU General Public License for more details.
12#
13#   You should have received a copy of the GNU General Public License
14#   along with PARPG.  If not, see <http://www.gnu.org/licenses/>.
[776]15"""Provides the controller that defines the behaviour of the character creation
[693]16   screen."""
[776]17
18import characterstatistics as char_stats
19from serializers import XmlSerializer
[693]20from controllerbase import ControllerBase
21from gamescenecontroller import GameSceneController
22from gamesceneview import GameSceneView
[776]23from parpg.inventory import Inventory
[693]24
[776]25DEFAULT_STAT_VALUE = 50
26
27
28def getStatCost(offset):
29    """Gets and returns the cost to increase stat based on the offset"""
30    if offset < 0:
31        offset *= -1
32    if offset < 22:
33        return 1
34    elif offset < 29:
35        return 2
36    elif offset < 32:
37        return 3
38    elif offset < 35:
39        return 4
40    elif offset < 36:
41        return 5
42    elif offset < 38:
43        return 6
44    elif offset < 39:
45        return 7
46    elif offset < 40:
47        return 8
48    elif offset < 41:
49        return 9
50    else:
51        return 10   
52
53#TODO: Should be replaced with the real character class once its possible
54class SimpleCharacter(object):
55    """This is a simple class that is used to store the data during the
56    character creation"""
57   
58    def __init__(self, name, gender, origin, age, picture, traits,
59                 primary_stats, secondary_stats, inventory):
60        self.name = name
61        self.gender = gender
62        self.origin = origin
63        self.age = age
64        self.picture = picture
65        self.traits = traits
66        self.statistics = {}
67        for primary_stat in primary_stats:
68            short_name = primary_stat.short_name
69            self.statistics[short_name] = char_stats.PrimaryStatisticValue(
70                                                    primary_stat,
71                                                    self,
72                                                    DEFAULT_STAT_VALUE)
73            long_name = primary_stat.long_name
74            self.statistics[long_name] = char_stats.PrimaryStatisticValue(
75                                                    primary_stat,
76                                                    self,
77                                                    DEFAULT_STAT_VALUE)
78        for secondary_stat in secondary_stats:
79            name = secondary_stat.name           
80            self.statistics[name] = char_stats.SecondaryStatisticValue(
81                                                    secondary_stat,
82                                                    self)
83        self.inventory = inventory
84           
[693]85class CharacterCreationController(ControllerBase):
[776]86    """Controller defining the behaviour of the character creation screen."""
87   
88    #TODO: Change to actual values
89    MAX_TRAITS = 3
90    MIN_AGE = 16
91    MAX_AGE = 40
92    ORIGINS = {"None": None,}
93    GENDERS = ["Male", "Female",]
94    PICTURES = {"Male": ["None",], "Female": ["None",],}
95    TRAITS = {}
[693]96    def __init__(self, engine, view, model, application):
97        """Construct a new L{CharacterCreationController} instance.
98           @param engine: Rendering engine used to display the associated view.
99           @type engine: L{fife.Engine}
100           @param view: View used to display the character creation screen.
101           @type view: L{ViewBase}
102           @param model: Model of the game state.
103           @type model: L{GameModel}
104           @param application: Application used to glue the various MVC
105               components together.
106           @type application:
107               L{fife.extensions.basicapplication.ApplicationBase}"""
108        ControllerBase.__init__(self, engine, view, model, application)
109        self.view.start_new_game_callback = self.startNewGame
110        self.view.cancel_new_game_callback = self.cancelNewGame
111        self.view.show()
[776]112        #TODO: Maybe this should not be hardcoded
113        stream = file("character_scripts/primary_stats.xml")       
114        prim_stats = XmlSerializer.deserialize(stream)
115        stream = file("character_scripts/secondary_stats.xml")       
116        sec_stats = XmlSerializer.deserialize(stream)
117        self.char_data = SimpleCharacter("",
118                                              self.GENDERS[0],
119                                              self.ORIGINS.keys()[0],
120                                              20,
121                                              self.PICTURES[self.GENDERS[0]][0],
122                                              [],
123                                              prim_stats,
124                                              sec_stats,
125                                              Inventory())
126        self._stat_points = 200
127 
128       
[693]129    def startNewGame(self):
130        """Create the new character and start a new game.
131           @return: None"""
132        view = GameSceneView(self.engine, self.model)
133        controller = GameSceneController(self.engine, view, self.model,
134                                         self.application)
135        self.application.view = view
136        self.application.switchController(controller)
[756]137        start_map = self.model.settings.parpg.Map
[693]138        self.model.changeMap(start_map)
139   
140    def cancelNewGame(self):
141        """Exit the character creation view and return the to main menu.
142           @return: None"""
143        # KLUDGE Technomage 2010-12-24: This is to prevent a circular import
144        #     but a better fix needs to be thought up.
145        from mainmenucontroller import MainMenuController
146        from mainmenuview import MainMenuView
147        view = MainMenuView(self.engine, self.model)
148        controller = MainMenuController(self.engine, view, self.model,
149                                        self.application)
150        self.application.view = view
151        self.application.switchController(controller)
152   
153    def onStop(self):
154        """Called when the controller is removed from the list.
155           @return: None"""
156        self.view.hide()
[745]157       
158    @property
159    def name(self):
160        """Returns the name of the character.
161        @return: Name of the character"""
[776]162        return self.char_data.name
[745]163   
164    @property
165    def age(self):
166        """Returns the age of the character.
167        @return: Age of the character"""
[776]168        return self.char_data.age
[745]169   
170    @property
171    def gender(self):
172        """Returns the gender of the character.
173        @return: Gender of the character"""
[776]174        return self.char_data.gender
[745]175   
176    @property
177    def origin(self):
178        """Returns the origin of the character.
179        @return: Origin of the character"""
[776]180        return self.char_data.origin
[745]181   
182    @property
183    def picture(self):
184        """Returns the ID of the current picture of the character."""
[776]185        return self.char_data.picture
[745]186   
[776]187    def getStatPoints(self):
188        """Returns the remaining statistic points that can be distributed"""
189        return self._stat_points
190   
[734]191    def increaseStatistic(self, statistic):
192        """Increases the given statistic by one.
193        @param statistic: Name of the statistic to increase
194        @type statistic: string""" 
[776]195        if self.canIncreaseStatistic(statistic):
196            cost = self.getStatisticIncreaseCost(statistic)
197            if  cost <= self._stat_points:
198                self.char_data.statistics[statistic].value += 1
199                self._stat_points -= cost 
200
201    def getStatisticIncreaseCost(self, statistic):
202        """Calculate and return the cost to increase the statistic
203        @param statistic: Name of the statistic to increase
204        @type statistic: string
205        @return cost to increase the statistic"""
206        cur_value = self.char_data.statistics[statistic].value
207        new_value = cur_value + 1
208        offset =  new_value - DEFAULT_STAT_VALUE
209        return getStatCost(offset)
[734]210   
211    def canIncreaseStatistic(self, statistic):
[745]212        """Checks whether the given statistic can be increased or not.
[734]213        @param statistic: Name of the statistic to check
214        @type statistic: string
215        @return: True if the statistic can be increased, False if not."""
[776]216        stat = self.char_data.statistics[statistic].value
217        return stat < stat.statistic_type.maximum
[734]218   
219    def decreaseStatistic(self, statistic):
220        """Decreases the given statistic by one.
221        @param statistic: Name of the statistic to decrease
222        @type statistic: string""" 
[776]223        if self.canDecreaseStatistic(statistic):
224            gain = self.getStatisticDecreaseGain(statistic)
225            self.char_data.statistics[statistic].value -= 1
226            self._stat_points += gain 
227
228    def getStatisticDecreaseGain(self, statistic):
229        """Calculate and return the gain of decreasing the statistic
230        @param statistic: Name of the statistic to decrease
231        @type statistic: string
232        @return cost to decrease the statistic"""
233        cur_value = self.char_data.statistics[statistic].value
234        new_value = cur_value - 1
235        offset =  new_value - DEFAULT_STAT_VALUE
236        return getStatCost(offset)
[734]237   
238    def canDecreaseStatistic(self, statistic):
[745]239        """Checks whether the given statistic can be decreased or not.
[734]240        @param statistic: Name of the statistic to check
241        @type statistic: string
242        @return: True if the statistic can be decreased, False if not."""
[776]243        stat = self.char_data.statistics[statistic].value
244        return stat > stat.statistic_type.minimum
[745]245   
246    def getStatisticValue(self, statistic):
247        """Returns the value of the given statistic.
248        @param statistic: Name of the primary or secondary statistic
249        @type statistic: string
250        @return: Value of the given statistic"""
[776]251        return self.char_data.statistics[statistic]
[745]252   
[776]253    def areAllStatisticsValid(self):
254        """Checks if all statistics are inside the minimum/maximum values
255        @return True if all statistics are valid False if not"""
256        for stat in self.char_data.statistics.items():
257            if not (stat.value > stat.statistic_type.minumum and\
258                stat.value < stat.statistic_type.maximum):
259                return False
260        return True
261   
[734]262    def setName(self, name):
263        """Sets the name of the character to the given value.
264        @param name: New name
265        @type name: string"""
[776]266        self.char_data.name = name
[734]267   
268    def isNameValid(self, name):
269        """Checks whether the name is valid.
270        @param name: Name to check
271        @type name: string
272        @return: True if the name is valid, False if not"""
[776]273        if name:
274            return True
[734]275        return False
276   
277    def changeOrigin(self, origin):
278        """Changes the origin of the character to the given value.
279        @param origin: New origin
[776]280        @type origin: string"""
281        if self.isOriginValid(origin):
282            self.char_data.origin = origin
283            #TODO: Make changes according to origin
[734]284   
285    def isOriginValid(self, origin):
286        """Checks whether the origin is valid.
287        @param origin: Origin to check
[776]288        @type origin: string
[734]289        @return: True if the origin is valid, False if not"""
[776]290        return origin in self.ORIGINS
[734]291   
292    def changeGender(self, gender):
293        """Changes the gender of the character to the given value.
294        @param gender: New gender
[776]295        @param gender: string"""
296        if self.isGenderValid(gender):
297            self.char_data.gender = gender
[734]298   
299    def isGenderValid(self, gender):
300        """Checks whether the gender is valid.
301        @param gender: Gender to check
302        @type gender: string?
[776]303        @return: True if the origin is valid, False if not"""       
304        return gender in self.GENDERS
[734]305   
306    def changeAge(self, age):
307        """Sets the age of the character to the given value.
308        @param age: New age
309        @type age: integer
310        """
[776]311        if self.isAgeValid(age):
312            self.char_data.age = age
[734]313   
314    def isAgeValid(self, age):
315        """Checks whether the age is valid.
316        @param age: Age to check
317        @type age: integer
318        @return: True if the origin is valid, False if not"""
[776]319        return age >= self.MIN_AGE and age <= self.MAX_AGE
[734]320   
321    def setPicture(self, picture):
322        """Set picture of the character.
323        @param picture: ID of the new picture
324        @type picture: string"""
[776]325        if self.isPictureValid(picture):
326            self.char_data.picture = picture
[734]327   
328    def isPictureValid(self, picture):
329        """Checks whether the picture is valid.
330        @param picture: ID of the picture to check
331        @type picture: string
332        @return: True if the picture is valid, False if not"""
[776]333        return picture in self.PICTURES[self.gender]
[734]334   
335    def addTrait(self, trait):
336        """Adds a trait to the character.
337        @param trait: ID of the trait to add
338        @type trait: string"""
[776]339        if self.canAddAnotherTrait() and self.isTraitValid(trait)\
340                and not self.hasTrait(trait):
341            self.char_data.traits.append(trait)               
[734]342   
343    def canAddAnotherTrait(self):
344        """Checks whether another trait can be added.
345        @return: True if another trait can be added, False if not"""
[776]346        return len(self.char_data.traits) < self.MAX_TRAITS
[734]347   
348    def removeTrait(self, trait):
349        """Remove trait from character.
350        @param trait: ID of the trait to remove
351        @type trait: string"""
[776]352        if self.hasTrait(trait):
353            self.char_data.traits.remove(trait)
[734]354   
355    def hasTrait(self, trait):
356        """Checks whether the character has the trait.
357        @param trait: ID of the trait to check
358        @type trait: string
359        @return: True if the character has the trait, False if not"""
[776]360        return trait in self.char_data.traits
[734]361   
362    def isTraitValid(self, trait):
363        """Checks whether the trait is valid.
364        @param trait: ID of the trait to check
365        @type trait: string
366        @return: True if the trait is valid, False if not"""
[776]367        return trait in self.TRAITS
[734]368   
369    def areCurrentTraitsValid(self):
370        """Checks whether the characters traits are valid.
371        @return: True if the traits are valid, False if not"""
[776]372        if len(self.char_data.traits) > self.MAX_TRAITS:
373            return False 
374        for trait in self.char_data.traits:
375            if not self.isTraitValid(trait):
376                return False
377        return True
[734]378   
379    def isCharacterValid(self):
[777]380        """Checks whether the character as a whole is valid.
381        @return: True if the character is valid, False if not"""
[776]382        #Single checks can be disabled by putting a "#" in front of them
383        if True\
384        and self._stat_points >= 0\
385        and self.areAllStatisticsValid() \
386        and self.areCurrentTraitsValid() \
387        and self.isNameValid(self.name)\
388        and self.isPictureValid(self.picture)\
389        and self.isAgeValid(self.age)\
390        and self.isGenderValid(self.gender)\
391        and self.isOriginValid(self.origin)\
392        :
393            return True
[756]394        return False
Note: See TracBrowser for help on using the repository browser.