Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/MAFH2/fortuneengine/fortuneengine
diff options
context:
space:
mode:
Diffstat (limited to 'MAFH2/fortuneengine/fortuneengine')
-rw-r--r--MAFH2/fortuneengine/fortuneengine/DrawableFontObject.py19
-rw-r--r--MAFH2/fortuneengine/fortuneengine/DrawableObject.py157
-rw-r--r--MAFH2/fortuneengine/fortuneengine/DynamicDrawableObject.py74
-rw-r--r--MAFH2/fortuneengine/fortuneengine/GameEngine.py502
-rw-r--r--MAFH2/fortuneengine/fortuneengine/GameEngineConsole.py77
-rw-r--r--MAFH2/fortuneengine/fortuneengine/GameEngineElement.py109
-rw-r--r--MAFH2/fortuneengine/fortuneengine/GameInspect.py231
-rw-r--r--MAFH2/fortuneengine/fortuneengine/Scene.py186
-rw-r--r--MAFH2/fortuneengine/fortuneengine/__init__.py14
-rw-r--r--MAFH2/fortuneengine/fortuneengine/pyconsole/__init__.py0
-rwxr-xr-xMAFH2/fortuneengine/fortuneengine/pyconsole/fonts/default.ttfbin0 -> 49224 bytes
-rw-r--r--MAFH2/fortuneengine/fortuneengine/pyconsole/pyconsole.cfg33
-rw-r--r--MAFH2/fortuneengine/fortuneengine/pyconsole/pyconsole.py637
13 files changed, 2039 insertions, 0 deletions
diff --git a/MAFH2/fortuneengine/fortuneengine/DrawableFontObject.py b/MAFH2/fortuneengine/fortuneengine/DrawableFontObject.py
new file mode 100644
index 0000000..92f641f
--- /dev/null
+++ b/MAFH2/fortuneengine/fortuneengine/DrawableFontObject.py
@@ -0,0 +1,19 @@
+import pygame
+from DrawableObject import DrawableObject
+
+class DrawableFontObject(DrawableObject, pygame.sprite.Sprite):
+
+ def __init__(self,text,font, x = 0, y = 0):
+
+ self.font = font
+ self.textImage = font.render(text, 1, (255,255,255))
+ self.text = text
+ DrawableObject.__init__(self, [self.textImage], '')
+
+ def changeText(self, newText, color=(0,0,0)):
+ self.text = newText
+ self._images[0] = self.font.render(str(newText), True, color)
+ self.image = self._images[0]
+
+ def getText(self):
+ return str(self.text)
diff --git a/MAFH2/fortuneengine/fortuneengine/DrawableObject.py b/MAFH2/fortuneengine/fortuneengine/DrawableObject.py
new file mode 100644
index 0000000..96bb8ef
--- /dev/null
+++ b/MAFH2/fortuneengine/fortuneengine/DrawableObject.py
@@ -0,0 +1,157 @@
+import pygame
+
+class DrawableObject(pygame.sprite.Sprite):
+
+ def __init__(self, images, textfileName, transparent = False, x = 0, y = 0):
+ pygame.sprite.Sprite.__init__(self)
+
+ self._images = []
+ self._origImages = []
+ for i in range(len(images)):
+ self._images.append(images[i].convert_alpha())
+ self._origImages.append(images[i].convert_alpha())
+
+ blank = pygame.Surface((0,0))
+
+ if(transparent):
+ for i in range(len(images)):
+ self._images[i] = blank
+
+ self._start = pygame.time.get_ticks()
+ self.image = self._images[0]
+ self._last_update = 0
+ self._frame = 0
+ self.animations = {}
+ self._current_anim = ""
+ self.rect = self.image.get_rect()
+ self.xPos = x
+ self.yPos = y
+ self.myAngle = 0
+ self.xSize = self.image.get_width()
+ self.ySize = self.image.get_height()
+ self.rect.topleft = (x,y)
+
+ if textfileName != '':
+
+ f = open(textfileName, 'r')
+ currentLine = f.readline()
+ while currentLine != '':
+
+ animValues = currentLine.split(",")
+ self.animations[animValues[0]] = [int(animValues[1]), int(animValues[2])]
+ currentLine = f.readline()
+
+ else:
+
+ self.animations["anim1"] = [0, len(self._images)]
+ self.goToAnim("anim1")
+
+ self.makeTransparent(transparent)
+
+ def repopulateImages(self, newImages):
+
+ self._images = []
+ self._origImages = []
+ for i in range(len(newImages)):
+ self._images.append(newImages[i].convert_alpha())
+ self._origImages.append(newImages[i].convert_alpha())
+
+ self.image = self._images[0]
+ self._frame = 0
+ self.xSize = self.image.get_width()
+ self.ySize = self.image.get_height()
+
+ def addImages(self, images):
+ self._images.extend(images)
+ self._origImages.extend(images)
+
+ def goToAnim(self, animName):
+ if self.animations.get(animName, 0) != 0:
+ self._current_anim = animName
+ self._frame = self.animations[animName][0]
+ self.image = self._images[self._frame]
+
+ def goToFrame(self, frame):
+
+ if frame <= len(self._images):
+ self._frame = frame
+ self.image = self._images[self._frame]
+
+ def nudge(self, x, y):
+ self.xPos += x
+ self.yPos += y
+ self.rect.right += x
+ self.rect.top += y
+
+ def scale(self, x=None, y=None):
+ if type(x).__name__=='int': self.xSize = x
+ if type(y).__name__=='int': self.ySize = y
+
+ for i in range(len(self._images)):
+ self._origImages[i] = pygame.transform.scale(self._origImages[i], (self.xSize, self.ySize))
+ self._images[i] = self._origImages[i]
+
+ def fill(self, color):
+ for i in range(len(self._images)):
+ #print "filling with ", color
+ self._origImages[i].fill(color)
+ self._images[i].fill(color)
+
+ def getXSize(self):
+ return self.xSize
+
+ def getYSize(self):
+ return self.ySize
+
+ def rotate(self,angle):
+ self.myAngle += angle
+ for i in range(len(self._images)):
+ self._images[i] = pygame.transform.rotate(self._origImages[i], self.myAngle)
+
+ def getRotation(self):
+ return self.myAngle
+
+ def setPosition(self, x = None, y = None):
+ if type(x).__name__=='int': self.xPos = x
+ if type(y).__name__=='int': self.yPos = y
+ self.rect.topleft = (self.xPos, self.yPos)
+
+ def getXPos(self):
+ return self.xPos
+
+ def getYPos(self):
+ return self.yPos
+
+ def calcColorKey(self, x=0, y=0):
+ myColorKey = images[0].get_at((x,y))
+ setColorKey(myColorKey)
+
+ def makeTransparent(self, bool = True):
+ if bool == True:
+ surf = pygame.Surface((0,0))
+ for i in range(len(self._images)):
+ self._images[i] = surf
+ else:
+ for i in range(len(self._images)):
+ self._images[i] = self._origImages[i]
+ self.image = self._images[self._frame]
+
+ def setColorKey(self, aColor):
+ for i in range(len(self._images)):
+ self._images[i].set_colorkey(aColor)
+
+ def update(self, t=None):
+ timePassed = t + self._last_update
+
+ if (timePassed) > 200:
+
+ self.image = self._images[self._frame]
+ self._last_update = timePassed%1000
+ else:
+ self._last_update = timePassed
+
+ def nextFrame(self):
+ pass
+
+ def nextCurrentAnimFrame(self):
+ pass
diff --git a/MAFH2/fortuneengine/fortuneengine/DynamicDrawableObject.py b/MAFH2/fortuneengine/fortuneengine/DynamicDrawableObject.py
new file mode 100644
index 0000000..7dcb831d
--- /dev/null
+++ b/MAFH2/fortuneengine/fortuneengine/DynamicDrawableObject.py
@@ -0,0 +1,74 @@
+import pygame
+from DrawableObject import DrawableObject
+
+class DynamicDrawableObject(DrawableObject, pygame.sprite.Sprite):
+
+ def __init__(self,images,textfileName,fps = 10, x = 0, y = 0, xVelocity = 0, yVelocity = 0):
+
+ self._delay = 1000/fps
+ DrawableObject.__init__(self, images, textfileName, x, y)
+
+ def addImages(self, images):
+
+ self._images.extend(images)
+
+ def setSpeed(self, xVelocity = None, yVelocity = None):
+
+ if xVelocity != None: self.xSpeed = xVelocity
+ if yVelocity != None: self.ySpeed = yVelocity
+
+ def getXSpeed(self):
+
+ return self.xSpeed
+
+ def getYSpeed(self):
+
+ return self.ySpeed
+
+ def move(self):
+ self.xPos += self.xSpeed
+ self.yPos += self.ySpeed
+ self.rect.right += self.xSpeed
+ self.rect.top += self.ySpeed
+
+ def update(self, t):
+
+ timePassed = t + self._last_update
+
+ if (timePassed) > self._delay:
+ if self._frame < self.animations.get(self._current_anim)[0] or self._frame > self.animations.get(self._current_anim)[1]:
+ self._frame = self.animations.get(self._current_anim)[0] - 1
+
+ self._frame += timePassed/self._delay
+
+ if self._frame >= self.animations.get(self._current_anim)[1]:
+ self._frame = self._frame%(self.animations.get(self._current_anim)[1])
+
+ self.image = self._images[self._frame]
+ self._last_update = timePassed%self._delay
+ else:
+ self._last_update = timePassed
+
+ def nextFrame(self):
+ self._frame += 1
+ if self._frame >= len(self._images):
+ framesPast = self._frame - len(self._images)
+ self._frame = framesPast
+
+ self.image = self._images[self._frame]
+
+ def nextCurrentAnimFrame(self):
+
+ for cnt in range(len(animations)):
+
+ if animations[cnt] == self._current_anim:
+ if self._frame < self.animations[self._current_anim][0] or self._frame > self.animations[self._current_anim][1]:
+ self._frame = self.animations[self._current_anim][0]
+
+ else: self._frame += 1
+
+ if self._frame > self.animations[self._current_anim][1]:
+ framesPast = self._frame - self.animations[self._current_anim][1]
+ self._frame = framesPast - 1 + self.animations[self._current_anim][0]
+
+ self.image = self._images[self._frame]
diff --git a/MAFH2/fortuneengine/fortuneengine/GameEngine.py b/MAFH2/fortuneengine/fortuneengine/GameEngine.py
new file mode 100644
index 0000000..48d7d3e
--- /dev/null
+++ b/MAFH2/fortuneengine/fortuneengine/GameEngine.py
@@ -0,0 +1,502 @@
+# FortuneEngine 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.
+#
+# FortuneEngine 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 the FortuneEngine. If not, see <http://www.gnu.org/licenses/>.
+#
+# Author: Justin Lewis <jlew.blackout@gmail.com>
+
+import pygame
+from time import time
+from GameEngineConsole import GameEngineConsole
+from GameInspect import GameInspect
+from DrawableFontObject import DrawableFontObject
+from Scene import Scene
+
+
+class GameEngine(object):
+ """
+ The Fortune Engine GameEngine is a main loop wrapper around pygame.
+ It manages the event and drawing loops allowing the user to just
+ register for user events and drawing time in the draw loop.
+ """
+ instance = None
+
+ def __init__(self, width=1200, height=900, always_draw=False,
+ fps_cap=15, version=False, title="FortuneEngine"):
+ """
+ Constructor for the game engine.
+
+ @param width: Window width
+ @param height: Window height
+ @param always_draw: Boolean to set the animation mode to always
+ draw vs draw when set_dirty is called
+ @param fps_cap: Sets the framerate cap. Set to 0 to disable
+ the cap. Warning: setting the cap to 0 when
+ always draw = False will cause cpu 100% when
+ not driving.
+ @param version: If true, use new rendering system, false uses
+ only the draw system
+ @param title: Window Title
+ """
+ GameEngine.instance = self
+ pygame.init()
+ pygame.mouse.set_visible(False)
+ self.__version = version #true is new, false is old
+
+ # Window Settings
+ self.width = width
+ self.height = height
+ size = width, height
+ self.screen = pygame.display.set_mode(size)
+ pygame.display.set_caption(title)
+ self.__fps = DrawableFontObject("", pygame.font.Font(None, 17))
+ self.__fps.setPosition(0, 0)
+ self.__scene = Scene(self.__fps)
+
+ # Engine Internal Variables
+ self.__fps_cap = fps_cap
+ self.__showfps = False
+ self.__dirty = True
+ self.__always_draw = always_draw
+ self.__font = pygame.font.Font(None, 17)
+ self.__run_event = False
+
+ # Variables to hold game engine elements and callbacks
+ self.__event_cb = []
+ self.__draw_lst = []
+ self.__object_hold = {}
+
+
+ # Game Timers
+ self.__active_event_timers = []
+ self.__active_event_timers_tick = []
+
+ # Game Clock
+ self.clock = pygame.time.Clock()
+ self.__tick_time = 0
+
+ # Inspector
+ self._inspector = GameInspect(self.__object_hold)
+
+ # Time Profiler Timers
+ self.__draw_time = {}
+ self.__draw_calls = {}
+ self.__event_time = {}
+ self.__event_calls = {}
+ self.__timer_time = {}
+ self.__timer_calls = {}
+
+ # Initialize Py Console
+ self.console = GameEngineConsole(self, (0, 0, width, height / 2))
+
+ # Disable Mouse Usage
+ # TODO Allow mouse motion on request
+ pygame.event.set_blocked(pygame.MOUSEMOTION)
+
+ def set_dirty(self):
+ """
+ Sets the dirty flag to force the engine to draw the next time
+ it enters the draw flag.
+ """
+ self.__dirty = True
+
+ def get_scene(self):
+ """
+ Returns the scene object
+
+ @return: Returns the scene object held by the game engine
+ """
+ return self.__scene
+
+ def start_event_timer(self, function_cb, time):
+ """
+ Starts a timer that fires a user event into the queue every "time"
+ milliseconds
+
+ @param function_cb: The function to call when timer fires
+ @param time: Milliseconds between fires
+ """
+ avail_timer = len(self.__active_event_timers)
+
+ if avail_timer + pygame.USEREVENT < pygame.NUMEVENTS:
+ if function_cb not in self.__active_event_timers:
+ self.__timer_time[str(function_cb)] = 0
+ self.__timer_calls[str(function_cb)] = 0
+
+ self.__active_event_timers.append(function_cb)
+ self.__active_event_timers_tick.append(time)
+ pygame.time.set_timer(pygame.USEREVENT + avail_timer, time)
+ else:
+ print "ERROR TIMER IN LIST"
+ else:
+ print "Ran out of timers :("
+ self.stop_event_loop()
+
+ def stop_event_timer(self, function_cb):
+ """
+ Stops the timer that has id from firing
+
+ @param function_cb: The function registered with the timer that
+ should be canceled
+ """
+ try:
+ timer_id = self.__active_event_timers.index(function_cb)
+ except ValueError:
+ return
+
+ pygame.time.set_timer(pygame.USEREVENT + timer_id, 0)
+ del self.__active_event_timers[timer_id]
+ del self.__active_event_timers_tick[timer_id]
+
+ # Timers have been removed, now need to clear any events
+ # already fired and in the queue
+ pygame.event.clear(pygame.USEREVENT + timer_id)
+
+ def list_event_timers(self):
+ """
+ returns a list of configured timers, if the timers has a time of 0 the
+ timer is disabled
+ """
+ timer_list = "Event Timers:\n"
+ i = 0
+ for timer_item in self.__active_event_timers:
+ timer_list += "\t%d: %d\n" % (timer_item,
+ self.__active_event_timers_tick[i])
+ i = i + 1
+
+ return timer_list
+
+ def list_timer_time(self):
+ """
+ Returns a string representation of the time the game spends
+ in each timer callback.
+ """
+ mystr = "Timer Times:\n\tName\tCalls\tTotal Time\tAvg"
+ for key in self.__timer_time:
+ timer_times = self.__timer_time[key]
+ timer_calls = self.__timer_calls[key]
+ if timer_calls == 0:
+ avg = 0
+ else:
+ avg = timer_times / timer_calls
+
+ mystr = "%s\n\t%s\n\t\t%d\t%f\t%f" % \
+ (mystr, key, timer_calls, timer_times, avg)
+ return mystr
+
+ def start_main_loop(self):
+ """
+ Starts the game loop.
+
+ This function does not return until after the game loop exits
+ """
+ self.__run_event = True
+ self._event_loop()
+
+ def _draw(self, tick_time):
+ """
+ Draws all elements in draw callback to the screen
+
+ @param tick_time: The amount of time passed since last
+ draw cycle. (should be produced by
+ pygamme.clock.tick method)
+ """
+ screen = self.screen
+
+ # If console is active, we want to draw console, pausing
+ # game drawing (events are still being fired, just no
+ # draw updates.
+ if self.__version:
+ if self.console.active:
+ self.console.draw()
+ pygame.display.flip()
+ else:
+ for fnc in self.__draw_lst:
+ start = time()
+ fnc()
+ self.__draw_time[str(fnc)] += time() - start
+ self.__draw_calls[str(fnc)] += 1
+ # Print Frame Rate
+ if self.__showfps:
+ self.__fps.changeText('FPS: %d' % self.clock.get_fps(),
+ (255, 255, 255))
+ else:
+ self.__fps.changeText('')
+ self.__scene.update(tick_time)
+ pygame.display.update(self.__scene.draw(screen))
+ else:
+ if self.console.active:
+ self.console.draw()
+ pygame.display.flip()
+ else:
+ for fnc in self.__draw_lst:
+ start = time()
+ fnc(screen, tick_time)
+ self.__draw_time[str(fnc)] += time() - start
+ self.__draw_calls[str(fnc)] += 1
+ # Print Frame Rate
+ if self.__showfps:
+ text = self.__font.render('FPS: %d' % \
+ self.clock.get_fps(), False, (255, 255, 255),
+ (159, 182, 205))
+ screen.blit(text, (0, 0))
+ pygame.display.flip()
+
+ def _event_loop(self):
+ """
+ The main event loop.
+ """
+ while self.__run_event:
+
+ event = pygame.event.poll()
+
+ # Handle Game Quit Message
+ if event.type == pygame.QUIT:
+ self.__run_event = False
+
+ # No-Op sent, draw if set to always draw
+ elif event.type == pygame.NOEVENT:
+ # Tick even if not drawing
+ # We want to pause the cpu from getting into a
+ # 100% usage looping on the poll until something
+ # becomes dirty
+ self.__tick_time += self.clock.tick(self.__fps_cap)
+ if self.__always_draw or self.__dirty:
+ self.__dirty = False
+ self._draw(self.__tick_time)
+ self.__tick_time = 0
+
+
+ # Handle User event Timers
+ elif event.type >= pygame.USEREVENT and \
+ event.type < pygame.NUMEVENTS:
+
+ timer_id = event.type - pygame.USEREVENT
+
+ # Call timer
+ str_rep = str(self.__active_event_timers[timer_id])
+ start = time()
+ self.__active_event_timers[timer_id]()
+ self.__timer_time[str_rep] += time() - start
+ self.__timer_calls[str_rep] += 1
+
+ # Check if we should activate the console
+ elif event.type == pygame.KEYDOWN and event.key == pygame.K_w \
+ and pygame.key.get_mods() & pygame.KMOD_CTRL:
+ self.console.set_active()
+ self.set_dirty()
+
+ # Pass event to console
+ elif self.console.process_input(event):
+ self.set_dirty()
+
+ # Pass events to all others
+ else:
+ # Make a copy first so that adding events don't get fired
+ # right away
+ list_cp = self.__event_cb[:]
+
+ # Reverse list so that newest stuff is on top
+ # TODO: cache this list
+ list_cp.reverse()
+
+ for cb in list_cp:
+ # Fire the event for all in cb and stop
+ # if the callback returns True
+ start = time()
+ retur_val = cb(event)
+ self.__event_time[str(cb)] += time() - start
+ self.__event_calls[str(cb)] += 1
+
+ if retur_val:
+ break
+
+ def stop_event_loop(self):
+ """
+ Sends a pygame.QUIT event into the event queue which
+ exits the event loop
+ """
+ pygame.event.post(pygame.event.Event(pygame.QUIT))
+
+ def add_event_callback(self, cb):
+ """
+ Adds event callback to the event callback stack
+
+ @param cb: Callback to be added to the stack when events are fired.
+ """
+ self.__event_time[str(cb)] = 0
+ self.__event_calls[str(cb)] = 0
+ self.__event_cb.append(cb)
+
+ def remove_event_callback(self, cb):
+ """
+ Removes an event from the event callback stack
+
+ @param cb: The callback to remove from the event callback stack
+ @return: Returns true if successful in removing callback
+ """
+ try:
+ self.__event_cb.remove(cb)
+ return True
+ except:
+ return False
+
+ def list_event_callbacks(self):
+ """
+ Returns a string representation of all events registered with the game
+ engine
+ """
+ event_callbacks = "Event Listeners:\n"
+ for eventlst in self.__event_cb:
+ event_callbacks = "%s\t%s\n" % (event_callbacks, str(eventlst))
+ return event_callbacks
+
+ def list_event_time(self):
+ """
+ Returns a string representation of the time the game spends
+ in each event callback.
+ """
+ mystr = "Event Times:\n\tName\tCalls\tTotal Time\tAvg"
+ for key in self.__event_time:
+ event_times = self.__event_time[key]
+ event_calls = self.__event_calls[key]
+ if event_calls == 0:
+ avg = 0
+ else:
+ avg = event_times / event_calls
+
+ mystr = "%s\n\t%s\n\t\t%d\t%f\t%f" % \
+ (mystr, key, event_calls, event_times, avg)
+ return mystr
+
+ def add_draw_callback(self, fnc):
+ """
+ Adds a callback to the draw list. Function will be passed the
+ game screen it should draw too.
+
+ @param fnc: The function to call when system is drawing
+ """
+
+ self.__draw_time[str(fnc)] = 0
+ self.__draw_calls[str(fnc)] = 0
+ self.__draw_lst.append(fnc)
+
+ def pop_draw_callback(self):
+ """
+ Removes top of draw stack and returns it
+
+ @return: Returns the top callback function that was removed
+ """
+ return self.__draw_lst.pop()
+
+ def clear_draw_callback(self):
+ """
+ Empties draw callback stack
+ """
+ self.__draw_lst = []
+
+ def remove_draw_callback(self, fnc):
+ """
+ Removes a draw callback from the game engine draw function
+
+ @param fnc: The callback function to remove
+ @return: Returns true if successful removal of the function
+ """
+ try:
+ self.__draw_lst.remove(fnc)
+ return True
+ except:
+ return False
+
+ def list_draw_callbacks(self):
+ """
+ Lists all the draw callbacks currently registered with the game engine
+ """
+
+ callbacks = "Draw Callbacks:\n"
+ for eventlst in self.__draw_lst:
+ callbacks += "\t%s\n" % str(eventlst)
+ return callbacks
+
+ def list_draw_time(self):
+ """
+ Returns a string representation of the time the game spends
+ in each drawing callback.
+ """
+ mystr = "Drawing Times:\n\tName\tCalls\tTotal Time\tAvg"
+ for key in self.__draw_time:
+ draw_times = self.__draw_time[key]
+ draw_calls = self.__draw_calls[key]
+ if draw_calls == 0:
+ avg = 0
+ else:
+ avg = draw_times / draw_calls
+
+ mystr = "%s\n\t%s\n\t\t%d\t%f\t%f" % \
+ (mystr, key, draw_calls, draw_times, avg)
+ return mystr
+
+ def has_object(self, name):
+ """
+ Returns true if object is stored in game engine
+
+ @param name: Name of the object to check if exists
+ @return: Returns true if object found
+ """
+ return name in self.__object_hold
+
+ def add_object(self, name, obj):
+ """
+ Adds an object to the game engine datastore
+
+ @param name: The name used to store the object
+ @param obj: The object to store
+ """
+ self.__object_hold[name] = obj
+
+ def get_object(self, name):
+ """
+ Returns an object from the game engine datastore
+
+ @param name: The name of object to return
+ @return: Returns the object
+ """
+ return self.__object_hold[name]
+
+ def remove_object(self, name):
+ """
+ Removes an object from the game engine datastore
+
+ @param name: The name of the object to remove
+ """
+ del self.__object_hold[name]
+
+ def list_objects(self):
+ """
+ Returns a sting of registered objects
+ """
+ objlist = "Objects Registered:\n"
+ for eventlst in self.__object_hold:
+ objlist += "\t%s\n" % str(eventlst)
+ return objlist
+
+ def toggle_fps(self):
+ """
+ Toggles fps display
+ """
+ self.__showfps = not self.__showfps
+
+ def art_scale(self, original, expected, width=True):
+ if width:
+ return int(self.width / float(expected) * float(original))
+ else:
+
+ return int(self.height / float(expected) * float(original))
diff --git a/MAFH2/fortuneengine/fortuneengine/GameEngineConsole.py b/MAFH2/fortuneengine/fortuneengine/GameEngineConsole.py
new file mode 100644
index 0000000..0f5efdc
--- /dev/null
+++ b/MAFH2/fortuneengine/fortuneengine/GameEngineConsole.py
@@ -0,0 +1,77 @@
+# FortuneEngine 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.
+#
+# FortuneEngine 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 the FortuneEngine. If not, see <http://www.gnu.org/licenses/>.
+#
+# Author: Justin Lewis <jlew.blackout@gmail.com>
+
+from pyconsole.pyconsole import Console
+
+
+class GameEngineConsole(Console):
+ """
+ GameEngineConsole is a class that extends the pyconsole adding
+ in game engine specific functions.
+ """
+
+ def __init__(self, gei, pos):
+ """
+ Init function of the GameEngineConsole
+
+ @param gei: Passing in the Game Engine Instance.
+ @param pos: The position tuple to place the pyconsole
+ (startx, starty, width, height)
+ """
+ # functions exposed to the console
+ function_list = {
+ "quit": gei.stop_event_loop,
+
+ "list_objects": gei.list_objects,
+ "list_drawcb": gei.list_draw_callbacks,
+ "list_eventcb": gei.list_event_callbacks,
+ "list_timers": gei.list_event_timers,
+ "inspect": gei._inspector.inspect_object,
+
+ "profile_draw":gei.list_draw_time,
+ "profile_event":gei.list_event_time,
+ "profile_timer":gei.list_timer_time,
+
+ "set_str": gei._inspector.set_str,
+ "set_int": gei._inspector.set_int,
+ "set_eval": gei._inspector.set_eval,
+
+ "fps": gei.toggle_fps,
+ }
+
+ # Ctrl + key mappings
+ key_calls = {
+ "d": gei.stop_event_loop,
+ "m": self.console_mode,
+ }
+
+ # Call parent class's init function passing in the
+ # function and key mapping dictionaries
+ Console.__init__(self, gei.screen, pos,
+ functions=function_list, key_calls=key_calls,
+ vars={}, syntax={})
+
+ def console_mode(self):
+ """
+ Switches console between console and python interpreter
+ """
+ # Deactivate Console if showing
+ if self.active:
+ self.set_active()
+ self.setvar("python_mode",
+ not self.getvar("python_mode"))
+
+ self.set_interpreter()
+ self.set_active()
diff --git a/MAFH2/fortuneengine/fortuneengine/GameEngineElement.py b/MAFH2/fortuneengine/fortuneengine/GameEngineElement.py
new file mode 100644
index 0000000..3f51f2f
--- /dev/null
+++ b/MAFH2/fortuneengine/fortuneengine/GameEngineElement.py
@@ -0,0 +1,109 @@
+# FortuneEngine 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.
+#
+# FortuneEngine 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 the FortuneEngine. If not, see <http://www.gnu.org/licenses/>.
+#
+# Author: Justin Lewis <jlew.blackout@gmail.com>
+
+from fortuneengine.GameEngine import GameEngine
+#from fortuneengine.DrawableFontObject import DrawableFontObject
+#from fortuneengine.DrawableObject import DrawableObject
+#from fortuneengine.DynamicDrawableObject import DynamicDrawableObject
+
+
+class GameEngineElement(object):
+ """
+ The GameEngineElement is a helper object that can be extended by
+ other classes. When the class is extended, it will automatically
+ register its self for the event and draw loops with the game engine.
+ """
+
+ def __init__(self, has_draw=True, has_event=True):
+ """
+ Default constructor for GameEngineElement
+
+ @param has_draw: boolean to signify if element should be drawn
+ @param has_event: boolean to signify whether the element should be
+ given events from the queue
+ """
+ self.__has_draw = has_draw
+ self.__has_event = has_event
+ self.__in_engine = False
+ self.game_engine = GameEngine.instance
+ self.__ddo_list = []
+
+ def is_in_engine(self):
+ """
+ Returns true if object has been registered with the game engine.
+ """
+ return self.__in_engine
+
+ def add_to_scene(self, objects):
+ """
+ Adds some objects to the DynamicDrawableObject list and the
+ game engine's scene.
+
+ @param objects: A list of DynamicDrawableObjects
+ """
+
+ self.game_engine.get_scene().addObjects(objects)
+ self.__ddo_list += objects
+
+ def add_to_engine(self):
+ """
+ Registers the object with the game engine. Registers draw and event
+ call backs separately if they were set to true in the constructor.
+ """
+ if not self.__in_engine:
+ self.__in_engine = True
+
+ if self.__has_draw:
+ self.game_engine.add_draw_callback(self.draw)
+
+ if self.__has_event:
+ self.game_engine.add_event_callback(self.event_handler)
+
+ def remove_from_engine(self):
+ """
+ Removes the object from the correct queues in the engine
+ """
+ if self.__in_engine:
+ self.__in_engine = False
+
+ if self.__has_draw:
+ self.game_engine.remove_draw_callback(self.draw)
+
+ if self.__has_event:
+ self.game_engine.remove_event_callback(self.event_handler)
+
+ if not (self.__ddo_list == []):
+ for object in self.__ddo_list:
+ self.game_engine.get_scene().removeObject(object)
+
+
+ def event_handler(self, event):
+ """
+ This method should be overridden by the user-specified class that
+ extends this GameEngineElement class. This method specifies how that
+ class will handle events given to it by the engine.
+
+ @return: true if the user wants to prevent the event from
+ continuing down the queue
+ """
+ pass
+
+ def draw(self, screen):
+ """
+ This method should be overridden by the user-specified class that
+ extends this GameEngineElement class. This method specifies how the
+ class will be drawn.
+ """
+ pass
diff --git a/MAFH2/fortuneengine/fortuneengine/GameInspect.py b/MAFH2/fortuneengine/fortuneengine/GameInspect.py
new file mode 100644
index 0000000..ea69540
--- /dev/null
+++ b/MAFH2/fortuneengine/fortuneengine/GameInspect.py
@@ -0,0 +1,231 @@
+# FortuneEngine 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.
+#
+# FortuneEngine 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 the FortuneEngine. If not, see <http://www.gnu.org/licenses/>.
+#
+# Author: Justin Lewis <jlew.blackout@gmail.com>
+
+import inspect
+
+
+class GameInspect(object):
+ """
+ GameInspect is a class that can inspect and modify object trees.
+
+ The top most object must be a dictionary
+ """
+
+ def __init__(self, object_root):
+ """
+ Init function of the GameInspect class.
+
+ @param object_root: The root dictionary of the tree
+ """
+ self.root = object_root
+
+ def drilldown_object(self, objectname):
+ """
+ Takes the objectname string and tries to find the object that it is
+ representing and returns that object.
+
+ Example: battle.enemy_list[1].sprite._images[1]
+
+ @param objectname: The string that represents the object's path.
+ @return: Returns the object requested
+ @raise Exception: Throws an Exception with the string being the
+ path error.
+ """
+ last = "empt"
+ obj = "empt"
+ last_token = ""
+
+ # Objects are separated by the period (".") symbol
+ object_tokens = objectname.split(".")
+
+ # Check if the first part of the name is registered with the
+ # game engine as that is our starting point
+ try:
+ obj = self.root[object_tokens[0]]
+ last = obj
+ last_token = object_tokens[0]
+
+ except KeyError:
+ raise Exception("%s is not registered with the game engine" %
+ object_tokens[0])
+
+ # Handles dot notation for sub modules by looping through the tokens
+ for token in object_tokens[1:]:
+
+ # Splits the dictionary/list token ("[")
+ dict_token = token.split('[')
+ try:
+ last = obj
+ obj = getattr(obj, dict_token[0])
+ last_token = dict_token[0]
+
+ except:
+ raise Exception("Error finding member element: %s" % token)
+
+ # Handles dictionaries
+ for d_token in dict_token[1:]:
+ if d_token[-1] == "]":
+ d_token = d_token[:-1]
+ # Try list notation first then try dictionary notation
+ try:
+ key = int(d_token)
+ except:
+ key = d_token
+
+ try:
+ last = obj
+ obj = obj[key]
+ last_token = key
+ except:
+ raise Exception("Unable to find %s" % key)
+
+ else:
+ raise Exception("Invalid Syntax, expected ] at end of %s" %
+ d_token)
+
+ return obj, last, last_token
+
+ def set_eval(self, objectname, statement):
+ """
+ Sets the object referenced by objectname to a value returned by
+ passing the string stored in the val parameter to an eval statement.
+
+ @param objectname: A string representation of the location
+ of the object being inspected in relation
+ to the game engine registered object.
+ @param val: A string to be evaluated and set to the object.
+ """
+ try:
+ obj, last, last_token = self.drilldown_object(objectname)
+ except Exception, detail:
+ return str(detail)
+
+ try:
+ setattr(last, last_token, eval(str(statement)))
+ except Exception, detail:
+ return str(detail)
+
+ def set_str(self, objectname, val):
+ """
+ Sets the object referenced by objectname to a string passed into the
+ val parameter.
+
+ @param objectname: A string representation of the location
+ of the object being inspected in relation
+ to the game engine registered object.
+ @param val: A string to be set as the value of the object.
+ """
+ try:
+ obj, last, last_token = self.drilldown_object(objectname)
+ except Exception, detail:
+ return str(detail)
+
+ setattr(last, last_token, val)
+
+ def set_int(self, objectname, val):
+ """
+ Sets the object referenced by objectname to an integer passed into the
+ val parameter. It may be a string that holds the int as it will be
+ type casted.
+
+ @param objectname: A string representation of the location
+ of the object being inspected in relation
+ to the game engine registered object.
+ @param val: An int/string containing an int to be set as
+ the value of the object.
+ """
+ try:
+ obj, last, last_token = self.drilldown_object(objectname)
+ except Exception, detail:
+ return str(detail)
+
+ try:
+ setattr(last, last_token, int(val))
+ except:
+ return str(detail)
+
+ def inspect_object(self, objectname):
+ """
+ Displays information about the object path it is passed
+
+ @param objectname: A string representation of the location
+ of the object being inspected in relation
+ to the game engine registered object.
+ """
+ try:
+ obj, last, last_token = self.drilldown_object(objectname)
+
+ except Exception, detail:
+ return str(detail)
+
+ classname = obj.__class__.__name__
+
+ # If it has the __dict__ attribute, it is an object we can inspect
+ if hasattr(obj, "__dict__"):
+ attribute_list = "Attributes:"
+ attributes = obj.__dict__
+ for attribute_key in attributes.keys():
+ attribute_list = "%s\n\t%s:%s" % (attribute_list,
+ attribute_key, str(attributes[attribute_key]))
+
+ # Inspect the object for all its methods
+ method_list = inspect.getmembers(obj, inspect.ismethod)
+ if method_list != []:
+
+ # Loop through the methods in the object and print them
+ # to the console
+ attribute_list = "%s\n\nMethods:" % attribute_list
+ for method in method_list:
+ attribute_list = "%s\n\t%s" % (attribute_list, method[0])
+
+ # Inspect the arguments to the current method
+ args, vargs, kwargs, local = inspect.getargspec(method[1])
+
+ # Display function arguments
+ attribute_list = "%s\n\t\tArgs: %s" % \
+ (attribute_list, ",".join(args))
+
+ # Display * and ** arguments if they were found
+ if vargs:
+ attribute_list = "%s\n\t\tVArgs: %s" % \
+ (attribute_list, ",".join(vargs))
+
+ # Display KW Arguments if they were found
+ if kwargs:
+ attribute_list = "%s\n\t\tKWArgs: %s" % \
+ (attribute_list, ",".join(kwargs))
+
+ # If dictionary, show keys
+ elif hasattr(obj, "keys"):
+ attribute_list = "Dictionary Items:"
+
+ for d_obj in obj.keys():
+ attribute_list = "%s\n\t%s:%s" % (attribute_list, d_obj,
+ str(obj[d_obj]))
+
+ # If list, iterate over the list and show its values
+ elif type(obj).__name__ == 'list':
+ i = 0
+ attribute_list = "List Items:"
+ for item in obj:
+ attribute_list = "%s\n\t%d:%s" % (attribute_list, i, str(item))
+ i = i + 1
+
+ # We don't know what it is, so just display string representation
+ # of the object in question
+ else:
+ attribute_list = str(obj)
+
+ return "Class: %s\n%s" % (classname, attribute_list)
diff --git a/MAFH2/fortuneengine/fortuneengine/Scene.py b/MAFH2/fortuneengine/fortuneengine/Scene.py
new file mode 100644
index 0000000..d2e50a9
--- /dev/null
+++ b/MAFH2/fortuneengine/fortuneengine/Scene.py
@@ -0,0 +1,186 @@
+import pygame
+from pygame.sprite import RenderUpdates
+
+class Scene(pygame.sprite.RenderUpdates):
+
+ def __init__(self, sprites):
+
+ self._spritelist = [[sprites, sprites.getXPos(), sprites.getYPos()]]
+ #self._spritelist.append([sprites, sprites.getXPos(), sprites.getYPos()])
+ RenderUpdates.__init__(self, sprites)
+
+ self.xPos = 0
+ self.yPos = 0
+ self.xSize = 0
+ self.ySize = 0
+
+ self.calcPosition()
+ self.calcSize()
+ self.setRelativePositions()
+
+ def calcPosition(self):
+
+ lowestX = 9000
+ lowestY = 9000
+
+ for i in range(len(self._spritelist)):
+ if self._spritelist[i][0].getXPos() < lowestX: lowestX = self._spritelist[i][0].getXPos()
+ if self._spritelist[i][0].getYPos() < lowestY: lowestY = self._spritelist[i][0].getYPos()
+
+ self.xPos = lowestX
+ self.yPos = lowestY
+
+ def calcSize(self):
+
+ highestX = 0
+ highestY = 0
+
+ for i in range(len(self._spritelist)):
+ if (self._spritelist[i][0].getXPos() + self._spritelist[i][0].getXSize()) > highestX: highestX = self._spritelist[i][0].getXPos() + self._spritelist[i][0].getXSize()
+ if (self._spritelist[i][0].getYPos() + self._spritelist[i][0].getYSize()) > highestY: highestY = self._spritelist[i][0].getYPos() + self._spritelist[i][0].getYSize()
+
+ self.xSize = highestX - self.xPos
+ self.ySize = highestY - self.yPos
+
+ def addObject(self, newDrawableObject):
+ RenderUpdates.add_internal(self, newDrawableObject)
+ self._spritelist.insert(len(self._spritelist) - 1, [newDrawableObject, newDrawableObject.getXPos(), newDrawableObject.getYPos()])
+
+ def addObjects(self, newDrawableObjects):
+ for sprite in newDrawableObjects:
+ RenderUpdates.add_internal(self, sprite)
+ self._spritelist.insert(len(self._spritelist) - 1, [sprite, sprite.getXPos(), sprite.getYPos()])
+
+ def setRelativePositions(self):
+
+ for i in range(len(self._spritelist)):
+ self._spritelist[i][1] = self._spritelist[i][0].getXPos() - self.xPos
+ self._spritelist[i][2] = self._spritelist[i][0].getYPos() - self.yPos
+
+ def removeObject(self, sprite):
+
+ for i in self._spritelist:
+ if i[0] == sprite:
+ self._spritelist.remove(i)
+ break
+ RenderUpdates.remove_internal(self, sprite)
+
+ def getObject(self, index):
+
+ if index < len(self._spritelist):
+ return self._spritelist[index][0]
+
+ def getListSize(self):
+
+ return len(self._spritelist)
+
+ def getList(self):
+
+ return list(self._spritelist)
+
+ def moveObjects(self):
+
+ for i in range(len(self._spritelist)):
+ self._spritelist[i][0].move()
+
+ self.calcPosition()
+ self.calcSize()
+ self.setRelativePositions()
+
+ def moveScene(self, xNudge = 0, yNudge = 0):
+
+
+ for i in range(len(self._spritelist)):
+
+ self._spritelist[i][0].nudge(xNudge, yNudge)
+
+
+ self.calcPosition()
+
+ def setPosition(self, newXPos = None, newYPos = None):
+
+ if newXPos != None: self.xPos = newXPos
+ if newYPos != None: self.yPos = newYPos
+
+ for i in range(len(self._spritelist)):
+
+ self._spritelist[i][0].setPosition(self.xPos + self._spritelist[i][1], self.yPos + self._spritelist[i][2])
+
+ def getXPos(self):
+ return self.xPos
+
+ def getYPos(self):
+ return self.yPos
+
+ def getXSize(self):
+ return self.xSize
+
+ def getYSize(self):
+ return self.ySize
+
+ def scaleObjects(self, newXSize = None, newYSize = None):
+
+
+ for i in range(len(self._spritelist)):
+ self._spritelist[i][0].scale(newXSize, newYSize)
+
+ def scaleScene(self, newXSize = None, newYSize = None):
+
+ self.calcPosition()
+ self.calcSize()
+
+ xScale = 1
+ yScale = 1
+
+ if newXSize != None: xScale = (newXSize * 1.0)/self.xSize
+ if newYSize != None: yScale = (newYSize * 1.0)/self.ySize
+
+ for i in range(len(self._spritelist)):
+ self._spritelist[i][0].scale(xScale * self._spritelist[iaw][0].getXSize(), yScale * self._spritelist[i][0].getYSize())
+ self._spritelist[i][1] = xScale * self._spritelist[i][1]
+ self._spritelist[i][2] = yScale * self._spritelist[i][2]
+
+ self.calcPosition()
+ self.calcSize()
+ self.setPosition()
+
+ def update(self, t):
+
+ for s in self._spritelist: s[0].update(t);
+
+ def draw(self, surface):
+ spritedict = self.spritedict
+ surface_blit = surface.blit
+ dirty = self.lostsprites
+ self.lostsprites = []
+ dirty_append = dirty.append
+ for s in self._spritelist:
+ r = spritedict[s[0]]
+ newrect = surface_blit(s[0].image, s[0].rect)
+ if r is 0:
+ dirty_append(newrect)
+ else:
+ if newrect.colliderect(r):
+ dirty_append(newrect.union(r))
+ else:
+ dirty_append(newrect)
+ dirty_append(r)
+ spritedict[s[0]] = newrect
+ return dirty
+
+ def drawEntireScene(self, surface):
+ spritedict = self.spritedict
+ surface_blit = surface.blit
+ dirty = self.lostsprites
+ self.lostsprites = []
+ dirty_append = dirty.append
+ for s in self._spritelist:
+ dirty_append(spritedict[s[0]])
+ dirty_append(surface_blit(s[0].image, s[0].rect))
+ return dirty
+
+ def nextFrame(self):
+
+ for i in range(len(self._spritelist)):
+
+ self._spritelist[i][0].nextFrame()
diff --git a/MAFH2/fortuneengine/fortuneengine/__init__.py b/MAFH2/fortuneengine/fortuneengine/__init__.py
new file mode 100644
index 0000000..211298b
--- /dev/null
+++ b/MAFH2/fortuneengine/fortuneengine/__init__.py
@@ -0,0 +1,14 @@
+# FortuneEngine 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.
+#
+# FortuneEngine 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 the FortuneEngine. If not, see <http://www.gnu.org/licenses/>.
+#
+# Author: Justin Lewis <jlew.blackout@gmail.com>
diff --git a/MAFH2/fortuneengine/fortuneengine/pyconsole/__init__.py b/MAFH2/fortuneengine/fortuneengine/pyconsole/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MAFH2/fortuneengine/fortuneengine/pyconsole/__init__.py
diff --git a/MAFH2/fortuneengine/fortuneengine/pyconsole/fonts/default.ttf b/MAFH2/fortuneengine/fortuneengine/pyconsole/fonts/default.ttf
new file mode 100755
index 0000000..139f0b4
--- /dev/null
+++ b/MAFH2/fortuneengine/fortuneengine/pyconsole/fonts/default.ttf
Binary files differ
diff --git a/MAFH2/fortuneengine/fortuneengine/pyconsole/pyconsole.cfg b/MAFH2/fortuneengine/fortuneengine/pyconsole/pyconsole.cfg
new file mode 100644
index 0000000..a1f8c0f
--- /dev/null
+++ b/MAFH2/fortuneengine/fortuneengine/pyconsole/pyconsole.cfg
@@ -0,0 +1,33 @@
+# # # # # # # # # # # # # # # # # # # # # # # # # # #
+# Available variables:
+#
+# bg_alpha - background alpha value, range 0 to 255 (Pretty, but a big performance hit)
+# bg_color - Background Color, RGB format
+# txt_color_i - Text Color (Input). Color of the input line, RGB format
+# txt_color_o - Text Color (Output). Color of the output lines, RGB format
+# ps1/ps2/ps3 - strings that are prefixed to each input line, like their POSIX counterparts
+# active - Whether or not the console is initially displayed
+# repeat_rate - value to pass to pygame.key.set_repeat
+# preserve_events - determines whether or not the console puts unused events back on the event queue
+# python_mode - Send commands to the python interpreter instead of the pyconsole interpreter
+# # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+bg_alpha 255
+bg_color [0x0,0x44,0xAA]
+
+txt_color_i [0xFF,0xFF,0xFF]
+txt_color_o [0xEE,0xEE,0xEE]
+
+ps1 "] "
+ps2 ">>> "
+ps3 "... "
+
+active False
+
+repeat_rate [500,30]
+
+preserve_events True
+
+python_mode False
+
+motd ["|Fortune Engine || PyConsole 0.7|","Type help for a list of commands","Ctrl_w toggle the console","Ctrl_m toggle python mode"]
diff --git a/MAFH2/fortuneengine/fortuneengine/pyconsole/pyconsole.py b/MAFH2/fortuneengine/fortuneengine/pyconsole/pyconsole.py
new file mode 100644
index 0000000..7bb68e4
--- /dev/null
+++ b/MAFH2/fortuneengine/fortuneengine/pyconsole/pyconsole.py
@@ -0,0 +1,637 @@
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# pyconsole - a simple console for pygame based applications
+#
+# Copyright (C) 2006 John Schanck
+#
+# This program 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 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+
+import pygame, os, sys
+from pygame.locals import *
+
+import re # Python's Regexp library. Used in pyconsole for parsing
+import textwrap # Used for proper word wrapping
+from string import ascii_letters
+from code import InteractiveConsole # Gives us access to the python interpreter
+
+
+__version__ = "0.7"
+
+WIDTH=0
+HEIGHT=1
+
+OUT = 0
+IN = 1
+ERR = 2
+
+PYCONSOLE = 1
+PYTHON = 2
+
+path = os.path.abspath(os.path.dirname(__file__))
+font_path = os.path.join(path, "fonts")
+cfg_path = os.path.join(path, "pyconsole.cfg")
+
+
+re_token = re.compile(r"""[\"].*?[\"]|[\{].*?[\}]|[\(].*?[\)]|[\[].*?[\]]|\S+""")
+re_is_list = re.compile(r'^[{\[(]')
+re_is_number = re.compile(r"""
+ (?x)
+ [-]?[0][x][0-9a-fA-F]+[lLjJ]? | # Hexadecimal
+ [-]?[0][0-7]+[lLjJ]? | # Octal
+ [-]?[\d]+(?:[.][\d]*)?[lLjJ]? # Decimal (Int or float)
+ """)
+re_is_assign = re.compile(r'[$](?P<name>[a-zA-Z_]+\S*)\s*[=]\s*(?P<value>.+)')
+re_is_comment = re.compile(r'\s*#.*')
+re_is_var = re.compile(r'^[$][a-zA-Z_]+\w*\Z')
+
+
+
+class Writable(list):
+ line_pointer = -1
+ def write(self, line):
+ self.append(str(line))
+ def reset(self):
+ self.__init__()
+ def readline(self, size=-1):
+ # Python's interactive help likes to try and call this, which causes the program to crash
+ # I see no reason to implement interactive help.
+ raise NotImplementedError
+
+class ParseError(Exception):
+ def __init__(self, token):
+ self.token = token
+ def at_token(self):
+ return self.token
+
+def balanced(t):
+ stack = []
+ pairs = {"\'":"\'", '\"':'\"', "{":"}", "[":"]", "(":")"}
+ for char in t:
+ if stack and char == pairs[stack[-1]]:
+ stack.pop()
+ elif char in pairs:
+ stack.append(char)
+ return not bool(stack)
+
+class Console:
+ def __init__(self, screen, rect, functions={}, key_calls={}, vars={}, syntax={}):
+ if not pygame.display.get_init():
+ raise pygame.error, "Display not initialized. Initialize the display before creating a Console"
+
+ if not pygame.font.get_init():
+ pygame.font.init()
+
+ self.parent_screen = screen
+ self.rect = pygame.Rect(rect)
+ self.size = self.rect.size
+
+ self.user_vars = vars
+ self.user_syntax = syntax
+ self.user_namespace = {}
+
+ self.variables = {\
+ "bg_alpha":int,\
+ "bg_color": list,\
+ "txt_color_i": list,\
+ "txt_color_o": list,\
+ "ps1": str,\
+ "ps2": str,\
+ "ps3": str,\
+ "active": bool,\
+ "repeat_rate": list,\
+ "preserve_events":bool,\
+ "python_mode":bool,\
+ "motd":list
+ }
+
+ self.load_cfg()
+
+ self.set_interpreter()
+
+ #pygame.key.set_repeat(*self.repeat_rate)
+
+ self.bg_layer = pygame.Surface(self.size)
+ self.bg_layer.set_alpha(self.bg_alpha)
+
+ self.txt_layer = pygame.Surface(self.size)
+ self.txt_layer.set_colorkey(self.bg_color)
+
+ try:
+ self.font = pygame.font.Font(os.path.join(font_path,"default.ttf"), 14)
+ except IOError:
+ self.font = pygame.font.SysFont("monospace", 14)
+
+ self.font_height = self.font.get_linesize()
+ self.max_lines = (self.size[HEIGHT] / self.font_height) - 1
+
+ self.max_chars = (self.size[WIDTH]/(self.font.size(ascii_letters)[WIDTH]/len(ascii_letters))) - 1
+ self.txt_wrapper = textwrap.TextWrapper()
+
+ self.c_out = self.motd
+ self.c_hist = [""]
+ self.c_hist_pos = 0
+ self.c_in = ""
+ self.c_pos = 0
+ self.c_draw_pos = 0
+ self.c_scroll = 0
+
+
+ self.changed = True
+
+ self.func_calls = {}
+ self.key_calls = {}
+
+ self.add_func_calls({"echo":self.output, "clear": self.clear, "help":self.help})
+ self.add_func_calls(functions)
+
+ self.add_key_calls({"l":self.clear, "c":self.clear_input, "w":self.set_active})
+ self.add_key_calls(key_calls)
+
+
+ ##################
+ #-Initialization-#
+ def load_cfg(self):
+ '''\
+ Loads the config file path/pygame-console.cfg\
+ All variables are initialized to their defaults,\
+ then new values will be loaded from the config file if it exists.
+ '''
+ self.init_default_cfg()
+ if os.path.exists(cfg_path):
+ for line in file(cfg_path):
+ tokens = self.tokenize(line)
+ if re_is_comment.match(line):
+ continue
+ elif len(tokens) != 2:
+ continue
+ self.safe_set_attr(tokens[0],tokens[1])
+
+ def init_default_cfg(self):
+ self.bg_alpha = 255
+ self.bg_color = [0x0,0x0,0x0]
+ self.txt_color_i = [0xFF,0xFF,0xFF]
+ self.txt_color_o = [0xCC,0xCC,0xCC]
+ self.ps1 = "] "
+ self.ps2 = ">>> "
+ self.ps3 = "... "
+ self.active = False
+ self.repeat_rate = [500,30]
+ self.python_mode = False
+ self.preserve_events = False
+ self.motd = ["[PyConsole 0.5]"]
+
+ def safe_set_attr(self, name, value):
+ '''\
+ Safely set the console variables
+ '''
+ if name in self.variables:
+ if isinstance(value, self.variables[name]) or not self.variables[name]:
+ self.__dict__[name] = value
+
+ def add_func_calls(self, functions):
+ '''\
+ Add functions to the func_calls dictionary.
+ Arguments:
+ functions -- dictionary of functions to add.
+ '''
+ if isinstance(functions,dict):
+ self.func_calls.update(functions)
+ self.user_namespace.update(self.func_calls)
+
+ def add_key_calls(self, functions):
+ '''\
+ Add functions to the key_calls dictionary.
+ Arguments:
+ functions -- dictionary of key_calls to add.
+ '''
+ if isinstance(functions,dict):
+ self.key_calls.update(functions)
+
+ def output(self, text):
+ '''\
+ Prepare text to be displayed
+ Arguments:
+ text -- Text to be displayed
+ '''
+ if not str(text):
+ return;
+
+ try:
+ self.changed = True
+ if not isinstance(text,str):
+ text = str(text)
+ text = text.expandtabs()
+ text = text.splitlines()
+ self.txt_wrapper.width = self.max_chars
+ for line in text:
+ for w in self.txt_wrapper.wrap(line):
+ self.c_out.append(w)
+ except:
+ pass
+
+ def set_active(self,b=None):
+ '''\
+ Activate or Deactivate the console
+ Arguments:
+ b -- Optional boolean argument, True=Activate False=Deactivate
+ '''
+ if not b:
+ self.active = not self.active
+ else:
+ self.active = b
+
+
+ def format_input_line(self):
+ '''\
+ Format input line to be displayed
+ '''
+ # The \v here is sort of a hack, it's just a character that isn't recognized by the font engine
+ text = self.c_in[:self.c_pos] + "\v" + self.c_in[self.c_pos+1:]
+ n_max = self.max_chars - len(self.c_ps)
+ vis_range = self.c_draw_pos, self.c_draw_pos + n_max
+ return self.c_ps + text[vis_range[0]:vis_range[1]]
+
+ def draw(self):
+ '''\
+ Draw the console to the parent screen
+ '''
+ if not self.active:
+ return;
+
+ if self.changed:
+ self.changed = False
+ # Draw Output
+ self.txt_layer.fill(self.bg_color)
+ lines = self.c_out[-(self.max_lines+self.c_scroll):len(self.c_out)-self.c_scroll]
+ y_pos = self.size[HEIGHT]-(self.font_height*(len(lines)+1))
+
+ for line in lines:
+ tmp_surf = self.font.render(line, True, self.txt_color_o)
+ self.txt_layer.blit(tmp_surf, (1, y_pos, 0, 0))
+ y_pos += self.font_height
+ # Draw Input
+ tmp_surf = self.font.render(self.format_input_line(), True, self.txt_color_i)
+ self.txt_layer.blit(tmp_surf, (1,self.size[HEIGHT]-self.font_height,0,0))
+ # Clear background and blit text to it
+ self.bg_layer.fill(self.bg_color)
+ self.bg_layer.blit(self.txt_layer,(0,0,0,0))
+
+ # Draw console to parent screen
+ # self.parent_screen.fill(self.txt_color_i, (self.rect.x-1, self.rect.y-1, self.size[WIDTH]+2, self.size[HEIGHT]+2))
+ pygame.draw.rect(self.parent_screen, self.txt_color_i, (self.rect.x-1, self.rect.y-1, self.size[WIDTH]+2, self.size[HEIGHT]+2), 1)
+ self.parent_screen.blit(self.bg_layer,self.rect)
+
+ #######################################################################
+ # Functions to communicate with the console and the python interpreter#
+ def set_interpreter(self):
+ if self.python_mode:
+ self.output("Entering Python mode")
+ self.python_mode = True
+ self.python_interpreter = InteractiveConsole()
+ self.tmp_fds = []
+ self.py_fds = [Writable() for i in range(3)]
+ self.c_ps = self.ps2
+ else:
+ self.output("Entering Pyconsole mode")
+ self.python_mode = False
+ self.c_ps = self.ps1
+
+ def catch_output(self):
+ if not self.tmp_fds:
+ self.tmp_fds = [sys.stdout, sys.stdin, sys.stderr]
+ sys.stdout, sys.stdin, sys.stderr = self.py_fds
+
+ def release_output(self):
+ if self.tmp_fds:
+ sys.stdout, sys.stdin, sys.stderr = self.tmp_fds
+ self.tmp_fds = []
+ [fd.reset() for fd in self.py_fds]
+
+ def submit_input(self, text):
+ '''\
+ Submit input
+ 1) Move input to output
+ 2) Evaluate input
+ 3) Clear input line
+ '''
+
+ self.clear_input()
+ self.output(self.c_ps + text)
+ self.c_scroll = 0
+ if self.python_mode:
+ self.send_python(text)
+ else:
+ self.send_pyconsole(text)
+
+ def send_python(self, text):
+ '''\
+ Sends input the the python interpreter in effect giving the user the ability to do anything python can.
+ '''
+ self.c_ps = self.ps2
+ self.catch_output()
+ if text:
+ self.add_to_history(text)
+ r = self.python_interpreter.push(text)
+ if r:
+ self.c_ps = self.ps3
+ else:
+ code = "".join(self.py_fds[OUT])
+ self.python_interpreter.push("\n")
+ self.python_interpreter.runsource(code)
+ for i in self.py_fds[OUT]+self.py_fds[ERR]:
+ self.output(i)
+ self.release_output()
+
+ def send_pyconsole(self, text):
+ '''\
+ Sends input to pyconsole to be interpreted
+ '''
+ if not text: # Output a blank row if nothing is entered
+ self.output("")
+ return;
+
+ self.add_to_history(text)
+
+ #Determine if the statement is an assignment
+ assign = re_is_assign.match(text)
+ try:
+ #If it is tokenize only the "value" part of $name = value
+ if assign:
+ tokens = self.tokenize(assign.group('value'))
+ else:
+ tokens = self.tokenize(text)
+ except ParseError, e:
+ self.output(r'At Token: "%s"' % e.at_token())
+ return;
+
+ if tokens == None:
+ return
+
+ #Evaluate
+ try:
+ out = None
+ # A variable alone on a line
+ if (len(tokens) is 1) and re_is_var.match(text) and not assign:
+ out = tokens[0]
+ # Statement in the form $name = value
+ elif (len(tokens) is 1) and assign:
+ self.setvar(assign.group('name'), tokens[0])
+ else:
+ # Function
+ out = self.func_calls[tokens[0]](*tokens[1:])
+ # Assignment from function's return value
+ if assign:
+ self.setvar(assign.group('name'), out)
+
+ if not out == None:
+ self.output(out)
+ except (KeyError,TypeError):
+ self.output("Unknown Command: " + str(tokens[0]))
+ self.output(r'Type "help" for a list of commands.')
+
+
+
+ ####################################################
+ #-Functions for sharing variables with the console-#
+ def setvar(self, name, value):
+ '''\
+ Sets the value of a variable
+ '''
+ if self.user_vars.has_key(name) or not self.__dict__.has_key(name):
+ self.user_vars.update({name:value})
+ self.user_namespace.update(self.user_vars)
+ elif self.__dict__.has_key(name):
+ self.__dict__.update({name:value})
+
+ def getvar(self, name):
+ '''\
+ Gets the value of a variable, this is useful for people that want to access console variables from within their game
+ '''
+ if self.user_vars.has_key(name) or not self.__dict__.has_key(name):
+ return self.user_vars[name]
+ elif self.__dict__.has_key(name):
+ return self.__dict__[name]
+
+ def setvars(self, vars):
+ try:
+ self.user_vars.update(vars)
+ self.user_namespace.update(self.user_vars)
+ except TypeError:
+ self.output("setvars requires a dictionary")
+
+ def getvars(self, opt_dict=None):
+ if opt_dict:
+ opt_dict.update(self.user_vars)
+ else:
+ return self.user_vars
+
+
+ def add_to_history(self, text):
+ '''\
+ Add specified text to the history
+ '''
+ self.c_hist.insert(-1,text)
+ self.c_hist_pos = len(self.c_hist)-1
+
+ def clear_input(self):
+ '''\
+ Clear input line and reset cursor position
+ '''
+ self.c_in = ""
+ self.c_pos = 0
+ self.c_draw_pos = 0
+
+ def set_pos(self, newpos):
+ '''\
+ Moves cursor safely
+ '''
+ self.c_pos = newpos
+ if (self.c_pos - self.c_draw_pos) >= (self.max_chars - len(self.c_ps)):
+ self.c_draw_pos = max(0, self.c_pos - (self.max_chars - len(self.c_ps)))
+ elif self.c_draw_pos > self.c_pos:
+ self.c_draw_pos = self.c_pos - (self.max_chars/2)
+ if self.c_draw_pos < 0:
+ self.c_draw_pos = 0
+ self.c_pos = 0
+
+ def str_insert(self, text, strn):
+ '''\
+ Insert characters at the current cursor position
+ '''
+ foo = text[:self.c_pos] + strn + text[self.c_pos:]
+ self.set_pos(self.c_pos + len(strn))
+ return foo
+
+ def process_input(self, event):
+ '''\
+ Loop through pygame events and evaluate them
+ '''
+ if not self.active:
+ return False;
+
+ if event.type == KEYDOWN:
+ self.changed = True
+ ## Special Character Manipulation
+ if event.key == K_TAB:
+ self.c_in = self.str_insert(self.c_in, " ")
+ elif event.key == K_BACKSPACE:
+ if self.c_pos > 0:
+ self.c_in = self.c_in[:self.c_pos-1] + self.c_in[self.c_pos:]
+ self.set_pos(self.c_pos-1)
+ elif event.key == K_DELETE:
+ if self.c_pos < len(self.c_in):
+ self.c_in = self.c_in[:self.c_pos] + self.c_in[self.c_pos+1:]
+ elif event.key == K_RETURN or event.key == 271:
+ self.submit_input(self.c_in)
+ ## Changing Cursor Position
+ elif event.key == K_LEFT:
+ if self.c_pos > 0:
+ self.set_pos(self.c_pos-1)
+ elif event.key == K_RIGHT:
+ if self.c_pos < len(self.c_in):
+ self.set_pos(self.c_pos+1)
+ elif event.key == K_HOME:
+ self.set_pos(0)
+ elif event.key == K_END:
+ self.set_pos(len(self.c_in))
+ ## History Navigation
+ elif event.key == K_UP:
+ if len(self.c_out):
+ if self.c_hist_pos > 0:
+ self.c_hist_pos -= 1
+ self.c_in = self.c_hist[self.c_hist_pos]
+ self.set_pos(len(self.c_in))
+ elif event.key == K_DOWN:
+ if len(self.c_out):
+ if self.c_hist_pos < len(self.c_hist)-1:
+ self.c_hist_pos += 1
+ self.c_in = self.c_hist[self.c_hist_pos]
+ self.set_pos(len(self.c_in))
+ ## Scrolling
+ elif event.key == K_PAGEUP:
+ if self.c_scroll < len(self.c_out)-1:
+ self.c_scroll += 1
+ elif event.key == K_PAGEDOWN:
+ if self.c_scroll > 0:
+ self.c_scroll -= 1
+ ## Normal character printing
+ elif event.key >= 32:
+ mods = pygame.key.get_mods()
+ if mods & KMOD_CTRL:
+ if event.key in range(256) and chr(event.key) in self.key_calls:
+ self.key_calls[chr(event.key)]()
+ else:
+ char = str(event.unicode)
+ self.c_in = self.str_insert(self.c_in, char)
+ return True
+
+ def convert_token(self, tok):
+ '''\
+ Convert a token to its proper type
+ '''
+ tok = tok.strip("$")
+ try:
+ tmp = eval(tok, self.__dict__, self.user_namespace)
+ except SyntaxError, strerror:
+ self.output("SyntaxError: " + str(strerror))
+ raise ParseError, tok
+ except TypeError, strerror:
+ self.output("TypeError: " + str(strerror))
+ raise ParseError, tok
+ except NameError, strerror:
+ self.output("NameError: " + str(strerror))
+ except:
+ self.output("Error:")
+ raise ParseError, tok
+ else:
+ return tmp
+
+ def tokenize(self, s):
+ '''\
+ Tokenize input line, convert tokens to proper types
+ '''
+ if re_is_comment.match(s):
+ return [s]
+
+ for re in self.user_syntax:
+ group = re.match(s)
+ if group:
+ self.user_syntax[re](self, group)
+ return
+
+ tokens = re_token.findall(s)
+ tokens = [i.strip("\"") for i in tokens]
+ cmd = []
+ i = 0
+ while i < len(tokens):
+ t_count = 0
+ val = tokens[i]
+
+ if re_is_number.match(val):
+ cmd.append(self.convert_token(val))
+ elif re_is_var.match(val):
+ cmd.append(self.convert_token(val))
+ elif val == "True":
+ cmd.append(True)
+ elif val == "False":
+ cmd.append(False)
+ elif re_is_list.match(val):
+ while not balanced(val) and (i + t_count) < len(tokens)-1:
+ t_count += 1
+ val += tokens[i+t_count]
+ else:
+ if (i + t_count) < len(tokens):
+ cmd.append(self.convert_token(val))
+ else:
+ raise ParseError, val
+ else:
+ cmd.append(val)
+ i += t_count + 1
+ return cmd
+
+
+ ##########################
+ #-Some Builtin functions-#
+ def clear(self):
+ '''\
+ Clear the Screen
+ '''
+ self.c_out = ["[Screen Cleared]"]
+ self.c_scroll = 0
+
+ def help(self, *args):
+ '''\
+ Output information about functions
+ Arguments:
+ args -- arbitrary argument list of function names
+ |- No Args - A list of available functions will be displayed
+ |- One or more Args - Docstring of each function will be displayed
+ '''
+ if args:
+ items = [(i,self.func_calls[i]) for i in args if i in self.func_calls]
+ for i,v in items:
+ out = i + ": Takes %d arguments. " % (v.func_code.co_argcount - (v.func_code.co_varnames[0] is "self"))
+ doc = v.func_doc
+ if doc:
+ out += textwrap.dedent(doc)
+ tmp_indent = self.txt_wrapper.subsequent_indent
+ self.txt_wrapper.subsequent_indent = " "*(len(i)+2)
+ self.output(out)
+ self.txt_wrapper.subsequent_indent = tmp_indent
+ else:
+ out = "Available commands: " + str(self.func_calls.keys()).strip("[]")
+ self.output(out)
+ self.output(r'Type "help command-name" for more information on that command')