view entities/action.py @ 157:79d6b17b80a3

Implemented simple script system.
author KarstenBock@gmx.net
date Sat, 12 Nov 2011 16:27:39 +0100
parents 2241722311bf
children 04854cf6e1ac
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
        self.executed = False
    
    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
        self.executed = True
                
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"]
                containable = self.item.containable
                new_item = self.model.createItemByType(object_type, 
                                                       object_id, 
                                                       self.item.world)
                container.put_item(containable.container, 
                                   new_item.containable,
                                   containable.slot)
                self.model.deleteObject(self.item.general.identifier)
                self.item.delete()
                self.view.hud.inventory.updateImages()
        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)
        self.model.updateObjectDB(self.item.world)
        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
        agent_values["Map"] = map_name
        self.model.deleteObject(identifier)
        self.model.addAgent(self.model.ALL_AGENTS_KEY, 
                            {identifier: agent_values})
        self.model.placeAgents(self.item.entity.world)
        self.model.updateObjectDB(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()
        container.remove_item(self.item.container, self.item.slot)
        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.container
        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").container)
        for item in self.pot.children:
            if not item:
                continue
            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.entity.description.view_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 = container.get_item(player_character, "Wood")
        if wood:
            has_wood = True        
        bottle = container.get_item(player_character, "Empty beer bottle")
        if bottle:
            has_bottle = True        
        if has_water and has_fruit and has_wood and has_bottle:
            container.remove_item(self.pot, water.slot)
            container.remove_item(self.pot, fruit.slot)
            if has_yeast:
                container.remove_item(self.pot, yeast.slot)
            container.remove_item(player_character, wood.slot)
            new_item = (self.model.createItemByType("Beer", "Beer", 
                                                    self.pot.entity.world)
                        )
            container.put_item(player_character, new_item.containable)
            self.view.hud.inventory.updateImages()
            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,
           }