diff options
Diffstat (limited to 'MAFH2/fortuneengine/fortuneengine')
-rw-r--r-- | MAFH2/fortuneengine/fortuneengine/DrawableFontObject.py | 19 | ||||
-rw-r--r-- | MAFH2/fortuneengine/fortuneengine/DrawableObject.py | 157 | ||||
-rw-r--r-- | MAFH2/fortuneengine/fortuneengine/DynamicDrawableObject.py | 74 | ||||
-rw-r--r-- | MAFH2/fortuneengine/fortuneengine/GameEngine.py | 502 | ||||
-rw-r--r-- | MAFH2/fortuneengine/fortuneengine/GameEngineConsole.py | 77 | ||||
-rw-r--r-- | MAFH2/fortuneengine/fortuneengine/GameEngineElement.py | 109 | ||||
-rw-r--r-- | MAFH2/fortuneengine/fortuneengine/GameInspect.py | 231 | ||||
-rw-r--r-- | MAFH2/fortuneengine/fortuneengine/Scene.py | 186 | ||||
-rw-r--r-- | MAFH2/fortuneengine/fortuneengine/__init__.py | 14 | ||||
-rw-r--r-- | MAFH2/fortuneengine/fortuneengine/pyconsole/__init__.py | 0 | ||||
-rwxr-xr-x | MAFH2/fortuneengine/fortuneengine/pyconsole/fonts/default.ttf | bin | 0 -> 49224 bytes | |||
-rw-r--r-- | MAFH2/fortuneengine/fortuneengine/pyconsole/pyconsole.cfg | 33 | ||||
-rw-r--r-- | MAFH2/fortuneengine/fortuneengine/pyconsole/pyconsole.py | 637 |
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 Binary files differnew file mode 100755 index 0000000..139f0b4 --- /dev/null +++ b/MAFH2/fortuneengine/fortuneengine/pyconsole/fonts/default.ttf 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') |