From: <cd...@us...> - 2007-03-05 09:10:31
|
Revision: 120 http://eos-game.svn.sourceforge.net/eos-game/?rev=120&view=rev Author: cduncan Date: 2007-03-05 01:10:28 -0800 (Mon, 05 Mar 2007) Log Message: ----------- - Add draken flak bomb weapon. Fire using left ctrl, detonates on keyup or at a minimum distance. - To balance the weapon, the shrapnel shards actually do more damage the further out they get. This makes the secondary damage useful without making a direct hit a sure kill. - Human player now flies a draken against the Naree. - Sizes of enemy waves increase over time - If all enemies are destroyed, next waves starts after a few seconds Modified Paths: -------------- bay.py game.py netsync.py projectile.py vessel.py vessels/rone/draken Added Paths: ----------- data/flak-bomb.png data/flak-shard.png sounds/flak-bomb.wav Modified: bay.py =================================================================== --- bay.py 2007-03-04 07:49:00 UTC (rev 119) +++ bay.py 2007-03-05 09:10:28 UTC (rev 120) @@ -5,12 +5,13 @@ # Carrier bays # $Id$ +import sys import game import vessel import ai import body import media -from vector import Vector2D, halfcircle +from vector import Vector2D, halfcircle, diagonal class FighterBay(vessel.Weapon): @@ -59,3 +60,33 @@ return distance < self.launch_distance else: return False + + +class BombBay(vessel.Weapon): + + priority = 1 + mass = 5 + + def __init__(self, vessel, bomb_factory, launch_velocity, max_ttl, reload_time): + self.vessel = vessel + module, class_name = bomb_factory.split('.') + self.bomb_factory = getattr(sys.modules[module], class_name) + self.launch_velocity = float(launch_velocity) + self.reload_time = float(reload_time) * 1000 + self.max_ttl = float(max_ttl) * 1000 + self.range = self.launch_velocity * float(max_ttl) + self.last_launch = 0 + + def update_system(self): + if (self.enabled and self.firing + and game.time > self.last_launch + self.reload_time): + bomb = self.bomb_factory(self.vessel, self, + Vector2D.unit(self.vessel.heading) * self.launch_velocity, + self.max_ttl) + self.last_launch = game.time + + @property + def targeted(self): + target = self.vessel.control.target + return (target and abs(self.vessel.bearing(target.sprite)) < diagonal + and self.vessel.position.distance(target.sprite.position) <= self.range) Added: data/flak-bomb.png =================================================================== (Binary files differ) Property changes on: data/flak-bomb.png ___________________________________________________________________ Name: svn:mime-type + image/png Added: data/flak-shard.png =================================================================== (Binary files differ) Property changes on: data/flak-shard.png ___________________________________________________________________ Name: svn:mime-type + image/png Modified: game.py =================================================================== --- game.py 2007-03-04 07:49:00 UTC (rev 119) +++ game.py 2007-03-05 09:10:28 UTC (rev 120) @@ -121,6 +121,8 @@ global universe, collision_space, screen, background global sprites, fps, avg_fps, clock, time, frame_no global ai_interval + enemies = pygame.sprite.Group() + next_wave = 0 while handle_events(): netsync.send_keystate(frame_no, pygame.key.get_pressed()) # send early collision_space.collide(None, check_collision) @@ -133,16 +135,25 @@ time += clock.tick(fps) avg_fps = clock.get_fps() or fps netsync.step(frame_no) # retrieve late, after clock.tick throttle - if (frame_no % int(fps * ai_interval)) == 0: + if time > next_wave: target = netsync.random.choice(players) position = vector.Vector2D.unit(netsync.random.random() * vector.fullcircle) * 1000 x = target.position.x + position.x y = target.position.y + position.y - for i in xrange(1): - ai = AIVessel.load('vessels/rone/draken', target=target) + if netsync.random.random() * frame_no > 1000: + ai = AIVessel.load('vessels/naree/lotus', target=target) ai.position.x, ai.position.y = x, y - for i in xrange(2): - ai = AIVessel.load('vessels/rone/drach', target=target) + enemies.add(ai) + barrier = 200 + while 1: + ai = AIVessel.load('vessels/naree/cress', target=target) ai.position.x, ai.position.y = x, y + enemies.add(ai) + if netsync.random.random() * frame_no < barrier: + break + barrier *= 2 + next_wave = time + ai_interval * 1000 ai_interval = max(ai_interval * .9, 5) + elif not enemies: + next_wave = min(time + 4000, next_wave) frame_no += 1 Modified: netsync.py =================================================================== --- netsync.py 2007-03-04 07:49:00 UTC (rev 119) +++ netsync.py 2007-03-05 09:10:28 UTC (rev 120) @@ -199,3 +199,8 @@ # for the time being it's good enough. Random.seed(self, hash(self._seed) + game.frame_no) return Random.random(self) + + def guass(self, mu, sigma): + Random.seed(self, hash(self._seed) + game.frame_no) + return Random.guass(self, mu, sigma) + Modified: projectile.py =================================================================== --- projectile.py 2007-03-04 07:49:00 UTC (rev 119) +++ projectile.py 2007-03-05 09:10:28 UTC (rev 120) @@ -161,7 +161,7 @@ self.plasma_img = media.RotatedImage('plasma.png') def update_system(self): - if self.firing and game.time - self.last_fire > self.cool_down: + if self.enabled and self.firing and game.time - self.last_fire > self.cool_down: Shot( shooter=self.gunmount, image=self.plasma_img.rotated(self.gunmount.heading), @@ -254,3 +254,49 @@ volume=self.damage_value / self.start_damage, position=self.position) + +class FlakBomb(Shot): + """Flak bomb that explodes into shrapnel for secondary damage in an area + """ + shards = 50 + shard_velocity = 500 + shard_range = 100 + shard_damage = 3.0 + min_time = 0.7 + + def __init__(self, shooter, bomb_bay, velocity, max_ttl): + bomb_img = media.RotatedImage('flak-bomb.png') + Shot.__init__(self, shooter, + image=bomb_img.rotated(shooter.heading), + damage=75, velocity=velocity, range=1000) + self.max_time = game.time + max_ttl + self.min_time = game.time + self.min_time * 1000 + media.play_sound('flak-bomb.wav', position=shooter.position) + self.bomb_bay = bomb_bay + self.detonating = False + + def update(self): + Shot.update(self) + if game.time > self.max_time or self.detonating or ( + not self.bomb_bay.firing and game.time > self.min_time): + self.detonate() + self.kill() + + def collide(self, other, contacts): + if self.alive(): + self.detonating = True + + def detonate(self): + # denonate into shrapnel + media.play_sound('explode.wav', position=self.position) + Shot.kill(self) + shard_img = media.RotatedImage('flak-shard.png') + for i in range(self.shards): + shard_angle = random.random() * fullcircle + Shot(self, + image=shard_img.rotated(shard_angle), + damage=self.shard_damage, + velocity=Vector2D.unit(shard_angle) * random.gauss( + self.shard_velocity, self.shard_velocity / 3), + range=self.shard_range, + damage_loss=-30.0) Added: sounds/flak-bomb.wav =================================================================== (Binary files differ) Property changes on: sounds/flak-bomb.wav ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Modified: vessel.py =================================================================== --- vessel.py 2007-03-04 07:49:00 UTC (rev 119) +++ vessel.py 2007-03-05 09:10:28 UTC (rev 120) @@ -15,7 +15,7 @@ import sys from pygame.locals import * from media import RotatedImage -from vector import Vector2D, fullcircle, halfcircle, rightangle +from vector import Vector2D, fullcircle, halfcircle, rightangle, diagonal max_weapons = 5 _empty_rect = pygame.rect.Rect(0, 0, 0, 0) @@ -59,7 +59,7 @@ @classmethod def new_player(cls): - player = Vessel.load('vessels/naree/lotus') + player = Vessel.load('vessels/rone/draken') player.setup_collision(body.friend, body.nothing) player.control = KeyboardControl() return player @@ -201,7 +201,6 @@ 'Exception creating system from section [%s] of vessel config file %s: %s' % (section, file_name, err)), sys.exc_info()[-1] vessel.add_system(system) - print vessel, vessel.mass return vessel def update(self): @@ -369,6 +368,37 @@ sprite.rect.center = game.screen_rect.center break + def bearing(self, other): + """Return the clockwise angle between the vessel heading and + the position of the other vessel. Counter clockwise angles + are negative + + >>> game.init() + >>> v1 = Vessel() + >>> v2 = Vessel() + >>> v1.heading = 0 + >>> v2.position = Vector2D(1, 0) + >>> v1.bearing(v2) == rightangle + True + >>> v1.heading = diagonal + >>> round(v1.bearing(v2), 5) == round(diagonal, 5) + True + >>> v1.heading = rightangle + >>> round(abs(v1.bearing(v2)), 5) + 0.0 + >>> v1.heading = halfcircle + >>> v1.bearing(v2) == -rightangle + True + """ + dx = other.position.x - self.position.x + dy = other.position.y - self.position.y + angle = math.atan2(dy, dx) - self.heading + if angle > halfcircle: + angle -= fullcircle + elif angle < -halfcircle: + angle += fullcircle + return angle + def __str__(self): return '<%s@%x "%s" at (%d, %d)>' % ( self.__class__.__name__, id(self), self.vessel_class, self.position.x, self.position.y) @@ -593,11 +623,13 @@ 0.0 >>> a.heat = 2 >>> a.update_system() - >>> a.heat - 1.0 + >>> new = a.heat + >>> 0 < a.heat < 2 + True >>> a.update_system() - >>> a.heat - 0.0 + >>> 0 < a.heat < new + True + >>> a.heat = 0.0 >>> a.update_system() >>> a.heat 0.0 Modified: vessels/rone/draken =================================================================== --- vessels/rone/draken 2007-03-04 07:49:00 UTC (rev 119) +++ vessels/rone/draken 2007-03-05 09:10:28 UTC (rev 120) @@ -27,3 +27,9 @@ range: 500 cool_down: 300 damage: 40 + +[bay.BombBay] +bomb_factory: projectile.FlakBomb +launch_velocity: 250 +reload_time: 3 +max_ttl: 2 This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cd...@us...> - 2007-04-22 07:41:26
|
Revision: 184 http://eos-game.svn.sourceforge.net/eos-game/?rev=184&view=rev Author: cduncan Date: 2007-04-22 00:41:22 -0700 (Sun, 22 Apr 2007) Log Message: ----------- - AI now retargets every few seconds which makes them seem a bit smarter - Increase collision radius of draken plasma cannon to increase user-friendliness - Minor optimization to RoundBody.update Modified Paths: -------------- ai.py body.py vessels/rone/draken Modified: ai.py =================================================================== --- ai.py 2007-04-21 07:06:31 UTC (rev 183) +++ ai.py 2007-04-22 07:41:22 UTC (rev 184) @@ -18,6 +18,8 @@ from vessel import Control, Vessel, DirectionalThrusters class AIControl(Control): + + target_timeout = 4000 def __init__(self, vessel, target=None, mothership=None, sensor=None): Control.__init__(self) @@ -38,6 +40,7 @@ self.proximity_radius = self.vessel.radius * 2 self.sensor = sensor self.steerfunc = self.pursue + self.target_time = 0 def seek_position(self, target_position, predict_ahead=0.75): """Return desired velocity towards a fixed position @@ -256,18 +259,19 @@ self.bw_maneuver = False def update(self): - if (not isinstance(self.target.sprite, Vessel) - or self.target.sprite is self.mothership.sprite + if (not self.target or game.time > self.target_time or self.target.sprite.explosion is not None): # acquire a target vessel if self.sensor is not None: # Use the sensor to find a target if self.sensor.closest_vessel: self.target.add(self.sensor.closest_vessel) + self.target_time = game.time + self.target_timeout self.sensor.disable() elif not self.sensor.enabled: self.sensor.enable() if not self.target: + self.target_time = 0 # look for other targets immediately if self.mothership: # head for the mothership self.target.add(self.mothership) @@ -278,7 +282,7 @@ elif (self.vessel.health < 0.66 and game.time - self.vessel.damage_time < 2000 and not self.vessel.is_friendly(self.target.sprite)): self.steerfunc = self.evade - elif (self.vessel.health > 0.75 + elif self.steerfunc is not self.pursue and (self.vessel.health > 0.75 or vector.distance(self.target.sprite.position, self.vessel.position) > 350): self.steerfunc = self.pursue # steering @@ -371,6 +375,8 @@ False """ self.geom.disable() + self._detected = [] + self.closest_vessel.empty() self.enabled = False def enable(self): Modified: body.py =================================================================== --- body.py 2007-04-21 07:06:31 UTC (rev 183) +++ body.py 2007-04-22 07:41:22 UTC (rev 184) @@ -151,8 +151,8 @@ def update(self): """Calculate onscreen position and size""" # Position, velocity and heading are cached because they are frequently accessed - self.position = self.get_position() - self.velocity = self.get_velocity() + self.position = vector.vector2(*self.body.getPosition()[:2]) + self.velocity = vector.vector2(*self.body.getLinearVel()[:2]) self.heading = self.get_heading() # place self relative to the camera at proper zoom level camera = game.camera Modified: vessels/rone/draken =================================================================== --- vessels/rone/draken 2007-04-21 07:06:31 UTC (rev 183) +++ vessels/rone/draken 2007-04-22 07:41:22 UTC (rev 184) @@ -26,6 +26,7 @@ [projectile.Cannon] range: 500 +radius: 15 fade_range: 125 cool_down: 300 damage: 40 This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cd...@us...> - 2007-05-01 07:32:32
|
Revision: 208 http://eos-game.svn.sourceforge.net/eos-game/?rev=208&view=rev Author: cduncan Date: 2007-05-01 00:32:26 -0700 (Tue, 01 May 2007) Log Message: ----------- - Implement dynamic zooming fish-eye perspective effect for all objects. There are still a few notable glitches, armor and shield images are not scaled and explosion trasparency is broken, also projectile ranges are artifically large, though that's not always a bad thing. - The effect is now much smoother than the previous implementation, and the field of view is narrower, making navigation more like it was before. There's still room for tweaks here, but these parameters feel pretty good. - The speeds of the environment and ships and the distances between planets were all adjusted to make things feel right. I think the end result is successful and gives the game very distinct look and feel. Modified Paths: -------------- bay.py beam.py body.py content/maps/sol game.py particle.py projectile.py selection.py stars.py staticbody.py vector.py vessel.py vessels/naree/cress vessels/naree/gnat vessels/naree/lotus vessels/rone/drach vessels/rone/draken Modified: bay.py =================================================================== --- bay.py 2007-04-30 06:24:17 UTC (rev 207) +++ bay.py 2007-05-01 07:32:26 UTC (rev 208) @@ -47,7 +47,6 @@ fighter.set_position(self.vessel.position) fighter.set_velocity(self.vessel.velocity) fighter.push(vector.unit(self.vessel.heading + halfcircle) * self.launch_force) - fighter.update() self.last_launch = game.time media.play_sound('launch.wav', position=self.vessel.position) Modified: beam.py =================================================================== --- beam.py 2007-04-30 06:24:17 UTC (rev 207) +++ beam.py 2007-05-01 07:32:26 UTC (rev 208) @@ -21,7 +21,7 @@ mass_factor = 0.1 # Mass per unit damage def __init__(self, gunmount, range, damage, efficiency, width=1, color='255,255,255'): - pygame.sprite.Sprite.__init__(self, game.sprites) + pygame.sprite.Sprite.__init__(self, game.new_sprites) self.gunmount = gunmount self.range = float(range) self.length = self.range @@ -49,16 +49,16 @@ if self.firing and self.enabled: camera = game.camera (sx, sy, ignored), (dx, dy, ignored) = self.ray.get() - camera_pos_x, camera_pos_y = vector.to_tuple(game.camera.position) - sx = camera.rect.centerx + (sx - camera_pos_x) * camera.zoom - sy = camera.rect.centery + (sy - camera_pos_y) * camera.zoom - dx *= self.length * camera.zoom - dy *= self.length * camera.zoom + start = vector.vector2(sx, sy) + end = start + vector.vector2(dx, dy) * self.length + apparent_size1, start_pos = vector.to_screen(start) + apparent_size2, end_pos = vector.to_screen(end) self.targeted = False c = random.randint(1, 4) self.rect = pygame.draw.line(surface, (self.color[0]/c, self.color[1]/c, self.color[2]/c), - (sx, sy), (sx + dx, sy + dy), self.width) + vector.to_tuple(start_pos), vector.to_tuple(end_pos), + max(self.width * max(apparent_size1, apparent_size2), 1)) self.length = self.range else: self.rect = _empty_rect Modified: body.py =================================================================== --- body.py 2007-04-30 06:24:17 UTC (rev 207) +++ body.py 2007-05-01 07:32:26 UTC (rev 208) @@ -9,6 +9,7 @@ import ode import game import pygame +from pygame.locals import * import media import vector @@ -45,6 +46,8 @@ xplode_animation = None rect = None + image = None + src_image = None def __init__(self, position=None, velocity=None, category=nothing, collides_with=nothing): """Create the body in the given position vector which @@ -57,7 +60,7 @@ """ global body_count body_count += 1 - pygame.sprite.Sprite.__init__(self, game.sprites) + pygame.sprite.Sprite.__init__(self, game.new_sprites) if self.xplode_animation is None: # Cache explosion animation in the class RoundBody.xplode_animation = media.Animation('splode/*.jpg', alpha=180, fade=8) @@ -82,6 +85,7 @@ self.enabled = True self.on_screen = True self.explosion = None + self.apparent_size = None def get_position(self): return vector.vector2(*self.body.getPosition()[:2]) @@ -150,6 +154,13 @@ def _turn_rate_fset(self, turn_rate): self.body.setAngularVel((0, 0, turn_rate)) turn_rate = property(fget=_turn_rate_fget, fset=_turn_rate_fset) + + def set_image(self, image): + """Set body sprite image""" + if self.src_image is not image: + self.src_image = image + self.rect = self.src_image.get_rect(center=self.rect.center) + self.apparent_size = None # Force rescale of image def update(self): """Calculate onscreen position and size""" @@ -158,18 +169,28 @@ self.velocity = vector.vector2(*self.body.getLinearVel()[:2]) self.heading = self.get_heading() # place self relative to the camera at proper zoom level - camera = game.camera - screen_pos = vector.vector2(*camera.rect.center) + ( - self.position - camera.position) * camera.zoom + apparent_size, screen_pos = vector.to_screen(self.position) self.rect.center = vector.to_tuple(screen_pos) - self.on_screen = self.rect.colliderect(game.screen_rect) if self.explosion is not None: try: - self.image = self.explosion.next() - self.rect = self.image.get_rect(center=self.rect.center) + self.set_image(self.explosion.next()) except StopIteration: self.kill() + self.on_screen = self.rect.colliderect(game.screen_rect) + if self.image is None or (apparent_size != self.apparent_size and self.on_screen): + if apparent_size == 1.0: + self.image = self.src_image + self.apparent_size = apparent_size + elif self.apparent_size is None or abs( + apparent_size - self.apparent_size) > self.apparent_size / 10: + self.image = image = pygame.transform.rotozoom(self.src_image, 0, apparent_size) + transparent = image.get_colorkey() + if transparent: + image.set_colorkey(transparent, RLEACCEL) + self.apparent_size = apparent_size + self.rect = self.image.get_rect(center=self.rect.center) + def draw(self, surface): """Draw either sprite image or offscreen pointer""" if self.on_screen: Modified: content/maps/sol =================================================================== --- content/maps/sol 2007-04-30 06:24:17 UTC (rev 207) +++ content/maps/sol 2007-05-01 07:32:26 UTC (rev 208) @@ -4,25 +4,25 @@ [planet:Earth] image: earth-east.png,earth-west.png -x: 1100 -y: -100 +x: 250 +y: -50 [planet:Moon] image: moon.png -x: -1000 -y: -2000 +x: -500 +y: -1000 [planet:Mars] image: mars.png -x: 15000 -y: 4500 +x: 7500 +y: 2200 [planet:Venus] image: venus.png -x: -12000 -y: -3000 +x: -6000 +y: -1500 [planet:Mercury] image: mercury.png -x: -18000 -y: -4500 +x: -9000 +y: -2200 Modified: game.py =================================================================== --- game.py 2007-04-30 06:24:17 UTC (rev 207) +++ game.py 2007-05-01 07:32:26 UTC (rev 208) @@ -16,7 +16,7 @@ fps = 40 # Framerate, averaged over time ai_interval = 70 # Release AI every N seconds -max_fleet_size = 12 # Maximum # of vessels for each side +max_fleet_size = 10 # Maximum # of vessels for each side universe = None # ode world collision_space = None # ode collision space screen = None # pygame screen @@ -25,6 +25,7 @@ # Sprite groups sprites = None # All sprites +new_sprites = None # sprites added this frame to be drawn next frame map = None # Other globally accessible things @@ -52,7 +53,7 @@ from map import Map global universe, collision_space, screen, screen_rect, background, map global sprites, fps, avg_fps, clock, time, players, local_player, camera, ai - global frame_lag, target, windowed + global frame_lag, target, windowed, new_sprites frame_lag = expected_lag windowed = not fullscreen @@ -89,6 +90,7 @@ # Create game elements clock = pygame.time.Clock() sprites = RenderedGroup() + new_sprites = RenderedGroup() StarField(screen.get_rect()) map = Map('sol') @@ -97,7 +99,7 @@ run_server, game_id, host, port, player_count, race, ship) camera = local_player camera.rect.center = screen.get_rect().center - camera.zoom = 1 + camera.zoom = 1.0 target = selection.Target(local_player) # Kickstart the soundtrack @@ -148,9 +150,8 @@ from ai import AIVessel import body import media - import body global universe, screen, background, debug - global sprites, fps, avg_fps, clock, time, frame_no + global sprites, new_sprites, fps, avg_fps, clock, time, frame_no global ai_interval, windowed friends = pygame.sprite.Group() enemies = pygame.sprite.Group() @@ -159,6 +160,11 @@ while handle_events(): netsync.send_keystate(frame_no, pygame.key.get_pressed()) # send early handle_collisions() + if new_sprites: + # New sprites were added last frame, add them to the general + # sprites group and draw them from here on out + sprites.add(new_sprites) + new_sprites.empty() sprites.clear(screen, background) sprites.update() dirty = sprites.draw(screen) @@ -173,7 +179,7 @@ this_frame_time = min(time - last_time, last_frame_time * 3) fps = clock.get_fps() or fps if debug and frame_no % 30 == 0: - print body.body_count, len(sprites), avg_fps + print body.body_count, len(sprites), fps if frame_no >= frame_lag: netsync.step(frame_no - frame_lag) # retrieve late, after clock.tick throttle if time > next_wave: Modified: particle.py =================================================================== --- particle.py 2007-04-30 06:24:17 UTC (rev 207) +++ particle.py 2007-05-01 07:32:26 UTC (rev 208) @@ -20,13 +20,14 @@ def __init__(self, position, velocity, accel_rate, color, growth_rate, time_to_live, opacity=255): - pygame.sprite.Sprite.__init__(self, game.sprites) - self.pos_x, self.pos_y = vector.to_tuple(position) - self.vel_x, self.vel_y = vector.to_tuple(velocity / game.fps) + pygame.sprite.Sprite.__init__(self, game.new_sprites) + self.position = position + self.velocity = velocity / game.fps self.accel_rate = float(accel_rate) / game.fps + 1 self.growth_rate = float(growth_rate) / game.fps self.color = color - self.size = 2 + self.apparent_size, screen_pos = vector.to_screen(position) + self.size = 2.0 * self.apparent_size self.frames = self.total_frames = time_to_live * game.fps self.opacity = opacity self.rect = pygame.Rect(0, 0, 0, 0) @@ -34,40 +35,40 @@ self.offscreen_img = None def set_image(self): - img_key = self.size, self.color - if img_key in self._image_cache: - self.image = self._image_cache[img_key] + if self.size >= 1.0: + img_key = self.size, self.color + if img_key in self._image_cache: + self.image = self._image_cache[img_key] + else: + self.image = pygame.Surface((self.size, self.size), 0, 8) + self.image.fill(self.key_color) + self.image.set_colorkey(self.key_color) + pygame.draw.ellipse(self.image, self.color, self.image.get_rect()) + self._image_cache[img_key] = self.image else: - self.image = pygame.Surface((self.size, self.size), 0, 8) - self.image.fill(self.key_color) - self.image.set_colorkey(self.key_color) - pygame.draw.ellipse(self.image, self.color, self.image.get_rect()) - self._image_cache[img_key] = self.image + self.image = pygame.Surface((0,0)) self.rect = self.image.get_rect(center=self.rect.center) def update(self): - self.pos_x += self.vel_x - self.pos_y += self.vel_y + self.position += self.velocity if self.accel_rate: - self.vel_x *= self.accel_rate - self.vel_y *= self.accel_rate + self.velocity *= self.accel_rate if random.random() <= self.growth_rate: - self.size += 1 + self.size += self.apparent_size self.set_image() if self.frames > 0: self.frames -= 1 - camera = game.camera - camera_x, camera_y = vector.to_tuple(camera.position) - self.rect.centerx = camera.rect.centerx + ( - (self.pos_x - camera_x) * camera.zoom) - self.rect.centery = camera.rect.centery + ( - (self.pos_y - camera_y) * camera.zoom) + apparent_size, screen_pos = vector.to_screen(self.position) + self.rect.center = vector.to_tuple(screen_pos) else: self.kill() def draw(self, surface): - self.image.set_alpha(self.frames * self.opacity / self.total_frames) - return surface.blit(self.image, self.rect) + if self.size >= 1.0: + self.image.set_alpha(self.frames * self.opacity / self.total_frames) + return surface.blit(self.image, self.rect) + else: + return self.rect class ParticleEmitter(pygame.sprite.Sprite): @@ -75,7 +76,7 @@ def __init__(self, body, accel_rate, color, growth_rate, time_to_live, opacity, emit_rate, velocity_error=0.0): - pygame.sprite.Sprite.__init__(self, game.sprites) + pygame.sprite.Sprite.__init__(self, game.new_sprites) self.body = body self.velocity_error = velocity_error self.accel_rate = accel_rate Modified: projectile.py =================================================================== --- projectile.py 2007-04-30 06:24:17 UTC (rev 207) +++ projectile.py 2007-05-01 07:32:26 UTC (rev 208) @@ -109,7 +109,13 @@ game.screen.fill((0,0,0), self.rect) def update(self): - RoundBody.update(self) + # Position, velocity and heading are cached because they are frequently accessed + self.position = vector.vector2(*self.body.getPosition()[:2]) + self.velocity = vector.vector2(*self.body.getLinearVel()[:2]) + self.heading = self.get_heading() + # place self relative to the camera at proper zoom level + self.apparent_size, screen_pos = vector.to_screen(self.position) + self.rect.center = vector.to_tuple(screen_pos) self.distance += self.distance_per_frame if self.range - self.distance < self.distance_per_frame * 10: self.colormax = int(self.colormax * 0.85) @@ -117,8 +123,8 @@ self.kill() def draw(self, surface): - partsize = self.partsize * game.camera.zoom - radius = self.charge / 2 * game.camera.zoom + partsize = self.partsize * self.apparent_size + radius = self.charge / 2 * self.apparent_size rects = [self.rect] bg = random.randint(0, self.colormax) surface.fill((self.colormax, bg, self.colormax-bg), @@ -247,8 +253,8 @@ collides_with=everything & ~shooter.category ) self.shooter = shooter - self.start_position = shooter.position - self.image = image + self.start_position = vector.vector2(shooter.position) + self.set_image(image) self.rect = image.get_rect(center=shooter.rect.center) self.damage_value = self.start_damage = damage self.range = range @@ -270,6 +276,7 @@ RoundBody.update(self) self.damage_value -= self.damage_loss distance = vector.distance(self.start_position, self.position) + print distance if distance > self.range: self.kill() elif distance > self.fade_range: Modified: selection.py =================================================================== --- selection.py 2007-04-30 06:24:17 UTC (rev 207) +++ selection.py 2007-05-01 07:32:26 UTC (rev 208) @@ -20,7 +20,7 @@ sensor = None def __init__(self, source_vessel): - pygame.sprite.Sprite.__init__(self, game.sprites) + pygame.sprite.Sprite.__init__(self, game.new_sprites) self.selected = pygame.sprite.GroupSingle() self._select_timeout = sys.maxint self.rect = pygame.rect.Rect(0, 0, 0, 0) Modified: stars.py =================================================================== --- stars.py 2007-04-30 06:24:17 UTC (rev 207) +++ stars.py 2007-05-01 07:32:26 UTC (rev 208) @@ -85,13 +85,13 @@ leftbound, topbound = self.rect.left, self.rect.top rightbound, botbound = self.rect.right, self.rect.bottom width, height = self.rect.width, self.rect.height - speed = 0.05 + speed = 0.25 debris = self.debris_count left = self.star_count / self.parallax_depths for star_rect in self.stars: if debris > 0: - star_rect[0] += vec[0] * (debris / 40.0 + 1.1) - star_rect[1] += vec[1] * (debris / 40.0 + 1.1) + star_rect[0] += vec[0] * (debris / 10.0 + 1.1) + star_rect[1] += vec[1] * (debris / 10.0 + 1.1) debris -= 1 else: left -= 1 @@ -114,8 +114,8 @@ elif star_rect[1] < topbound: star_rect[1] += height star_rect[0] = randint(leftbound, rightbound) - self.nebula_pos[0] += vec[0] * 0.025 - self.nebula_pos[1] += vec[1] * 0.025 + self.nebula_pos[0] += vec[0] * 0.15 + self.nebula_pos[1] += vec[1] * 0.15 def update(self): star_velocity = game.camera.zoom * -game.camera.velocity / game.fps Modified: staticbody.py =================================================================== --- staticbody.py 2007-04-30 06:24:17 UTC (rev 207) +++ staticbody.py 2007-05-01 07:32:26 UTC (rev 208) @@ -19,7 +19,7 @@ mass = 1000000 max_visible_dist = float('inf') - max_size_factor = 1.5 + max_size_factor = 1.4 def __init__(self, name, image, x=0, y=0): RoundBody.__init__(self, (int(x), int(y))) @@ -39,23 +39,13 @@ self.velocity = vector.vector2(*self.body.getLinearVel()[:2]) self.heading = self.get_heading() # place self relative to the camera at proper zoom level - camera = game.camera - distance = vector.distance(self.position, camera.position) - # compute the apparent size - if distance <= self.max_radius * 5: - apparent_size = self.max_size_factor - else: - m = distance - self.max_radius * 5 - apparent_size = round(self.max_size_factor / (m / 30000 + 1)**25, 3) + apparent_size, screen_pos = vector.to_screen(self.position, 1.0) if apparent_size != self.apparent_size and self.on_screen: self.apparent_size = apparent_size - self.image = pygame.transform.rotozoom(self.src_image, 0, apparent_size) - self.rect=self.image.get_rect(center=self.rect.center) - offset = (self.position - camera.position) * camera.zoom - if distance: - offset /= math.log10(distance * 500000 + 10) / (1 + self.apparent_size) - screen_pos = vector.vector2(*camera.rect.center) + offset - self.rect.center = vector.to_tuple(screen_pos) + self.image = pygame.transform.rotozoom( + self.src_image, 0, apparent_size * self.max_size_factor) + self.rect=self.image.get_rect(center=vector.to_tuple(screen_pos)) + else: + self.rect.center = vector.to_tuple(screen_pos) self.on_screen = self.rect.colliderect(game.screen_rect) - Modified: vector.py =================================================================== --- vector.py 2007-04-30 06:24:17 UTC (rev 207) +++ vector.py 2007-05-01 07:32:26 UTC (rev 208) @@ -5,6 +5,8 @@ # 2D vectors using python complex numbers # $Id$ +from sys import maxint +import game import math import cmath @@ -41,6 +43,24 @@ def distance(vector1, vector2): return length(vector1 - vector2) +def to_screen(position, scale=1.0): + """Given an absolute position vector in space return a tuple of + (apparent_size, screen_location) where apparent_size is a + float value where 0 < apparent_size <= 1.0 and screen_location + is a vector of the cooresponding location on-screen. + """ + # distance at which objects have an apparent size of 1.0 + unity_radius = game.screen_rect.width / 2 + visible_limit = 200 + camera = game.camera + distance = length(position - camera.position) + offset = (position - camera.position) * camera.zoom + apparent_size = camera.zoom * ( + math.log10(10 * visible_limit / (distance + visible_limit))) + offset = (offset + offset * apparent_size**3) + # Limit the precision to prevent unecessary resizing of images + return round(apparent_size, 3), vector2(*camera.rect.center) + clamp(offset * scale, maxint) + # angle aliases fullcircle = math.pi * 2 halfcircle = math.pi Modified: vessel.py =================================================================== --- vessel.py 2007-04-30 06:24:17 UTC (rev 207) +++ vessel.py 2007-05-01 07:32:26 UTC (rev 208) @@ -247,6 +247,8 @@ return vessel def update(self): + if self.explosion is None: + self.set_image(self.vessel_img.rotated(self.heading)) body.RoundBody.update(self) if self.enabled: self.control.update() @@ -278,9 +280,6 @@ else: # very overspeed, clamp down quick self.set_velocity(vector.clamp(self.velocity, self.max_speed + overspeed / 2)) - if self.explosion is None: - self.image = self.vessel_img.rotated(self.heading) - self.rect = self.image.get_rect(center=self.rect.center) def draw_status(self): """Return an image for the vessel status""" @@ -631,7 +630,7 @@ timeout = 1500 # millis shields stay active def __init__(self, vessel, max_level, regeneration): - pygame.sprite.Sprite.__init__(self, game.sprites) + pygame.sprite.Sprite.__init__(self, game.new_sprites) self.vessel = vessel self.max_level = self.level = float(max_level) self.regeneration = self.max_regeneration = float(regeneration) @@ -758,7 +757,7 @@ mass_factor = 0.005 # Mass per durability unit per ship length unit def __init__(self, vessel, durability, dissipation, regeneration=0): - pygame.sprite.Sprite.__init__(self, game.sprites) + pygame.sprite.Sprite.__init__(self, game.new_sprites) self.armor_img = RotatedImage('%s-armor.png' % vessel.vessel_class, enhanced=False) self.vessel = vessel self.durability = float(durability) Modified: vessels/naree/cress =================================================================== --- vessels/naree/cress 2007-04-30 06:24:17 UTC (rev 207) +++ vessels/naree/cress 2007-05-01 07:32:26 UTC (rev 208) @@ -7,7 +7,7 @@ hull_mass: 5 hull_length: 5 crew: 1 -max_speed: 300 +max_speed: 150 max_energy: 200 [vessel.Shield] Modified: vessels/naree/gnat =================================================================== --- vessels/naree/gnat 2007-04-30 06:24:17 UTC (rev 207) +++ vessels/naree/gnat 2007-05-01 07:32:26 UTC (rev 208) @@ -6,7 +6,7 @@ description: The Naree can ill afford to squander their pilots in delicate stub fighters, nor can they afford to waste resources on crude "fire and forget" weapons. The gnat was designed as an alternative to both, a reuseable weapon effective in both defense and offense. The gnat is a self contained autonomous unmanned craft wielding a laser cannon. Gnats are launched by a system resembling a cross between a figher bay and a torpedo tube that can be carried by medium-sized warships. Launched in groups the gnats can de directed to swarm hostile targets inflicting damage while using their agility and advanced flight control to avoid being destroyed. Once their energy is depleted, they are automatically programmed to return to their host ship to recharge. In defensive mode, gnats act as mobile point-defense weapons, staying near their host and intercepting incoming threats. hull_mass: 1 hull_length: 2 -max_speed: 450 +max_speed: 225 max_energy: 50 [vessel.Shield] Modified: vessels/naree/lotus =================================================================== --- vessels/naree/lotus 2007-04-30 06:24:17 UTC (rev 207) +++ vessels/naree/lotus 2007-05-01 07:32:26 UTC (rev 208) @@ -7,7 +7,7 @@ hull_mass: 25 hull_length: 20 crew: 3 -max_speed: 250 +max_speed: 125 max_energy: 500 [vessel.Shield] Modified: vessels/rone/drach =================================================================== --- vessels/rone/drach 2007-04-30 06:24:17 UTC (rev 207) +++ vessels/rone/drach 2007-05-01 07:32:26 UTC (rev 208) @@ -7,7 +7,7 @@ hull_mass: 6 hull_length: 4 crew: 2 -max_speed: 270 +max_speed: 135 max_energy: 250 [vessel.Armor] Modified: vessels/rone/draken =================================================================== --- vessels/rone/draken 2007-04-30 06:24:17 UTC (rev 207) +++ vessels/rone/draken 2007-05-01 07:32:26 UTC (rev 208) @@ -7,7 +7,7 @@ hull_mass: 20 hull_length: 17 crew: 12 -max_speed: 215 +max_speed: 107 max_energy: 1000 [vessel.Armor] This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cd...@us...> - 2007-05-08 23:34:48
|
Revision: 221 http://eos-game.svn.sourceforge.net/eos-game/?rev=221&view=rev Author: cduncan Date: 2007-05-08 16:34:43 -0700 (Tue, 08 May 2007) Log Message: ----------- - Fix maneuvering thrust, rebalance levels - Rescale directional thrust - More minor ai tweaks, ship spec fine tuning Modified Paths: -------------- ai.py body.py camera.py data/moon.png particle.py vessel.py vessels/naree/cress vessels/naree/gnat vessels/naree/lotus vessels/rone/drach vessels/rone/draken Modified: ai.py =================================================================== --- ai.py 2007-05-08 23:12:17 UTC (rev 220) +++ ai.py 2007-05-08 23:34:43 UTC (rev 221) @@ -58,7 +58,7 @@ position = self.vessel.position + self.vessel.velocity * predict_ahead return vector.normal(target_position - position) * self.vessel.max_speed - def seek(self): + def seek(self, target=None): """Return desired velocity towards a fixed target >>> game.init() @@ -76,7 +76,10 @@ True """ position = self.vessel.position - target_position = self.target.sprite.position + if target is None: + target_position = self.target.sprite.position + else: + target_position = target.position return vector.normal(target_position - position) * self.vessel.max_speed def flee(self): @@ -160,7 +163,8 @@ return vector.radians(target.position - position), target.velocity predict_ahead = vector.distance(position + velocity * predict_ahead, target.position + target.velocity * predict_ahead) / self.vessel.max_speed - approach = (target.position + target.velocity * predict_ahead) - position + approach = (target.position + target.velocity * min(predict_ahead, 5.0)) - ( + position + velocity * 0.75) if vector.length(approach) > target.radius * 6: # Far from target, catch up as fast as we can return vector.radians(approach), vector.clamp( @@ -286,6 +290,10 @@ # steering desired_heading, desired_velocity = self.steerfunc() desired_velocity += self.avoid_vessels() + #if self.mothership: + # mothership = self.mothership.sprite + # desired_velocity += self.seek(mothership) * (vector.distance( + # self.vessel.position, mothership.position) / 1000)**3 self.steer(desired_heading, desired_velocity) # Fire all targeted weapons for i in range(len(self.vessel.weapons)): Modified: body.py =================================================================== --- body.py 2007-05-08 23:12:17 UTC (rev 220) +++ body.py 2007-05-08 23:34:43 UTC (rev 221) @@ -111,7 +111,8 @@ with (collides_with) and what objects collide with us (category) """ if self.geom is None: - self.geom = ode.GeomSphere(game.collision_space, self.collision_radius or self.radius) + self.geom = ode.GeomSphere( + game.collision_space, radius or self.collision_radius or self.radius) self.geom.setBody(self.body) # Move with the ode body self.geom.parent = self # attach ourself for callback purposes self.geom.setCategoryBits(category) Modified: camera.py =================================================================== --- camera.py 2007-05-08 23:12:17 UTC (rev 220) +++ camera.py 2007-05-08 23:34:43 UTC (rev 221) @@ -13,7 +13,7 @@ class Camera: centering = 0.98 # Lower values == more centering force - adhesion = 7.0 # Higher values == closer camera tracking under acceleration + adhesion = 8.0 # Higher values == closer camera tracking under acceleration virtual_width = 1280 # distance across screen at 1.0 zoom regardless of resolution def __init__(self, target): Modified: data/moon.png =================================================================== (Binary files differ) Modified: particle.py =================================================================== --- particle.py 2007-05-08 23:12:17 UTC (rev 220) +++ particle.py 2007-05-08 23:34:43 UTC (rev 221) @@ -40,7 +40,7 @@ if img_key in self._image_cache: self.image = self._image_cache[img_key] else: - self.image = pygame.Surface((self.size, self.size), 0, 8) + self.image = pygame.Surface((int(self.size), int(self.size)), 0, 8) self.image.fill(self.key_color) self.image.set_colorkey(self.key_color) pygame.draw.ellipse(self.image, self.color, self.image.get_rect()) Modified: vessel.py =================================================================== --- vessel.py 2007-05-08 23:12:17 UTC (rev 220) +++ vessel.py 2007-05-08 23:34:43 UTC (rev 221) @@ -131,7 +131,7 @@ img_rect = self.vessel_img.get_rect() self.hull_length = float(hull_length) self.radius = self.hull_length - self.collision_radius = max(img_rect.size) / 2.25 + self.collision_radius = sum(img_rect.size) / 2 / 2.25 body.RoundBody.__init__(self) self.vessel_name = vessel_name self.vessel_class = vessel_class @@ -547,7 +547,7 @@ def __str__(self): return '<%s@%x "%s" at (%d, %d)>' % ( - self.__class__.__name__, id(self), self.vessel_class, vector.to_tuple(self.position)) + (self.__class__.__name__, id(self), self.vessel_class) + vector.to_tuple(self.position)) class System: @@ -857,7 +857,7 @@ max_turn_rate is in radians per second """ self.vessel = vessel - self.thrust = self.full_thrust = float(thrust) * 10000 + self.thrust = self.full_thrust = float(thrust) * 1000 self.max_turn_rate = self.full_turn_rate = float(max_turn_rate) self.enabled = True @@ -911,7 +911,7 @@ def __init__(self, vessel, thrust): self.vessel = vessel - self.thrust = float(thrust) + self.thrust = float(thrust) * 100 self.mass = float(thrust) * self.mass_factor self.enabled = True Modified: vessels/naree/cress =================================================================== --- vessels/naree/cress 2007-05-08 23:12:17 UTC (rev 220) +++ vessels/naree/cress 2007-05-08 23:34:43 UTC (rev 221) @@ -15,11 +15,11 @@ regeneration: 18 [vessel.DirectionalThrusters] -thrust: 0.6 +thrust: 6 max_turn_rate: 4 [vessel.ManeuveringThrusters] -thrust: 35 +thrust: 30 [vessel.Engine] thrust: 55 Modified: vessels/naree/gnat =================================================================== --- vessels/naree/gnat 2007-05-08 23:12:17 UTC (rev 220) +++ vessels/naree/gnat 2007-05-08 23:34:43 UTC (rev 221) @@ -14,7 +14,7 @@ regeneration: 0 [vessel.DirectionalThrusters] -thrust: 0.1 +thrust: 1 max_turn_rate: 5 [vessel.ManeuveringThrusters] Modified: vessels/naree/lotus =================================================================== --- vessels/naree/lotus 2007-05-08 23:12:17 UTC (rev 220) +++ vessels/naree/lotus 2007-05-08 23:34:43 UTC (rev 221) @@ -19,7 +19,7 @@ # dissipation: 10 [vessel.DirectionalThrusters] -thrust: 10 +thrust: 100 max_turn_rate: 2.7 [vessel.ManeuveringThrusters] Modified: vessels/rone/drach =================================================================== --- vessels/rone/drach 2007-05-08 23:12:17 UTC (rev 220) +++ vessels/rone/drach 2007-05-08 23:34:43 UTC (rev 221) @@ -15,11 +15,11 @@ dissipation: 6 [vessel.DirectionalThrusters] -thrust: 0.55 +thrust: 5.5 max_turn_rate: 3.2 [vessel.ManeuveringThrusters] -thrust: 35 +thrust: 30 [vessel.Engine] thrust: 65 @@ -30,7 +30,7 @@ fade_range: 100 cool_down: 300 damage: 15 -efficiency: 0.65 +efficiency: 0.75 velocity: 500 radius: 20 image: proton-grenade.png Modified: vessels/rone/draken =================================================================== --- vessels/rone/draken 2007-05-08 23:12:17 UTC (rev 220) +++ vessels/rone/draken 2007-05-08 23:34:43 UTC (rev 221) @@ -15,7 +15,7 @@ dissipation: 16 [vessel.DirectionalThrusters] -thrust: 10 +thrust: 100 max_turn_rate: 2.8 [vessel.ManeuveringThrusters] @@ -28,7 +28,7 @@ range: 400 radius: 15 fade_range: 125 -cool_down: 350 +cool_down: 325 damage: 40 efficiency: 0.85 damage_loss: 35 This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cd...@us...> - 2007-06-10 08:03:42
|
Revision: 231 http://eos-game.svn.sourceforge.net/eos-game/?rev=231&view=rev Author: cduncan Date: 2007-06-10 01:03:35 -0700 (Sun, 10 Jun 2007) Log Message: ----------- - Gnats now intercept the targeted vessel and return to their mothership to recharge. Gnats also regenerate when destroyed. This makes the lotus much more powerful and fun. - Gnats get new launch, dock and weapon sounds. - To facilitate the above, the gnats get their own ai behavior. Special ai classes can be wired in the vessel config. Other ships will get their own ais too eventually. - Fix bug in fullerene cannon where it would wait to fire if energy was low. Now it just uses whatever energy is remaining and fires immediately. Vessel adjustments: - Draken turn rate reduced to better reflect its heft. This gives you a fighting chance against it in the cress. It's still potent in human hands, however. - Slash lotus max energy since its recharge rate is so fast. This makes precision more important (offset by the new improved gnats). - Gnats are faster, more maneuverable and their laser is more powerful. But since they cannot regenerate energy in flight, the amount of damage they can do in one outing is limited. Typically four gnats can't destroy a drach in one flight. - Lotus launches gnats 2 at a time and further away from its target. TODO: - Fix collision setup race condition so that gnats don't fire at their mothership when first launched. - Add graphical display of gnats' status when docked so you know when they are fully charged. Modified Paths: -------------- ai.py bay.py beam.py body.py projectile.py staticbody.py vessel.py vessels/naree/gnat vessels/naree/lotus vessels/rone/draken Added Paths: ----------- sounds/gnat_dock.wav sounds/gnat_laser.wav sounds/gnat_launch.wav Modified: ai.py =================================================================== --- ai.py 2007-06-10 07:42:50 UTC (rev 230) +++ ai.py 2007-06-10 08:03:35 UTC (rev 231) @@ -17,9 +17,15 @@ from vector import diagonal, halfcircle, rightangle, fullcircle from vessel import Control, Vessel, DirectionalThrusters -class AIControl(Control): +class BasicAI(Control): + """Basic AI Control""" target_timeout = 4000 + + evade_min_health = 0.66 + evade_max_health = 0.75 + evade_damage_timeout = 2000 + evade_max_distance = 350 def __init__(self, vessel, target=None, mothership=None, sensor=None): Control.__init__(self) @@ -36,17 +42,17 @@ self.mothership = sprite.GroupSingle() if mothership: self.mothership.add(mothership) + self.steerfunc = self.standoff self.close_vessels = sprite.Group() self.proximity_radius = self.vessel.collision_radius * 2 self.sensor = sensor - self.steerfunc = self.pursue self.target_time = 0 - def seek_position(self, target_position, predict_ahead=0.75): + def seek_position(self, target_position, predict_ahead=2.0): """Return desired velocity towards a fixed position >>> game.init() - >>> ai = AIControl(Vessel(), None) + >>> ai = BasicAI(Vessel(), None) >>> ai.vessel.max_speed = 10 >>> ai.vessel.position = (100, 0) >>> vel = ai.seek_position((-100, 0)) @@ -63,7 +69,7 @@ >>> game.init() >>> target = Vessel() - >>> ai = AIControl(Vessel(), target) + >>> ai = BasicAI(Vessel(), target) >>> ai.vessel.max_speed = 33.0 >>> ai.vessel.position = Vector2D(0, 0) >>> target.position = Vector2D(0, 400) @@ -80,14 +86,15 @@ target_position = self.target.sprite.position else: target_position = target.position - return vector.normal(target_position - position) * self.vessel.max_speed + velocity = self.seek_position(target_position) + return vector.radians(velocity), velocity def flee(self): """Return desired velocity away from our target >>> game.init() >>> target = Vessel() - >>> ai = AIControl(Vessel(), target) + >>> ai = BasicAI(Vessel(), target) >>> ai.vessel.max_speed = 33.0 >>> ai.vessel.position = Vector2D(0, 0) >>> target.position = Vector2D(0, 400) @@ -99,12 +106,11 @@ >>> head == rightangle + halfcircle True """ - velocity = self.seek() - return (vector.radians(velocity) + halfcircle) % fullcircle, -velocity + heading, velocity = self.seek() + return (heading + halfcircle) % fullcircle, -velocity - def predict_intercept(self, target, predict_ahead=2): - """Return predicted position of target at the time - when we should intercept them + def predict_intercept(self, target, target_predict=0.75, self_predict=0.75): + """Return an approach vector to the target predict_ahead -- Number of seconds to predict the position ahead when determining the time and distance to target. Low values encourage @@ -112,7 +118,7 @@ >>> game.init() >>> target = Vessel() - >>> ai = AIControl(Vessel(), target) + >>> ai = BasicAI(Vessel(), target) >>> ai.vessel.max_speed = 100 >>> ai.vessel.position = Vector2D(0, 0) >>> target.position = Vector2D(150, 0) @@ -123,17 +129,12 @@ >>> int(pos.y) 0 """ - if target.velocity: - predict_ahead *= (vector.distance(self.vessel.velocity, target.velocity) / - (vector.distance(self.vessel.position, target.position) + 1)) - position = self.vessel.position + self.vessel.velocity * predict_ahead - predicted_velocity = (target.velocity + - vector.unit(target.heading) * vector.length(target.velocity)) / 2 - T = vector.distance(position, - target.position + predicted_velocity * predict_ahead) / self.vessel.max_speed - return target.position + (target.velocity * T) - else: - return target.position + velocity = self.vessel.velocity + position = self.vessel.position + predict_ahead = vector.distance(position + velocity * self_predict, + target.position + target.velocity * target_predict) / self.vessel.max_speed + return (target.position + target.velocity * min(predict_ahead, 5.0)) - ( + position + velocity * self_predict) def pursue(self, predict_ahead=0.75): """Return desired velocity towards where we predict @@ -141,7 +142,7 @@ >>> game.init() >>> target = Vessel() - >>> ai = AIControl(Vessel(), target) + >>> ai = BasicAI(Vessel(), target) >>> ai.vessel.max_speed = 100 >>> ai.vessel.position = Vector2D(0, 0) >>> target.position = Vector2D(150, 0) @@ -154,18 +155,25 @@ >>> head 0.0 """ - velocity = self.vessel.velocity + target = self.target.sprite position = self.vessel.position + if not target.velocity and vector.distance( + position, target.position) < target.collision_radius * 1.5: + # close in to stationary target + return vector.radians(target.position - position), target.velocity + approach = self.predict_intercept(target, self_predict=0.4) + return vector.radians(approach), vector.clamp(approach * game.fps, self.vessel.max_speed) + + def standoff(self, distance_factor=6): + """Pursue a target, keeping our distance""" target = self.target.sprite + position = self.vessel.position if not target.velocity and vector.distance( position, target.position) < target.collision_radius * 1.5: # close in to stationary target return vector.radians(target.position - position), target.velocity - predict_ahead = vector.distance(position + velocity * predict_ahead, - target.position + target.velocity * predict_ahead) / self.vessel.max_speed - approach = (target.position + target.velocity * min(predict_ahead, 5.0)) - ( - position + velocity * 0.75) - if vector.length(approach) > target.collision_radius * 6: + approach = self.predict_intercept(target) + if vector.length(approach) > target.collision_radius * distance_factor: # Far from target, catch up as fast as we can return vector.radians(approach), vector.clamp( approach * game.fps, self.vessel.max_speed) @@ -178,7 +186,7 @@ # Face enemy target head-on heading = vector.radians(target.position - position) return heading, vector.clamp(-(approach * - (self.vessel.collision_radius * 6 - vector.length(approach))), + (self.vessel.collision_radius * distance_factor - vector.length(approach))), self.vessel.max_speed) def evade(self): @@ -189,7 +197,7 @@ >>> game.init() >>> target = Vessel() - >>> ai = AIControl(Vessel(), target) + >>> ai = BasicAI(Vessel(), target) >>> ai.vessel.max_speed = 42 >>> ai.vessel.position = Vector2D(0, 0) >>> target.position = Vector2D(0, 150) @@ -260,8 +268,9 @@ self.right_maneuver = False self.left_maneuver = False self.bw_maneuver = False - - def update(self): + + def acquire_target(self): + """Select the approriate target vessel""" if (not self.target or game.time > self.target_time or self.target.sprite.explosion is not None): # acquire a target vessel @@ -281,20 +290,27 @@ else: # No mothership, just head toward a planet self.target.add(game.map.planets) - self.steerfunc = self.pursue - elif (self.vessel.health < 0.66 and game.time - self.vessel.damage_time < 2000 + + def select_steerfunc(self): + """Select the appropirate steering function""" + if (self.vessel.health < self.evade_min_health + and game.time - self.vessel.damage_time < self.evade_damage_timeout and not self.vessel.is_friendly(self.target.sprite)): self.steerfunc = self.evade - elif self.steerfunc is not self.pursue and (self.vessel.health > 0.75 - or vector.distance(self.target.sprite.position, self.vessel.position) > 350): - self.steerfunc = self.pursue + elif self.steerfunc is not self.standoff and ( + self.vessel.health > self.evade_max_health + or vector.distance(self.target.sprite.position, self.vessel.position) + > self.evade_max_distance): + self.steerfunc = self.standoff + else: + self.steerfunc = self.standoff + + def update(self): + self.acquire_target() + self.select_steerfunc() # steering desired_heading, desired_velocity = self.steerfunc() desired_velocity += self.avoid_vessels() - #if self.mothership: - # mothership = self.mothership.sprite - # desired_velocity += self.seek(mothership) * (vector.distance( - # self.vessel.position, mothership.position) / 1000)**3 self.steer(desired_heading, desired_velocity) # Fire all targeted weapons for i in range(len(self.vessel.weapons)): @@ -304,7 +320,7 @@ """Detect contact with other vessels to avoid stacking >>> game.init() - >>> ai = AIControl(Vessel(), None) + >>> ai = BasicAI(Vessel(), None) >>> len(ai.close_vessels) 0 >>> v = Vessel() @@ -320,16 +336,78 @@ self.close_vessels.add(other) +class GnatAI(BasicAI): + """Gnat AI control""" + + is_returning = False # Is returning to the mothership + had_target = False + + def steer(self, desired_heading, desired_velocity): + BasicAI.steer(self, desired_heading, desired_velocity) + # gnats only have maneuvering thrusters + self.fw_maneuver = self.fw_maneuver or self.thrust + self.thrust = False + + def acquire_target(self): + if self.target and not self.vessel.is_friendly(self.target.sprite): + self.had_target = True + elif self.had_target: + # Target was destroyed + self.is_returning = True + if self.is_returning or (self.vessel.energy < (self.vessel.max_energy * 0.1) + or self.vessel.health < 0.25): + # Return to the mothership if our energy is expended + self.target.add(self.mothership) + self.is_returning = True + elif (not self.target or self.vessel.is_friendly(self.target.sprite)) and self.mothership: + # Target whatever the mothership is targeting + self.target.add(self.mothership.sprite.control.target) + if not self.target: + if self.mothership: + # head for the mothership + self.target.add(self.mothership) + else: + # No mothership, just head toward a planet + self.target.add(game.map.planets) + + def select_steerfunc(self): + if (self.target + and vector.distance(self.target.sprite.position, self.vessel.position) < 350): + # At close range, circle the target + self.steerfunc = self.orbit + else: + self.steerfunc = self.pursue + + def orbit(self): + """Steer in an orbit around our target""" + heading, seek_velocity = self.pursue(predict_ahead=2.0) + velocity = vector.unit( + vector.radians(seek_velocity) + diagonal) * self.vessel.max_speed + return heading, seek_velocity + + def collide(self, other, contacts): + if self.is_returning and other in self.mothership: + if hasattr(self.mothership.sprite, 'fighter_bay'): + self.mothership.sprite.fighter_bay.dock(self.vessel) + self.is_returning = False + self.had_target = False + self.target.empty() + else: + BasicAI.collide(self, other, contacts) + + class AIVessel(Vessel): """Vessel under ai control""" - def __init__(self, target=None, category=body.foe, mothership=None, sensor=None, **kw): + def __init__(self, target=None, category=body.foe, mothership=None, sensor=None, + ai='BasicAI', **kw): Vessel.__init__(self, **kw) self.setup_collision(category, body.everything & ~body.shot) if sensor is None: sensor = Sensor(self, 10000, body.everything & ~category) sensor.disable() - self.control = AIControl(self, target, mothership, sensor) + ai_class = globals()[ai] + self.control = ai_class(self, target, mothership, sensor) class Sensor: Modified: bay.py =================================================================== --- bay.py 2007-06-10 07:42:50 UTC (rev 230) +++ bay.py 2007-06-10 08:03:35 UTC (rev 231) @@ -6,6 +6,7 @@ # $Id$ import sys +import pygame import game import vessel import ai @@ -16,47 +17,116 @@ class FighterBay(vessel.Weapon): + attr_name = 'fighter_bay' + priority = 1 def __init__(self, vessel, fighter_config, capacity, - launch_delay, launch_distance, launch_force=250): + launch_delay, launch_distance, launch_force=1550, + launch_salvo=1, launch_sound=None, dock_sound=None, regeneration_time=0): """Bay that holds a specific number of fighters""" self.vessel = vessel self.fighter_config = fighter_config self.capacity = int(capacity) - self.fighters = [] + self.fighters = pygame.sprite.Group() + self.docked = pygame.sprite.Group() self.launch_delay = float(launch_delay) * 1000 self.launch_force = int(launch_force) self.launch_distance = int(launch_distance) + self.launch_salvo = int(launch_salvo) + self.regeneration_time = float(regeneration_time) + self.regeneration_end = None self.last_launch = 0 - self.sensor = None + self.launch_sound = launch_sound + self.dock_sound = dock_sound + self.sensor = None + # Load the bay with fighters + while len(self.fighters) < self.capacity: + self.create_fighter() def update_system(self): - if (self.enabled and self.firing - and game.time > self.last_launch + self.launch_delay - and len(self.fighters) < self.capacity): - if self.sensor is None: - self.sensor = ai.SharedSensor( - self.vessel, 1000, body.everything & ~self.vessel.category) - fighter = ai.AIVessel.load(self.fighter_config, - target=self.vessel.control.target, - mothership=self.vessel, - sensor=self.sensor, - category=self.vessel.category) - self.fighters.append(fighter) - fighter.set_position(self.vessel.position) - fighter.set_velocity(self.vessel.velocity) - fighter.push(vector.unit(self.vessel.heading + halfcircle) * self.launch_force) - self.last_launch = game.time - media.play_sound('launch.wav', position=self.vessel.position) + for vessel in self.docked.sprites(): + # Docked vessels are not drawn, but systems are still updated + vessel.update_systems() + if self.vessel.control.show_status: + for vessel in self.fighters.sprites(): + vessel.show_status() + if self.enabled: + if self.firing and game.time > self.last_launch + self.launch_delay: + self.launch() + if self.regeneration_end is not None: + if game.time < self.regeneration_end: + # Constructing a fighter + energy = self.fighter_energy * 4 / (self.regeneration_time * game.fps) + self.vessel.use_energy(energy) + else: + # New fighter is finished + self.create_fighter() + self.regeneration_end = None + elif self.regeneration_time and len(self.fighters) < self.capacity: + # Start building a new fighter + self.regeneration_end = game.time + self.regeneration_time * 1000 + else: + self.regeneration_end = None + + def dock(self, vessel): + """Dock a fighter vessel in the bay. Only fighters launched from the bay can dock""" + if vessel in self.fighters: + self.docked.add(vessel) + vessel.hide() + if self.dock_sound is not None: + media.play_sound(self.dock_sound, position=self.vessel.position) + + def create_fighter(self): + """Create a new fighter and add it to the bay as docked""" + assert len(self.fighters) < self.capacity, "Cannot create fighter, bay full" + if self.sensor is None: + self.sensor = ai.SharedSensor( + self.vessel, 1000, body.everything & ~self.vessel.category) + fighter = ai.AIVessel.load(self.fighter_config, + target=self.vessel.control.target, + mothership=self.vessel, + sensor=self.sensor, + category=self.vessel.category) + self.fighters.add(fighter) + self.dock(fighter) + self.fighter_energy = fighter.max_energy + + def launch(self): + """Launch fighters""" + # Healthy fighters get launched first + fighters = sorted(self.docked.sprites(), key=lambda v: v.health) + for i in range(self.launch_salvo): + if fighters: + fighter = fighters.pop() + self.docked.remove(fighter) + fighter.set_position(self.vessel.position) + fighter.set_velocity(self.vessel.velocity) + fighter.set_heading(self.vessel.heading) + fighter.setup_collision(self.vessel.category, body.everything & ~body.shot) + fighter.push(vector.unit(self.vessel.heading + halfcircle) * self.launch_force) + fighter.show() + fighter.show_status() + fighter.update_systems() + self.last_launch = game.time + if self.launch_sound is not None: + media.play_sound(self.launch_sound, position=self.vessel.position) @property def targeted(self): target = self.vessel.control.target.sprite if (not target or not target.category & body.everything & ~self.vessel.category): return False - return (len(self.fighters) < self.capacity + return (self.docked and vector.distance(self.vessel.position, target.position) < self.launch_distance) + + def kill(self): + for fighter in self.docked.sprites(): + fighter.kill() + + def __contains__(self, vessel): + """Return true if vessel is docked in the fighter bay""" + return vessel in self.docked class BombBay(vessel.Weapon): Modified: beam.py =================================================================== --- beam.py 2007-06-10 07:42:50 UTC (rev 230) +++ beam.py 2007-06-10 08:03:35 UTC (rev 231) @@ -12,6 +12,7 @@ import random import vessel import vector +import media _empty_rect = pygame.rect.Rect(0, 0, 0, 0) @@ -19,9 +20,10 @@ class BeamWeapon(vessel.Weapon, pygame.sprite.Sprite): mass_factor = 0.1 # Mass per unit damage + sound_interval = 750 # time between sound plays def __init__(self, gunmount, range, damage, efficiency, arc_degrees=0, - width=1, color='255,255,255'): + width=1, color='255,255,255', sound=None): pygame.sprite.Sprite.__init__(self, game.new_sprites) self.gunmount = gunmount self.range = float(range) @@ -32,6 +34,8 @@ self.mass = self.beam_damage * self.mass_factor self.width = int(width) self.color = tuple(int(c) for c in color.split(',')) + self.sound = sound + self.last_sound_time = 0 assert len(self.color) == 3, 'Invalid color value "%s"' % color self.ray = None if game.screen.get_bitsize() == 32: @@ -44,8 +48,8 @@ # Create a ray geom to determine beam collision self.ray = ode.GeomRay(game.collision_space, self.range) self.ray.setCategoryBits(body.nothing) - self.ray.setCollideBits(body.everything & ~self.gunmount.category) self.ray.parent = self + self.ray.setCollideBits(body.everything & ~self.gunmount.category) if self.arc > 0 and self.gunmount is game.local_player and game.target.target: # Track the selected target track_angle = self.gunmount.bearing(game.target.target) @@ -60,7 +64,7 @@ self.beam_damage / self.efficiency / game.fps) def draw(self, surface): - if self.firing and self.enabled: + if self.firing and self.enabled and self.gunmount.enabled: camera = game.camera (sx, sy, ignored), (dx, dy, ignored) = self.ray.get() start = vector.vector2(sx, sy) @@ -74,6 +78,9 @@ vector.to_tuple(start_pos), vector.to_tuple(end_pos), int(max(self.width * max(apparent_size1, apparent_size2), 1))) self.length = self.range + if self.sound is not None and game.time - self.last_sound_time > self.sound_interval: + media.play_sound(self.sound, position=self.gunmount.position) + self.last_sound_time = game.time else: self.rect = _empty_rect return self.rect Modified: body.py =================================================================== --- body.py 2007-06-10 07:42:50 UTC (rev 230) +++ body.py 2007-06-10 08:03:35 UTC (rev 231) @@ -226,7 +226,23 @@ return rect return self.rect + def hide(self): + """Hide a body so that is is no longer considered in ode, updated or drawn""" + if self.geom is not None: + self.geom.disable() # don't participate in collisions anymore + if self in game.sprites: + game.sprites.remove(self) + if self in game.new_sprites: + game.new_sprites.remove(self) + + def show(self): + """Show a body previously hidden""" + if self.geom is not None: + self.geom.enable() # don't participate in collisions anymore + game.new_sprites.add(self) + def disable(self): + """Physically disable a body""" if self.geom is not None: self.geom.disable() # don't participate in collisions anymore self.enabled = False Modified: projectile.py =================================================================== --- projectile.py 2007-06-10 07:42:50 UTC (rev 230) +++ projectile.py 2007-06-10 08:03:35 UTC (rev 231) @@ -49,8 +49,10 @@ self.charge_start = game.time self.charge = min(self.charge + self.charge_rate / game.fps, self.max_charge) elif self.charge_start and self.charge >= self.min_charge: - if self.gunmount.use_energy((self.charge+1)**2 / self.efficiency): - Fullerene(self.charge, self.gunmount) + desired_energy = (self.charge+1)**2 / self.efficiency + energy = self.gunmount.use_energy(desired_energy, partial=True) + if energy: + Fullerene(self.charge * energy / desired_energy, self.gunmount) self.charge = 0 elif not self.firing: self.charge = 0 Added: sounds/gnat_dock.wav =================================================================== (Binary files differ) Property changes on: sounds/gnat_dock.wav ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: sounds/gnat_laser.wav =================================================================== (Binary files differ) Property changes on: sounds/gnat_laser.wav ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: sounds/gnat_launch.wav =================================================================== (Binary files differ) Property changes on: sounds/gnat_launch.wav ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Modified: staticbody.py =================================================================== --- staticbody.py 2007-06-10 07:42:50 UTC (rev 230) +++ staticbody.py 2007-06-10 08:03:35 UTC (rev 231) @@ -18,10 +18,8 @@ class Planet(RoundBody): mass = 1000000 - max_visible_dist = float('inf') - max_size_factor = 1.4 - def __init__(self, name, image, x=0, y=0): + def __init__(self, name, image, x=0, y=0, resource_gen=0): RoundBody.__init__(self, (int(x), int(y))) self.body.disable() # immobile self.name = name @@ -29,8 +27,9 @@ self.offscreen_img = RotatedImage('pointy-blue.gif') self.rect = self.image.get_rect() self.collision_radius = self.radius = self.rect.width / 2 + self.resource_gen = float(resource_gen) + self.harvester_count = 0 self.apparent_size = None - self.max_radius = self.radius * self.max_size_factor def update(self): """Calculate onscreen position and size""" @@ -48,3 +47,9 @@ self.rect.center = vector.to_tuple(screen_pos) self.on_screen = self.rect.colliderect(game.screen_rect) + def harvest(self): + """Return the amount of resources that can be harvested by a single + harvester this frame. + """ + assert self.harvester_count > 0, 'No harvesters registered' + return self.resource_gen / game.fps / self.harvester_count Modified: vessel.py =================================================================== --- vessel.py 2007-06-10 07:42:50 UTC (rev 230) +++ vessel.py 2007-06-10 08:03:35 UTC (rev 231) @@ -60,6 +60,7 @@ self.show_status = keystate[K_RSHIFT] self.weapons[0] = keystate[K_SPACE] self.weapons[1] = keystate[K_LSHIFT] + self.target = game.target.selected @classmethod def new_player(cls, race=None, ship=None): @@ -113,6 +114,7 @@ max_energy = 0 energy_storage_mass = 0.02 # Mass per energy unit stored damage_smoke = None + status_bar_width = 37 # Width of status bar on-screen status_fade_time = 300 # status fade out/in speed status_timeout = 5000 # millis status remains visible @@ -122,16 +124,14 @@ def __init__(self, vessel_name=None, vessel_class='', vessel_type='', description='', image_name=None, hull_mass=1, hull_length=1, crew=0, max_speed=0, max_energy=0, race=None): - if image_name is None: - if vessel_class: - image_name = vessel_class + '.png' - else: - image_name = 'ship.png' - self.vessel_img = RotatedImage(image_name) + if image_name is None and vessel_class: + image_name = vessel_class + '.png' + if image_name is not None: + self.vessel_img = RotatedImage(image_name) img_rect = self.vessel_img.get_rect() self.hull_length = float(hull_length) self.radius = self.hull_length - self.collision_radius = sum(img_rect.size) / 2 / 2.25 + self.collision_radius = sum(img_rect.size) / 5 body.RoundBody.__init__(self) self.vessel_name = vessel_name self.vessel_class = vessel_class @@ -258,20 +258,10 @@ self.show_status() for weapon, ctrl_state in zip(self.weapons, self.control.weapons): weapon.firing = ctrl_state - for system in self._sys: - system.update_system() + self.update_systems() game.map.minimap.update_vessel(self) - if self._systems_to_repair: - if self._repair_time is None: - # begin repair of first system in repair list - self._repair_time = game.time + self.system_repair_time - elif game.time > self._repair_time: - # System repair completed, re-enable it - repaired_system = self._systems_to_repair.pop(0) - repaired_system.enable() - self._repair_time = None - elif self.damage_smoke is not None: - # Systems all repaired, no more somke + if not self._systems_to_repair and self.damage_smoke is not None: + # Systems all repaired, no more smoke self.damage_smoke.kill() self.damage_smoke = None if vector.length(self.velocity) > self.max_speed: @@ -284,13 +274,30 @@ # very overspeed, clamp down quick self.set_velocity(vector.clamp(self.velocity, self.max_speed + overspeed / 2)) + def update_systems(self): + for system in self._sys: + system.update_system() + if self._systems_to_repair: + if self._repair_time is None: + # begin repair of first system in repair list + self._repair_time = game.time + self.system_repair_time + elif game.time > self._repair_time: + # System repair completed, re-enable it + repaired_system = self._systems_to_repair.pop(0) + repaired_system.enable() + self._repair_time = None + + def draw_status(self): """Return an image for the vessel status""" - bar_width = int(37 * game.camera.base_scale) + bar_width = int(self.status_bar_width * game.camera.base_scale) bar_height = 3 health = self.health health_bar_width = int(health * bar_width) - energy_bar_width = int(self.energy * bar_width / self.max_energy) + if self.max_energy: + energy_bar_width = int(self.energy * bar_width / self.max_energy) + else: + energy_bar_width = None if self.last_status != (health_bar_width, energy_bar_width): image = pygame.Surface((bar_width, bar_height * 2 + 2), 0, 8) image.fill((255, 255, 255)) @@ -310,15 +317,16 @@ image.fill((0, 0, 0), graph_rect) image.fill(color, graph_bar) pygame.draw.rect(image, (50, 50, 50), graph_rect, 1) - # Energy Bar - graph_rect.top = graph_rect.bottom + 1 - graph_bar.top = graph_bar.bottom + 1 - graph_bar.width = energy_bar_width - image.fill((0, 0, 0), graph_rect) - image.fill((50, 50, 255), graph_bar) - pygame.draw.rect(image, (0, 0, 125), graph_rect, 1) - self.status_image = image - self.last_status = health_bar_width, energy_bar_width + if self.max_energy: + # Energy Bar + graph_rect.top = graph_rect.bottom + 1 + graph_bar.top = graph_bar.bottom + 1 + graph_bar.width = energy_bar_width + image.fill((0, 0, 0), graph_rect) + image.fill((50, 50, 255), graph_bar) + pygame.draw.rect(image, (0, 0, 125), graph_rect, 1) + self.status_image = image + self.last_status = health_bar_width, energy_bar_width return self.status_image def draw(self, surface): @@ -491,7 +499,7 @@ def use_energy(self, energy, partial=False): """Consume vessel energy. Return the amount of energy that is available. - If parital is true, return whatever energy is available up to the amount + If partial is true, return whatever energy is available up to the amount specified, otherwise return all of what was specified or zero if that amount is not available. """ @@ -1050,6 +1058,26 @@ self.vessel.energy + self.generation / game.fps, self.vessel.max_energy) +class EnergyCoupler(System): + """Enables energy transfer to vessels docked to a mothership""" + + name = 'energy coupler' + + def __init__(self, vessel, energy_flow): + self.vessel = vessel + self.energy_flow = float(energy_flow) + + def update_system(self): + if self.vessel.control.mothership: + mothership = self.vessel.control.mothership.sprite + if (self.vessel.energy < self.vessel.max_energy + and hasattr(mothership, 'fighter_bay') + and self.vessel in mothership.fighter_bay): + energy = min( + self.energy_flow / game.fps, self.vessel.max_energy - self.vessel.energy) + self.vessel.energy += mothership.use_energy(energy, partial=True) + + def resolve(dotted_name): """Return the object corresponding to the dotted module/object name pair specified. Modified: vessels/naree/gnat =================================================================== --- vessels/naree/gnat 2007-06-10 07:42:50 UTC (rev 230) +++ vessels/naree/gnat 2007-06-10 08:03:35 UTC (rev 231) @@ -6,25 +6,27 @@ description: The Naree can ill afford to squander their pilots in delicate stub fighters, nor can they afford to waste resources on crude "fire and forget" weapons. The gnat was designed as an alternative to both, a reuseable weapon effective in both defense and offense. The gnat is a self contained autonomous unmanned craft wielding a laser cannon. Gnats are launched by a system resembling a cross between a figher bay and a torpedo tube that can be carried by medium-sized warships. Launched in groups the gnats can de directed to swarm hostile targets inflicting damage while using their agility and advanced flight control to avoid being destroyed. Once their energy is depleted, they are automatically programmed to return to their host ship to recharge. In defensive mode, gnats act as mobile point-defense weapons, staying near their host and intercepting incoming threats. hull_mass: 1 hull_length: 2 -max_speed: 225 +max_speed: 300 max_energy: 50 +ai: GnatAI [vessel.Shield] max_level: 40 -regeneration: 0 +regeneration: 6 [vessel.DirectionalThrusters] thrust: 1 max_turn_rate: 5 [vessel.ManeuveringThrusters] -thrust: 10 +thrust: 40 -[vessel.Engine] -thrust: 10 +[vessel.EnergyCoupler] +energy_flow: 15 [beam.BeamWeapon] range: 100 -damage: 25 +damage: 50 efficiency: 0.98 color: 255, 80, 120 +sound: gnat_laser.wav Modified: vessels/naree/lotus =================================================================== --- vessels/naree/lotus 2007-06-10 07:42:50 UTC (rev 230) +++ vessels/naree/lotus 2007-06-10 08:03:35 UTC (rev 231) @@ -8,7 +8,7 @@ hull_length: 20 crew: 3 max_speed: 125 -max_energy: 500 +max_energy: 250 [vessel.Shield] max_level: 600 @@ -37,6 +37,10 @@ [bay.FighterBay] fighter_config: vessels/naree/gnat +launch_sound: gnat_launch.wav +dock_sound: gnat_dock.wav capacity: 4 launch_delay: 1.5 -launch_distance: 400 +launch_salvo: 2 +launch_distance: 600 +regeneration_time: 12 Modified: vessels/rone/draken =================================================================== --- vessels/rone/draken 2007-06-10 07:42:50 UTC (rev 230) +++ vessels/rone/draken 2007-06-10 08:03:35 UTC (rev 231) @@ -15,8 +15,8 @@ dissipation: 16 [vessel.DirectionalThrusters] -thrust: 100 -max_turn_rate: 2.8 +thrust: 80 +max_turn_rate: 2.0 [vessel.ManeuveringThrusters] thrust: 50 This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cd...@us...> - 2007-06-19 07:33:31
|
Revision: 233 http://eos-game.svn.sourceforge.net/eos-game/?rev=233&view=rev Author: cduncan Date: 2007-06-19 00:33:17 -0700 (Tue, 19 Jun 2007) Log Message: ----------- - Add rone & naree stations (mothership and outpost respectively) built by human player. They don't do much yet, but you can build 'em. They thoroughly confuse the ai currently, gotta fix that. - Variable camera zoom using "-" and "=" keys - Add messaging system currently only used to report status. Stations report when they finish building and vessel systems report when they are damaged or repaired. More will be done with this later. Modified Paths: -------------- camera.py event.py game.py map.py panel.py vessel.py Added Paths: ----------- data/mothership.png data/outpost.png message.py station.py vessels/naree/outpost vessels/rone/mothership Modified: camera.py =================================================================== --- camera.py 2007-06-11 07:32:13 UTC (rev 232) +++ camera.py 2007-06-19 07:33:17 UTC (rev 233) @@ -15,13 +15,16 @@ centering = 0.98 # Lower values == more centering force adhesion = 8.0 # Higher values == closer camera tracking under acceleration virtual_width = 1280 # distance across screen at 1.0 zoom regardless of resolution + zoom_factor = 1.5 # Zoom change for each zoom in/out + zoom_speed = 1.0 def __init__(self, target): self.target = target self.rect = pygame.Rect(game.screen_rect.width / 2, game.screen_rect.height / 2, 0, 0) self.offset = vector.vector2() self.last_velocity = target.velocity - self.zoom = self.base_scale = float(game.screen_rect.width) / float(self.virtual_width) + self.zoom = self.dest_zoom = self.base_scale = float( + game.screen_rect.width) / float(self.virtual_width) @property def position(self): @@ -40,12 +43,22 @@ accel = self.target.velocity - self.last_velocity self.offset += -vector.normal(accel) * (vector.length(accel) / self.adhesion)**2 self.last_velocity = self.target.velocity + if self.zoom < self.dest_zoom: + self.zoom = min(self.zoom * (1.0 + self.zoom_speed / game.fps), self.dest_zoom) + elif self.zoom > self.dest_zoom: + self.zoom = max(self.zoom * (1.0 - self.zoom_speed / game.fps), self.dest_zoom) def acquire_target(self): # Select a new target for sprite in game.sprites.sprites(): if (sprite.alive() and isinstance(sprite, vessel.Vessel) - and sprite.vessel_type != 'missile'): + and sprite.vessel_type not in ('missile', 'station')): self.follow(sprite) break + + def zoom_in(self): + self.dest_zoom = min(self.dest_zoom * self.zoom_factor, self.base_scale * self.zoom_factor) + + def zoom_out(self): + self.dest_zoom = max(self.dest_zoom / self.zoom_factor, self.base_scale / 3.0) Added: data/mothership.png =================================================================== (Binary files differ) Property changes on: data/mothership.png ___________________________________________________________________ Name: svn:mime-type + image/png Added: data/outpost.png =================================================================== (Binary files differ) Property changes on: data/outpost.png ___________________________________________________________________ Name: svn:mime-type + image/png Modified: event.py =================================================================== --- event.py 2007-06-11 07:32:13 UTC (rev 232) +++ event.py 2007-06-19 07:33:17 UTC (rev 233) @@ -9,8 +9,9 @@ import pygame from pygame.locals import * import game +import station +import vector - class Handler: """Top-level event handler""" @@ -61,6 +62,35 @@ game.map.minimap.hide() else: game.map.minimap.show() + elif event.key == K_b: + if station.current_placer: + placer = station.current_placer.sprite + if game.local_player.race == 'rone': + station_config = 'vessels/rone/mothership' + else: + station_config = 'vessels/naree/outpost' + station.Station.load( + station_config, planet=placer.planet, + building=True, builder=game.local_player, + orbit_angle=game.local_player.heading) + placer.kill() + else: + player_pos = game.local_player.position + for planet in game.map.planets: + if vector.distance(player_pos, planet.position) <= planet.radius: + break + else: + return # Not near a planet + if game.local_player.race == 'rone': + station_image = 'mothership.png' + else: + station_image = 'outpost.png' + placer = station.StationPlacer('%s-station.png' % game.local_player.race, planet) + station.current_placer.add(placer) + elif event.key == K_MINUS: + game.camera.zoom_out() + elif event.key == K_EQUALS: + game.camera.zoom_in() def quit(self, event=None): game.exit() Modified: game.py =================================================================== --- game.py 2007-06-11 07:32:13 UTC (rev 232) +++ game.py 2007-06-19 07:33:17 UTC (rev 233) @@ -15,6 +15,7 @@ import pygame from pygame.locals import * import vector +import message fps = 40 # Framerate, averaged over time ai_interval = 70 # Release AI every N seconds @@ -28,6 +29,7 @@ # Sprite groups sprites = None # All sprites new_sprites = None # sprites added this frame to be drawn next frame +ui_sprites = None # User interface sprites map = None # Other globally accessible things @@ -57,7 +59,7 @@ from camera import Camera global universe, collision_space, screen, screen_rect, background, map global sprites, fps, avg_fps, clock, time, players, local_player, camera, ai - global frame_lag, target, windowed, new_sprites + global frame_lag, target, windowed, new_sprites, messenger, send_msg, ui_sprites frame_lag = expected_lag windowed = not fullscreen @@ -114,6 +116,7 @@ clock = pygame.time.Clock() sprites = RenderedGroup() new_sprites = RenderedGroup() + ui_sprites = RenderedGroup() StarField(screen.get_rect()) map = Map('sol') @@ -179,6 +182,9 @@ camera = Camera(local_player) target = selection.Target(local_player) + # Setup local messaging + message.init(local_player) + # Kickstart the soundtrack media.Soundtrack(local_player.race).start() @@ -221,7 +227,7 @@ import event global universe, screen, background, debug global sprites, new_sprites, fps, avg_fps, clock, time, frame_no - global ai_interval, windowed, camera, map + global ai_interval, windowed, camera, map, ui_sprites friends = pygame.sprite.Group() enemies = pygame.sprite.Group() next_wave = 0 @@ -237,11 +243,11 @@ sprites.add(new_sprites) new_sprites.empty() sprites.clear(screen, background) - map.minimap.update() + ui_sprites.update() sprites.update() camera.update() dirty = sprites.draw(screen) - map.minimap.draw(screen) + ui_sprites.draw(screen) #pygame.display.update(dirty) pygame.display.flip() universe.quickStep(this_frame_time / 1000.0) @@ -292,7 +298,7 @@ friends.add(friend) barrier *= 2 friendly = False - barrier = 300 + barrier = 300 + min(50 * len(enemies) - 50 * len(friends), -250) while len(enemies) < max_fleet_size: if warship and netsync.random.random() * frame_no < barrier: break Modified: map.py =================================================================== --- map.py 2007-06-11 07:32:13 UTC (rev 232) +++ map.py 2007-06-19 07:33:17 UTC (rev 233) @@ -122,6 +122,12 @@ if desired_height > self.rect.height: self.rect.inflate_ip(0, desired_height - self.rect.height) self.minimap = panel.MiniMap(self) + + def get_planet(self, name): + """Return the planet with the name specified or None if no planet exists""" + for planet in self.planets: + if planet.name == name: + return planet class MapConfigError(Exception): Added: message.py =================================================================== --- message.py (rev 0) +++ message.py 2007-06-19 07:33:17 UTC (rev 233) @@ -0,0 +1,186 @@ +## Eos, Dawn of Light -- A Space Opera +## Copyright (c) 2007 Casey Duncan and contributors +## See LICENSE.txt for licensing details + +# User messaging system +# $Id$ + +import pygame +import game + +# Message priorities +panic = 0 +critical = 1 +important = 2 +notice = 3 +info = 4 +trivial = 5 + +class Message: + + def __init__(self, sender, recipient, subject, body=None, priority=info): + """Create an object encapsulating a message + + sender -- Object or user that created the message + recipient -- Object or user the message is targeted to. + subject -- One line summary of message, or the entire message if short. + body -- Detail of message, if any. + priority -- Message priority, lower values designate a higher priority. + """ + self.time = game.time + self.sender = sender + self.recipient = recipient + self.subject = subject + self.body = body + self.priority = priority + + +class StatusMessage(Message): + pass + + +all_players = object() # all player recipient marker + + +class Messenger: + """Delivers messages to their recipients, currently ignores all messages except + those directed to the local user + """ + + def __init__(self, local_player): + self.local_queue = [] + self.local_player = local_player + + @staticmethod + def _queue_msg(queue, msg): + """Add a message to a queue + + >>> class Msg: + ... def __init__(self, time, priority): + ... self.time = time + ... self.priority = priority + >>> qmsg = Messenger._queue_msg + >>> myqueue = [] + >>> msgs = [Msg(100, 99), Msg(99, 99), Msg(101, 99), Msg(200, 0)] + >>> qmsg(myqueue, msgs[0]) + >>> len(myqueue) + 1 + >>> qmsg(myqueue, msgs[1]) + >>> len(myqueue) + 2 + >>> qmsg(myqueue, msgs[2]) + >>> len(myqueue) + 3 + >>> qmsg(myqueue, msgs[3]) + >>> len(myqueue) + 4 + >>> myqueue == [msgs[3], msgs[1], msgs[0], msgs[2]] + True + """ + queue.append(msg) + queue.sort(key=lambda msg: (msg.priority, msg.time)) + + def send(self, msg): + """Send the message to its recipient queue""" + if msg.recipient in (self.local_player, all_players): + self._queue_msg(self.local_queue, msg) + + def get_next_msg(self, recipient, msg_type=None, current_msg=None): + """Get the next queued message for the specified recipient and remove + it from the queue. If msg_type is specified, return only instances of + this message type. If current_msg is specified, return only messages + of greater priority. If there is no such message, return None + + >>> class Msg: + ... def __init__(self, time, priority, recipient): + ... self.time = time + ... self.priority = priority + ... self.recipient = recipient + >>> class Gram(Msg): pass + >>> player = object() + >>> other = object() + >>> msgr = Messenger(player) + >>> msgr.get_next_msg(player) + >>> msgs = [Msg(1, 1, player), Gram(2, 1, other), + ... Msg(3, 1, all_players), Msg(4, 0, player), Gram(5, 1, player)] + >>> for m in msgs: msgr.send(m) + >>> msgr.get_next_msg(player) is msgs[3] + True + >>> msgr.get_next_msg(player, Gram) is msgs[4] + True + >>> msgr.get_next_msg(player) is msgs[0] + True + >>> msgr.get_next_msg(player) is msgs[2] + True + >>> msgr.get_next_msg(player) + >>> msgr.get_next_msg(other) + """ + for msg in self.local_queue: + if msg.recipient in (recipient, all_players) and ( + msg_type is None or isinstance(msg, msg_type)) and ( + current_msg is None or msg.priority > current_msg.priority): + self.local_queue.remove(msg) + if isinstance(msg.sender, pygame.sprite.Sprite) and not msg.sender.alive(): + continue # Cull msgs from dead senders + return msg + + +class StatusView(pygame.sprite.Sprite): + """Status message view""" + + #font_name = 'fonts/ratio/ratio___.ttf' + font_name = 'fonts/forgottenfuturist/Forgotti.ttf' + font_size = 18 + color = (255, 255, 255) + message_ttl = 5000 + + def __init__(self, messenger, player): + pygame.sprite.Sprite.__init__(self, game.ui_sprites) + self.messenger = messenger + self.player = player + self.message = None + self.message_timeout = None + self.image = None + self.rect = pygame.Rect(0, 0, 0, 0) + self.font = pygame.font.Font(self.font_name, self.font_size * game.camera.base_scale) + + def update(self): + if game.time > self.message_timeout: + self.message = None + self.rect = pygame.Rect(0, 0, 0, 0) + next_msg = self.messenger.get_next_msg(self.player, StatusMessage, self.message) + if next_msg is not None: + self.message = next_msg + self.message_timeout = game.time + self.message_ttl + self._render_msg() + + def draw(self, surface): + if self.message is not None: + return surface.blit(self.image, self.rect) + else: + return self.rect + + def _render_msg(self): + """Create an image from the message""" + self.image = self.font.render(self.message.subject, 1, self.color) + self.rect = self.image.get_rect(topleft=(3, 3)) + + +def init(local_player): + """Initialize a messenger and status view for the local player""" + global messenger + messenger = Messenger(local_player) + StatusView(messenger, local_player) + +def send_status(sender, recipient, text, priority=info): + """Convenience function to send status messages using the global messenger""" + global messenger + messenger.send(StatusMessage(sender, recipient, text, priority=priority)) + + +if __name__ == '__main__': + """Run tests if executed directly""" + import sys, doctest + failed, count = doctest.testmod() + print 'Ran', count, 'test cases with', failed, 'failures' + sys.exit(failed) Property changes on: message.py ___________________________________________________________________ Name: svn:keywords + Id Name: svn:eol-style + native Modified: panel.py =================================================================== --- panel.py 2007-06-11 07:32:13 UTC (rev 232) +++ panel.py 2007-06-19 07:33:17 UTC (rev 233) @@ -143,6 +143,7 @@ rect.width - border_width*2, rect.height - border_width*2) self.map_scale = self.map_rect.width / float(map.rect.width) Panel.__init__(self, rect, enabled=False) + game.ui_sprites.add(self) def create_image(self): image = pygame.Surface(self.rect.size, pygame.SRCALPHA, 32) @@ -190,6 +191,7 @@ surface.blit(self.image, self.rect) surface.blit(self.map_mask, self.rect) surface.blit(self.map_points, self.rect) + return self.rect def show(self): if not self.enabled: Added: station.py =================================================================== --- station.py (rev 0) +++ station.py 2007-06-19 07:33:17 UTC (rev 233) @@ -0,0 +1,185 @@ +## Eos, Dawn of Light -- A Space Opera +## Copyright (c) 2007 Casey Duncan and contributors +## See LICENSE.txt for licensing details + +# Space stations +# $Id$ + +import pygame +import math +import game +import vessel +import body +import vector +import media +import map +import message + + +class Station(vessel.Vessel): + """Space station""" + + planet = None # Planet station is harvesting + + disable_factor = 0 # Stations are destroyed once their armor is breached + build_time = 30 + status_bar_width = 70 + station_altitude = 150 # distance from planet surface + + def __init__(self, planet, orbit_angle, building=False, builder=None, + vessel_type='station', **kw): + if isinstance(planet, str): + self.planet = game.map.get_planet(planet_name) + if self.planet is None: + raise map.MapConfigError('Cannot add station, planet "%s" not found in map') + else: + self.planet = planet + vessel.Vessel.__init__(self, vessel_type=vessel_type, **kw) + self.set_position(self.planet.position + vector.unit(orbit_angle) * ( + self.station_altitude + self.planet.radius)) + self.set_heading(vector.radians(self.planet.position - self.position)) + #self.offscreen_img = media.RotatedImage('pointy-blue.gif') + self.setup_collision(body.friend | body.foe, body.nothing) + self.collision_radius = sum(self.vessel_img.get_rect().size) / 5 + self.builder = builder + if building: + self.build_end = int(game.time + self.build_time * 1000) + self.defense_level = 0 + self.built = False + else: + self.build_end = 0 + self.defense_level = 1.0 + self.built = True + + def update(self): + if game.time < self.build_end: + build_millis = self.build_time * 1000.0 + self.defense_level = (build_millis - (self.build_end - game.time)) / build_millis + self.show_status() + else: + self.defense_level = 1.0 + if not self.built: + message.send_status(self, self.builder, 'Station built at %s' % self.planet.name) + self.built = True + vessel.Vessel.update(self) + + def use_energy(self, energy): + """Stations have unlimited energy""" + return energy + + +class Armor(vessel.System): + """Station defensive armor + + Station armor takes 50% less damage from normal weapons, however it + takes 150% damage from siege weapons. + """ + name = 'armor' + + def __init__(self, station, durability, regeneration): + self.station = station + self.durability = float(durability) + self.damage_level = 0.0 + self.regeneration = float(regeneration) + + def update_system(self): + if self.damage_level > 0: + self.damage_level = max(self.damage_level - self.regeneration / game.fps, 0) + + def damage(self, damage, siege=False): + if siege: + damage = damage * 1.5 + else: + damage = damage * 0.5 + self.damage_level += damage + durability = self.durability * self.station.defense_level + if self.damage_level > durability: + return min(self.damage_level - durability, damage) + else: + return 0 + + @property + def health(self): + return (self.durability * self.station.defense_level - self.damage_level) / self.durability + + +class StationPlacer(pygame.sprite.Sprite): + """Interface element for choosing a station site""" + + def __init__(self, image_name, planet): + pygame.sprite.Sprite.__init__(self, game.new_sprites) + self.image_src = media.RotatedImage(image_name) + self.planet = planet + self.distance = planet.radius + Station.station_altitude + + def update(self): + if vector.distance(game.local_player.position, self.planet.position) > self.distance: + # Player is outside of station "orbit", cancel placement + self.kill() + return + self.position = self.planet.position + vector.unit( + game.local_player.heading) * self.distance + self.heading = vector.radians(self.planet.position - self.position) + apparent_size, screen_pos = vector.to_screen(self.position) + self.image = media.scale_image(self.image_src.rotated(self.heading), apparent_size) + self.rect = self.image.get_rect(center=vector.to_tuple(screen_pos)) + +current_placer = pygame.sprite.GroupSingle() + + +class Conduit(pygame.sprite.Sprite): + """Naree resource conduit""" + + min_length = 300 + max_length = 3000 + width = 75 + + # Cosmetic settings + flow_speed = 4 # pixels / second + arc_length = 8000 + arc_period = 31.0 + max_brightness = 100 + + _src_image = None + + def __init__(self, source, target): + pygame.sprite.Sprite.__init__(self, game.new_sprites) + self.source = source + self.target = target + if self._src_image is None: + self.create_image() + self.offset = 0 + self.last_time = game.time + sx, sy = vector.to_screen(source) + tx, ty = vector.to_screen(target) + distance = math.sqrt((tx - sx)**2 + (ty - sy)**2) + self.w2mage = pygame.Surface((distance, target), self.width) + + @staticmethod + def create_image(): + """Generate conduit source image""" + length = self.max_length + self.arc_period + s = pygame.Surface((length, self.width), 0, 32) + arc_rect = pygame.Rect(0, 0, self.arc_length, self.width) + arc_rect.right = 0 + brightness = 0 + while arc_rect.left < self.length: + brightness += self.max_brightness / self.arc_period + if brightness > self.max_brightness: + brightness = 0 + pygame.draw.arc(s, (brightness, brightness, 255), arc_rect, -math.pi/2, math.pi/2, 1) + arc_rect.move_ip(1, 0) + src_img = pygame.Surface((length, height), pygame.SRCALPHA, 32) + r = src_img.get_rect() + r.height = 1 + i = 0 + # Make a cylinder by blending the outer pixels more than the inner + while r.top < height: + s.set_alpha(math.sin(i * (math.pi / height))**2 * 180) + src_img.blit(s, (0, i), r) + r.move_ip(0, 1) + i += 1 + src_img.set_alpha(255) + # Cache in the class to use for all instances + Conduit._src_image = src_img + Property changes on: station.py ___________________________________________________________________ Name: svn:keywords + Id Name: svn:eol-style + native Modified: vessel.py =================================================================== --- vessel.py 2007-06-11 07:32:13 UTC (rev 232) +++ vessel.py 2007-06-19 07:33:17 UTC (rev 233) @@ -20,6 +20,7 @@ import vector from vector import fullcircle, halfcircle, rightangle, diagonal import particle +import message max_weapons = 5 _empty_rect = pygame.rect.Rect(0, 0, 0, 0) @@ -286,6 +287,8 @@ repaired_system = self._systems_to_repair.pop(0) repaired_system.enable() self._repair_time = None + message.send_status( + self, self, '%s Repaired' % system.name.title(), message.notice) def draw_status(self): @@ -325,8 +328,8 @@ image.fill((0, 0, 0), graph_rect) image.fill((50, 50, 255), graph_bar) pygame.draw.rect(image, (0, 0, 125), graph_rect, 1) - self.status_image = image - self.last_status = health_bar_width, energy_bar_width + self.status_image = image + self.last_status = health_bar_width, energy_bar_width return self.status_image def draw(self, surface): @@ -493,9 +496,15 @@ system = netsync.random.choice(self._sys) system.disable() self._systems_to_repair.append(system) + message.send_status( + self, self, '%s Damaged' % system.name.title(), message.important) self.system_damage -= self.system_damage_threshold if self.damage_smoke is None and self.vessel_type != 'missile': self.damage_smoke = particle.SmokeTrail(self) + if (self.hull_damage > self.disable_factor * self.hull_mass * 0.75 and + self.explosion is None): + message.send_status(self, self, 'Severe Hull Damage', message.critical) + def use_energy(self, energy, partial=False): """Consume vessel energy. Return the amount of energy that is available. @@ -1109,6 +1118,7 @@ import projectile import beam import bay +import station if __name__ == '__main__': """Run tests if executed directly""" Added: vessels/naree/outpost =================================================================== --- vessels/naree/outpost (rev 0) +++ vessels/naree/outpost 2007-06-19 07:33:17 UTC (rev 233) @@ -0,0 +1,11 @@ +# $Id: lotus 116 2007-02-22 08:52:18Z cduncan $ + +[general] +vessel_class: outpost +vessel_type: station +description: The naree outpost is a research platform retrofitted to manufacture weapons of war. + +[station.Armor] +durability: 1000 +regeneration: 15 + Property changes on: vessels/naree/outpost ___________________________________________________________________ Name: svn:eol-style + native Added: vessels/rone/mothership =================================================================== --- vessels/rone/mothership (rev 0) +++ vessels/rone/mothership 2007-06-19 07:33:17 UTC (rev 233) @@ -0,0 +1,11 @@ +# $Id: lotus 116 2007-02-22 08:52:18Z cduncan $ + +[general] +vessel_class: mothership +vessel_type: station +description: The nomadic Rone have no static base of operations. Appearing seemingly out of nowhere, Rone motherships appear to plunder resources and resupply their battle fleets. Although the mothership has no offensive weaponry, it is protected by a nearly impenetrable armor defense. + +[station.Armor] +durability: 1200 +regeneration: 12 + Property changes on: vessels/rone/mothership ___________________________________________________________________ Name: svn:eol-style + native This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cd...@us...> - 2007-10-13 17:39:25
|
Revision: 287 http://eos-game.svn.sourceforge.net/eos-game/?rev=287&view=rev Author: cduncan Date: 2007-10-13 10:38:53 -0700 (Sat, 13 Oct 2007) Log Message: ----------- - Add Rone Kraken assault ship - Re-org art by race Modified Paths: -------------- game.py vessels/naree/corde Added Paths: ----------- art/naree/shield.xcf art/rone/ art/rone/drach.xcf art/rone/flak-bomb.png art/rone/kraken.xcf art/sc/ art/sc/missile.xcf art/sc/pegasus.xcf vessels/rone/kraken Removed Paths: ------------- art/flak-bomb.png art/pegasus.xcf art/sc-missile.xcf art/shield.xcf Deleted: art/flak-bomb.png =================================================================== (Binary files differ) Copied: art/naree/shield.xcf (from rev 279, art/shield.xcf) =================================================================== (Binary files differ) Deleted: art/pegasus.xcf =================================================================== (Binary files differ) Added: art/rone/drach.xcf =================================================================== (Binary files differ) Property changes on: art/rone/drach.xcf ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Copied: art/rone/flak-bomb.png (from rev 279, art/flak-bomb.png) =================================================================== (Binary files differ) Added: art/rone/kraken.xcf =================================================================== (Binary files differ) Property changes on: art/rone/kraken.xcf ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Copied: art/sc/missile.xcf (from rev 279, art/sc-missile.xcf) =================================================================== (Binary files differ) Copied: art/sc/pegasus.xcf (from rev 279, art/pegasus.xcf) =================================================================== (Binary files differ) Deleted: art/sc-missile.xcf =================================================================== (Binary files differ) Deleted: art/shield.xcf =================================================================== (Binary files differ) Modified: game.py =================================================================== --- game.py 2007-10-12 07:18:58 UTC (rev 286) +++ game.py 2007-10-13 17:38:53 UTC (rev 287) @@ -122,6 +122,7 @@ ships = [ Vessel.load('vessels/rone/draken'), Vessel.load('vessels/rone/drach'), + Vessel.load('vessels/rone/kraken'), Vessel.load('vessels/naree/lotus'), Vessel.load('vessels/naree/cress'), Vessel.load('vessels/naree/corde'), Modified: vessels/naree/corde =================================================================== --- vessels/naree/corde 2007-10-12 07:18:58 UTC (rev 286) +++ vessels/naree/corde 2007-10-13 17:38:53 UTC (rev 287) @@ -10,7 +10,7 @@ max_speed: 80 max_energy: 1000 ai: EvaderAI -cost: 300 +cost: 400 [vessel.Shield] max_level: 1200 Added: vessels/rone/kraken =================================================================== --- vessels/rone/kraken (rev 0) +++ vessels/rone/kraken 2007-10-13 17:38:53 UTC (rev 287) @@ -0,0 +1,27 @@ +# $Id: lotus 271 2007-09-29 16:46:25Z cduncan $ + +[general] +vessel_class: kraken +vessel_type: assault ship +description: The sight of the kraken over a planet's skies is a foreboding one indeed. Teeming with droid war machines, the kraken's brutal efficiency is unmatched in ground attack. It is rumored that kraken variants can dispatch their droid warriors into space as well, though this is unconfirmed. +hull_mass: 120 +hull_length: 60 +crew: 100 +max_speed: 80 +max_energy: 1000 +ai: EvaderAI +cost: 400 + +[vessel.Shield] +max_level: 1500 +regeneration: 6 + +[vessel.DirectionalThrusters] +thrust: 200 +max_turn_rate: 1.0 + +[vessel.ManeuveringThrusters] +thrust: 100 + +[vessel.Engine] +thrust: 80 Property changes on: vessels/rone/kraken ___________________________________________________________________ Name: svn:eol-style + native This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cd...@us...> - 2007-10-26 06:27:03
|
Revision: 313 http://eos-game.svn.sourceforge.net/eos-game/?rev=313&view=rev Author: cduncan Date: 2007-10-25 23:26:59 -0700 (Thu, 25 Oct 2007) Log Message: ----------- - Add "PlanetaryLander" weapon for assault ships which lands the ship establishing a base on the selected planet once you are over it. - Remove free moon base For now you must pilot the assault ship yourself, which is silly, but next I'll add ai to do it. Modified Paths: -------------- body.py display.py game.py vessel.py vessels/naree/corde vessels/rone/kraken Added Paths: ----------- sounds/land.wav Modified: body.py =================================================================== --- body.py 2007-10-26 06:22:56 UTC (rev 312) +++ body.py 2007-10-26 06:26:59 UTC (rev 313) @@ -41,6 +41,7 @@ radius = 0.1 # Radius in meters, affects rotation collision_radius = None # Optional collision radius if different from above max_visible_dist = 10000 # Maximum distance pointy is shown + scale = 1.0 # Onscreen scale adjustment category = nothing # category for collision interaction collides_with = nothing # category bitmap this body collides with @@ -189,9 +190,9 @@ # place self relative to the camera at proper zoom level self.apparent_size, screen_pos = vector.to_screen(self.position) if self.explosion is None: - self.image = media.image(self.image_name, self.apparent_size, self.heading) + self.image = media.image(self.image_name, self.apparent_size * self.scale, self.heading) else: - image = self.explosion.next_image(self.apparent_size) + image = self.explosion.next_image(self.apparent_size * self.scale) if image is not None: self.image = image else: @@ -202,7 +203,6 @@ def draw(self, surface): """Draw either sprite image or offscreen pointer""" if self.on_screen: - #pygame.draw.circle(surface, (255,255,255), self.rect.center, self.collision_radius * self.appsz, 2) surface.blit(self.image, self.rect) return self.rect elif self.offscreen_img is not None: Modified: display.py =================================================================== --- display.py 2007-10-26 06:22:56 UTC (rev 312) +++ display.py 2007-10-26 06:26:59 UTC (rev 313) @@ -52,7 +52,7 @@ surface = pygame.display.set_mode(window_size) pygame.display.set_icon(media.load_image('eos-icon.png')) - rect = surface.get_rect() + rect = surface.get_rect() pygame.display.set_caption('EOS [ctrl+f full screen]', 'EOS') def toggle_fullscreen(): Modified: game.py =================================================================== --- game.py 2007-10-26 06:22:56 UTC (rev 312) +++ game.py 2007-10-26 06:26:59 UTC (rev 313) @@ -160,9 +160,9 @@ earth = map.get_planet('Earth') earth.base = base.PlanetaryBase(earth, local_player, level=2) panel.BasePanel(earth) - moon = map.get_planet('Moon') - moon.base = base.PlanetaryBase(moon, local_player, level=1) - panel.BasePanel(moon) + #moon = map.get_planet('Moon') + #moon.base = base.PlanetaryBase(moon, local_player, level=1) + #panel.BasePanel(moon) # Setup local messaging message.init(local_player) @@ -229,7 +229,7 @@ if windowed: sleep_time = 0.01 else: - sleep_time = 0.0015 + sleep_time = 0.001 while 1: event_handler.handle_events() keystate = pygame.key.get_pressed() Added: sounds/land.wav =================================================================== (Binary files differ) Property changes on: sounds/land.wav ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Modified: vessel.py =================================================================== --- vessel.py 2007-10-26 06:22:56 UTC (rev 312) +++ vessel.py 2007-10-26 06:26:59 UTC (rev 313) @@ -21,6 +21,7 @@ import particle import message import sprite +import staticbody max_weapons = 5 _empty_rect = pygame.rect.Rect(0, 0, 0, 0) @@ -483,6 +484,13 @@ massobj.adjust(self.mass) self.body.setMass(massobj) + def set_scale(self, scale): + """Change the vessel scale, adjusting both the collision geom + radius and image scaling factor + """ + self.scale = scale + self.geom.setRadius(self.collision_radius * scale) + def __iter__(self): return iter(self._sys) @@ -1217,6 +1225,49 @@ self.vessel.energy += mothership.use_energy(energy, partial=True) +class PlanetaryLander(Weapon): + """Land on a planet and make a base""" + + name = 'landing system' + priority = 1 + mass = 100 + landing_time = 3000 # Milliseconds it takes to descend + + def __init__(self, vessel): + self.vessel = vessel + self.planet = None + self.landing_end = None + + def update_system(self): + if (self.enabled and self.firing and self.targetted and self.landing_end is None): + # Start landing cycle + self.planet = self.vessel.control.target.sprite + self.landing_end = game.time + self.landing_time + media.play_sound('land.wav', position=self.vessel.position, min_spacing=2.0) + # restrict movement + self.vessel.max_speed = 0 + self.vessel.engine.max_speed = 0 + elif self.landing_end is not None: + # We are landing + if game.time < self.landing_end: + self.vessel.set_scale(float(self.landing_end - game.time) / self.landing_time) + else: + import base, panel # Avoid circular imports + # Landing cycle complete, build a base + self.planet.base = base.PlanetaryBase(self.planet, game.local_player) + message.send_status(self.planet, game.local_player, + 'Base esablished on %s' % self.planet.name) + panel.BasePanel(self.planet) + self.vessel.kill() + + @property + def targetted(self): + target = self.vessel.control.target.sprite + return (isinstance(target, staticbody.Planet) and + (target.base is None or target.base.owner.category & self.vessel.category == 0) and + vector.distance(target.position, self.vessel.position) < target.collision_radius / 2) + + def resolve(dotted_name): """Return the object corresponding to the dotted module/object name pair specified. Modified: vessels/naree/corde =================================================================== --- vessels/naree/corde 2007-10-26 06:22:56 UTC (rev 312) +++ vessels/naree/corde 2007-10-26 06:26:59 UTC (rev 313) @@ -25,3 +25,5 @@ [vessel.Engine] thrust: 80 + +[vessel.PlanetaryLander] Modified: vessels/rone/kraken =================================================================== --- vessels/rone/kraken 2007-10-26 06:22:56 UTC (rev 312) +++ vessels/rone/kraken 2007-10-26 06:26:59 UTC (rev 313) @@ -23,5 +23,7 @@ [vessel.ManeuveringThrusters] thrust: 100 +[vessel.PlanetaryLander] + [vessel.Engine] thrust: 80 This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |