diff src/parpg/bGrease/impl/controls.py @ 66:96af64cf3b81

Renamed grease to bGrease (Basic Grease) to get rid of conflicts with an already installed grease.
author KarstenBock@gmx.net
date Mon, 05 Sep 2011 15:00:34 +0200
parents src/parpg/grease/impl/controls.py@09b581087d68
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/parpg/bGrease/impl/controls.py	Mon Sep 05 15:00:34 2011 +0200
@@ -0,0 +1,216 @@
+#############################################################################
+#
+# Copyright (c) 2010 by Casey Duncan
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the MIT License
+# A copy of the license should accompany this distribution.
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+#
+#############################################################################
+"""Control systems for binding controls to game logic"""
+
+import bGrease
+from pyglet.window import key
+
+class KeyControls(grease.System):
+	"""System that maps subclass-defined action methods to keys. 
+
+	Keys may be mapped in the subclass definition using decorators
+	defined here as class methods or at runtime using the ``bind_key_*`` 
+	instance methods.
+
+	See :ref:`an example implementation in the tutorial <tut-controls-example>`.
+	"""
+	MODIFIER_MASK = ~(key.MOD_NUMLOCK | key.MOD_SCROLLLOCK | key.MOD_CAPSLOCK)
+	"""The MODIFIER_MASK allows you to filter out modifier keys that should be
+	ignored by the application. By default, capslock, numlock, and scrolllock 
+	are ignored.
+	"""
+
+	world = None
+	""":class:`grease.World` object this system is bound to"""
+
+	def __init__(self):
+		self._key_press_map = {}
+		self._key_release_map = {}
+		self._key_hold_map = {}
+		for name in self.__class__.__dict__:
+			member = getattr(self, name)
+			if hasattr(member, '_grease_hold_key_binding'):
+				for binding in member._grease_hold_key_binding:
+					self.bind_key_hold(member, *binding)
+			if hasattr(member, '_grease_press_key_binding'):
+				for binding in member._grease_press_key_binding:
+					self.bind_key_press(member, *binding)
+			if hasattr(member, '_grease_release_key_binding'):
+				for binding in member._grease_release_key_binding:
+					self.bind_key_release(member, *binding)
+		self.held_keys = set()
+
+	## decorator methods for binding methods to key input events ##
+
+	@classmethod
+	def key_hold(cls, symbol, modifiers=0):
+		"""Decorator to bind a method to be executed where a key is held down"""
+		def bind(f):
+			if not hasattr(f, '_grease_hold_key_binding'):
+				f._grease_hold_key_binding = []
+			f._grease_hold_key_binding.append((symbol, modifiers & cls.MODIFIER_MASK))
+			return f
+		return bind
+
+	@classmethod
+	def key_press(cls, symbol, modifiers=0):
+		"""Decorator to bind a method to be executed where a key is initially depressed"""
+		def bind(f):
+			if not hasattr(f, '_grease_press_key_binding'):
+				f._grease_press_key_binding = []
+			f._grease_press_key_binding.append((symbol, modifiers & cls.MODIFIER_MASK))
+			return f
+		return bind
+
+	@classmethod
+	def key_release(cls, symbol, modifiers=0):
+		"""Decorator to bind a method to be executed where a key is released"""
+		def bind(f):
+			if not hasattr(f, '_grease_release_key_binding'):
+				f._grease_release_key_binding = []
+			f._grease_release_key_binding.append((symbol, modifiers & cls.MODIFIER_MASK))
+			return f
+		return bind
+	
+	## runtime binding methods ##
+	
+	def bind_key_hold(self, method, key, modifiers=0):
+		"""Bind a method to a key at runtime to be invoked when the key is
+		held down, this replaces any existing key hold binding for this key.
+		To unbind the key entirely, pass ``None`` for method.
+		"""
+		if method is not None:
+			self._key_hold_map[key, modifiers & self.MODIFIER_MASK] = method
+		else:
+			try:
+				del self._key_hold_map[key, modifiers & self.MODIFIER_MASK]
+			except KeyError:
+				pass
+
+	def bind_key_press(self, method, key, modifiers=0):
+		"""Bind a method to a key at runtime to be invoked when the key is initially
+		pressed, this replaces any existing key hold binding for this key. To unbind
+		the key entirely, pass ``None`` for method.
+		"""
+		if method is not None:
+			self._key_press_map[key, modifiers & self.MODIFIER_MASK] = method
+		else:
+			try:
+				del self._key_press_map[key, modifiers & self.MODIFIER_MASK]
+			except KeyError:
+				pass
+
+	def bind_key_release(self, method, key, modifiers=0):
+		"""Bind a method to a key at runtime to be invoked when the key is releaseed,
+		this replaces any existing key hold binding for this key. To unbind
+		the key entirely, pass ``None`` for method.
+		"""
+		if method is not None:
+			self._key_release_map[key, modifiers & self.MODIFIER_MASK] = method
+		else:
+			try:
+				del self._key_release_map[key, modifiers & self.MODIFIER_MASK]
+			except KeyError:
+				pass
+
+	def step(self, dt):
+		"""invoke held key functions"""
+		already_run = set()
+		for key in self.held_keys:
+			func = self._key_hold_map.get(key)
+			if func is not None and func not in already_run:
+				already_run.add(func)
+				func(dt)
+
+	def on_key_press(self, key, modifiers):
+		"""Handle pyglet key press. Invoke key press methods and
+		activate key hold functions
+		"""
+		key_mod = (key, modifiers & self.MODIFIER_MASK)
+		if key_mod in self._key_press_map:
+			self._key_press_map[key_mod]()
+		self.held_keys.add(key_mod)
+	
+	def on_key_release(self, key, modifiers):
+		"""Handle pyglet key release. Invoke key release methods and
+		deactivate key hold functions
+		"""
+		key_mod = (key, modifiers & self.MODIFIER_MASK)
+		if key_mod in self._key_release_map:
+			self._key_release_map[key_mod]()
+		self.held_keys.discard(key_mod)
+
+
+if __name__ == '__main__':
+	import pyglet
+
+	class TestKeyControls(KeyControls):
+		
+		MODIFIER_MASK = ~(key.MOD_NUMLOCK | key.MOD_SCROLLLOCK | key.MOD_CTRL)
+
+		remapped = False
+		
+		@KeyControls.key_hold(key.UP)
+		@KeyControls.key_hold(key.W)
+		def up(self, dt):
+			print 'UP!'
+		
+		@KeyControls.key_hold(key.LEFT)
+		@KeyControls.key_hold(key.A)
+		def left(self, dt):
+			print 'LEFT!'
+		
+		@KeyControls.key_hold(key.RIGHT)
+		@KeyControls.key_hold(key.D)
+		def right(self, dt):
+			print 'RIGHT!'
+		
+		@KeyControls.key_hold(key.DOWN)
+		@KeyControls.key_hold(key.S)
+		def down(self, dt):
+			print 'DOWN!'
+
+		@KeyControls.key_press(key.SPACE)
+		def fire(self):
+			print 'FIRE!'
+
+		@KeyControls.key_press(key.R)
+		def remap_keys(self):
+			if not self.remapped:
+				self.bind_key_hold(None, key.W)
+				self.bind_key_hold(None, key.A)
+				self.bind_key_hold(None, key.S)
+				self.bind_key_hold(None, key.D)
+				self.bind_key_hold(self.up, key.I)
+				self.bind_key_hold(self.left, key.J)
+				self.bind_key_hold(self.right, key.L)
+				self.bind_key_hold(self.down, key.K)
+			else:
+				self.bind_key_hold(None, key.I)
+				self.bind_key_hold(None, key.J)
+				self.bind_key_hold(None, key.K)
+				self.bind_key_hold(None, key.L)
+				self.bind_key_hold(self.up, key.W)
+				self.bind_key_hold(self.left, key.A)
+				self.bind_key_hold(self.right, key.D)
+				self.bind_key_hold(self.down, key.S)
+			self.remapped = not self.remapped
+			
+
+	window = pyglet.window.Window()
+	window.clear()
+	controls = TestKeyControls()
+	window.push_handlers(controls)
+	pyglet.clock.schedule_interval(controls.step, 0.5)
+	pyglet.app.run()
+