Commits

schlangen committed ca92fe3

- implemented gun params
- fixed game constants

Comments (0)

Files changed (10)

data/sounds/photo.wav

Binary file modified.

gallox/editor/editor.py

 # coding: utf-8
 
-import elementtree.ElementTree as ET
+import xml.etree.ElementTree as ET
 from xml.dom.minidom import parseString
 
 import pygame

gallox/editor/widgets.py

 import os
 import math
 import weakref
-from elementtree.ElementTree import SubElement
+from xml.etree.ElementTree import SubElement
 
 import pygame
 
         if self.init:
             self.build_dir_info()
             
-            
-    
-DEFAULT_ATTRS = {"fix_no":False, "turnable":False, "resizable":False}
-SPRITES = {
-        "plattform":{"resizable":True},
-        "gun":{"turnable":True},
-        "ship":{"fix_no":True, "turnable":True},
-           }
-           
-           
-def mybool(s):
-    """
-    convert a string to a boolean and also consider "False" as False
-    
-    this is neccessary to save booleans with str(boolean) explicitly
-    as "True" and "False". Otherwise False would have to be saved as
-    "" what is too implicit.    
-    """
-    return s not in ("", "False")
-
-# parameter types
-BOOL = 1
-INT = 2
-FLOAT = 3
-# list of functions to convert a value from a string back to its original type
-CONVERT_FUNCTIONS = [None, mybool, int, float]
-           
-"""
-General information about the parameters a sprite of a certain type
-can have, structured as {type : [list of params]} dict where one 
-parameter consists of 
-
-  - a name, that is used for any internal usage
-  - a repr_name, that is used whenever the name is displayed for the user
-  - a type, that must be one of BOOL, INT, FLOAT
-  - a default value
-  - an args dict, that will be passed to the widget used for this type.
-    
-Two useful values for the arg dict of an int param are, for example, "min_val"
-and "max_val" (used by gui.NumberInput) to control the range of the param value.
-    
-"""
-SPRITE_PARAMS = {
-        "plattform":[
-               { "name": "refuel",
-                 "repr_name": "refuel",
-                 "type": BOOL,
-                 "default": True,
-                 "args": {}
-                },
-                    ],
-        "gun":[
-               {"name": "actionScope",
-                "repr_name": "action scope",
-                "type": INT,
-                "default":60,
-                "args": {"max_val":361,
-                         "min_val":0}
-                },
-                {"name": "autoAim",
-                 "repr_name": "auto aim",
-                 "type": BOOL,
-                 "default": False,
-                 "args": {}
-                },
-                {"name":"rotSpeed",
-                "repr_name":"rotary speed [deg/s]",
-                "type":FLOAT,
-                "default": 4,
-                "args":{"max_val":100,
-                        "min_val":0}
-                },
-                {"name":"shotFreq",
-                "repr_name":"shot frequency [px/s]",
-                "type":FLOAT,
-                "default": 5,
-                "args":{"max_val":100,
-                        "min_val":1}
-                },
-               ],
-        "ship":[],
-           }
 
 #! do not define such values here
 HIGHLIGHT_COLOR = (255,0, 200)
         #self.can_turn = SPRITES[name].get("can_turn", False)
         self.degrees = degrees
         # what a sprite of this type can do 
-        self.attributes = dict(DEFAULT_ATTRS)
+        self.attributes = dict(DEFAULT_SPRITE_ATTRS)
         self.attributes.update(SPRITES[name])
         
         # individual parameters for this single sprite object
                 
         # "menu" or "game" or "editor"
         self.state = "menu"
-#        self.fade_to(self.menu)
+        self.fade_to(self.menu)
 #        self.start_game("testlevel")
-        self.start_editor()
+#        self.start_editor()
         self.photo_sound = load_sound("photo.wav")
         self.c = 0
         
-import elementtree.ElementTree as ET
+import xml.etree.ElementTree as ET
 from xml.dom.minidom import parseString
 
 import pygame
                 size = int(s.get("w")), int(s.get("h"))
                 degrees = int(se.get("degrees", 0))
                 
+                params = {}
+                for param in SPRITE_PARAMS[name]:
+                    #! improve
+                    cf = CONVERT_FUNCTIONS[param["type"]]
+                    val = se.findtext(param["name"])
+                    params[param["name"]] = cf(val) or cf(param["default"])
+                        
                 if name == "plattform":
-                    Plattform(pos, size, self, self.allsprites, self.plattforms)
+                    Plattform(pos, size, params, self, self.allsprites,
+                              self.plattforms)
                 if name == "ship" and not has_ship:
-                    self.ship = Ship(pos, self, self.allsprites)
+                    self.ship = Ship(pos, params, self, self.allsprites)
                     has_ship = True
                 if name == "gun":
-                    Gun(pos, degrees, self, self.allsprites)
+                    Gun(pos, degrees, params, self, self.allsprites)
                 
             if not has_ship:
                 raise Exception, "LevelSyntaxError"

gallox/settings.py

 CAMERA_SIZE = (400,400)
 
 # variables
-TURN_STEP = 0.01
+# px/s
+SHIP_POWER = 35
+# deg/s
+TURN_STEP = 270
 
 
-# total number of minutes with boost on with 100% fuel
-BOOST_DURATION = 0.1
+# total number of minutes with boost on from 100% to 0% fuel
+BOOST_DURATION = 1.0
 # percentage points per minute
 MILEAGE = 100 / BOOST_DURATION
 
 
 # world settings
-AIR_DRAG = 0.1
-
+#AIR_DRAG = 0.1
+# ship will fall x px/s
+GRAVITY = 5.75
 
 # game settings
 FPS = 40
 
 ALLOWED_LANDING_ANGLE = 45
 
+# px/s
+BULLET_SPEED = 400
+GUN_BULLET_SPEED = BULLET_SPEED / 3.
+SHIP_BULLET_SPEED = BULLET_SPEED
+
+
 # paths
 PROJECT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__),".."))
 DATA_PATH = os.path.join(PROJECT_DIR, "data")
 
-GUI_DATA_PATH = os.path.join(DATA_PATH, "gui")
+GUI_DATA_PATH = os.path.join(DATA_PATH, "gui")
+
+
+
+
+# ===== internal ===== #
+
+DEFAULT_SPRITE_ATTRS = {"fix_no":False, "turnable":False, "resizable":False}
+SPRITES = {
+        "plattform":{"resizable":True},
+        "gun":{"turnable":True},
+        "ship":{"fix_no":True, "turnable":True},
+           }
+           
+           
+def mybool(s):
+    """
+    convert a string to a boolean and also consider "False" as False
+    
+    this is neccessary to save booleans with str(boolean) explicitly
+    as "True" and "False". Otherwise False would have to be saved as
+    "" what is too implicit.    
+    """
+    return s not in ("", "False")
+
+# parameter types
+BOOL = 1
+INT = 2
+FLOAT = 3
+# list of functions to convert a value from a string back to its original type
+CONVERT_FUNCTIONS = [None, mybool, int, float]
+           
+"""
+General information about the parameters a sprite of a certain type
+can have, structured as {type : [list of params]} dict where one 
+parameter consists of 
+
+  - a name, that is used for any internal usage
+  - a repr_name, that is used whenever the name is displayed for the user
+  - a type, that must be one of BOOL, INT, FLOAT
+  - a default value
+  - an args dict, that will be passed to the widget used for this type.
+    
+Two useful values for the arg dict of an int param are, for example, "min_val"
+and "max_val" (used by gui.NumberInput) to control the range of the param value.
+    
+"""
+SPRITE_PARAMS = {
+        "plattform":[
+               { "name": "refuel",
+                 "repr_name": "refuel",
+                 "type": BOOL,
+                 "default": True,
+                 "args": {}
+                },
+                    ],
+        "gun":[
+                {"name":"shotFreq",
+                "repr_name":"shot frequency [shots/min]",
+                "type":INT,
+                "default": 5,
+                "args":{"max_val":100,
+                        "min_val":1}
+                },   
+                {"name":"rotSpeed",
+                "repr_name":"rotary speed [deg/s]",
+                "type":FLOAT,
+                "default": 4,
+                "args":{"max_val":100,
+                        "min_val":0}
+                },     
+               {"name": "rotRange",
+                "repr_name": "rotation range",
+                "type": INT,
+                "default":60,
+                "args": {"max_val":361,
+                         "min_val":0}
+                },
+                {"name": "autoAimScope",
+                 "repr_name": "auto aim scope [px]",
+                 "type": INT,
+                 "default": False,
+                 "args": {}
+                },
+               ],
+        "ship":[],
+           }

gallox/sprites/enemies.py

+import math
+
 import pygame
 
 from gallox.utils import load_image
 from gallox.sprites.common import Bullet, SmallExplosion
+from gallox.settings import *
+
 
 class Gun(pygame.sprite.Sprite):
-    # shots per minute
-    frequency = 10
-    def __init__(self, pos, rot, world, *groups):
+    """
+    A gun that shoots with a constant freequency.
+    It can sway in a predefined range with a certain speed.
+    Auto aim can be enabled by defining a distance within 
+    the ship will be aimmed automatically.
+    """
+    def __init__(self, pos, rot, params, map, *groups):
         pygame.sprite.Sprite.__init__(self, *groups)
-        self.world = world
-        self.rot = rot
+        self.auto_aim_scope = params.get("autoAimScope", 0)
+        self.rot_range = params.get("rotRange", 0)
+        self.rot_speed = params.get("rotSpeed", 4)
+        self.shoot_freq = params.get("shotFreq", 5)
+        self.map = map
+        self.rot = self.orig_rot = rot
         self.image, self.rect = load_image("gun",alpha=True)
+        self.orig_image = self.image.copy()
         self.image = pygame.transform.rotozoom(self.image, rot, 1)
         self.rect = self.image.get_rect(topleft=pos)
         self.delta = 0
         
     def update(self, events, ticks):
         if ticks > self.delta:
-            self.delta += 60. / self.frequency * 1000
-            GunBullet(self.rect.center, self.rot, self.world, self.groups())
+            self.delta += 60. / self.shoot_freq * 1000
+            GunBullet(self.rect.center, self.rot, self.map, self.groups())
         self.delta -= ticks
         
+        rotated = False
+        auto_rotated = False
+
+        # if rotation is possible
+        if self.rot_range and self.rot_speed:
+            old_rot = self.rot
+            
+            if self.auto_aim_scope:
+                ship_pos = sx, sy = self.map.ship.rect.center
+                my_x, my_y = self.rect.center
+                dist = math.hypot(sx-my_x, sy - my_y)
+                # test if the ship is near enough
+                if dist < self.auto_aim_scope:
+                    angle = 360-(math.degrees(math.atan2(sy - my_y, sx - my_x))+450)%360
+                    # test if the angle to the ship is allowed
+                    if abs(angle - self.orig_rot) <= self.rot_range:
+                        if angle < self.rot:
+                            self.rot = max(self.rot - abs(self.rot_speed) * ticks / 1000., angle)
+                        else:
+                            self.rot = min(self.rot + abs(self.rot_speed) * ticks / 1000., angle)
+                        if abs(self.rot - self.orig_rot) > self.rot_range:
+                            self.rot = old_rot
+                        auto_rotated = True
+            
+            if not auto_rotated:
+                self.rot += self.rot_speed * ticks / 1000.
+                if abs(self.rot - self.orig_rot) > self.rot_range:
+                    self.rot_speed *= -1
+            # only update image on rotation > 1 degree
+            if int(old_rot) != self.rot:
+                rotated = True
+            
+        if rotated:
+            self.image = pygame.transform.rotozoom(self.orig_image, self.rot, 1)
+            self.rect = self.image.get_rect(center=self.rect.center)
+            
+        
 class GunBullet(Bullet):
-    speed = 0.01
+    speed = GUN_BULLET_SPEED
     def explode(self):
         SmallExplosion(self.rect.center, self.map.allsprites)
          

gallox/sprites/player.py

 from gallox.settings import *
 
 class Ship(pygame.sprite.Sprite):
-    power = 0.001
-    def __init__(self, pos, map, *groups):
+    """
+    The central object in the game that is controlled by the player.
+    It can fly around by turning around and using the boost. It also
+    has a internal gun to shoot holes into the map and create new
+    ways. Its amount of fuel is limited but can be refuelld.
+    """
+    def __init__(self, pos, params, map, *groups):
         pygame.sprite.Sprite.__init__(self, *groups)
-        # for debugging, delete later:
+        # for debugging, delete later !? (or use for multiple lives)
         self._start_pos = pos
-        
+        # reference to the map
         self.map = map
+        # images used here:
+        # self.image: the image used to blit (normal Sprite.image)
+        # self.orig_image: the original of self.image (without rotation)
+        # self.orig_image_normal: the original ship image (no boost)
+        # self.orig_image_boost: the original ship image (with boost)
         self.image, self.rect = load_image("ship", alpha=True)
         self.rect.center = pos
         # keep copy to use for rotations
         self.orig_image_boost, r = load_image("ship_boost", alpha=True)
         self.mask = pygame.mask.from_surface(self.image)
         
+        self.fuel = 100
+        
         # yet unused
         self.munition = 100
-        self.fuel = 100
         self.health = 100
         
         # indicators to replace key.set_repeat
         self.boost_on = False
         self.turn = 0
         
-        # rotation and moving values
+        # rotation and movement values
         self.rot = 0
-#        self.speed = 0
         self.direction = [0, 0]
+        # if docked to a plattform, this will hold a reference
         self.plattform = None
         
     def fire(self):
         ShipBullet(self.rect.center, self.rot, self.map, self.groups())
         
     def update(self, events, ticks):
+        # if docked allow only undocking with boost 
         if self.plattform:
             for event in events:
                 if (event.type == pygame.KEYDOWN and 
                     event.key == CONTROLS["boost"] and self.fuel > 0):
                     self.plattform.undock()
                     self.plattform = None
+            # if still docked, stop updating
+            # otherwise, continue with normal update to process boost
             if self.plattform:
                 return
+        # save old values to detect changes and be able to reset
         rot = self.rot
         old = self.rect.copy()
         # yet another flag
         update = False
         step = TURN_STEP
-        if pygame.key.get_mods() & (pygame.K_RCTRL | pygame.K_LCTRL):
+        if pygame.key.get_mods() & (pygame.KMOD_RCTRL | pygame.KMOD_LCTRL):
             step *= 2
         
         for event in events:
                     
                 
         if self.turn:
-            self.rot += (- self.turn * step) * 1000. / FPS * ticks
+            self.rot += (- self.turn * step) * ticks / 1000.
         if self.boost_on:
-            f = self.power * 1000. / FPS * ticks
+            f = SHIP_POWER * ticks / 1000.
             self.direction[0] += math.cos(math.radians(270-rot)) * f
             self.direction[1] += math.sin(math.radians(270-rot)) * f
             self.fuel -= MILEAGE * (ticks/ (1000. * 60.))
             self.mask = pygame.mask.from_surface(img)
             
             #! ???
-            if self.map.collides(self):
-                self.rot = rot 
-                self.image = pygame.transform.rotate(self.orig_image, self.rot)
-                self.rect = self.image.get_rect(center=self.rect.center)
-                self.mask = pygame.mask.from_surface(self.image)
+#            if self.map.collides(self):
+#                self.rot = rot 
+#                self.image = pygame.transform.rotate(self.orig_image, self.rot)
+#                self.rect = self.image.get_rect(center=self.rect.center)
+#                self.mask = pygame.mask.from_surface(self.image)
         # movement
         
         # gravity
        # air_drag = AIR_DRAG * self.direction[1] * self.direction[1] * ticks
        # print air_drag
      #  print ticks, ((0.001*1000.) / FPS ) * ticks
-        self.direction[1] += ((0.0003*1000.) / FPS ) * ticks
+#        self.direction[1] += ((0.0003*1000.) / FPS ) * ticks
+        self.direction[1] += GRAVITY * ticks / 1000.
         
         # dirty fix to remove the pause at maximum
 #        if self.direction[1] < round(self.direction[1]) == 0:
 #            self.direction = [0, 0]
             print "map collision"
             self.kill()
+        # check for colliding plattforms and try to land/dock
         plattform = pygame.sprite.spritecollideany(self, self.map.plattforms)
         if plattform:
-            print "collide"
+#            print "collide"
             if not (0 <= self.rot <= ALLOWED_LANDING_ANGLE or
                     360 - ALLOWED_LANDING_ANGLE  <= self.rot <= 360):
                 print "bad landing angle:", self.rot, self.rot%360, ALLOWED_LANDING_ANGLE
                 self.kill()
+                return
             if not self.rect.bottom - plattform.rect.top < 5:
                 print "bad landing speed/pos"
                 self.kill()
+                return
             if not plattform.rect.left < self.rect.centerx < plattform.rect.right:
                 print "bad landing position"
                 self.kill()
+                return                
             self.image = self.orig_image_normal
             self.rect = self.image.get_rect(center=self.rect.center)
             self.mask = pygame.mask.from_surface(self.image)
             self.plattform = plattform
             
     def kill(self):
-        print "killed"
+        print "killed",
         self.rot = 0
         self.turn = 0
         self.boost_on = False
         self.plattform = None
-        self.rect.center = self._start_pos
         self.direction = [0,0]
-    #print "killed"
-        
-#            print "b00m!!!  [reset]"
+        self.image = self.orig_image = self.orig_image_normal
+        self.rect = self.image.get_rect(center=self._start_pos)
         
             
 class ShipBullet(Bullet):
+    speed = SHIP_BULLET_SPEED
     def explode(self):
         Explosion(self.rect.center, self.map.allsprites)

gallox/sprites/static.py

 from gallox.utils import load_image, load_sound
 
 class Plattform(pygame.sprite.Sprite):
-    def __init__(self, pos, size, world, *groups):
+    def __init__(self, pos, size, params, map, *groups):
         pygame.sprite.Sprite.__init__(self, *groups)
-        self.world = world
+        self.map = map
         self.image, self.rect = load_image("plattform")
         self.image = pygame.transform.scale(self.image, size)
         self.rect = self.image.get_rect(topleft=pos)

testlevel/level.xml

     <sprite degrees="80" type="gun">
       <position x="292" y="167" />
       <size h="34" w="39" />
-      <rotSpeed>4.0</rotSpeed>
-      <actionScope>20</actionScope>
-      <autoAim>False</autoAim>
-      <shotFreq>1.0</shotFreq>
+      <rotSpeed>60.0</rotSpeed>
+      <autoAimScope>100</autoAimScope>
+      <rotRange>60</rotRange>
+      <shotFreq>20</shotFreq>
     </sprite>
     <sprite degrees="-130" type="ship">
       <position x="239" y="184" />
       <position x="789" y="490" />
       <size h="34" w="39" />
       <rotSpeed>4.0</rotSpeed>
-      <actionScope>60</actionScope>
-      <autoAim>True</autoAim>
-      <shotFreq>-5.0</shotFreq>
+      <autoAimScope>0</autoAimScope>
+      <rotRange>60</rotRange>
+      <shotFreq>5</shotFreq>
     </sprite>
     <sprite type="plattform">
       <position x="818" y="611" />
       <position x="342" y="552" />
       <size h="34" w="39" />
       <rotSpeed>4.0</rotSpeed>
-      <actionScope>60</actionScope>
-      <autoAim>True</autoAim>
-      <shotFreq>-5.0</shotFreq>
+      <autoAimScope>0</autoAimScope>
+      <rotRange>60</rotRange>
+      <shotFreq>5</shotFreq>
     </sprite>
   </sprites>
 </level>