view objects/action.py @ 98:c0db5f521695

updateObjectDB of GameModel now uses saveable_fields.
author KarstenBock@gmx.net
date Tue, 27 Sep 2011 16:29:39 +0200
parents a9cc5559ec2a
children 824f3068ef2a
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/>.

#exceptions

import logging

logger = logging.getLogger('action')

from parpg.gui import drag_drop_data as data_drag
from parpg.dialoguecontroller import DialogueController
from parpg.components import container


class NoSuchQuestException(Exception):
    """NoQuestException is used when there is no active quest with the id"""
    pass

#classes

class Action(object):
    """Base Action class, to define the structure"""


    def __init__(self, controller, commands = None):
        """Basic action constructor
        @param controller: A reference to the GameSceneController.
        @type controller: parpg.GameSceneController
        @param commands: Special commands that are executed
        @type commands: Dictionary 
        """
        self.commands = commands or ()
        self.controller = controller
        self.model = controller.model
    
    def execute(self):
        """To be overwritten"""        
        #Check if there are special commands and execute them
        for command_data in self.commands:
            command = command_data["Command"]
            if command == "SetQuestVariable":
                quest_id = command_data["ID"]
                variable = command_data["Variable"]
                value = command_data["Value"]
                quest_engine = self.model.game_state.quest_engine 
                if quest_engine.hasQuest(quest_id):
                    quest_engine[quest_id].setValue(variable, value)
                else:
                    raise NoSuchQuestException
            elif command == "ResetMouseCursor":
                self.controller.resetMouseCursor()
            elif command == "StopDragging":
                data_drag.dragging = False
                
class ChangeMapAction(Action):
    """A change map scheduled"""
    def __init__(self, controller, target_map_name, target_pos, commands=None):
        """Initiates a change of the position of the character
        possibly flagging a new map to be loaded.
        @param controller: A reference to the GameSceneController.
        @type controller: parpg.GameSceneController
        @param commands: Special commands that are executed
        @type commands: Dictionary 
        @type view: class derived from parpg.ViewBase
        @param view: The view
        @type target_map_name: String
        @param target_map_name: Target map id 
        @type target_pos: Tuple
        @param target_pos: (X, Y) coordinates on the target map.
        @return: None"""
        super(ChangeMapAction, self).__init__(controller, commands)
        self.view = controller.view
        self.target_pos = target_pos
        self.target_map_name = target_map_name

    def execute(self):
        """Executes the map change."""
        self.model.changeMap(self.target_map_name,
                              self.target_pos)
        super(ChangeMapAction, self).execute()

class OpenAction(Action):
    """Open a container"""
    def __init__(self, controller, container, commands=None):
        """
        @param controller: A reference to the GameSceneController.
        @type controller: parpg.GameSceneController
        @param commands: Special commands that are executed
        @type commands: Dictionary 
        @type view: class derived from parpg.ViewBase
        @param view: The view
        @param container: A reference to the container
        """
        super(OpenAction, self).__init__(controller, commands)
        self.view = controller.view
        self.container = container
    def execute(self):
        """Open the box."""
        self.view.hud.createBoxGUI(self.container.name, \
                                              self.container)
        super(OpenAction, self).execute()
       
       
class OpenBoxAction(OpenAction):
    """Open a box. Needs to be more generic, but will do for now."""
    def __init__(self, controller, container, commands = None):
        """
        @param controller: A reference to the GameSceneController.
        @type controller: parpg.GameSceneController
        @param commands: Special commands that are executed
        @type commands: Dictionary 
        @type view: class derived from parpg.ViewBase
        @param view: The view
        @param container: A reference to the container
        """
        super(OpenBoxAction, self).__init__(controller, commands)
        self.view = controller.view
        self.container = container
        
    def execute(self):
        """Open the box."""
        try:
            self.container.open()
            super(OpenBoxAction, self).execute()

        except ValueError:
            self.view.hud.createExamineBox(self.container.name, \
                                                  "The container is locked")
        
class UnlockBoxAction(Action):
    """Unlocks a box. Needs to be more generic, but will do for now."""
    def __init__(self, controller, container, commands = None):
        """
        @param controller: A reference to the GameSceneController.
        @type controller: parpg.GameSceneController
        @param commands: Special commands that are executed
        @type commands: Dictionary 
        @param container: A reference to the container
        """
        super(UnlockBoxAction, self).__init__(controller, commands)
        self.container = container
    
    def execute(self):
        """Open the box."""
        self.container.unlock()
        super(UnlockBoxAction, self).execute()
        
class LockBoxAction(Action):
    """Locks a box. Needs to be more generic, but will do for now."""
    def __init__(self, controller, container, commands = None):
        """
        @param controller: A reference to the GameSceneController.
        @type controller: parpg.GameSceneController
        @param commands: Special commands that are executed
        @type commands: Dictionary 
        @param container: A reference to the container
        """
        super(LockBoxAction, self).__init__(controller, commands)
        self.container = container
        
    def execute(self):
        """Lock the box."""
        self.container.lock()
        super(LockBoxAction, self).execute()


class ExamineAction(Action):
    """Examine an object."""
    def __init__(self, controller, examine_id, examine_name, examine_desc=None, commands=None):
        """
        @param controller: A reference to the GameSceneController.
        @type controller: parpg.GameSceneController
        @param examine_id: An object id
        @type examine_id: integer
        @param examine_name: An object name
        @type examine_name: string
        @param examine_desc: A description of the object that will be displayed.
        @type examine_desc: string
        @param commands: Special commands that are executed
        @type commands: Dictionary 
        @type view: class derived from parpg.ViewBase
        @param view: The view
        
        """
        super(ExamineAction, self).__init__(controller, commands)
        self.view = controller.view
        self.examine_id = examine_id
        self.examine_name = examine_name
        if examine_desc is not None:
            self.examine_desc = examine_desc
        else:
            self.examine_desc = "No Description"
        
    def execute(self):
        """Display the text."""
        action_text = self.examine_desc
        self.view.hud.addAction(unicode(action_text))
        logger.debug(action_text)
        #this code will cut the line up into smaller lines that will be displayed
        place = 25
        while place < len(action_text):
            if action_text[place] == ' ':
                action_text = action_text[:place] +'\n'+action_text[place:]
                place += 26 #plus 1 character to offset the new line
            else: place += 1
        self.view.displayObjectText(self.examine_id, unicode(action_text), time=3000)

class ExamineItemAction(Action):
    """Examine an item."""
    def __init__(self, controller, examine_name, examine_desc, commands = None):
        """
        @param controller: A reference to the GameSceneController.
        @type controller: parpg.GameSceneController
        @param commands: Special commands that are executed
        @type commands: Dictionary 
        @type view: class derived from parpg.ViewBase
        @param view: The view
        @type examine_name: String
        @param examine_name: Name of the object to be examined.
        @type examine_name: String
        @param examine_name: Description of the object to be examined.
        """
        super(ExamineItemAction, self).__init__(controller, commands)
        self.view = controller.view
        self.examine_name = examine_name
        self.examine_desc = examine_desc
        
    def execute(self):
        """Display the text."""
        action_text = unicode(self.examine_desc)
        self.view.hud.addAction(action_text)
        logger.debug(action_text)

class ReadAction(Action):
    """Read a text."""
    def __init__(self, controller, text_name, text, commands = None):
        """
        @param controller: A reference to the GameSceneController.
        @type controller: parpg.GameSceneController
        @param commands: Special commands that are executed
        @type commands: Dictionary 
        @param view: The view
        @type view: class derived from parpg.ViewBase
        @param text_name: Name of the object containing the text
        @type text_name: String
        @param text: Text to be displayied
        @type text: String
        """
        super(ReadAction, self).__init__(controller, commands)
        self.view = controller.view
        self.text_name = text_name
        self.text = text
        
    def execute(self):
        """Examine the box."""
        action_text = unicode('\n'.join(["You read " + self.text_name + ".", 
                                         self.text]))
        self.view.hud.addAction(action_text)
        logger.debug(action_text)
        super(ReadAction, self).execute()

class TalkAction(Action):
    """An action to represent starting a dialogue"""
    def __init__(self, controller, npc, commands = None):
        """
        @param controller: A reference to the GameSceneController.
        @type controller: parpg.GameSceneController
        @param commands: Special commands that are executed
        @type commands: Dictionary 
        @type view: class derived from parpg.ViewBase
        @param view: The view
        @type npc: NonPlayerCharacter
        @param npc: NPC to interact with.
        """
        super(TalkAction, self).__init__(controller, commands)
        self.view = controller.view
        self.npc = npc
        
    def execute(self):
        """Talk with the NPC when close enough, otherwise move closer.
           @return: None"""
        player_char = self.model.game_state.\
                    getObjectById("PlayerCharacter").fifeagent
        npc_coordinates = self.npc.fifeagent.behaviour.getLocation().\
                        getLayerCoordinates()
        pc_coordinates = player_char.behaviour.agent.\
                            getLocation().getLayerCoordinates()
        
        distance_squared = (npc_coordinates.x - pc_coordinates.x) *\
                           (npc_coordinates.x - pc_coordinates.x) +\
                           (npc_coordinates.y - pc_coordinates.y) *\
                           (npc_coordinates.y - pc_coordinates.y)
        
        # If we are too far away, we approach the NPC again
        if distance_squared > 2:
            player_char.behaviour.approach(
                [npc_coordinates.x, npc_coordinates.y],
                        TalkAction(self.controller, 
                                   self.npc, 
                                   self.commands
                                   )
            )        
        else:
            player_char.behaviour.agent.act(
                'stand', 
                self.npc.fifeagent.behaviour.getLocation()
            )
    
            if self.npc.dialogue.dialogue is not None:
                dialogue_controller = DialogueController(
                    self.controller.engine,
                    self.view,
                    self.model,
                    self.controller.application
                )
                self.controller.application.manager.push_mode(
                    dialogue_controller
                )
                dialogue_controller.startTalk(self.npc)
            else:
                self.npc.fifeagent.behaviour.agent.say("Leave me alone!", 1000)
                
            self.model.game_state.getObjectById("PlayerCharacter").\
                fifeagent.behaviour.idle()
            self.model.game_state.getObjectById("PlayerCharacter").\
                fifeagent.behaviour.nextAction = None
            super(TalkAction, self).execute()

class UseAction(Action):
    """Action for carryable items. It executes special commands that can be only
    used on carryable utens"""


    def __init__(self, controller, item, commands = None):
        """
        @param controller: A reference to the GameSceneController.
        @type controller: parpg.GameSceneController
        @param item: Item on which the action is called
        @type item: CarryableItem
        @param commands: Special commands that are executed
        @type commands: Dictionary 
        """
        super(UseAction, self).__init__(controller, commands)
        self.view = controller.view
        self.item = item
    
    def execute(self):
        #Check if there are special commands and execute them
        for command_data in self.commands:
            command = command_data["Command"]
            if command == "ReplaceItem":
                object_id = command_data["ID"]
                object_type = command_data["ObjectType"]
                container = self.item.in_container
                inst_dict = {}
                inst_dict["ID"] = object_id
                inst_dict["object_type"] = object_type
                new_item = self.model.createContainerObject(inst_dict)
                container.replaceItem(self.item, new_item)
                self.view.hud.inventory.updateInventoryButtons()
        super(UseAction, self).execute()

class PickUpAction(Action):
    """Action for picking up items from a map"""

    def __init__(self, controller, item, commands = None):
        super(PickUpAction, self).__init__(controller, commands)
        self.item = item
        self.view = controller.view
        
    def execute(self):
        real_item = self.item.containable
        player = self.model.game_state.getObjectById("PlayerCharacter")
        self.model.moveObject(self.item.general.identifier, None)
        container.put_item(player.container, real_item)
        super(PickUpAction, self).execute()

class DropItemAction(Action):
    """Action for dropping an items on a map"""
    def __init__(self, controller, item, commands = None):
        super(DropItemAction, self).__init__(controller, commands)
        self.item = item
        
    def execute(self):
        map_name = self.model.game_state.current_map_name
        map_item_values = {}
        map_item_values["ViewName"] = self.item.name
        map_item_values["ObjectType"] = "MapItem"
        map_item_values["ItemType"] = self.item.item_type
        map_item_values["Map"] = map_name
        coords = self.model.game_state.getObjectById("PlayerCharacter").fifeagent.\
                                        getLocation().getExactLayerCoordinates()
        map_item_values["Position"] = (coords.x, coords.y)
        map_item_values["Rotation"] = 0
        map_item_values["item"] = self.item
        agent = {}
        agent[self.item.ID] = map_item_values
        self.model.addAgent("All", agent)
        self.model.placeAgents()
        super(DropItemAction, self).execute()
        
class DropItemFromContainerAction(DropItemAction):
    """Action for dropping an items from the Inventory to a map"""

    def __init__(self, controller, item, container_gui, commands = None):
        super(DropItemFromContainerAction, self).__init__(controller, item, commands)
        self.container_gui = container_gui

    def execute(self):
        super(DropItemFromContainerAction, self).execute()
        self.item.in_container.takeItem(self.item)
        self.container_gui.updateImages()
        
class BrewBeerAction(Action):
    """Action for brewing beer in a pot"""
    def __init__(self, controller, pot, commands = None):
        super(BrewBeerAction, self).__init__(controller, commands)
        self.pot = pot
        self.view = controller.view
        
    def execute(self):
        """Brew the beer"""
        has_water = False
        has_yeast = False
        has_fruit = False
        has_wood = False
        has_bottle = False
        player_character = self.model.game_state.getObjectById("PlayerCharacter").fifeagent
        for item in self.pot.items.itervalues():
            if item.item_type == "Questionable water":
                if has_water:
                    self.view.hud.addAction(unicode(\
                        "Please put only 1 water in the pot"))
                    return
                has_water = True
                water_type = 1 
                water = item
            elif item.item_type == "Pure water":
                if has_water:
                    self.view.hud.addAction(unicode(\
                        "Please put only 1 water in the pot"))
                    return
                has_water = True
                water_type = 2
                water = item
            elif item.item_type == "Grain":
                if has_fruit:
                    self.view.hud.addAction(unicode(\
                        "Please put only 1 fruit in the pot"))
                    return
                has_fruit = True
                fruit_type = 3
                fruit = item
            elif item.item_type == "Wild potato":
                if has_fruit:
                    self.view.hud.addAction(unicode(\
                        "Please put only 1 fruit in the pot"))
                    return
                has_fruit = True
                fruit_type = 2
                fruit = item
            elif item.item_type == "Rotten yam":
                if has_fruit:
                    self.view.hud.addAction(unicode(\
                        "Please put only 1 fruit in the pot"))
                    return
                has_fruit = True
                fruit_type = 1
                fruit = item
            elif item.item_type == "Yeast":
                if has_yeast:
                    self.view.hud.addAction(unicode(\
                        "Please put only 1 yeast in the pot"))
                    return
                has_yeast = True
                yeast = item 
            else:
                self.view.hud.addAction(unicode("Item " + item.name + \
                                                " is not needed for brewing beer"))
                self.view.hud.addAction(unicode(\
                    "Please put only ingredients for the beer in the pot.\
                    Things like bottles and wood have to be in your inventory"))
                return
        wood = player_character.hasItem("Wood")
        if wood:
            has_wood = True        
        bottle = player_character.hasItem("Empty beer bottle")
        if bottle:
            has_bottle = True        
        if has_water and has_fruit and has_wood and has_bottle:
            self.pot.removeItem(water)
            self.pot.removeItem(fruit)
            if has_yeast:
                self.pot.removeItem(yeast)
            player_character.inventory.removeItem(wood)
            inst_dict = {}
            inst_dict["ID"] = "Beer"
            inst_dict["object_type"] = "Beer"
            new_item = self.model.createContainerObject(inst_dict)
            player_character.inventory.placeItem(new_item)
            self.view.hud.inventory.updateInventoryButtons()
            beer_quality = 0
            if water_type == 1:
                if fruit_type == 1:
                    beer_quality = -1
                elif fruit_type == 2:
                    beer_quality = 2
                elif fruit_type == 3:
                    beer_quality = 3
            if water_type == 2:
                if fruit_type == 1:
                    beer_quality = 1
                elif fruit_type == 2:
                    beer_quality = 3
                elif fruit_type == 3:
                    beer_quality = 4
            if beer_quality > 0 and has_yeast:
                beer_quality += 1
            self.model.game_state.quest_engine.quests["beer"].\
                    setValue("beer_quality", beer_quality)
        else:
            self.view.hud.addAction(unicode(
            """For brewing beer you need at least:
            In the pot:
                Fruit (like grain, potato, yam)
                Water
                Optionally:
                    Good quality yeast.
                    Wild yeast will be used if none present.
            In the inventory:
                Wood
                Empty bottle"""))
        super(BrewBeerAction, self).execute()

ACTIONS = {"ChangeMap":ChangeMapAction, 
           "Open":OpenAction,
           "OpenBox":OpenBoxAction, 
           "Unlock":UnlockBoxAction,
           "Lock":LockBoxAction,
           "ExamineItem":ExamineItemAction,
           "Examine":ExamineAction,
           "Look":ExamineItemAction,
           "Read":ReadAction,
           "Talk":TalkAction,
           "Use":UseAction,
           "PickUp":PickUpAction,
           "DropFromInventory":DropItemFromContainerAction,
           "BrewBeer":BrewBeerAction}