diff --git a/python/samples/worm.py b/python/samples/worm.py index 25a1c6c38..e43a9d921 100644 --- a/python/samples/worm.py +++ b/python/samples/worm.py @@ -1,240 +1,275 @@ #!/usr/bin/python -from PySFML import * +from PySFML import sf import math import random -import sys + +class Menu: + def __init__(self, screen_width, screen_height): + self.selection = 0 + text_color = sf.Color(220, 220, 20, 255) + self.spacing = screen_height/7 + + self.title = sf.String("PyWorm!") + self.title.SetColor(text_color) + self.title.SetPosition(screen_width/2-80., self.spacing) + + levels = ["Very Easy", "Easy", "Medium", "Hard"] + x_align = [-80., -50., -70., -50.] + self.strings = [] + for i in range(0, 4): + string = sf.String(levels[i]) + string.SetColor(text_color) + string.SetPosition(screen_width/2+x_align[i], (2+i)*self.spacing+20) + self.strings.append(string) + + self.rectangle = sf.Shape.Rectangle(0, 0, screen_width, 40, sf.Color(50, 50, 10)) + + def next_frame(self, win): + self.rectangle.SetY(self.spacing*(2 + self.selection)+20) + win.Draw(self.rectangle) + win.Draw(self.title) + win.Draw(self.strings) + + def key_up(self, pressed): + if pressed: + self.selection = (self.selection - 1) % 4 + def key_down(self, pressed): + if pressed: + self.selection = (self.selection + 1) % 4 -def Game(Difficulty): - PartsPerFrame = 1 + Difficulty # Number of drawn base parts each frame - PartsSpacing = 3 # Each worm's base part is separated by PartsSpacing pixels - TurnStep = 0.15 # Turn the worm's head of 0.15 rad +class Apple(sf.Sprite): + def __init__(self): + apple_img = sf.Image() # Apple's image + if not apple_img.LoadFromFile("./data/apple.png"): + print "Could not load data/apple.png" + sf.Sprite.__init__(self, apple_img) + self.SetCenter(apple_img.GetWidth()/2, apple_img.GetHeight()/2) + self.size = apple_img.GetWidth() - PartSize = 6.0 # worm's base part size for collision - PartRealSize = 18.0 # worm's real base part size for drawing - - # Load images - Rond = sf.Image() # Image containing the base part of the worm - if not Rond.LoadFromFile("./data/rond2.png"): - print "Could not load data/rond2.png" - return - WormPart = sf.Sprite(Rond) - WormPart.SetCenter(Rond.GetWidth()/2, Rond.GetHeight()/2) - AppleImg = sf.Image() # Apple's image - if not AppleImg.LoadFromFile("./data/apple.png"): - print "Could not load data/apple.png" - return - Apple = sf.Sprite(AppleImg, 0, 0, 1, 1, 0) # Corresponding sprite - Black = sf.Color(0,0,0,255) - UglyYellow = sf.Color(220, 220, 20, 255) - - Stop = False - - Event = sf.Event() # Our events manager - - Level = 0 - ShrinkValue = 20 - - Border = 30 - ArenaTop = 20 - ArenaBottom = 520 - - RequiredLength = 300 - - ExitLeft = 350 - ExitRight = 450 - ExitImg = sf.Image(ExitRight-ExitLeft, ArenaTop, Black) - Exit = sf.Sprite(ExitImg, ExitLeft, 0, 1, 1, 0) - - Score = 0 - - HeadX, HeadY = 0, 0 - - while not Stop: - - #Initialize a new game - - Level += 1 - - ArenaLeft = ShrinkValue*Level - ArenaRight = 800-ShrinkValue*Level - ArenaImg = sf.Image(ArenaRight-ArenaLeft, ArenaBottom-ArenaTop, Black) - Arena = sf.Sprite(ArenaImg, ArenaLeft, ArenaTop, 1, 1, 0) - - AppleX, AppleY = random.randrange(ArenaLeft+Border, ArenaRight-Border), random.randrange(ArenaTop+Border, ArenaBottom-Border) - Apple.SetX(AppleX - AppleImg.GetWidth()/2) # We move the apple to somewhere else, randomly - Apple.SetY(AppleY - AppleImg.GetHeight()/2) - - Crash = False - Running = True - - LevelStr = sf.String("Level: " + str(Level)) - LevelStr.SetPosition(60., 540.) - LevelStr.SetColor(UglyYellow) - - ScoreStr = sf.String("Score: 0") - ScoreStr.SetPosition(260., 540.) - ScoreStr.SetColor(UglyYellow) - - Length = 1 - TargetedLength = 30 - - Worm = [[ArenaLeft+50., ArenaTop+50.]] - - Angle = 0 - i = 0 - Dir = 0 - - while Running: # Game main loop - while App.GetEvent(Event): # Event Handler - if Event.Type == sf.Event.Closed: - App.Close() - return - if Event.Type == sf.Event.KeyPressed: - if Event.Key.Code == sf.Key.Escape: - Running = False - Stop = True - if Event.Key.Code == sf.Key.Left: - Dir = -1 - if Event.Key.Code == sf.Key.Right: - Dir = 1 - if Crash and Length<=1: - Running = False - if Event.Type == sf.Event.KeyReleased: - if Event.Key.Code == sf.Key.Left and Dir == -1: - Dir = 0 - if Event.Key.Code == sf.Key.Right and Dir == 1: - Dir = 0 - - App.Draw(Arena) + def random_move(self, arena): + self.SetPosition( \ + random.randrange(arena.arena_left+arena.border, arena.arena_right-arena.border), \ + random.randrange(arena.arena_top+arena.border, arena.arena_bottom-arena.border) \ + ) - if not Crash: # Create new parts and check collisions if the worm hasn't crashed yet - for i in range(0, PartsPerFrame): # We create PartsPerFrame Worm's parts - Angle += Dir*TurnStep - HeadX, HeadY = Worm[Length-1][0]+PartsSpacing*math.cos(Angle), Worm[Length-1][1]+PartsSpacing*math.sin(Angle) - if TargetedLength <= RequiredLength: - if math.sqrt ( (AppleX - HeadX)**2 + (AppleY - HeadY)**2 ) < 14 + PartSize/2: # The Worm ate the apple - Score += 1 - TargetedLength += 20 # The worm gets longer - if TargetedLength <= RequiredLength: - AppleX, AppleY = random.randrange(ArenaLeft+Border, ArenaRight-Border), random.randrange(ArenaTop+Border, ArenaBottom-Border) - Apple.SetX(AppleX - AppleImg.GetWidth()/2) # We move the apple to somewhere else, randomly - Apple.SetY(AppleY - AppleImg.GetHeight()/2) - App.Draw(Apple) +class Arena(dict): + shrink_value, border, arena_top = 20, 30, 20 + def __init__(self, window_width, window_height): + self.window_width = window_width + self.arena_bottom, self.exit_left, self.exit_right = window_height-80, window_width/2 - 50, window_width/2 + 50 + self['level_str'] = sf.String() + self['level_str'].SetColor(sf.Color.White) + self['level_str'].SetPosition(60., window_height-60) + self['score_str'] = sf.String() + self['score_str'].SetColor(sf.Color.White) + self['score_str'].SetPosition(260., window_height-60) + self.exit_rect = sf.Shape.Rectangle(self.exit_left, 0, self.exit_right, self.arena_top, sf.Color.Black) + self.reset() + self.update_arena_rect() - if HeadXArenaRight-PartSize/2 or HeadYArenaBottom-PartSize/2: # Crash into a wall - if Length > RequiredLength: - if HeadYExitRight-PartSize/2: - Crash = True - elif HeadY < 0: - Length = 0 - Running = False # Level completed! - else: - Crash = True - elif Running: - Crash = True - if not Crash: - Worm.append([HeadX, HeadY]) - Length += 1 + def update_arena_rect(self): + self['arena_rect'] = sf.Shape.Rectangle(self.arena_left, self.arena_top, self.arena_right, self.arena_bottom, sf.Color.Black) + def reset(self): + self.level, self.score, self.arena_left, self.arena_right = 1, 0, self.shrink_value, self.window_width-self.shrink_value + self.update_arena_rect() + self['level_str'].SetText("Level: 1") + self['score_str'].SetText("Score: 0") - if TargetedLength > RequiredLength: - App.Draw(Exit) + def update_score(self): + self.score += 1 + self['score_str'].SetText("Score: " + str(self.score)) - if Length >= TargetedLength: - Worm[0:TargetedLength] = Worm[Length-TargetedLength:Length] - for i in range(Length, TargetedLength): - del Worm[i] - Worm[TargetedLength:Length] = [] - Length = TargetedLength + def next_level(self): + self.level += 1 + self['level_str'].SetText("Level: " + str(self.level)) + self.arena_left += self.shrink_value + self.arena_right -= self.shrink_value + self.update_arena_rect() + self.score += 4 + self.update_score() - for i in range(0, Length): - WormPart.SetPosition(Worm[i][0], Worm[i][1]) - App.Draw(WormPart) # Draw the part on screen - if i < Length - PartSize/PartsSpacing - 1: - if math.sqrt( (HeadX-Worm[i][0])**2 + (HeadY-Worm[i][1])**2 ) < PartSize and Running: # Check for collision - Crash = True +class Part(sf.Sprite): + def __init__(self, rond, x, y): + sf.Sprite.__init__(self, rond) + self.SetCenter(rond.GetWidth()/2, rond.GetHeight()/2) + self.SetPosition(x, y) - if Crash and Length>0: - TargetedLength -= PartsPerFrame +class Worm(list): + parts_spacing, turn_step, part_size, start_x, start_y, required_length, grow_length = 3, 0.15, 6.0, 50., 50., 300, 20 + def __init__(self, difficulty): + self.parts_per_frame = 1 + difficulty + self.angle = 0 + self.direction = 0 # 0, 1 or -1 according to the key pressed + self.rond = sf.Image() + self.level_completed = False + if not self.rond.LoadFromFile("./data/rond2.png"): + print "Could not load data/rond2.png" - ScoreStr.SetText("Score: " + str(Score)) + def reset(self, arena): + self.targeted_length, self.angle, self.direction = 30, 0, 0 + self[:] = [Part(self.rond, arena.arena_left+self.start_x, arena.arena_top+self.start_y)] - App.Draw(ScoreStr) - App.Draw(LevelStr) - App.Display() # Refresh Screen - App.Clear(BGColor) - - - # End of the game - if Crash: - Level = 0 - Score = 0 + def left(self, pressed): + if pressed: + self.direction = -1 + elif self.direction == -1: + self.direction = 0 + def right(self, pressed): + if pressed: + self.direction = 1 + elif self.direction == 1: + self.direction = 0 + def restart(self, arena): + if self.targeted_length == 0 and not self.level_completed: + arena.reset() + self.reset(arena) + return True else: - Score += 5 # End level bonus + return False - del Worm - del Arena - del ArenaImg + def crash(self): + self.targeted_length = 0 -def Menu(): + def move(self, arena, apple): + head_x, head_y = -1, -1 + if self.is_running(): # Create new parts and check collisions if the worm hasn't crashed yet + for i in range(self.parts_per_frame): # We create PartsPerFrame Worm's parts + self.angle += self.direction*self.turn_step + head_x, head_y = self[-1].GetPosition() + head_x += self.parts_spacing*math.cos(self.angle) + head_y += self.parts_spacing*math.sin(self.angle) + if self.is_running() and self.targeted_length <= self.required_length: # let's check if the worm ate the apple + if math.hypot(apple.GetPosition()[0] - head_x, apple.GetPosition()[1] - head_y) < apple.size/2 + self.part_size/2: # Yes it did + arena.update_score() + self.targeted_length += self.grow_length # The worm gets longer + apple.random_move(arena) + if head_xarena.arena_right-self.part_size/2 or head_yarena.arena_bottom-self.part_size/2: # Crash into a wall + if len(self) > self.required_length: + if head_yarena.exit_right-self.part_size/2: # Crash into the exit walls + self.crash() + elif head_y < 0: + self.level_completed = True + self.targeted_length = 0 + else: + self.crash() + elif self.is_running(): + self.crash() + if self.is_running(): + self.append(Part(self.rond, head_x, head_y)) + if len(self) > self.targeted_length: + if len(self) - self.targeted_length >= self.parts_per_frame: + del self[0:self.parts_per_frame] + else: + del self[0:len(self) - self.targeted_length] - Selection = 0 + if (head_x, head_y) == (-1, -1) and len(self) > 0: + head_x, head_y = self[-1].GetPosition() - TextColor = sf.Color(220, 220, 20, 255) + if len(self) > self.part_size/self.parts_spacing + 1: + for i in range(len(self)): + if i < len(self) - self.part_size/self.parts_spacing - 1: + test_x, test_y = self[i].GetPosition() + if math.hypot(head_x-test_x, head_y-test_y) < self.part_size and self.is_running(): # Check for collision + self.crash() - Running = True - Event = sf.Event() + if len(self) == 0: + if self.level_completed: + self.level_completed = False + arena.next_level() + self.reset(arena) - Title = sf.String("PyWorm!") - Title.SetX(320.) - Title.SetY(50.) - Title.SetColor(TextColor) + def is_running(self): + return (self.targeted_length > 0) and not self.level_completed + def draw_exit(self): + return self.targeted_length > self.required_length or self.level_completed - Levels = ["Very Easy", "Easy", "Medium", "Hard"] - Xs = [320., 350., 330., 350.] - Strings = [0,0,0,0] - for i in range(0, 4): - Strings[i] = sf.String(Levels[i]) - Strings[i].SetColor(TextColor) - Strings[i].SetPosition(Xs[i], 200. + 80*i) +class Game: + def __init__(self, difficulty, window_width, window_height): + self.arena = Arena(window_width, window_height) + self.worm = Worm(difficulty) + self.worm.reset(self.arena) + self.apple = Apple() + self.apple.random_move(self.arena) + self.pause = False - RectangleImg = sf.Image(ScreenWidth, 40, sf.Color(50,50,10,255)) - Rectangle = sf.Sprite(RectangleImg, 0, 350, 1, 1, 0) + def enter(self, pressed): + if pressed: + if not self.worm.restart(self.arena): + self.pause = not self.pause - while App.IsOpened(): # Game main loop - while App.GetEvent(Event): # Event Handler - if Event.Type == sf.Event.Closed: - App.Close() - if Event.Type == sf.Event.KeyPressed: - if Event.Key.Code == sf.Key.Escape: - App.Close() - elif Event.Key.Code == sf.Key.Up: - Selection = (Selection - 1) % 4 - elif Event.Key.Code == sf.Key.Down: - Selection = (Selection + 1) % 4 - elif Event.Key.Code == sf.Key.Return: - Game(Selection) - - Rectangle.SetY(200 + Selection*80) - App.Draw(Rectangle) - App.Draw(Title) - for i in range(0,4): - App.Draw(Strings[i]) - App.Display() - App.Clear(BGColor) - + def next_frame(self, win): + win.Draw(self.arena.values()) + if not self.pause: + self.worm.move(self.arena, self.apple) + if self.worm.draw_exit(): + win.Draw(self.arena.exit_rect) + elif self.worm.is_running(): + win.Draw(self.apple) + win.Draw(self.worm) -# Initialize the window -ScreenWidth, ScreenHeight = 800, 600 -App = sf.RenderWindow(sf.VideoMode(ScreenWidth,ScreenHeight,32), "PyWorm", sf.Style.Close) # Creates the window -BGColor = sf.Color(100,100,0,255) -App.SetFramerateLimit(30) -Menu() +class Main: + # Entry Point + def __init__(self): + # Initialize the window + self.win = sf.RenderWindow(sf.VideoMode(800, 600,32), "PyWorm", sf.Style.Close) # Creates the window + self.win.EnableKeyRepeat(False) + background_color = sf.Color(100, 100, 0, 255) + self.win.SetFramerateLimit(30) + event = sf.Event() + self.keys = {} # keys to watch + self.menu_begin() + + # Boucle principale + while self.win.IsOpened(): + while self.win.GetEvent(event): # Event Handler + if event.Type == sf.Event.Closed: + self.win.Close() + elif event.Type == sf.Event.KeyPressed: + for key in self.keys: + if event.Key.Code == key: + self.keys[key](True) + elif event.Type == sf.Event.KeyReleased: + for key in self.keys: + if event.Key.Code == key: + self.keys[key](False) + self.win.Display() + self.win.Clear(background_color) + self.next_frame(self.win) + + # Menu + def menu_begin(self): + self.menu = Menu(self.win.GetWidth(), self.win.GetHeight()) + self.keys = {sf.Key.Escape:self.close_window, sf.Key.Up:self.menu.key_up, sf.Key.Down:self.menu.key_down, sf.Key.Return:self.menu_end} + self.next_frame = self.menu.next_frame + + def close_window(self, pressed): + if pressed: + self.win.Close() + + def menu_end(self, pressed): + if pressed: + selection = self.menu.selection + del self.menu + self.game_begin(selection) + + # Game + def game_begin(self, selection): + self.game = Game(selection, self.win.GetWidth(), self.win.GetHeight()) + self.keys = {sf.Key.Left:self.game.worm.left, sf.Key.Right:self.game.worm.right, sf.Key.Return:self.game.enter, sf.Key.Escape:self.game_end} + self.next_frame = self.game.next_frame + + def game_end(self, pressed): + if pressed: + del self.game + self.menu_begin() + +Main() diff --git a/python/src/Image.cpp b/python/src/Image.cpp index bd1f57d67..e3ac4d8b7 100644 --- a/python/src/Image.cpp +++ b/python/src/Image.cpp @@ -293,7 +293,8 @@ Copy pixels from another image onto this one. This function does a slow pixel co Source : Source image to copy\n\ DestX : X coordinate of the destination position\n\ DestY : Y coordinate of the destination position\n\ - SourceRect : Sub-rectangle of the source image to copy (empty by default - entire image)"}, + SourceRect : Sub-rectangle of the source image to copy (empty by default - entire image)\n\ + ApplyAlpha : Should the copy take in account the source transparency? (false by default)"}, {"Create", (PyCFunction)PySfImage_Create, METH_VARARGS, "Create(Width=0, Height=0, Color=sf.Color.Black)\n\ Create an empty image.\n\ Width : Image width\n\ @@ -396,17 +397,22 @@ PySfImage_Copy(PySfImage* self, PyObject *args) { PySfIntRect *SourceRect = NULL; PySfImage *Source = NULL; - unsigned int DestX, DestY; - if (! PyArg_ParseTuple(args, "O!II|O!", &PySfImageType, &Source, &DestX, &DestY, &PySfIntRectType, &SourceRect)) + unsigned int DestX, DestY; + PyObject *PyApplyAlpha; + bool ApplyAlpha = false; + if (! PyArg_ParseTuple(args, "O!II|O!O", &PySfImageType, &Source, &DestX, &DestY, &PySfIntRectType, &SourceRect, &PyApplyAlpha)) return NULL; + if (PyObject_IsTrue(PyApplyAlpha)) + ApplyAlpha = true; + if (SourceRect) { PySfIntRectUpdateObj(SourceRect); - self->obj->Copy(*(Source->obj), DestX, DestY, *(SourceRect->obj)); + self->obj->Copy(*(Source->obj), DestX, DestY, *(SourceRect->obj), ApplyAlpha); } else - self->obj->Copy(*(Source->obj), DestX, DestY); + self->obj->Copy(*(Source->obj), DestX, DestY, sf::IntRect(0, 0, 0, 0), ApplyAlpha); Py_RETURN_NONE; } diff --git a/python/src/SoundStream.cpp b/python/src/SoundStream.cpp index 112fea03b..a52b81fc1 100644 --- a/python/src/SoundStream.cpp +++ b/python/src/SoundStream.cpp @@ -38,6 +38,7 @@ bool CustomSoundStream::OnGetData(Chunk& Data) if (PyObject_HasAttrString(SoundStream, "OnGetData")) { PyObject *PyData=NULL; + Data.NbSamples = 0; if ((PyData = PyObject_CallFunction(PyObject_GetAttrString(SoundStream, "OnGetData"), NULL))) { if (PyArg_Parse(PyData, "s#", &(Data.Samples), &(Data.NbSamples))) diff --git a/python/src/SoundStream.hpp b/python/src/SoundStream.hpp index 72ddd355a..8c7b26f4c 100644 --- a/python/src/SoundStream.hpp +++ b/python/src/SoundStream.hpp @@ -25,12 +25,12 @@ #ifndef __PYSOUNDSTREAM_HPP #define __PYSOUNDSTREAM_HPP -#include -#include - #include #include +#include +#include + class CustomSoundStream : public sf::SoundStream { public :