Source code for conversation.conversation

import sys
import os
from os.path import dirname, realpath, join
sys.path.append(join(dirname(realpath(__file__)), "../CommonFunctions"))
from setCalico import *
from utilityFunctions import *
from makeShapeFunctions import makePic
from response import Response
from Myro import getRobot

[docs]class Thread(object): """ Keeps a list of consecutive reponses of different NPCs in a conversation. Can be associated with an area, such that the Thread only becomes active when the players is in that area. """ def __init__(self, actor=None): """ Constructs a Thread object. By default the a Thread is always active if it contains responses, but if an actor is passed to the constructor, the thread will only be active if player is within a certain area of that actor. Args: actor: A :class:`SpriteActor` associated with this response. """ #: A list of responses self.responses = [] #: Index pointing to the current response self.currentResponseIndex = 0 #: Area that indicates where this thread is active self.area = None self.cachedPortrait = None self.debug=False self.blockNextSkip = False if actor: self.setActor(actor)
[docs] def setActor(self, actor, size=150): """ Sets an actor for this Thread. When an actor is set, as square area around the actor will be set as the area for this Thread. In addition, the image returned by getAvatar() will be used as the portrait of this Thread. Args: actor: The SpriteActor to associate with this Thread. size: The size of the area around the actor in which this Thread will be active. """ self.area = [actor.getX()-size, actor.getX()+size, actor.getY()-size, actor.getY()+size] self.cachedPortrait = Picture(actor.getAvatar())
[docs] def addResponse(self, portrait=None, text=None, action=None, sticky=False): """ Adds a response to this Thread. Args: portait: The portrait that will be shown with this response. text: A string containing the text associated with this response. action: A function executed when this response is 'spoken'. sticky: If sticky is True, this response will not be removed from the Thread, meaning that this response will be repeated over and untill some other event 'unsticks' it. """ if not self.hasNext() and self.currentResponse().sticky: if self.debug: print("Current response was sticky: current response skipped.") self.currentResponseIndex += 1 if isinstance(portrait, Response): if self.debug: print("Manually added response:", portrait.text) self.responses.append(portrait) elif (text is not None) and isinstance(portrait, tuple): # If portrait is a tuple, assume it is a portrait-action pair if action is not None: action = [portrait[1], action] else: action = [portrait[1]] if self.debug: print("Portrait action pair (experimental):", text) r = Response(text, action, makePic(portrait[0]), sticky) self.responses.append(r) elif (text is None) or hasattr(text, "__call__"): if self.debug: print("Text as first argument (deprecated):", portrait) r = Response(portrait, text, makePic(self.cachedPortrait), sticky) self.responses.append(r) else: if self.debug: print("Added text:", text) self.cachedPortrait = portrait r = Response(text, action, makePic(portrait), sticky) self.responses.append(r)
[docs] def blockSkip(self): """ Blocks the next skip command for this Thread. Blocking the skip command may be necessary to prevent skipping animations that would otherwise block progress. """ self.blockNextSkip = True
[docs] def setResponse(self, portrait=None, text="", action=None, sticky=False): """ Adds a response to this speech bubble and make it the current response. Args: text: A string containing the text associated with this response. action: A function executed when this response is 'spoken'. """ if self.debug: print("Response set") self.currentResponseIndex = len(self.responses) self.addResponse(portrait, text, action, sticky)
[docs] def currentResponse(self): """ Retrieve the current response. Returns: A Response object. """ if self.currentResponseIndex >= len(self.responses): return Response("") return self.responses[self.currentResponseIndex]
[docs] def active(self): """ Indicates whether this Thread is active. By default a Thread will be active is the player is within the provided area, or if no such area is defined for this Thread. Return: Boolean indicating whether this Thread is active. """ if self.currentResponseIndex >= len(self.responses): return False bb = self.area if not bb: return True r = getRobot() if not r: return False x = r.frame.x y = r.frame.y return x > bb[0] and x < bb[1] and y > bb[2] and y < bb[3]
[docs] def setArea(self, actor, size=150): """ Sets the area of this Thread around an actor. In contrast to setActor, this will not set the portrait of this Thread. Args: actor: The actor around whom to center the area. size: The size of the area. """ self.area = [actor.getX()-size, actor.getX()+size, actor.getY()-size, actor.getY()+size]
[docs] def hasNext(self): """ Indicates whether this Thread has a next response. Return: True if this Thread has a response left, False otherwise. """ return self.currentResponseIndex < (len(self.responses)-1)
[docs] def hasCurrent(self): """ Indicates whether this Thread has a current response. Return: True if this Thread has a current response, False otherwise. """ return self.currentResponseIndex < len(self.responses)
[docs] def nextResponse(self): """ Skips to the next response, if there is one. """ if self.currentResponse().sticky and not self.hasNext(): return if(self.hasCurrent()): if self.debug: print("Passed response:", self.currentResponse().text) self.currentResponseIndex += 1
[docs] def skip(self,launchAction=True): """ Skips all responses and launches all actions, except sticky ones. This should allow skipping in conversations where the last response should never be removed. Args: launchAction: If set to False, reponses are still skipped, but the associated actions are not executed. """ if self.debug: print("Responses skipped") while self.hasCurrent() and not self.blockNextSkip: if self.debug: print("Launched action for: ", self.currentResponse().text) # Temporarily storing response so we the response is skipped before # its action is launched. tempResponse = self.currentResponse() self.nextResponse() tempResponse.launchActions()
#if self.debug: # print("Launched action for: ", self.currentResponse().text) #self.currentResponse().launchActions() #self.nextResponse()
[docs] def clear(self,launchAction=True): """ Clears all responses and launches any actions associated with them. The difference between :meth:`skip` and :meth:`clear` is that clear() ignores sticky actions or :meth:`blockSkip`, and guarentees that the response list is empty once called. Essentially resets the :class:`Thread`. Args: launchAction: If set to False, reponses are still skipped, but the associated actions are not executed. """ if self.debug: print("Responses cleared") if launchAction: while self.hasNext(): self.currentResponse().sticky = False self.currentResponse().launchActions() self.nextResponse() self.currentResponse().launchActions() self.responses=[] self.currentResponseIndex = 0 self.area = None self.cachedPortrait = None
[docs]class Conversation(object): """ Class for keeping track of a conversation between actors. A conversation can consist of several threads, which can be activated or deactivated seperately. Using different threads can be useful when a conversation has a choice, or when you want certain threads to be active only when the player is close enough to the relevant NPS. If no threads are added, all responses will be added to the default thread (threads[0]). """ def __init__(self): """ Creates a Conversation object. """ self.threads = [Thread()] #self.responses = [] #self.currentResponseIndex = 0 self.blocked = False self.autoUnpause = False self.speechBoxYPos = None self.debug=False
[docs] def getActiveThread(self): """ Returns the currently active thread. Returns the first active thread from the threads list. if no threads are active, the default (threads[0]) thread is returned. Return: The first active Thread object. """ for thread in self.threads: if thread.active(): return thread return self.threads[0]
[docs] def addThread(self, thread): """ Adds a new conversation Thread to the conversation. Args: thread: The Thread to add. """ self.threads.append(thread)
[docs] def addResponse(self, portrait=None, text="", action=None, sticky=False): """ Adds a response to the default Thread. Args: text: A string containing the text associated with this response. action: A function executed when this response is 'spoken'. """ self.threads[0].addResponse(portrait, text, action, sticky)
#if isinstance(text, Response): # self.responses.append(text) #else: # r = Response(text, action, makePic(portrait), sticky) # self.responses.append(r)
[docs] def setResponse(self, portrait=None, text="", action=None, sticky=False): """ Adds a response to this speech bubble and make it the current response. Args: text: A string containing the text associated with this response. action: A function executed when this response is 'spoken'. """ self.currentResponseIndex = len(self.responses) self.addResponse(portrait, text, action, sticky)
[docs] def currentResponse(self): """ Retrieve the current response of the default thread. Returns: A Response object. """ if self.blocked: return Response("") return self.threads[0].currentResponse()
[docs] def hasNext(self): """ Checks if the default thread has a next response available. Return: Boolean indicating whether the default thread has a response left. """ return self.threads[0].hasNext()
[docs] def nextResponse(self): """ Skips to the next response, if there is one. """ self.threads[0].nextResponse()
[docs] def block(self): """ Sets the conversation to blocked. While the conversation is blocked, calling the current response will return an empty response. """ self.blocked = True
[docs] def unblock(self): """ Unblocks the conversation. """ self.blocked = False
[docs] def setDebug(self, value=True): """ Enables debug mode for this conversation, printing extra information. Args: value: Booleans indicating whether to enable (True) or disable (false) debug mode. """ self.debug=value for thread in self.threads: thread.debug=value