June 7, 2018♦
from pygame import display
from pygame.font import Font
from pygame.time import get_ticks, wait
from GameChild import GameChild
class Mainloop(GameChild):
def __init__(self, parent):
GameChild.__init__(self, parent)
self.overflow = 0
self.frame_count = 1
self.actual_frame_duration = 0
self.frames_this_second = 0
self.last_framerate_display = 0
self.load_configuration()
self.init_framerate_display()
self.last_ticks = get_ticks()
self.stopping = False
def load_configuration(self):
config = self.get_configuration("display")
self.target_frame_duration = config["frame-duration"]
self.wait_duration = config["wait-duration"]
self.skip_frames = config["skip-frames"]
self.show_framerate = config["show-framerate"]
self.framerate_text_size = config["framerate-text-size"]
self.framerate_text_color = config["framerate-text-color"]
self.framerate_text_background = config["framerate-text-background"]
self.framerate_display_flag = config["framerate-display-flag"]
def init_framerate_display(self):
if self.framerate_display_active():
screen = self.get_screen()
self.last_framerate_count = 0
self.framerate_topright = screen.get_rect().topright
self.display_surface = screen
self.font = Font(None, self.framerate_text_size)
self.font.set_bold(True)
self.render_framerate()
def framerate_display_active(self):
return self.check_command_line(self.framerate_display_flag) or \
self.show_framerate
def render_framerate(self):
text = self.font.render(str(self.last_framerate_count), False,
self.framerate_text_color,
self.framerate_text_background)
rect = text.get_rect()
rect.topright = self.framerate_topright
self.framerate_text = text
self.framerate_text_rect = rect
def run(self):
while not self.stopping:
self.advance_frame()
self.update_frame_duration()
self.update_overflow()
self.stopping = False
def advance_frame(self):
refresh = False
while self.frame_count > 0:
refresh = True
self.parent.frame()
if self.framerate_display_active():
self.update_framerate()
self.frame_count -= 1
if not self.skip_frames:
break
if refresh:
display.update()
def update_frame_duration(self):
last_ticks = self.last_ticks
actual_frame_duration = get_ticks() - last_ticks
last_ticks = get_ticks()
while actual_frame_duration < self.target_frame_duration:
wait(self.wait_duration)
actual_frame_duration += get_ticks() - last_ticks
last_ticks = get_ticks()
self.actual_frame_duration = actual_frame_duration
self.last_ticks = last_ticks
def update_overflow(self):
self.frame_count = 1
target_frame_duration = self.target_frame_duration
overflow = self.overflow
overflow += self.actual_frame_duration - target_frame_duration
while overflow > target_frame_duration:
self.frame_count += 1
overflow -= target_frame_duration
overflow = self.overflow
def update_framerate(self):
count = self.frames_this_second + 1
if get_ticks() - self.last_framerate_display > 1000:
if count != self.last_framerate_count:
self.last_framerate_count = count
self.render_framerate()
self.last_framerate_display = get_ticks()
count = 0
self.display_surface.blit(self.framerate_text, self.framerate_text_rect)
self.frames_this_second = count
def stop(self):
self.stopping = True
from os import makedirs
from os.path import exists, join
from sys import exc_info
from time import strftime
from pygame import image
from GameChild import *
from Input import *
class ScreenGrabber(GameChild):
def __init__(self, game):
GameChild.__init__(self, game)
self.delegate = self.get_delegate()
self.load_configuration()
self.subscribe(self.save_display)
def load_configuration(self):
config = self.get_configuration("screen-captures")
self.save_path = config["path"]
self.file_name_format = config["file-name-format"]
self.file_extension = config["file-extension"]
def save_display(self, event):
if self.delegate.compare(event, "capture-screen"):
directory = self.save_path
try:
if not exists(directory):
makedirs(directory)
name = self.build_name()
path = join(directory, name)
capture = image.save(self.get_screen(), path)
self.print_debug("Saved screen capture to %s" % (path))
except:
self.print_debug("Couldn't save screen capture to %s, %s" %\
(directory, exc_info()[1]))
def build_name(self):
return "{0}.{1}".format(strftime(self.file_name_format),
self.file_extension)
from random import randint
from math import sin, cos, atan2, radians, sqrt
from pygame import Surface, PixelArray, Color
from pygame.mixer import get_num_channels, Channel
from pygame.locals import *
def get_step(start, end, speed):
angle = get_angle(start, end)
return speed * sin(angle), speed * cos(angle)
def get_angle(start, end):
return atan2(end[0] - start[0], end[1] - start[1])
def get_endpoint(start, angle, magnitude):
"""clockwise, 0 is up"""
x0, y0 = start
dx, dy = get_delta(angle, magnitude)
return x0 + dx, y0 + dy
def get_delta(angle, magnitude):
angle = radians(angle)
return sin(angle) * magnitude, -cos(angle) * magnitude
def rotate_2d(point, center, angle, translate_angle=True):
if translate_angle:
angle = radians(angle)
x, y = point
cx, cy = center
return cos(angle) * (x - cx) - sin(angle) * (y - cy) + cx, \
sin(angle) * (x - cx) + cos(angle) * (y - cy) + cy
def get_points_on_circle(center, radius, count, offset=0):
angle_step = 360.0 / count
points = []
current_angle = 0
for _ in xrange(count):
points.append(get_point_on_circle(center, radius,
current_angle + offset))
current_angle += angle_step
return points
def get_point_on_circle(center, radius, angle, translate_angle=True):
if translate_angle:
angle = radians(angle)
return center[0] + sin(angle) * radius, center[1] - cos(angle) * radius
def get_range_steps(start, end, count):
for ii in xrange(count):
yield start + (end - start) * ii / float(count - 1)
def get_distance(p0, p1):
return sqrt((p0[0] - p1[0]) ** 2 + (p0[1] - p1[1]) ** 2)
def place_in_rect(rect, incoming, contain=True, *args):
while True:
incoming.center = randint(0, rect.w), randint(0, rect.h)
if not contain or rect.contains(incoming):
collides = False
for inner in args:
if inner.colliderect(incoming):
collides = True
break
if not collides:
break
# from http://www.realtimerendering.com/resources/GraphicsGems/gemsii/xlines.c
def get_intersection(p0, p1, p2, p3):
x0, y0 = p0
x1, y1 = p1
x2, y2 = p2
x3, y3 = p3
a0 = y1 - y0
b0 = x0 - x1
c0 = x1 * y0 - x0 * y1
r2 = a0 * x2 + b0 * y2 + c0
r3 = a0 * x3 + b0 * y3 + c0
if r2 != 0 and r3 != 0 and r2 * r3 > 0:
return None
a1 = y3 - y2
b1 = x2 - x3
c1 = x3 * y2 - x2 * y3
r0 = a1 * x0 + b1 * y0 + c1
r1 = a1 * x1 + b1 * y1 + c1
if r0 != 0 and r1 != 0 and r0 * r1 > 0:
return None
denominator = a0 * b1 - a1 * b0
if denominator == 0:
return (x0 + x1 + x2 + x3) / 4, (y0 + y1 + y2 + y3) / 4
if denominator < 0:
offset = -denominator / 2
else:
offset = denominator / 2
numerator = b0 * c1 - b1 * c0
x = ((-1, 1)[numerator < 0] * offset + numerator) / denominator
numerator = a1 * c0 - a0 * c1
y = ((-1, 1)[numerator < 0] * offset + numerator) / denominator
return x, y
def collide_line_with_rect(rect, p0, p1):
for line in ((rect.topleft, rect.topright),
(rect.topright, rect.bottomright),
(rect.bottomright, rect.bottomleft),
(rect.bottomleft, rect.topleft)):
if get_intersection(p0, p1, *line):
return True
def render_box(font, text, antialias, color, background=None, border=None,
border_width=1, padding=0):
surface = font.render(text, antialias, color, background)
if padding:
if isinstance(padding, int):
padding = [padding] * 2
padding = [x * 2 for x in padding]
rect = surface.get_rect()
padded_surface = Surface(rect.inflate(padding).size, SRCALPHA)
if background is not None:
padded_surface.fill(background)
rect.center = padded_surface.get_rect().center
padded_surface.blit(surface, rect)
surface = padded_surface
if border is not None:
if isinstance(border_width, int):
border_width = [border_width] * 2
border_width = [x * 2 for x in border_width]
rect = surface.get_rect()
bordered_surface = Surface(rect.inflate(border_width).size)
bordered_surface.fill(border)
rect.center = bordered_surface.get_rect().center
bordered_surface.blit(surface, rect)
surface = bordered_surface
return surface
def get_color_swapped_surface(surface, current, replacement):
swapped = surface.copy()
pixels = PixelArray(swapped)
pixels.replace(current, replacement)
del pixels
return swapped
def get_busy_channel_count():
count = 0
for index in xrange(get_num_channels()):
count += Channel(index).get_busy()
return count
def get_hue_shifted_surface(base, offset):
surface = base.copy()
pixels = PixelArray(surface)
color = Color(0, 0, 0)
for x in xrange(surface.get_width()):
for y in xrange(surface.get_height()):
h, s, l, a = Color(*surface.unmap_rgb(pixels[x][y])).hsla
if a:
color.hsla = (h + offset) % 360, s, l, a
pixels[x][y] = color
del pixels
return surface
def fill_tile(surface, tile):
for x in xrange(0, surface.get_width(), tile.get_width()):
for y in xrange(0, surface.get_height(), tile.get_height()):
surface.blit(tile, (x, y))
def get_shadowed_text(text, font, offset, color, antialias=True, shadow_color=(0, 0, 0),
colorkey=(255, 0, 255)):
foreground = font.render(text, antialias, color)
background = font.render(text, antialias, shadow_color)
alpha = SRCALPHA if antialias else 0
surface = Surface((foreground.get_width() + offset[0],
foreground.get_height() + offset[1]), alpha)
if not antialias:
surface.set_colorkey(colorkey)
surface.fill(colorkey)
surface.blit(background, ((abs(offset[0]) + offset[0]) / 2,
(abs(offset[1]) + offset[1]) / 2))
surface.blit(foreground, ((abs(offset[0]) - offset[0]) / 2,
(abs(offset[1]) - offset[1]) / 2))
return surface
def get_hsla_color(hue, saturation=100, lightness=50, alpha=100):
color = Color(0, 0, 0, 0)
color.hsla = hue, saturation, lightness, alpha
return color