view objects/action.py @ 119:2399a8c3da0c

Modified EquipmentSlot to display an image instead of a text. Added EquipmentGui class, which handles the equipment slots of the player screen. An EquipmentGui instance will be created in the InventoryGUI constructor. The initializeInventory method of the Hud class supplies the players inventory and equipment to the InventoryGUI constructor.
author KarstenBock@gmx.net
date Wed, 05 Oct 2011 11:04:39 +0200
parents 7f7f54c4077b
children 452bbc3d915d
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, lockable


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 an lockable"""
    def __init__(self, controller, lockable, 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 lockable: A reference to the lockable
        """
        Action.__init__(self, controller, commands)
        self.view = controller.view
        self.lockable = lockable
    
    def execute(self):
        """Open the lockable."""
        try:
            lockable.open(self.lockable.lockable)
            self.lockable.fifeagent.behaviour.animate("open")
            self.lockable.fifeagent.behaviour.queue_animation("opened", 
                                                              repeating=True)
        except lockable.LockedError:
            self.view.hud.createExamineBox(self.lockable.description.view_name,
                                           "Locked")            
        Action.execute(self)

class CloseAction(Action):
    """Close an lockable"""
    def __init__(self, controller, lockable, 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 lockable: A reference to the lockable
        """
        Action.__init__(self, controller, commands)
        self.lockable = lockable
    
    def execute(self):
        """Close the lockable."""
        lockable.close(self.lockable.lockable)
        self.lockable.fifeagent.behaviour.animate("close")
        self.lockable.fifeagent.behaviour.queue_animation("closed", 
                                                          repeating=True)
        Action.execute(self)
        
class UnlockAction(Action):
    """Unlocks a lockable."""
    def __init__(self, controller, lockable, commands = None):
        """
        @param controller: A reference to the GameSceneController.
        @type controller: parpg.GameSceneController
        @param commands: Special commands that are executed
        @type commands: Dictionary 
        @param lockable: A reference to the lockable
        """
        Action.__init__(self, controller, commands)
        self.lockable = lockable
    
    def execute(self):
        """Open the box."""
        lockable.unlock(self.lockable.lockable)
        Action.execute(self)
        
class LockAction(Action):
    """Locks a lockable."""
    def __init__(self, controller, lockable, commands = None):
        """
        @param controller: A reference to the GameSceneController.
        @type controller: parpg.GameSceneController
        @param commands: Special commands that are executed
        @type commands: Dictionary 
        @param lockable: A reference to the lockable
        """
        Action.__init__(self, controller, commands)
        self.lockable = lockable
        self.view = controller.view
        
    def execute(self):
        """Lock the box."""
        try:
            lockable.lock(self.lockable.lockable)
        except lockable.OpenError:
            self.view.hud.createExamineBox(self.lockable.description.view_name,
                                           "Is open")            
            
        Action.execute(self)


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 
        """
        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)
        Action.execute(self)

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)
        Action.execute(self)

class ExamineContentsAction(Action):
    """Examine the contens of an container"""
    def __init__(self, controller, container, commands=None):
        """
        @param controller: A reference to the GameSceneController.
        @type controller: parpg.GameSceneController
        @param container: The container
        @type container: parpg.entities.General
        @param commands: Special commands that are executed
        @type commands: Dictionary         
        """
        Action.__init__(self, controller, commands)
        self.view = controller.view
        self.container = container
        
    def execute(self):
        """Examine the contents"""
        self.view.hud.createBoxGUI(self.container.description.view_name,
                                   self.container.container)
        Action.execute(self)

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
        self.item.fifeagent = None
        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
        identifier = self.item.entity.general.identifier
        agent_values = self.model.items[identifier]
        coords = (self.model.game_state.getObjectById("PlayerCharacter").
                  fifeagent.behaviour.getLocation().getExactLayerCoordinates()
                  )
        agent_values["Position"] = (coords.x, coords.y)
        agent_values["Rotation"] = 0
        self.model.deleteObject(identifier)
        self.model.addAgent(map_name, {identifier: agent_values})
        self.model.placeAgents(self.item.entity.world)
        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,
           "Close":CloseAction,
           "Unlock":UnlockAction,
           "Lock":LockAction,
           "ExamineItem":ExamineItemAction,
           "Examine":ExamineAction,
           "Look":ExamineItemAction,
           "Read":ReadAction,
           "Talk":TalkAction,
           "Use":UseAction,
           "PickUp":PickUpAction,
           "DropFromInventory":DropItemFromContainerAction,
           "BrewBeer":BrewBeerAction,
           "ExamineContents": ExamineContentsAction,
           }