changeset 550:d0282579668c

Added QuestManager. The player can now move from map to map and the state of the quests remains persistent. Both quests the NPC gives you are now completable. Still have to clean up the quest loading code. Started adding more comments.
author prock@33b003aa-7bff-0310-803a-e67f0ece8222
date Tue, 15 Jun 2010 17:53:20 +0000
parents c9113e23b004
children 3b933753cba8
files demos/rpg/scripts/actors/player.py demos/rpg/scripts/actors/questgiver.py demos/rpg/scripts/gamecontroller.py demos/rpg/scripts/quests/basequest.py demos/rpg/scripts/quests/questmanager.py demos/rpg/scripts/rpg.py demos/rpg/scripts/scene.py
diffstat 7 files changed, 260 insertions(+), 51 deletions(-) [+]
line wrap: on
line diff
--- a/demos/rpg/scripts/actors/player.py	Fri Jun 11 21:29:58 2010 +0000
+++ b/demos/rpg/scripts/actors/player.py	Tue Jun 15 17:53:20 2010 +0000
@@ -44,7 +44,47 @@
 		super(Player, self).__init__(gamecontroller, layer, "Player", "player", playermodelname, "player", True)
 		self._type = GameObjectTypes["PLAYER"]
 		
-		self._playermodelname = playermodelname
-		
 		self._actionlistener = PlayerActionListener(self._gamecontroller, self)
 		self._actionlistener.attachActionListener()
+		
+		self._quests = []
+		
+	def serialize(self):
+		lvars = super(Player, self).serialize()
+
+		activequests = ""
+
+		for quest in self._gamecontroller.questmanager.activequests:
+			if activequests == "":
+				activequests = quest.id
+			else:
+				activequests = activequests + "," + quest.id
+
+		lvars['activequests'] = activequests
+		
+		completedquests = ""
+
+		for quest in self._gamecontroller.questmanager.completedquests:
+			if completedquests == "":
+				completedquests = quest.id
+			else:
+				completedquests = completedquests + "," + quest.id
+
+		lvars['completedquests'] = completedquests
+		
+		return lvars
+
+	def deserialize(self, valuedict):
+		super(Player, self).deserialize(valuedict)
+		
+		activequests = valuedict['activequests'].split(",")
+		
+		for questid in activequests:
+			self._gamecontroller.questmanager.activateQuestById(questid)
+			print "loaded active quest: " + questid
+			
+		completedquests = valuedict['completedquests'].split(",")
+		
+		for questid in completedquests:
+			self._gamecontroller.questmanager.completeQuestById(questid)
+			print "loaded completed quest: " + questid
--- a/demos/rpg/scripts/actors/questgiver.py	Fri Jun 11 21:29:58 2010 +0000
+++ b/demos/rpg/scripts/actors/questgiver.py	Tue Jun 15 17:53:20 2010 +0000
@@ -36,50 +36,44 @@
 		super(QuestGiver, self).__init__(gamecontroller, layer, typename, baseobjectname, instancename, instanceid, createInstance)
 		self._type = GameObjectTypes["QUESTGIVER"]
 	
-		self._quests = []
-		
-		self._activequest = None
-	
 	def addQuest(self, quest):
-		self._quests.append(quest)
+		pass
 		
 	def offerNextQuest(self):
-		if self._activequest:
-			return
-	
-		if len(self._quests) > 0:
+		if self._gamecontroller.questmanager.getNextQuest(self.id):
 			self._gamecontroller.guicontroller.showQuestDialog(self)
 		
 	def getNextQuest(self):
-		if len(self._quests) > 0:
-			return self._quests[0]
-		
-		return None
+		return self._gamecontroller.questmanager.getNextQuest(self.id)
 		
 	def activateQuest(self, quest):
-		self._activequest = quest
+		self._gamecontroller.questmanager.activateQuest(quest)
 			
 	def completeQuest(self):
-		if self._activequest in self._quests:
-			if self._activequest.checkQuestCompleted(self._gamecontroller.scene.player):
-				self.say("That everything I need.  Thank you!")
-				self._gamecontroller.scene.player.gold = self._gamecontroller.scene.player.gold - self._activequest.requiredgold
+		for activequest in self._gamecontroller.questmanager.activequests:
+			if activequest.ownerid == self.id:
+				if activequest.checkQuestCompleted(self._gamecontroller.scene.player):
+					self.say("That everything I need.  Thank you!")
+			
+					self._gamecontroller.scene.player.gold = self._gamecontroller.scene.player.gold - activequest.requiredgold
 				
-				for itemid in self._activequest.requireditems:
-					self._gamecontroller.scene.player.removeItemFromInventory(itemid)
-					
-				self._quests.remove(self._activequest)
-				self._activequest = None
-			else:
-				self.say("Come back when you have all the items I requested!")
-		else:
-			#something went wrong
-			self._activequest = None
+					for itemid in activequest.requireditems:
+						self._gamecontroller.scene.player.removeItemFromInventory(itemid)
+			
+					self._gamecontroller.questmanager.completeQuest(activequest)
+				else:
+					self.say("Come back when you have all the items I requested!")
 	
 	def haveQuest(self):
-		return len(self._quests) > 0
+		return bool(self._gamecontroller.questmanager.getNextQuest(self.id)) or bool(self._getActiveQuest())
 	
 	def _getActiveQuest(self):
-		return self._activequest
+		"""
+		Returns the first active quest in the list.  There should only be one
+		active quest per questgiver anyway.
+		"""
+		for quest in self._gamecontroller.questmanager.activequests:
+			if quest.ownerid == self.id:
+				return quest
 		
 	activequest = property(_getActiveQuest)
--- a/demos/rpg/scripts/gamecontroller.py	Fri Jun 11 21:29:58 2010 +0000
+++ b/demos/rpg/scripts/gamecontroller.py	Tue Jun 15 17:53:20 2010 +0000
@@ -36,9 +36,14 @@
 from scripts.actors.baseactor import TalkAction, PickUpItemAction, EnterPortalAction
 from scripts.objects.baseobject import GameObjectTypes
 from scripts.misc.exceptions import ObjectNotFoundError, ObjectAlreadyInSceneError
+from scripts.quests.questmanager import QuestManager
 
 
 class KeyState(object):
+	"""
+	Holds the current state of the keys on the keyboard (down or up).
+	False = down, True = up.
+	"""
 	def __init__(self):
 		self._keystate = { }
 		
@@ -52,9 +57,19 @@
 			return False
 			
 	def reset(self):
+		"""
+		Empties the keystate dictionary (assumes all keys are False)
+		"""
 		self._keystate.clear()
 
 class GameListener(fife.IKeyListener, fife.IMouseListener):
+	"""
+	Main game listener.  Listens for Mouse and Keyboard events.
+	
+	This class also has the ability to attach and detach itself from
+	the event manager in cases where you do not want input processed (i.e. when
+	the main menu is visible).  It is NOT attached by default.
+	"""
 	def __init__(self, gamecontroller):
 		self._engine = gamecontroller.engine
 		self._gamecontroller = gamecontroller
@@ -69,6 +84,9 @@
 		self._lastmousepos = (0.0,0.0)
 		
 	def attach(self):
+		"""
+		Attaches to the event manager.
+		"""
 		if not self._attached:
 			self._gamecontroller.keystate.reset()
 			self._eventmanager.addMouseListenerFront(self)
@@ -76,6 +94,9 @@
 			self._attached = True
 	
 	def detach(self):
+		"""
+		Detaches from the event manager.
+		"""
 		if self._attached:
 			self._eventmanager.removeMouseListener(self)
 			self._eventmanager.removeKeyListener(self)
@@ -180,6 +201,12 @@
 		self._gamecontroller.keystate.updateKey(keyval, False)
 		
 class GameController(object):
+	"""
+	The main game class.  
+	
+	This handles all game related code including setting up the scene, displaying user
+	interfaces, managing sound, etc etc.
+	"""
 	def __init__(self, application, engine, settings):
 		self._application = application
 		self._engine = engine
@@ -195,6 +222,8 @@
 		
 		self._guicontroller.showMainMenu()
 		
+		self._questmanager = QuestManager(self)
+		
 		self._scene = None
 		self._instancerenderer = None
 		self._floatingtextrenderer = None
@@ -204,8 +233,7 @@
 		
 	def onConsoleCommand(self, command):
 		"""
-		Might be useful if you want to have the game parse a command.
-		Not sure if I am going to keep this or not.
+		Parses game related console commands.
 		"""
 		
 		result = ""
@@ -257,6 +285,10 @@
 		return result
 		
 	def newGame(self):
+		"""
+		Removes any save games and starts a new game.
+		"""
+		
 		self._guicontroller.hideMainMenu()
 		
 		for filename in glob.glob(os.path.join("saves" , "*.xml")):
@@ -267,6 +299,10 @@
 		
 		
 	def loadMap(self, mapname):
+		"""
+		Creates the scene for the map and attaches the listener.		
+		"""
+	
 		if self._listener:
 			self._listener.detach()
 		
@@ -288,6 +324,10 @@
 			self._listener.attach()
 
 	def switchMap(self, newmapname):
+		"""
+		Queues a map switch for next frame.  This must be done next frame to ensure
+		all events pertaining to the current frame have finished being processed.
+		"""
 		self._switchmaprequested = True
 		self._newmap = newmapname
 		
@@ -295,6 +335,9 @@
 		self._scene.serialize()
 	
 	def endGame(self):
+		"""
+		Saves the game state and destroys the scene.
+		"""
 		if self._scene:
 			self._scene.serialize()
 		
@@ -321,6 +364,9 @@
 	def _getGUIController(self):
 		return self._guicontroller
 		
+	def _getQuestManager(self):
+			return self._questmanager
+	
 	def _getEngine(self):
 		return self._engine
 		
@@ -340,6 +386,7 @@
 		return self._application.logger
 	
 	guicontroller = property(_getGUIController) 
+	questmanager = property(_getQuestManager)
 	engine = property(_getEngine)
 	settings = property(_getSettings)
 	scene = property(_getScene)
--- a/demos/rpg/scripts/quests/basequest.py	Fri Jun 11 21:29:58 2010 +0000
+++ b/demos/rpg/scripts/quests/basequest.py	Tue Jun 15 17:53:20 2010 +0000
@@ -33,22 +33,26 @@
 		   'RETURN_ITEM':1}
 
 class Quest(object):
-	def __init__(self, owner, questname, questtext):
-		self._owner = owner
-		self._name = questname
+	def __init__(self, ownerid, questid, questtitle, questtext):
+		self._ownerid = ownerid
+		self._questid = questid
+		self._name = questtitle
 		self._text = questtext
 		
+	def __eq__(self, other):
+		return self._questid == other.id
+		
 	def checkQuestCompleted(self, actor):
 		pass
 
-	def _getOwner(self):
-		return self._owner
+	def _getOwnerID(self):
+		return self._ownerid
 	
 	def _getName(self):
 		return self._name
 		
-	def _setName(self, questname):
-		self._name = questname
+	def _setName(self, questtitle):
+		self._name = questtitle
 		
 	def _getText(self):
 		return self._text
@@ -56,13 +60,17 @@
 	def _setText(self, questtext):
 		self._text = questtext
 
-	owner = property(_getOwner)
+	def _getID(self):
+		return self._questid
+		
+	ownerid = property(_getOwnerID)
 	name = property(_getName, _setName)
 	text = property(_getText, _setText)
+	id = property(_getID)
 
 class ReturnItemQuest(Quest):
-	def __init__(self, owner, questname, questtext):
-		super(ReturnItemQuest, self).__init__(owner, questname, questtext)
+	def __init__(self, ownerid, questid, questtitle, questtext):
+		super(ReturnItemQuest, self).__init__(ownerid, questid, questtitle, questtext)
 
 		self._requireditems = []
 		self._requiredgold = 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demos/rpg/scripts/quests/questmanager.py	Tue Jun 15 17:53:20 2010 +0000
@@ -0,0 +1,91 @@
+#!/usr/bin/env python
+
+# -*- coding: utf-8 -*-
+
+# ####################################################################
+#  Copyright (C) 2005-2010 by the FIFE team
+#  http://www.fifengine.net
+#  This file is part of FIFE.
+#
+#  FIFE is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public
+#  License as published by the Free Software Foundation; either
+#  version 2.1 of the License, or (at your option) any later version.
+#
+#  This library 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
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public
+#  License along with this library; if not, write to the
+#  Free Software Foundation, Inc.,
+#  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+# ####################################################################
+# This is the rio de hola client for FIFE.
+
+from fife import fife
+
+class QuestManager(object):
+	def __init__(self, gamecontroller):
+		self._gamecontroller = gamecontroller
+		
+		self._quests = {}
+		self._activequests = []
+		self._completedquests = []
+		
+	def addQuest(self, quest):
+		if self._quests.has_key(quest.ownerid):
+			if not quest in self._quests[quest.ownerid]:
+				self._quests[quest.ownerid].append(quest)
+		else:
+			self._quests[quest.ownerid] = [quest]
+
+	def getQuest(self, questid):
+		for owner in self._quests:
+			for quest in self._quests[owner]:
+				if quest.id == questid:
+					return quest
+		
+		return None
+
+	def getNextQuest(self, ownerid):
+		for quest in self._quests[ownerid]:
+			if not quest in self._activequests and not quest in self._completedquests:
+				return quest
+				
+		return None
+		
+	def activateQuest(self, quest):
+		if not quest in self._activequests:
+			self._activequests.append(quest)
+		
+	def completeQuest(self, quest):
+		if not quest in self._completedquests:
+			self._completedquests.append(quest)
+		
+		if quest in self._activequests:
+			self._activequests.remove(quest)
+		
+	def activateQuestById(self, questid):
+		quest = self.getQuest(questid)
+		if quest:
+			self.activateQuest(quest)
+		
+	def completeQuestById(self, questid):
+		quest = self.getQuest(questid)
+		if quest:
+			self.completeQuest(quest)
+	
+	def _getActiveQuests(self):
+		return self._activequests
+		
+	def _getCompletedQuests(self):
+		return self._completedquests
+	
+	def _getAllQuests(self):
+		return self._quests
+	
+	activequests = property(_getActiveQuests)
+	completedquests = property(_getCompletedQuests)
+	quests = property(_getAllQuests)
--- a/demos/rpg/scripts/rpg.py	Fri Jun 11 21:29:58 2010 +0000
+++ b/demos/rpg/scripts/rpg.py	Tue Jun 15 17:53:20 2010 +0000
@@ -36,6 +36,11 @@
 from fife.extensions.fife_utils import getUserDataDirectory
 
 class KeyFilter(fife.IKeyFilter):
+	"""
+	This is the implementation of the fife.IKeyFilter class.
+	
+	Prevents any filtered keys from being consumed by guichan.
+	"""
 	def __init__(self, keys):
 		fife.IKeyFilter.__init__(self)
 		self._keys = keys
@@ -44,7 +49,15 @@
 		return event.getKey().getValue() in self._keys
 
 class ApplicationListener(fife.IKeyListener, fife.ICommandListener, fife.ConsoleExecuter):
+	"""
+	Listens for window commands, console commands and keyboard input.
+	
+	Does not process game related input.	
+	"""
 	def __init__(self, engine, gamecontroller):
+		"""
+		Initializes all listeners and registers itself with the eventmanager.
+		"""
 		self._engine = engine
 		self._gamecontroller = gamecontroller
 		self._eventmanager = self._engine.getEventManager()
@@ -66,12 +79,15 @@
 		self.quit = False
 
 	def keyPressed(self, event):
+		"""
+		Processes any non game related keyboar input.
+		"""
+		if event.isConsumed():
+			return
+
 		keyval = event.getKey().getValue()
 		keystr = event.getKey().getAsString().lower()
 		
-		if event.isConsumed():
-			return
-		
 		if keyval == fife.Key.ESCAPE:
 			self.quit = True
 			event.consume()
@@ -89,7 +105,6 @@
 		self.quit = (command.getCommandType() == fife.CMD_QUIT_GAME)
 		if self.quit:
 			command.consume()
-			self._gamecontroller.endGame()
 
 	def onConsoleCommand(self, command):
 		result = ""
@@ -125,6 +140,11 @@
 		print "No tools set up yet"
 
 class RPGApplication(ApplicationBase):
+	"""
+	The main application.  It inherits fife.extensions.ApplicationBase.
+	
+	Implements ApplicationBase._pump().
+	"""
 	def __init__(self, TDS):
 		super(RPGApplication,self).__init__(TDS)
 		self._settings = TDS
@@ -142,6 +162,11 @@
 		return self._listener
 		
 	def requestQuit(self):
+		"""
+		Sends the quit command to the application's listener.  We could set
+		self.quitRequested to true also but this is a good example on how
+		to build and dispatch a fife.Command.
+		"""
 		cmd = fife.Command()
 		cmd.setSource(None)
 		cmd.setCommandType(fife.CMD_QUIT_GAME)
@@ -149,7 +174,8 @@
 
 	def _pump(self):
 		if self._listener.quit:
-			self.breakRequested = True
+			self._gamecontroller.endGame()
+			self.quitRequested = True
 		else:
 			self._gamecontroller.pump()
 			
--- a/demos/rpg/scripts/scene.py	Fri Jun 11 21:29:58 2010 +0000
+++ b/demos/rpg/scripts/scene.py	Tue Jun 15 17:53:20 2010 +0000
@@ -80,7 +80,7 @@
 					questdict = self._questsettings.get(identifier, quest, {})
 					
 					if questdict['type'] == "RETURN_ITEM":
-						questobj = ReturnItemQuest(newobject, questdict['name'], questdict['desc'])
+						questobj = ReturnItemQuest(newobject.id, quest, questdict['name'], questdict['desc'])
 						for ritem in self._questsettings.get(quest+"_items", "itemlist", []):
 							itemdict = self._questsettings.get(quest+"_items", ritem, {})
 							if itemdict["name"] == "GOLD_COINS":
@@ -88,9 +88,12 @@
 							else:
 								questobj.addRequiredItem(ritem)
 					else:
-						questobj = Quest(actor, questdict['name'], questdict['desc'])
+						questobj = Quest(newobject.id, quest, questdict['name'], questdict['desc'])
 						
 					newobject.addQuest(questobj)
+					
+					#add quest to quest manager as well
+					self._gamecontroller.questmanager.addQuest(questobj)
 
 			elif objdict["type"] == "NPC":
 				newobject = Actor(self._gamecontroller, self.actorlayer, objdict["type"], objectname, modeldict["model"], identifier, True)