changeset 492:16ceb3228324

Moved the SoundManager and the 2D math function (helpers) from the shooter demo to the fife extensions. Fixed fife_timer as it didn't work in the first place. Modified the SoundManager to use the now working fife_timer and removed the Timer class from the shooter demo entirely
author prock@33b003aa-7bff-0310-803a-e67f0ece8222
date Fri, 07 May 2010 21:07:27 +0000
parents c4168eb47a44
children e29853880e87
files demos/shooter/scripts/common/baseobject.py demos/shooter/scripts/common/helpers.py demos/shooter/scripts/powerups.py demos/shooter/scripts/scene.py demos/shooter/scripts/ships/enemies.py demos/shooter/scripts/ships/player.py demos/shooter/scripts/ships/shipbase.py demos/shooter/scripts/soundmanager.py demos/shooter/scripts/weapons.py demos/shooter/scripts/world.py engine/python/fife/extensions/__init__.py engine/python/fife/extensions/fife_math.py engine/python/fife/extensions/fife_timer.py engine/python/fife/extensions/soundmanager.py setup.py
diffstat 15 files changed, 526 insertions(+), 519 deletions(-) [+]
line wrap: on
line diff
--- a/demos/shooter/scripts/common/baseobject.py	Thu May 06 19:34:21 2010 +0000
+++ b/demos/shooter/scripts/common/baseobject.py	Fri May 07 21:07:27 2010 +0000
@@ -22,8 +22,8 @@
 # ####################################################################
 
 from fife import fife
-from scripts.common.helpers import normalize
-from scripts.common.helpers import Rect
+from fife.extensions.fife_math import normalize
+from fife.extensions.fife_math import Rect
 
 
 SHTR_DEFAULT = 0
--- a/demos/shooter/scripts/common/helpers.py	Thu May 06 19:34:21 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,204 +0,0 @@
-# -*- 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
-# ####################################################################
-
-from fife import fife
-import math
-
-
-def normalize(vector):
-	"""
-	Helper function to normalize a vector
-	
-	@param vector a fife.DoublePoint() to be normalized
-	
-	@return A normalized fife.DoublePoint()
-	"""
-	norm = fife.DoublePoint(0,0) 
-		
-	invLength = 1.0/vector.length()
-	if invLength > 1e-06:
-		norm.x = vector.x * invLength;
-		norm.y = vector.y * invLength;
-	else:
-		norm.x = 0
-		norm.y = 0
-	
-	return norm
-	
-class Rect(object):
-	"""
-	Rect
-	
-	A class used to specify the bounding box of objects.  For use
-	with collision detection.
-	"""
-	def __init__(self, x = 0, y = 0, w = 0, h = 0):
-		"""
-		@param x The x coordinate
-		@param y The y coordinate
-		@param w The width
-		@param h The height
-		"""
-		self._x = x
-		self._y = y
-		self._w = w
-		self._h = h
-		
-	def intersects(self, rect):
-		"""
-		Returns if the rect intersects with this Rect.
-		
-		@param rect the Rect to perform the test against
-		"""
-		_x = self._x - rect.x;
-		_y = self._y - rect.y;
-		_w = self._w;
-		_h = self._h;
-
-		if _x < 0:
-			_w += _x
-			_x = 0
-
-		if _y < 0:
-			_h += _y
-			_y = 0
-
-		if _x + _w > rect.w:
-			_w = rect.w - _x
-
-		if _y + _h > rect.h:
-			_h = rect.h - _y
-			
-		if _w <= 0 or _h <= 0:
-			return False
-
-		return True
-
-	def _setX(self, x):
-		self._x = x
-		
-	def _getX(self):
-		return self._x
-
-	def _setY(self, y):
-		self._y = y
-		
-	def _getY(self):
-		return self._y
-		
-	def _setW(self, w):
-		self._w = w
-		
-	def _getW(self):
-		return self._w
-		
-	def _setH(self, h):
-		self._h = h
-		
-	def _getH(self):
-		return self._h
-		
-	x = property(_getX, _setX)
-	y = property(_getY, _setY)
-	w = property(_getW, _setW)
-	h = property(_getH, _setH)
-	
-def rotatePoint(origin, point, angle):
-	"""
-	Rotates a point around the specified origin.
-	
-	@param origin A fife.DoublePoint() specifying the origin
-	@param point A fife.DoublePoint() to be rotated
-	@param angle The angle in which to rotate the point
-	
-	@return A fife.DoublePoint() representing the rotated point
-	"""
-	newp = fife.DoublePoint(0,0)
-	
-	theta = (angle * math.pi)/180
-	
-	costheta = math.cos(theta)
-	sintheta = math.sin(theta)
-	
-	x = point.x - origin.x
-	y = point.y - origin.y
-	
-	newp.x = costheta * x - sintheta * y
-	newp.y = sintheta * x + costheta * y
-	
-	return newp
-
-class Timer(fife.TimeEvent):
-	"""
-	Timer
-	
-	This class wraps the fife.TimeEvent class to make it easily usable from Python
-	It allows for a TimeEvent to be executed once or multiple times.
-	"""
-	def __init__(self,manager, delay=0,callback=None,repeat=0):
-		"""
-		@param manager The FIFE time manager
-		@param delay The delay in milliseconds to execute the callback
-		@param callback The function to execute
-		@param repeat The number of times to execute the callback.  1=once, 0=forever 
-		"""
-		super(Timer,self).__init__(delay)
-		self._is_registered = False
-		self._callback = callback
-		self._manager = manager
-		self.setPeriod(delay)
-		self._repeat = repeat
-		self._executed = 0
-
-	def start(self):
-		"""
-		Must be called before the timer will start
-		"""
-		if self._is_registered:
-			return
-		self._is_registered = True
-		self._executed = 0
-		self._manager.registerEvent(self)
-
-	def stop(self):
-		"""
-		Stops the timer
-		"""
-		if not self._is_registered:
-			return
-		self._is_registered = False
-		self._manager.unregisterEvent(self)
-
-	def updateEvent(self,delta):
-		"""
-		Should not be called directly.  This is called by FIFE.
-		"""
-		if callable(self._callback):
-			self._callback()
-			
-		if self._repeat != 0:
-			self._executed += 1
-			if self._executed >= self._repeat:
-				self.stop()
-
-	
--- a/demos/shooter/scripts/powerups.py	Thu May 06 19:34:21 2010 +0000
+++ b/demos/shooter/scripts/powerups.py	Fri May 07 21:07:27 2010 +0000
@@ -48,11 +48,11 @@
 		self._velocity.x = -0.25
 		self._velocity.y = 0
 		
-		self._pickupclip = self._scene.soundmanager.loadSoundClip("sounds/pickup.ogg")		
+		self._pickupclip = self._scene.soundmanager.createSoundEmitter("sounds/pickup.ogg")		
 	
 	def applyPowerUp(self, ship):
 		ship.weapon = CannonSpread5(self._scene, ship, 300)
-		self._scene.soundmanager.playClip(self._pickupclip)
+		self._pickupclip.play()
 		self.destroy()
 		self._scene.queueObjectForRemoval(self)
 		
@@ -78,11 +78,11 @@
 	def __init__(self, scene, powerupName, instance, findInstance=True):
 		super(ExtraLifePU, self).__init__(scene, powerupName, instance, findInstance)
 		
-		self._pickupclip = self._scene.soundmanager.loadSoundClip("sounds/pickup.ogg")	
+		self._pickupclip = self._scene.soundmanager.createSoundEmitter("sounds/pickup.ogg")	
 	
 	def applyPowerUp(self, ship):
 		ship.lives += 1
-		self._scene.soundmanager.playClip(self._pickupclip)
+		self._pickupclip.play()
 		self.destroy()
 		self._scene.queueObjectForRemoval(self)	
 		
--- a/demos/shooter/scripts/scene.py	Thu May 06 19:34:21 2010 +0000
+++ b/demos/shooter/scripts/scene.py	Fri May 07 21:07:27 2010 +0000
@@ -26,7 +26,7 @@
 from scripts.ships.player import Player
 from scripts.ships.enemies import *
 from scripts.powerups import *
-from scripts.common.helpers import Rect
+from fife.extensions.fife_math import Rect
 
 class SceneNode(object):
 	"""
@@ -209,7 +209,7 @@
 		#and finally add the player to the scene
 		self.addObjectToScene(self._player)
 		
-		self._music = self._soundmanager.loadSoundClip("music/waynesmind2.ogg")
+		self._music = self._soundmanager.createSoundEmitter("music/waynesmind2.ogg")
 		self._music.callback = self.musicHasFinished
 		self._music.looping = True
 		self._soundmanager.playClip(self._music)
--- a/demos/shooter/scripts/ships/enemies.py	Thu May 06 19:34:21 2010 +0000
+++ b/demos/shooter/scripts/ships/enemies.py	Fri May 07 21:07:27 2010 +0000
@@ -24,7 +24,7 @@
 from fife import fife
 from scripts.ships.shipbase import *
 from scripts.common.baseobject import *
-from scripts.common.helpers import Rect
+from fife.extensions.fife_math import Rect
 from scripts.weapons import *
 
 
@@ -211,7 +211,7 @@
 		self.hitpoints = 30
 		self.scorevalue = 1000
 		
-		self._explodclip = self._scene.soundmanager.loadSoundClip("sounds/bossexplode.ogg")
+		self._explodclip = self._scene.soundmanager.createSoundEmitter("sounds/bossexplode.ogg")
 		
 	def endLevel(self):
 		self._scene.endLevel()
--- a/demos/shooter/scripts/ships/player.py	Thu May 06 19:34:21 2010 +0000
+++ b/demos/shooter/scripts/ships/player.py	Fri May 07 21:07:27 2010 +0000
@@ -24,7 +24,7 @@
 from fife import fife
 from scripts.common.baseobject import *
 from scripts.ships.shipbase import *
-from scripts.common.helpers import *
+from fife.extensions.fife_math import *
 from scripts.weapons import *
 
 
@@ -112,7 +112,7 @@
 	def destroy(self):
 		if not self._invulnerable and not self._dead:
 			self._instance.act('explode', self._instance.getFacingLocation())
-			self._scene.soundmanager.playClip(self._explodclip)
+			self._explodclip.play()
 			self._dead = True
 			self._invulnerable = True
 			self._lives -= 1		
--- a/demos/shooter/scripts/ships/shipbase.py	Thu May 06 19:34:21 2010 +0000
+++ b/demos/shooter/scripts/ships/shipbase.py	Fri May 07 21:07:27 2010 +0000
@@ -86,8 +86,8 @@
 		self._hitpoints = 0
 		self._scorevalue = 0
 		
-		self._hitclip = self._scene.soundmanager.loadSoundClip("sounds/hit.ogg")
-		self._explodclip = self._scene.soundmanager.loadSoundClip("sounds/explode.ogg")
+		self._hitclip = self._scene.soundmanager.createSoundEmitter("sounds/hit.ogg")
+		self._explodclip = self._scene.soundmanager.createSoundEmitter("sounds/explode.ogg")
 	
 	def _setWeapon(self, weapon):
 		self._weapon = weapon
@@ -127,7 +127,7 @@
 		if self._hitpoints <= 0:
 			self.destroy()
 		else:
-			self._scene.soundmanager.playClip(self._hitclip)
+			self._hitclip.play()
 		
 	def destroy(self):
 		"""
@@ -135,7 +135,7 @@
 		"""
 		if self._running:
 			self._instance.act('explode', self._instance.getFacingLocation())
-			self._scene.soundmanager.playClip(self._explodclip)
+			self._explodclip.play()
 			super(Ship, self).destroy()
 	
 	def _getHitPoints(self):
--- a/demos/shooter/scripts/soundmanager.py	Thu May 06 19:34:21 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,240 +0,0 @@
-# -*- 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
-# ####################################################################
-
-from fife import fife
-
-from scripts.common.helpers import Timer
-from fife.extensions.pychan.tools import callbackWithArguments as cbwa
-
-class SoundClip(object):
-	"""
-	SoundClip
-
-	This class stores an instance of a FIFE SoundEmitter class along 
-	with some information about a sound clip (like gain and if its
-	looping).  All instances of SoundClip should be created by SoundManager.
-	"""
-	def __init__(self, soundmanager, clipid, soundname, emitter):
-		"""
-		@param soundmanager A reference to the SoundManager
-		@param clipid The FIFE sound clip ID from the sound clip pool
-		@param soundname The filename of the sound
-		@param emitter A reference to the FIFE SoundEmitter associated with this clip
-		
-		"""
-		self._soundmanager = soundmanager
-		self._name = soundname
-			
-		#The FIFE SoundEmitter associated with this SoundClip.
-		#Note that we do NOT own the emitter.
-		self._fifeemitter = emitter
-		self._fifeemitter.thisown = 0
-		self._fifeclipid = clipid
-				
-		#0 = mute, 255 = normal volume
-		self._gain = 255.0
-		self._looping = False
-		
-		#if you set the callback it will be executed after the sound
-		#has finished playing.
-		self._callback = None
-		
-		#length of the sound
-		self._duration = 0
-		
-		self._timer = None
-		
-	def _getClipID(self):
-		return self._fifeclipid
-
-	def _getGain(self):
-		return self._gain
-		
-	def _setGain(self, gain):
-		self._gain = float(gain)
-		
-	def _getLooping(self):
-		return self._looping
-		
-	def _setLooping(self, looping):
-		self._looping = looping
-		
-	def _getFifeEmitter(self):
-		return self._fifeemitter
-		
-	def _setFifeEmitter(self, emitter):
-		self._fifeemitter = emitter
-		if self._fifeemitter:
-			self._fifeemitter.thisown = 0
-
-	def _getName(self):
-		return self._name
-
-	def _getCallback(self):
-		return self._callback
-
-	def _setCallback(self, cb):
-		self._callback = cb	
-		
-	def _getDuration(self):
-		return self._duration
-	
-	def _setDuration(self, millliseconds):
-		self._duration = millliseconds
-
-	def _getTimer(self):
-		return self._timer
-		
-	def _setTimer(self, timer):
-		self._timer = timer
-
-	timer = property(_getTimer, _setTimer)
-	clipid = property(_getClipID)
-	gain = property(_getGain, _setGain)
-	looping = property(_getLooping, _setLooping)
-	fifeemitter = property(_getFifeEmitter, _setFifeEmitter)
-	name = property(_getName)
-	callback = property(_getCallback, _setCallback)
-	duration = property(_getDuration, _setDuration)
-
-class SoundManager(object):
-	"""
-	SoundManger
-
-	This class manages and plays all the sounds of the game.  
-	It creates SoundClips and ensures that there is only one 
-	FIFE SoundEmitter per unique sound.
-	"""
-	def __init__(self, engine):
-		"""
-		@param engine A reference to the FIFE engine
-		"""
-		self._engine = engine
-		
-		self._fifesoundmanager = self._engine.getSoundManager()
-		self._fifesoundmanager.init()
-		
-		#A dict of fife emitters
-		self._loadedclips = {}
-		
-	def loadSoundClip(self, filename):
-		"""
-		Returns a valid SoundClip instance.
-		
-		@param filename the relative path and filename of the sound file
-		
-		@return Returns a new SoundClip instance.
-		"""
-		if not self._loadedclips.has_key(filename):
-			clipid = self._engine.getSoundClipPool().addResourceFromFile(filename)
-			fifeemitter = self._fifesoundmanager.createEmitter()
-			fifeemitter.thisown = 0
-			fifeemitter.setSoundClip(clipid)
-			
-			self._loadedclips[filename] = fifeemitter
-			clip = SoundClip(self, clipid, filename, fifeemitter)
-			clip.duration = fifeemitter.getDuration()
-		else:
-			fifeemitter = self._loadedclips[filename]
-			clip = SoundClip(self, fifeemitter.getID(), filename, fifeemitter)
-			clip.duration = fifeemitter.getDuration()
-		
-		return clip
-		
-	def playClip(self, clip):
-		"""
-		Plays a sound clip.  
-		
-		This function does not use the FIFE
-		emitters "looping" property to loop a sound.  Instead
-		it registers a new timer and uses the duration of the 
-		clip as the timer length.
-		
-		If the SoundClip is invalid (no fifeemitter) then
-		it attempts to load it before playing it.
-		
-		Note that this will stop any clips that use the same
-		FIFE emitter.  You cannot play the same sound more than
-		once at a time.
-		
-		@param clip The SoundClip to be played
-		"""
-		
-		if clip.fifeemitter:
-			if clip.callback:
-				if clip.timer:
-					clip.timer.stop()
-					timer = None
-					
-				if clip.looping:
-					repeat = 0
-					def real_callback(c, e, g):
-						c()
-						e.stop()
-						e.setGain(g)
-						e.play()
-
-					clip.callback = cbwa(real_callback, clip.callback, clip.fifeemitter, clip.gain)
-
-				else:
-					repeat = 1
-					
-				clip.timer = Timer(self._engine.getTimeManager(), clip.duration, clip.callback, repeat)
-				clip.timer.start()
-				
-			clip.fifeemitter.setGain(clip.gain)
-			clip.fifeemitter.play()	
-		else:
-			clip = self.loadSoundClip(clip.name)
-			self.playClip(clip)
-				
-	def stopClip(self, clip):
-		"""
-		Stops playing the sound clip.   Note that this will
-		affect all clips that use the same FIFE emitter.
-		
-		@parm clip The SoundClip to stop.
-		"""
-		if clip.fifeemitter:
-			clip.fifeemitter.stop()
-			
-		if clip.timer:
-			clip.timer.stop()
-			clip.timer = None
-
-	def stopAllSounds(self):
-		for emitter in self._loadedclips.values():	
-			emitter.stop()
-			
-	def destroy(self):
-		"""
-		Releases all FIFE emitters.  This does not free the resources
-		from the FIFE sound clip pool.
-		"""
-		self.stopAllSounds()
-	
-		for emitter in self._loadedclips.values():
-			self._fifesoundmanager.releaseEmitter(emitter.getID())
-			emitter = None
-		
-		self._loadedclips.clear()
--- a/demos/shooter/scripts/weapons.py	Thu May 06 19:34:21 2010 +0000
+++ b/demos/shooter/scripts/weapons.py	Fri May 07 21:07:27 2010 +0000
@@ -23,8 +23,7 @@
 
 from fife import fife
 from scripts.common.baseobject import *
-from scripts.common.helpers import normalize, rotatePoint
-from scripts.soundmanager import *
+from fife.extensions.fife_math import normalize, rotatePoint
 
 class Projectile(SpaceObject):
 	"""
@@ -133,7 +132,7 @@
 		super(Cannon, self).__init__(scene, ship, firerate)
 		
 		self._projectileVelocity = 0.75
-		self._soundclip = scene.soundmanager.loadSoundClip("sounds/cannon.ogg")
+		self._soundclip = scene.soundmanager.createSoundEmitter("sounds/cannon.ogg")
 
 		
 	def fire(self, direction):
@@ -147,14 +146,14 @@
 			self._lastfired = self._scene.time
 			self._scene.addObjectToScene(pjctl)
 			if self._soundclip:
-				self._scene.soundmanager.playClip(self._soundclip)
+				self._soundclip.play()
 
 class FireBall(Weapon):
 	def __init__(self, scene, ship, firerate):
 		super(FireBall, self).__init__(scene, ship, firerate)
 		
 		self._projectileVelocity = 0.50
-		self._soundclip = scene.soundmanager.loadSoundClip("sounds/fireball.ogg")		
+		self._soundclip = scene.soundmanager.createSoundEmitter("sounds/fireball.ogg")		
 
 	def fire(self, direction):
 		velocity = normalize(direction)
@@ -167,14 +166,14 @@
 			self._lastfired = self._scene.time
 			self._scene.addObjectToScene(pjctl)	
 			if self._soundclip:
-				self._scene.soundmanager.playClip(self._soundclip)
+				self._soundclip.play()
 			
 class FireBallBurst(Weapon):
 	def __init__(self, scene, ship, firerate, burstrate, burstnumber):
 		super(FireBallBurst, self).__init__(scene, ship, firerate)
 		
 		self._projectileVelocity = 0.50
-		self._soundclip = scene.soundmanager.loadSoundClip("sounds/fireball.ogg")	
+		self._soundclip = scene.soundmanager.createSoundEmitter("sounds/fireball.ogg")	
 
 		self._burstrate = burstrate
 		self._burstnumber = int(burstnumber)
@@ -195,7 +194,7 @@
 				self._scene.addObjectToScene(pjctl)
 			
 				if self._soundclip:
-					self._scene.soundmanager.playClip(self._soundclip)		
+					self._soundclip.play()		
 				
 				self._lastburstfired = self._scene.time
 				self._burstcount -= 1
@@ -211,7 +210,7 @@
 		super(FireBallSpread, self).__init__(scene, ship, firerate)
 		
 		self._projectileVelocity = 0.50
-		self._soundclip = scene.soundmanager.loadSoundClip("sounds/fireball.ogg")
+		self._soundclip = scene.soundmanager.createSoundEmitter("sounds/fireball.ogg")
 		
 	def fire(self, direction):
 	
@@ -259,7 +258,7 @@
 			self._scene.addObjectToScene(pjctl7)
 			
 			if self._soundclip:
-				self._scene.soundmanager.playClip(self._soundclip)
+				self._soundclip.play()
 
 			self._lastfired = self._scene.time
 			
@@ -268,7 +267,7 @@
 		super(CannonSpread5, self).__init__(scene, ship, firerate)
 		
 		self._projectileVelocity = 1
-		self._soundclip = scene.soundmanager.loadSoundClip("sounds/cannon.ogg")
+		self._soundclip = scene.soundmanager.createSoundEmitter("sounds/cannon.ogg")
 
 	def fire(self, direction):
 	
@@ -306,7 +305,7 @@
 			self._scene.addObjectToScene(pjctl6)
 			
 			if self._soundclip:
-				self._scene.soundmanager.playClip(self._soundclip)
+				self._soundclip.play()
 			
 			self._lastfired = self._scene.time
 
--- a/demos/shooter/scripts/world.py	Thu May 06 19:34:21 2010 +0000
+++ b/demos/shooter/scripts/world.py	Fri May 07 21:07:27 2010 +0000
@@ -26,9 +26,9 @@
 import math, random
 from fife.extensions import pychan
 from fife.extensions.pychan import widgets
+from fife.extensions.soundmanager import SoundManager
 
 from scripts.common.eventlistenerbase import EventListenerBase
-from scripts.common.helpers import normalize
 from fife.extensions.loaders import loadMapFile
 
 from scripts.gui.guis import *
@@ -36,7 +36,6 @@
 from scripts.ships.shipbase import Ship
 from scripts.ships.player import Player
 from scripts.scene import Scene
-from scripts.soundmanager import SoundManager
 
 class World(EventListenerBase):
 	"""
--- a/engine/python/fife/extensions/__init__.py	Thu May 06 19:34:21 2010 +0000
+++ b/engine/python/fife/extensions/__init__.py	Fri May 07 21:07:27 2010 +0000
@@ -9,4 +9,6 @@
 	'filebrowser',
 	'pythonize',
 	'savers',
-]
\ No newline at end of file
+	'soundmanager',
+	'fife_math'
+]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/engine/python/fife/extensions/fife_math.py	Fri May 07 21:07:27 2010 +0000
@@ -0,0 +1,161 @@
+# -*- 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
+# ####################################################################
+
+from fife import fife
+import math
+
+"""
+Math Library
+==================================
+
+This is a collection of useful 2D math functions/classes.
+"""
+
+
+def normalize(vector):
+	"""
+	Helper function to normalize a 2D vector
+	
+	@param vector a L{fife.DoublePoint} to be normalized
+	
+	@return A normalized L{fife.DoublePoint}
+	"""
+	norm = fife.DoublePoint(0,0) 
+		
+	invLength = 1.0/vector.length()
+	if invLength > 1e-06:
+		norm.x = vector.x * invLength;
+		norm.y = vector.y * invLength;
+	else:
+		norm.x = 0
+		norm.y = 0
+	
+	return norm
+	
+class Rect(object):
+	"""
+	Rect
+	
+	A class used to specify the bounding box of objects.  For use
+	with collision detection.  This was written in python because
+	FIFE does not provide a Rect class that can use floating point
+	values.
+	"""
+	def __init__(self, x = 0, y = 0, w = 0, h = 0):
+		"""
+		@param x The x coordinate
+		@param y The y coordinate
+		@param w The width
+		@param h The height
+		"""
+		self._x = x
+		self._y = y
+		self._w = w
+		self._h = h
+		
+	def intersects(self, rect):
+		"""
+		Tests for intersection of rect.
+		
+		@param rect the Rect to perform the test against
+		@return True if the rectancles intersect, False if not.
+		"""
+		_x = self._x - rect.x;
+		_y = self._y - rect.y;
+		_w = self._w;
+		_h = self._h;
+
+		if _x < 0:
+			_w += _x
+			_x = 0
+
+		if _y < 0:
+			_h += _y
+			_y = 0
+
+		if _x + _w > rect.w:
+			_w = rect.w - _x
+
+		if _y + _h > rect.h:
+			_h = rect.h - _y
+			
+		if _w <= 0 or _h <= 0:
+			return False
+
+		return True
+
+	def _setX(self, x):
+		self._x = x
+		
+	def _getX(self):
+		return self._x
+
+	def _setY(self, y):
+		self._y = y
+		
+	def _getY(self):
+		return self._y
+		
+	def _setW(self, w):
+		self._w = w
+		
+	def _getW(self):
+		return self._w
+		
+	def _setH(self, h):
+		self._h = h
+		
+	def _getH(self):
+		return self._h
+		
+	x = property(_getX, _setX)
+	y = property(_getY, _setY)
+	w = property(_getW, _setW)
+	h = property(_getH, _setH)
+	
+def rotatePoint(origin, point, angle):
+	"""
+	Rotates a point around the specified origin.
+	
+	@param origin A L{fife.DoublePoint} specifying the origin.
+	@param point A L{fife.DoublePoint} to be rotated.
+	@param angle The angle in which to rotate the point.
+	
+	@return A L{fife.DoublePoint} representing the rotated point.
+	"""
+	newp = fife.DoublePoint(0,0)
+	
+	theta = (angle * math.pi)/180
+	
+	costheta = math.cos(theta)
+	sintheta = math.sin(theta)
+	
+	x = point.x - origin.x
+	y = point.y - origin.y
+	
+	newp.x = costheta * x - sintheta * y
+	newp.y = sintheta * x + costheta * y
+	
+	return newp
+
+__all__ = ['normalize','Rect','rotatePoint']
--- a/engine/python/fife/extensions/fife_timer.py	Thu May 06 19:34:21 2010 +0000
+++ b/engine/python/fife/extensions/fife_timer.py	Fri May 07 21:07:27 2010 +0000
@@ -28,22 +28,21 @@
 =================
 
 Usage::
-  import timer
-  timer.init( my_fife_engine.getTimeManager() )
+  import fife.extensions.fife_timer
+  fife_timer.init( my_fife_engine.getTimeManager() )
   def spam():
      print "SPAM SPAM ",
-     return "More spam?" # a string is a true value, so it's repeated.
-  repeater = timer.repeatCall(500,spam)
+  repeater = fife_timer.repeatCall(500,spam)
   def stop_spam():
      repeater.stop()
      print "BACON EGGS AND SPAM"
-  timer.delayCall(50000,stop_spam)
+  fife_timer.delayCall(50000,stop_spam)
 
 """
 
 
 _manager = None
-_alltimers = {}
+_alltimers = []
 
 def init(timemanager):
 	"""
@@ -55,32 +54,63 @@
 	_manager = timemanager
 
 class Timer(fife.TimeEvent):
-	def __init__(self,delay=0,callback=None):
-		super(Timer,self).__init__(0)
-		self.manager = _manager
-		self.is_registered = False
-		self.callback = callback
+	"""
+	Timer
+	
+	This class wraps the fife.TimeEvent class to make it easily usable from Python
+	It allows for a TimeEvent to be executed once or multiple times.
+	"""
+	def __init__(self,delay=0,callback=None,repeat=0):
+		"""
+		@param delay The delay in milliseconds to execute the callback
+		@param callback The function to execute
+		@param repeat The number of times to execute the callback.  1=once, 0=forever 
+		"""
+		super(Timer,self).__init__(delay)
+		self._is_registered = False
+		self._callback = callback
+		self._manager = _manager
 		self.setPeriod(delay)
+		self._repeat = repeat
+		self._executed = 0
 
 	def start(self):
-		if self.is_registered:
+		"""
+		Must be called before the timer will start
+		"""
+		if self._is_registered:
 			return
-		self.is_registered = True
+		self._is_registered = True
+		self._executed = 0
+		self._manager.registerEvent(self)
+
 		global _alltimers
-		_alltimers[self]=1
-		self.manager.registerEvent(self)
+		_alltimers.append(self)
 
 	def stop(self):
-		if not self.is_registered:
+		"""
+		Stops the timer
+		"""
+		if not self._is_registered:
 			return
-		self.is_registered = False
+		self._is_registered = False
+		self._manager.unregisterEvent(self)
+
 		global _alltimers
-		del _alltimers[self]
-		self.manager.unregisterEvent(self)
+		_alltimers.remove(self)
+
 
 	def updateEvent(self,delta):
-		if callable(self.callback):
-			self.callback()
+		"""
+		Should not be called directly.  This is called by FIFE.
+		"""
+		if callable(self._callback):
+			self._callback()
+			
+		if self._repeat != 0:
+			self._executed += 1
+			if self._executed >= self._repeat:
+				self.stop()
 
 def delayCall(delay,callback):
 	"""
@@ -91,18 +121,11 @@
 
 	@return The timer.
 	"""
-	timer = Timer(delay)
-
-	def real_callback(c, t):
-		t.stop()
-		c()
-		
-	from fife.extensions.pychan.tools import callbackWithArguments as cbwa
-	timer.callback = cbwa(real_callback, callback, timer)
+	timer = Timer(delay, callback, 1)
 	timer.start()
 	return timer
 
-from traceback import print_exc
+
 def repeatCall(period,callback):
 	"""
 	Repeat a function call.
@@ -112,21 +135,11 @@
 
 	@return The timer.
 
-	The call is repeated until the callback returns a False
-	value (i.e. None) or the timer is stopped.
+	The call is repeated until the timer is stopped.
 	"""
-	timer = Timer(period)
-	def real_callback():
-		try:
-			if not callback():
-				timer.stop()
-		except Exception:
-			print_exc()
-			timer.stop()
-
-	timer.callback = real_callback
+	timer = Timer(period, callback, 0)
 	timer.start()
 	return timer
 
-__all__ = [init,Timer,delayCall,repeatCall]
+__all__ = ['init','Timer','delayCall','repeatCall']
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/engine/python/fife/extensions/soundmanager.py	Fri May 07 21:07:27 2010 +0000
@@ -0,0 +1,277 @@
+# -*- 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
+# ####################################################################
+
+from fife import fife
+
+import fife.extensions.fife_timer as fife_timer
+from fife.extensions.pychan.tools import callbackWithArguments as cbwa
+
+"""
+Sound Manager
+==================================
+
+This is a simple implementation of a sound manager that was originaly
+intended for the shooter demo.  It was functional enough that we decided
+to include it in the FIFE extensions.  This is by no means a fully featured
+implementation for several reasons.  It doesnt limit how many sounds can 
+play at once or allow the positioning of sounds.  It does however provide 
+a good starting point for a more advanced version of a sound manager.
+
+"""
+
+class SoundEmitter(object):
+	"""
+	SoundEmitter
+
+	This class wraps an instance of a L{fife.SoundEmitter} class along 
+	with some information about a sound clip (like gain and if its
+	looping).  All instances of SoundEmitter should be created by SoundManager.
+	
+	@todo At some point this class will store positional information
+	and also be responsible for updating the L{fife.SoundEmitter} position.
+	"""
+	def __init__(self, soundmanager, clipid, soundname, emitter):
+		"""
+		@param soundmanager A reference to the SoundManager
+		@param clipid The FIFE sound clip ID from the sound clip pool
+		@param soundname The filename of the sound
+		@param emitter A reference to the L{fife.SoundEmitter} associated with this clip
+		
+		"""
+		self._soundmanager = soundmanager
+		self._name = soundname
+			
+		#The FIFE SoundEmitter associated with this SoundEmitter.
+		#Note that we do NOT own the emitter.
+		self._fifeemitter = emitter
+		self._fifeemitter.thisown = 0
+		self._fifeclipid = clipid
+				
+		#0 = mute, 255 = normal volume
+		self._gain = 255.0
+		self._looping = False
+		
+		#if you set the callback it will be executed after the sound
+		#has finished playing.
+		self._callback = None
+		
+		#length of the sound
+		self._duration = 0
+		
+		self._timer = None
+		
+	def play(self):
+		self._soundmanager.playClip(self)
+		
+	def stop(self):
+		self._soundmanager.stopClip(self)
+		
+	def _getClipID(self):
+		return self._fifeclipid
+
+	def _getGain(self):
+		return self._gain
+		
+	def _setGain(self, gain):
+		self._gain = float(gain)
+		
+	def _getLooping(self):
+		return self._looping
+		
+	def _setLooping(self, looping):
+		self._looping = looping
+		
+	def _getFifeEmitter(self):
+		return self._fifeemitter
+		
+	def _setFifeEmitter(self, emitter):
+		self._fifeemitter = emitter
+		if self._fifeemitter:
+			self._fifeemitter.thisown = 0
+
+	def _getName(self):
+		return self._name
+
+	def _getCallback(self):
+		return self._callback
+
+	def _setCallback(self, cb):
+		self._callback = cb	
+		
+	def _getDuration(self):
+		return self._duration
+	
+	def _setDuration(self, millliseconds):
+		self._duration = millliseconds
+
+	def _getTimer(self):
+		return self._timer
+		
+	def _setTimer(self, timer):
+		self._timer = timer
+
+	timer = property(_getTimer, _setTimer)
+	clipid = property(_getClipID)
+	gain = property(_getGain, _setGain)
+	looping = property(_getLooping, _setLooping)
+	fifeemitter = property(_getFifeEmitter, _setFifeEmitter)
+	name = property(_getName)
+	callback = property(_getCallback, _setCallback)
+	duration = property(_getDuration, _setDuration)
+
+class SoundManager(object):
+	"""
+	SoundManger
+
+	This class manages and plays all the sounds of the game.  
+	It creates SoundEmitters and ensures that there is only one 
+	L{fife.SoundEmitter} per unique sound.
+	"""
+	def __init__(self, engine):
+		"""
+		@param engine A reference to the FIFE engine
+		"""
+		
+		self._engine = engine
+		
+		self._fifesoundmanager = self._engine.getSoundManager()
+		self._fifesoundmanager.init()
+		
+		#A dict of fife emitters
+		self._loadedclips = {}
+		
+	def createSoundEmitter(self, filename, forceUnique=False):
+		"""
+		Returns a valid SoundEmitter instance.
+		
+		@param filename The relative path and filename of the sound file
+		@parm forceUnique This forces a new L{fife.SoundEmitter} to be created.  
+		This is useful if you want more than one instance of the same sound 
+		to be played at the same time.
+		
+		@return Returns a new SoundEmitter instance.
+		"""
+		if not self._loadedclips.has_key(filename):
+			clipid = self._engine.getSoundClipPool().addResourceFromFile(filename)
+			fifeemitter = self._fifesoundmanager.createEmitter()
+			fifeemitter.thisown = 0
+			fifeemitter.setSoundClip(clipid)
+			
+			self._loadedclips[filename] = [fifeemitter]
+			clip = SoundEmitter(self, clipid, filename, fifeemitter)
+			clip.duration = fifeemitter.getDuration()
+		else:
+			if forceUnique:
+				clipid = self._engine.getSoundClipPool().addResourceFromFile(filename)
+				fifeemitter = self._fifesoundmanager.createEmitter()
+				fifeemitter.thisown = 0
+				fifeemitter.setSoundClip(clipid)
+				self._loadedclips[filename].append(fifeemitter)	
+			else:
+				fifeemitter = self._loadedclips[filename][0]
+				
+			clip = SoundEmitter(self, fifeemitter.getID(), filename, fifeemitter)
+			clip.duration = fifeemitter.getDuration()
+		
+		return clip
+		
+	def playClip(self, clip):
+		"""
+		Plays a sound clip.  
+		
+		This function does not use the L{fife.SoundEmitter}
+		"looping" property to loop a sound.  Instead it registers 
+		a new timer and uses the duration of the clip as the timer length.
+		
+		If the SoundEmitter is invalid (no fifeemitter) then it attempts 
+		to load it before playing it.
+		
+		@note This will stop any clips that use the same L{fife.SoundEmitter}.
+		You cannot play the same sound more than once at a time unless you create
+		the SoundEmitter with the forceUnique paramater set to True.
+		
+		@param clip The SoundEmitter to be played
+		"""
+		
+		if clip.fifeemitter:
+			if clip.callback:
+				if clip.timer:
+					clip.timer.stop()
+					timer = None
+					
+				if clip.looping:
+					repeat = 0
+					def real_callback(c, e, g):
+						c()
+						e.stop()
+						e.setGain(g)
+						e.play()
+
+					clip.callback = cbwa(real_callback, clip.callback, clip.fifeemitter, clip.gain)
+
+				else:
+					repeat = 1
+					
+				clip.timer = fife_timer.delayCall(clip.duration, clip.callback)
+				#clip.timer.start()
+				
+			clip.fifeemitter.setGain(clip.gain)
+			clip.fifeemitter.play()	
+		else:
+			clip = self.createSoundEmitter(clip.name)
+			self.playClip(clip)
+				
+	def stopClip(self, clip):
+		"""
+		Stops playing the sound clip.   Note that this will stop all clips that 
+		use the same FIFE emitter.
+		
+		@parm clip The SoundEmitter to stop.
+		"""
+		if clip.fifeemitter:
+			clip.fifeemitter.stop()
+			
+		if clip.timer:
+			clip.timer.stop()
+			clip.timer = None
+
+	def stopAllSounds(self):
+		for emitterlist in self._loadedclips.values():
+			for emitter in emitterlist:
+				emitter.stop()
+			
+	def destroy(self):
+		"""
+		Releases all instances of L{fife.SoundEmitter}.  This does not free 
+		the resources from the FIFE sound clip pool.
+		"""
+		self.stopAllSounds()
+	
+		for emitterlist in self._loadedclips.values():
+			for emitter in emitterlist:
+				self._fifesoundmanager.releaseEmitter(emitter.getID())
+				emitter = None
+		
+		self._loadedclips.clear()
+
+__all__ = ['SoundEmitter','SoundManager']
--- a/setup.py	Thu May 06 19:34:21 2010 +0000
+++ b/setup.py	Fri May 07 21:07:27 2010 +0000
@@ -35,7 +35,7 @@
 setup(name='fife',
       version='0.3.1',
       description='Flexible Isometric Free Engine',
-      url='www.fifengine.de',
+      url='www.fifengine.net',
       packages = ['fife', 'fife.extensions', 'fife.extensions.pychan', 'fife.extensions.pychan.widgets', 'fife.extensions.pychan.widgets.ext', 'fife.extensions.serializers' ],
       package_dir = { '': os.path.join('engine','python') },
       package_data = pkg_data,