123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860 |
- #!/usr/bin/env python
- #! coding: utf-8
- #! python3
- # Python ish game layer à la GM
- # Dirty as hay, if you learn python with this file, you will learn garbage.
- # 2022-2024
- import random
- import os
- #os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = '1'
- import pygame
- import pygame.freetype
- # Basic message system
- def info( message="!?" ) :
- color = ""
- reset = ""
- if __colorConsole__ :
- reset = colorama.Style.RESET_ALL
- color = colorama.Style.BRIGHT + colorama.Fore.CYAN
- print( f"{color}[info] {message}{reset}" )
- def warning( message="!?" ) :
- color = ""
- reset = ""
- if __colorConsole__ :
- reset = colorama.Style.RESET_ALL
- color = colorama.Style.BRIGHT + colorama.Fore.YELLOW
- print( f"{color}[Warn] {message}{reset}" )
- def error( message="!?" ) :
- color = ""
- reset = ""
- if __colorConsole__ :
- reset = colorama.Style.RESET_ALL
- color = colorama.Style.BRIGHT + colorama.Fore.RED
- print( f"\a{color}[ERR.] {message}{reset}" )
- # Optional libraries
- __colorConsole__ = True
- try :
- import colorama
- colorama.init( autoreset=False )
- info( "color console output" )
- except ImportError or ModuleNotFoundError :
- __colorConsole__ = False
- info( "monochrome console output" )
- _internal_backgrounds = {}
- _internal_sounds = []
- _internal_musics = {}
- _internal_fonts = []
- #===== Computing things =====
- #===== Game play =====
- #===== User interaction =====
- #===== Game graphics =====
- #===== Sound and music =====
- #----- Basic sound functions -----
- # Plays the indicates sound once. If the sound is background music the current background music is stopped.
- def sound_play( index=None ) :
- # if index in _internal_sounds :
- # snd = _internal_sounds[index]
- # if not snd.loaded :
- # pygame.mixer.music.load( snd.filename )
- # snd.loaded = True
- # pygame.mixer.music.play()
- pass
- # Plays the indicates sound, looping continuously. If the sound is background music the current background music is stopped.
- def sound_loop( index=None ) :
- pass
- # Stops the indicates sound. If there are multiple sounds with this index playing simultaneously, all will be stopped.
- def sound_stop( index=None ) :
- pass
- # Stops all sounds.
- def sound_stop_all() :
- for sndIndex in _internal_sounds :
- sound_stop( sndIndex )
-
- # Returns whether (a copy of) the indicated sound is playing.
- def sound_isplaying( index=None ) :
- pass
- # Plays or loops the music. The current music is stopped beforehand.
- def music_play( name=None, loop=False ) :
- global _internal_musics
-
- if music_exists( name ) :
- msk = _internal_musics[name]
-
- pygame.mixer.music.unload()
- if msk["intro"] != None :
- pygame.mixer.music.load( msk["intro"])
- pygame.mixer.music.play()
- pygame.mixer.music.queue( msk["file"], loops={True:-1, False:0}[loop] )
- else :
- pygame.mixer.music.load( msk["file"] )
- pygame.mixer.music.play( {True:-1, False:0}[loop])
-
- # Plays the indicated music, looping continuously.
- # The current music is stopped beforehand.
- # You can use music_play( loop=True ) too
- def music_loop( index=None ) :
- music_play( index, loop=True )
- # Stops the music.
- def music_stop() :
- pygame.mixer.music.stop()
- pygame.mixer.music.unload()
- # Returns whether music is playing.
- def music_isplaying() :
- return pygame.mixer.music.get_busy()
- #----- Special effects -----
- #----- 3D music -----
- #----- CD music -----
- #===== Splash screens, highscores and other pop-ups =====
- #===== Resources =====
- #===== Changing resources =====
- #----- Sprites -----
- #----- Sound -----
- # Adds a sound resource to the game. fname is the name of the sound file.
- # kind indicates the kind of sound (0=normal, 1=background, 2=3d, 3=mmplayer) preload indicates whether the sound should immediately be stored in audio memory (true or false).
- # The function returns the index of the new sound, which can be used to play the sound. (-1 if an error occurred, e.g. the file does not exist).
- def sound_add(fname,kind,preload) :
- pass
- # Same as the previous function but this time a new sound is not created but the existing sound index is replaced, freeing the old sound.
- # Returns whether correct.
- def sound_replace(index,fname,kind,preload) :
- pass
- # Deletes the indicated sound, freeing all memory associated with it.
- # It can no longer be restored.
- def sound_delete(index) :
- pass
- def music_add( name, filename, intro=None ):
- global _internal_musics
-
- ret = None
- if not isinstance( name, str ) :
- error( f"music_delete: name must be a string, got '{name}' instead." )
- else :
- if len( name ) > 0 :
- if not os.path.exists( filename ) :
- warning( f"music_add: Unable to check file '{filename}'." )
- else :
- msk = {"file":filename, "intro":None}
- if intro != None :
- if not os.path.exists( intro ) :
- warning( f"music_add: Unable to check intro file '{intro}'." )
- else :
- msk["intro"] = intro
- _internal_musics[name] = msk
- ret = name
-
- return ret
-
- # Delete the music with the given name
- def music_delete( name ):
- global _internal_musics
-
- ret = None
- if not isinstance( name, str ) :
- error( f"music_delete: name must be a string, got '{name}' instead." )
- else :
- if name in _internal_musics :
- # No data is loaded until playing, so we can simply remove the entry
- del _internal_musics[name]
- else :
- warning( f"music_delete: '{name}' is not a loaded music." )
-
- return ret
- # Checks if a music with this name already exists
- def music_exists( name ):
- global _internal_musics
-
- ret = False
- if not isinstance( name, str ) :
- error( f"music_exists: name must be a string, got '{name}' instead." )
- else :
- if name in _internal_musics :
- ret = True
-
- return ret
- #----- Backgrounds -----
- def background_add( name, filename, removeback=False, smooth=False ) :
- global _internal_backgrounds
-
- ret = None
- if not isinstance( name, str ) :
- error( f"background_add: Unnacceptable name '{name}'." )
- else :
- if len( name ) > 0 :
- if not os.path.exists( filename ) :
- warning( f"background_add: Unable to check file '{filename}'." )
- else :
- bck = {"file":filename, "surface":None}
- bck["surface"] = pygame.image.load( bck["file"] )
- _internal_backgrounds[name] = bck
- ret = name
-
- return ret
- def background_delete( name ):
- global _internal_backgrounds
-
- ret = None
- if not isinstance( name, str ) :
- error( f"background_delete: name must be a string, got '{name}' instead." )
- else :
- if name in _internal_backgrounds :
- del _internal_backgrounds[name]["surface"]
- del _internal_backgrounds[name]
- else :
- print( f"background_delete: '{name}' is not a loaded background" )
-
- return ret
- def background_exists( name ):
- global _internal_backgrounds
-
- ret = False
- if not isinstance( name, str ) :
- error( f"background_delete: name must be a string, got '{name}' instead." )
- else :
- if name in _internal_backgrounds :
- ret = True
-
- return ret
- #----- Fonts -----
- def font_add( name=None, size=16, bold=False, italic=False, first=32, last=128 ) :
- global _internal_fonts
-
- # Size sanity check
- size = int( size )
- if size < 1 : size = 1
-
- # Font file check
- if name != None :
- if not os.path.exists( name ) :
- warning( f"Can't find file '{name}', fall back to default font." )
- name = None
-
- # Load it
- if name != None :
- # font = pygame.freetype.Font( name, size )
- font = pygame.font.Font( name, size )
- else :
- font = pygame.font.SysFont( None, size )
-
- _internal_fonts.append( font )
- return font
- #----- Paths -----
- #----- Scripts -----
- #----- time lines -----
- #----- Objects -----
- #----- Rooms -----
- #===== Files, registry, and executing programs =====
- #===== Data structures =====
- #===== Creating particles =====
- #===== Multiplayer games =====
- #===== Using DLL's =====
- #===== 3D Graphics =====
- #----- Timelines -----
- #----- Objects -----
- #----- Instances -----
- #----- Rooms -----
- #===== General Game Control =====
- game_initialized = False
- game_id = 0
- game_save_id = game_id
- game_display_name = "Great Mishmash"
- game_project_name = "Undefined"
- game_clock = 0
- # Initialize
- def game_init():
- global game_clock
-
- pygame.init()
- pygame.mixer.init()
- game_clock = pygame.time.Clock()
-
- WINDOW_SIZE = (800,600)
- surface_create( WINDOW_SIZE )
- window_set( WINDOW_SIZE )
-
- _draw_text_currentFont = font_add()
-
- game_initialized = True
- #
- def game_end() :
- pygame.quit()
- #
- def game_restart( ) :
- pass
- #
- def game_load( ) :
- pass
- #
- def game_load_buffer( ) :
- pass
- #
- def game_save( ) :
- pass
- #
- def game_save_buffer( ) :
- pass
- #
- def game_get_speed( ) :
- pass
- #
- def game_set_speed( ) :
- pass
- #
- def highscore_add() :
- pass
- #
- def highscore_name() :
- pass
- #
- def highscore_value() :
- pass
- #
- def highscore_clear() :
- pass
- #
- def draw_highscore() :
- pass
- #
- def cursor_sprite() :
- pass
- #===== Movement And Collisions =====
- #===== Drawing =====
- _draw_surface_currentSurface = None
- #----- Drawing shapes -----
- # Clears the current surface in the given color
- def draw_clear( color=(0,0,0) ) :
- global _draw_surface_currentSurface
- _draw_surface_currentSurface.fill( color )
- # Clears the current surface in the given color and alpha value
- def draw_clear_alpha( color=(0,0,0), alpha=0) :
- pass
- # Draws a point at (x,y) in the current color.
- def draw_point( coords=(0,0) ) : pass
- # Draws a line from (x1,y1) to (x2,y2).
- def draw_line( start=(0,0), end=(16,16) ) : pass
- # Draws a line from (x1,y1) to (x2,y2) with width w.
- def draw_line_width( start=(0,0), end=(16,16), width=1 ) : pass
- # Draws a rectangle. outline indicates whether only the outline must be drawn (true) or it should be filled (false).
- def draw_rectangle( start=(0,0), end=(16,16), outline=True ) :
- global _draw_surface_currentSurface
- global _draw_color_currentColor
- global _draw_color_currentAlpha
-
- width = 0
- if outline == True : width = 1
-
- color = _draw_color_currentColor
-
- if (_draw_color_currentAlpha != 1) :
- s = pygame.Surface( (end[0]-start[0], end[1]-start[1]) )
- s.set_alpha( int(_draw_color_currentAlpha*255) )
- s.fill( color )
- _draw_surface_currentSurface.blit( s, (start[0], start[1]) )
- else :
- pygame.draw.rect( _draw_surface_currentSurface, color, (start[0], start[1], end[0]-start[0], end[1]-start[1]), width=width )
-
- #----- Colour And Alpha -----
- c_aqua = (0,255,255)
- c_black = (0,0,0)
- c_blue = (0,0,255)
- c_dkgray = (64,64,64)
- c_fuchsia = (255,0,255)
- c_gray = (128,128,128)
- c_green = (0,128,0)
- c_lime = (0,255,0)
- c_ltgray = (192,192,192)
- c_maroon = (128,0,0)
- c_navy = (0,0,128)
- c_olive = (128,128,0)
- c_orange = (64,160,0)
- c_purple = (128,0,128)
- c_red = (255,0,0)
- c_silver = (192,192,192)
- c_teal = (0,128,128)
- c_white = (255,255,255)
- c_yellow = (255,255,0)
- _draw_color_currentColor = c_black
- _draw_color_currentAlpha = 1
- #
- def colour_get_blue() :
- pass
- #
- def colour_get_green() :
- pass
- #
- def colour_get_red() :
- pass
- #
- def colour_get_hue() :
- pass
- #
- def colour_get_saturation() :
- pass
- #
- def colour_get_value() :
- pass
- #
- def draw_getpixel() :
- pass
- #
- def draw_getpixel_ext() :
- pass
- #
- def draw_get_colour() :
- pass
- #
- def draw_get_alpha() :
- pass
- #
- def make_colour_hsv() :
- pass
- #
- def make_colour_rgb( r=0, g=0, b=0) :
- return (r,g,b)
- #
- def merge_colour() :
- pass
- #
- def draw_set_alpha( alpha=0 ) :
- global _draw_color_currentAlpha
- _draw_color_currentAlpha = alpha
- #
- def draw_set_color( color=(0,0,0) ) :
- global _draw_color_currentColor
- if len(color) == 4 :
- draw_set_alpha( color[3]/255 )
- _draw_color_currentColor = color[:3]
- #Non standard GM
- def sanitize_color( color=None ):
-
- # Default to black
- if color == None : color = 0
-
- # String families (Hex, HTML and stringified lists)
- if isinstance( color, str ) :
- if (color[0]=='(') and (color[-1]==')') and color.count(',') in (2,3) :
- color = [int(c) for c in color[1:-1].split(',')]
- else:
- #TODO: scan for $ or # for hex color, and maybe some html names
- color = (0,0,0)
-
- # RGB integer
- if isinstance( color, int ) :
- make_color_rgb( (color % 256), ((color/256)%256), ((color/65536)%256) )
-
- # multiple values
- if isinstance( color, tuple ) or isinstance( color, list ) :
- if len( color ) not in [3,4] :
- color = (0,0,0)
-
- return color
- #----- GPU Control -----
- #----- Mipmapping -----
- #----- Basic Forms -----
- #----- Sprites And Tiles -----
- def draw_background( background=None, x=0, y=0 ) :
- # width = background_get_width( background )
- # height = background_get_height( background )
- width, height = background_get_size( background )
- draw_background_general( background, 0,0,width,height, x,y, 1,1, 0, c_white,c_white,c_white,c_white, 1 )
-
- def draw_background_general( background=None, left=0,top=0, width=16,height=16, x=0,y=0, xscale=1,yscale=1, rotation=0, c1=c_white,c2=c_white,c3=c_white,c4=c_white, alpha=1) :
- global _internal_backgrounds
-
- if background in _internal_backgrounds :
- surface_get_target().blit( _internal_backgrounds[background]["surface"], (x,y) )
-
- #----- Text -----
- fa_left = "left"
- fa_center = "center"
- fa_right = "right"
- fa_top = "top"
- fa_middle = "middle"
- fa_bottom = "bottom"
- _draw_text_currentFont = None
- _draw_text_hAlign = fa_left
- _draw_text_vAlign = fa_top
- #
- def draw_set_font( font=None ) :
- global _internal_fonts
- global _draw_text_currentFont
-
- if font not in _internal_fonts : font = _internal_fonts[0]
-
- _draw_text_currentFont = font
-
- #
- def draw_set_halign( halign=fa_left ) :
- global _draw_text_hAlign
-
- if halign not in [fa_left, fa_center, fa_right] :
- halign = fa_left
-
- _draw_text_hAlign = halign
-
- #
- def draw_set_valign( valign=fa_top ) :
- global _draw_text_vAlign
-
- if valign not in [fa_top, fa_middle, fa_bottom] :
- valign = fa_top
-
- _draw_text_vAlign = valign
-
- #
- def draw_get_font( ) :
- global _draw_text_currentFont
- return _draw_text_currentFont
- #
- def draw_get_halign( ) :
- global _draw_text_hAlign
- return _draw_text_hAlign
- #
- def draw_get_valign( ) :
- global _draw_text_vAlign
- return _draw_text_vAlign
- #
- def draw_text( coords=(0,0), string="Text" ) :
- draw_text_color( coords, string, _draw_color_currentColor, _draw_color_currentAlpha )
- #
- def draw_text_ext( ) :
- pass
- #
- def draw_text_color( coords=(0,0), string="Text", colors=( c_black, c_black, c_white, c_white), alpha=0.5 ) :
- global _draw_surface_currentSurface
- global _draw_text_currentFont
- global _draw_text_hAlign
- global _draw_text_vAlign
-
- error = None
-
- # Normalization and sanity checks
-
- if _draw_surface_currentSurface == None : error = "No surface"
- if _draw_text_currentFont == None : error = "No Font"
-
- if isinstance( coords, tuple ) or isinstance( coords, list ) :
- x,y = coords
- else :
- error = "Malformed coords"
-
- if not isinstance( string, str ) :
- string = str( string )
-
- if isinstance( colors, tuple ) or isinstance( colors, list ) :
- #TODO find a way to do it cleanly. Use sanitize_color()
- if len( colors ) == 3 :
- c = colors
- colors = (c,c,c,c)
-
-
-
- if error == None :
-
- textImage = _draw_text_currentFont.render( string, False, colors[0])
-
- # Compute coordinates
- if _draw_text_hAlign == fa_center :
- w,h = textImage.get_size()
- x -= w/2
-
- if _draw_text_hAlign == fa_right :
- w,h = textImage.get_size()
- x += w
-
- if _draw_text_vAlign == fa_middle :
- w,h = textImage.get_size()
- y -= h/2
-
- if _draw_text_vAlign == fa_bottom :
- w,h = textImage.get_size()
- y += h
-
- _draw_surface_currentSurface.blit( textImage, (x,y) )
-
- else :
- error( error )
-
- #
- def draw_text_transformed( ) :
- pass
- #
- def draw_text_ext_colour( ) :
- pass
- #
- def draw_text_ext_transformed( ) :
- pass
- #
- def draw_text_transformed_colour( ) :
- pass
- #
- def draw_text_ext_transformed_colour( ) :
- pass
- #----- Primitives And Vertex Formats -----
- #----- Surfaces -----
- _internal_surfaces = []
- #
- def surface_exists() :
- pass
- # Creates a surface of the indicated width and height.
- # Returns the id of the surface, which must be used in all further calls.
- # Note that the surface will not be cleared.
- def surface_create( size=(128,128) ) :
- return surface_create_ext( size, color=None, mode=pygame.SRCALPHA )
- #
- def surface_create_ext( size=(128,128), color=None, mode=pygame.SRCALPHA ) :
- global _internal_surfaces
-
- surface = pygame.Surface( size, mode )
- _internal_surfaces.append( surface )
-
- if color != None :
- surface_set_target( surface )
- draw_clear( color )
- surface_reset_target()
-
- return surface
- #
- def surface_resize() :
- pass
- #
- def surface_set_target( surface=None ) :
- global _internal_surfaces
- global _draw_surface_currentSurface
-
- if surface not in _internal_surfaces : surface = _internal_surfaces[0]
-
- _draw_surface_currentSurface = surface
-
- #
- def surface_get_target() :
- global _draw_surface_currentSurface
-
- return _draw_surface_currentSurface
-
- # Resets the drawing target to the normal screen.
- def surface_reset_target() :
- global _internal_surfaces
- global _draw_surface_currentSurface
-
- _draw_surface_currentSurface = _internal_surfaces[0]
-
- #----- Lighting -----
- #----- Particles -----
- #----- Textures -----
- #----- Shaders -----
- #----- Video Playback -----
- #===== Cameras And Display =====
- def window_set( size=(640,480), flags=pygame.SHOWN ) :
- global _internal_surfaces
- global _draw_surface_currentSurface
-
- pygame.display.quit()
- pygame.display.init()
-
- _internal_surfaces[0] = pygame.display.set_mode( size, flags )
- surface_reset_target()
-
- #===== Ressources =====
- #----- Background -----
- # Returns whether a background with the given index exists.
- def background_exists( index=None ):
- pass
- # Returns the name of the background with the given index.
- def background_get_name( index=None ):
- pass
- # Returns the width of the background with the given index.
- def background_get_width( name=None ):
- global _internal_backgrounds
- pass
- # Returns the height of the background with the given index.
- def background_get_height( name=None ):
- global _internal_backgrounds
- pass
- #
- def background_get_size( name=None ):
- return ( background_get_width( name ), background_get_height( name ) )
- # In certain situations you might want to save the bitmap corresponding the background to a file. For this the following function can be used:
- # Saves the background ind to the file with the name fname. This must be a .png file.
- def background_save( index=None, fname=None ):
- pass
- #===== Game Input =====
- #===== Data Structures =====
- #===== Strings =====
- #===== Maths And Numbers =====
- #===== Physics =====
- #===== Asynchronous Functions =====
- #===== Networking =====
- #===== File Handling =====
- #===== Buffers =====
- #===== Debugging =====
- #===== Garbage Collection =====
- # Miscelaneous
- # Choose one item of a list at random
- def choose( l=None ) :
- r = None
- if l != None :
- r = l[ random.randrange( 0, len(l) ) ]
- return r
- # Initialisation
- if __name__=="__main__":
-
- game_init()
- draw_set_color( (255,255,255) )
-
- bigFont = font_add( None, 64 )
- smlFont = font_add( None, 16 )
-
- running = True
- while running:
-
- # Event handling, gets all event from the event queue
- for event in pygame.event.get():
- if event.type == pygame.QUIT:
- running = False
-
-
- # Draw
- draw_clear()
-
- draw_set_font( bigFont )
- draw_text( (8,8), "GM" )
-
- draw_set_font( smlFont )
- draw_text( (8,48), "is meant to be used as a lib." )
-
- pygame.display.flip()
- game_clock.tick(60)
- pygame.event.poll()
-
|