You can subscribe to this list here.
2007 |
Jan
(68) |
Feb
(49) |
Mar
(46) |
Apr
(44) |
May
(22) |
Jun
(6) |
Jul
(3) |
Aug
(3) |
Sep
(32) |
Oct
(44) |
Nov
(12) |
Dec
(1) |
---|
From: <cd...@us...> - 2007-12-11 08:40:52
|
Revision: 330 http://eos-game.svn.sourceforge.net/eos-game/?rev=330&view=rev Author: cduncan Date: 2007-12-11 00:40:40 -0800 (Tue, 11 Dec 2007) Log Message: ----------- Set lower-limit of physics speed to 20fps, if the actual frame rate drops below this then the physics will slow, but this is avoids big jumps when an occasional single frame is very slow and generally makes the game less jerky when the framerate varies. Modified Paths: -------------- game.py Modified: game.py =================================================================== --- game.py 2007-11-24 08:18:22 UTC (rev 329) +++ game.py 2007-12-11 08:40:40 UTC (rev 330) @@ -242,7 +242,7 @@ camera.update() sprite.layers.draw(display.surface) pygame.display.flip() - universe.quickStep(clock.get_time() / 1000.0) + universe.quickStep(min(clock.get_time() / 1000.0, 0.05)) if server: server.step() time += clock.tick() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cd...@us...> - 2007-11-24 08:18:26
|
Revision: 329 http://eos-game.svn.sourceforge.net/eos-game/?rev=329&view=rev Author: cduncan Date: 2007-11-24 00:18:22 -0800 (Sat, 24 Nov 2007) Log Message: ----------- - Add callback scheduler - Vessel system repair now handled by the scheduler - Panel updates are now done via the scheduler - Add --stats option to print internal stats during the game - Minor particle optimizations, among others Modified Paths: -------------- base.py bay.py camera.py eos.py event.py game.py map.py media.py panel.py particle.py projectile.py vector.py vessel.py Added Paths: ----------- scheduler.py Modified: base.py =================================================================== --- base.py 2007-11-19 07:55:50 UTC (rev 328) +++ base.py 2007-11-24 08:18:22 UTC (rev 329) @@ -87,10 +87,12 @@ def build_vessel(self, config_file): """Start building the vessel if we can build it and nothing else - is being built + is being built. Return True if the vessel will be built. """ if len(self.build_queue) < self.max_build_queue and self.can_build_vessel(config_file): self.build_queue.append(VesselBuilder(self, config_file)) + return True + return False def cancel_builder(self, builder_idx=None): """Remove the builder from the build queue. If no index is given, @@ -183,8 +185,7 @@ vessel.set_position(self.base.planet.position) vessel.set_heading(random.random() * vector.fullcircle) self.vessel_built = True - message.send_status(self.base, self.base.owner, - '%s built at %s' % (vessel.vessel_class, self.base.planet.name)) + self.base.owner.vessel_built(self.base, vessel) if __name__ == '__main__': """Run tests if executed directly""" Modified: bay.py =================================================================== --- bay.py 2007-11-19 07:55:50 UTC (rev 328) +++ bay.py 2007-11-24 08:18:22 UTC (rev 329) @@ -48,9 +48,6 @@ 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() Modified: camera.py =================================================================== --- camera.py 2007-11-19 07:55:50 UTC (rev 328) +++ camera.py 2007-11-24 08:18:22 UTC (rev 329) @@ -30,6 +30,7 @@ def center(self): """Setup camera center""" self.rect = pygame.Rect(display.rect.width / 2, display.rect.height / 2, 0, 0) + self.screen_pos = vector.vector2(*self.rect.center) self.base_scale = (float(display.rect.width) / float(self.virtual_width))**0.5 @property Modified: eos.py =================================================================== --- eos.py 2007-11-19 07:55:50 UTC (rev 328) +++ eos.py 2007-11-24 08:18:22 UTC (rev 329) @@ -45,6 +45,9 @@ parser.add_option('-d', '--debug', dest='debug', default=False, action='store_true', help='Drop into the debugger on error') + parser.add_option('--stats', + dest='stats', default=False, action='store_true', + help='Output object stats during game') parser.add_option('--fake-latency', dest='latency', type='float', default=0.0, help='artificial latency [default %default secs]') @@ -61,6 +64,7 @@ if args: raise optparse.OptParseError('Unrecognized args: %s' % args) game.debug = opts.debug + game.stats = opts.stats game.init(opts.is_server, opts.is_client, opts.host, opts.port, opts.fullscreen, opts.race, opts.ship, opts.resolution, opts.latency, opts.packet_loss, opts.mute) @@ -71,7 +75,18 @@ import cProfile as profile except ImportError: import profile - profile.run('game.play()') + import pstats + # Monkey patch the pstats class to combine cum time and callees + def print_func_and_callees(self, func): + self.print_func_only(func) + self.print_call_line(self.max_name_len, func, self.all_callees[func]) + pstats.Stats.print_func_only = pstats.Stats.print_line + pstats.Stats.print_line = print_func_and_callees + # Run the profiler and print the report + profile.run('game.play()', 'profile.out') + stats = pstats.Stats('profile.out') + stats.calc_callees() + stats.sort_stats('cum').print_stats(20) else: game.play() except SystemExit: Modified: event.py =================================================================== --- event.py 2007-11-19 07:55:50 UTC (rev 328) +++ event.py 2007-11-24 08:18:22 UTC (rev 329) @@ -213,6 +213,10 @@ if target: game.local_player.switch_vessel(target) + def show_vessel_status(self): + if isinstance(game.local_player.vessel, Vessel): + game.local_player.vessel.show_status() + def __init__(self): sprite.Sprite.__init__(self, sprite.layers.ui) key_map = { @@ -228,6 +232,7 @@ '\b': self.switch_local_player_vessel, } self.key_map = dict((ord(key), method) for key, method in key_map.items()) + self.key_map[K_RSHIFT] = self.show_vessel_status def key_down(self, event): if event.key in self.key_map: Modified: game.py =================================================================== --- game.py 2007-11-19 07:55:50 UTC (rev 328) +++ game.py 2007-11-24 08:18:22 UTC (rev 329) @@ -21,6 +21,7 @@ import message import particle import sprite +import scheduler fps = 40 # Framerate, averaged over time ai_interval = 70 # Release AI every N seconds @@ -36,11 +37,13 @@ client = None objects = sprite.NetGroup() local_player = None +map = None starfield = None camera = None target = None windowed = False debug = False +stats = False def init(is_server=False, is_client=False, host='127.0.0.1', port=252525, fullscreen=False, race=None, ship=None, resolution=None, latency=None, @@ -57,8 +60,8 @@ from camera import Camera import player global universe, collision_space, map, starfield - global fps, avg_fps, clock, time, local_player, camera, ai - global target, windowed, messenger, send_msg + global fps, clock, time, local_player, camera + global target, windowed windowed = not fullscreen # Initialize pygame and setup main screen @@ -156,6 +159,7 @@ # Setup background and map starfield = StarField(display.rect) map = Map('sol') + map.vessels.add(local_player.vessel) # Find the base and put the player next to it for planet in map.planets: @@ -217,9 +221,9 @@ import media import event import panel - global universe, background, debug - global fps, avg_fps, clock, time, frame_no - global ai_interval, windowed, camera, map + global universe, background, stats + global fps, clock, time, frame_no + global ai_interval, camera, map friends = pygame.sprite.Group() enemies = pygame.sprite.Group() next_wave = 0 @@ -234,18 +238,24 @@ handle_collisions() display.surface.fill((0, 0, 0)) sprite.layers.update() + scheduler.dispatch(time) camera.update() sprite.layers.draw(display.surface) pygame.display.flip() universe.quickStep(clock.get_time() / 1000.0) if server: server.step() - last_time = time time += clock.tick() sleep(0) fps = clock.get_fps() or fps - if debug and frame_no % 30 == 0: - print body.body_count, len(sprite.layers), len(media._scaled_image_cache), fps + if stats and frame_no % 30 == 0: + cache = media._scaled_image_cache + print 'b:', body.body_count, + print 's:', len(sprite.layers), + print 'i:', len(cache), (cache.accesses - cache.misses) * 100 / cache.accesses, '%', + print 'p:', particle.particle_count, + print 't:', len(scheduler._taskq), + print 'fps:', fps ## Temporary wave-based game play ## if not client: Modified: map.py =================================================================== --- map.py 2007-11-19 07:55:50 UTC (rev 328) +++ map.py 2007-11-24 08:18:22 UTC (rev 329) @@ -78,6 +78,7 @@ parser.readfp(config_file) self.rect = None self.planets = [] + self.vessels = pygame.sprite.Group() for section in parser.sections(): if section == 'general': for option, value in parser.items(section): Modified: media.py =================================================================== --- media.py 2007-11-19 07:55:50 UTC (rev 328) +++ media.py 2007-11-24 08:18:22 UTC (rev 329) @@ -73,7 +73,7 @@ self.purged = 0 def __getitem__(self, key): - #self.accesses += 1 + self.accesses += 1 try: try: return self._recent[key] @@ -94,7 +94,7 @@ def __setitem__(self, key, value): assert value is not None - #self.adds += 1 + self.adds += 1 if key in self._aged: del self._aged[key] if len(self._recent) >= self.max_recent_size: @@ -109,29 +109,33 @@ self._recent[key] = value while self._aged and len(self) > self.max_size: # Over max size, purge aged entries - #self.purged += 1 + self.purged += 1 self._aged.popitem() _scaled_image_cache = Cache(6000) _rotation_step_angle = fullcircle / 250.0 +_empty_image = pygame.Surface((0, 0)) def image(name, scale=1.0, rotation=0, colorkey=False): """Return a preloaded image by name at the specified scale and rotation""" - global _images, _scaled_image_cache, _rotation_step_angle + global _images, _scaled_image_cache, _rotation_step_angle, _empty_image orig_image, orig_width = _images[name] - scaled_width = round(orig_width * scale) - rotation_step = int(rotation / _rotation_step_angle) % 250 - try: - return _scaled_image_cache[name, scaled_width, rotation_step] - except KeyError: - image = pygame.transform.rotozoom(orig_image, 270 - degrees(rotation), scale) - if colorkey: - image = image.convert() - image.set_colorkey(image.get_at((0, 0)), pygame.RLEACCEL) - else: - image.set_alpha(0, pygame.RLEACCEL) - _scaled_image_cache[name, scaled_width, rotation_step] = image - return image + scaled_width = int(orig_width * scale) + if scaled_width: + rotation_step = int(rotation / _rotation_step_angle) % 250 + try: + return _scaled_image_cache[name, scaled_width, rotation_step] + except KeyError: + image = pygame.transform.rotozoom(orig_image, 270 - degrees(rotation), scale) + if colorkey: + image = image.convert() + image.set_colorkey(image.get_at((0, 0)), pygame.RLEACCEL) + else: + image.set_alpha(0, pygame.RLEACCEL) + _scaled_image_cache[name, scaled_width, rotation_step] = image + return image + else: + return _empty_image def fit_image(name, rect): """Return a preloaded image scaled to fit inside rect""" Modified: panel.py =================================================================== --- panel.py 2007-11-19 07:55:50 UTC (rev 328) +++ panel.py 2007-11-24 08:18:22 UTC (rev 329) @@ -19,6 +19,7 @@ import sprite import media import selection +import scheduler class Panel(sprite.Sprite, event.Handler): """User interface panel. Panels contain controls. Enabled panels receive @@ -29,6 +30,7 @@ slide_time = 0.25 border_color = (40, 40, 60, 150) background_color = (0, 0, 40, 150) + update_interval = 300 # millis between panel updates def __init__(self, enabled=True, *controls): """Create a panel and add it to the panel handler""" @@ -44,6 +46,7 @@ self.slide_end_frame = None handler.add_panel(self) sprite.layers.ui.to_back(self) + scheduler.schedule(self.update_panel, sprite=self, recurrance=self.update_interval) def create_image(self): """Return a panel image that is a background for the panel controls. @@ -62,14 +65,13 @@ return control def update_panel(self): - """Panel-specific updates, called once per frame. + """Panel-specific updates, called once per update_inteval. This is intended to be overridden by subclasses """ def update(self): if self.slide_end_frame is not None and game.frame_no < self.slide_end_frame: self.slide() - self.update_panel() self.controls.update() def draw(self, surface): @@ -267,6 +269,7 @@ width - border_width*2, height - border_width*2) self.map_scale = self.map_rect.width / float(map.rect.width) BottomPanel.__init__(self, width, height, align_right=True) + self.image = pygame.Surface(self.rect.size, pygame.SRCALPHA, 32) self.tab = PanelTab(self, align_right=True) self.controls.add(self.tab) self.select_rect = None @@ -294,30 +297,42 @@ centerx=pos_x + self.map_rect.centerx, centery=pos_y + self.map_rect.centery) image.blit(planet_img, planet_rect) self.map_points = pygame.Surface(self.rect.size, pygame.SRCALPHA, 32) + self.map_points.set_clip( + self.map_points.get_rect().inflate(-self.border_width * 2, -self.border_width * 2)) self.map_mask = pygame.Surface(self.rect.size, pygame.SRCALPHA, 32) + self.map_mask.set_clip( + self.map_mask.get_rect().inflate(-self.border_width * 2, -self.border_width * 2)) image.set_alpha(0, pygame.RLEACCEL) + self.background = image return image def update_panel(self): - self.map_points.fill((0, 0, 0, 0)) - self.map_mask.fill((0, 0, 0, 0)) - self.tab.rect.right = self.rect.right + if self.enabled: + self.map_points.fill((0, 0, 0, 0)) + self.map_mask.fill((0, 0, 0, 0)) + self.tab.rect.right = self.rect.right + for vessel in self.map.vessels: + if not vessel.incidental: + if vessel.is_friendly(game.local_player.vessel): + if vessel is game.local_player.vessel: + color = (50, 50, 255) + elif vessel.selected: + color = (255, 255, 255) + else: + color = (0, 255, 0) + else: + color = (255, 0, 0) + pos_x, pos_y = vector.to_tuple(vessel.position * self.map_scale) + point_rect = pygame.Rect( + pos_x + self.map_rect.centerx, pos_y + self.map_rect.centery, 2, 2) + self.map_points.fill(color, point_rect) + self.map_mask.fill((0, 0, 0, 120), point_rect.inflate(4, 4)) + self.image.fill((0, 0, 0, 0)) + self.image.blit(self.background, (0, 0)) + self.image.blit(self.map_mask, (0, 0)) + self.image.blit(self.map_points, (0, 0)) + self.image.set_alpha(0, pygame.RLEACCEL) - def update_vessel(self, vessel): - if not vessel.incidental: - if vessel.is_friendly(game.local_player.vessel): - if vessel.selected: - color = (255, 255, 255) - else: - color = (0, 255, 0) - else: - color = (255, 0, 0) - pos_x, pos_y = vector.to_tuple(vessel.position * self.map_scale) - point_rect = pygame.Rect( - pos_x + self.map_rect.centerx, pos_y + self.map_rect.centery, 2, 2) - self.map_points.fill(color, point_rect) - self.map_mask.fill((0, 0, 0, 120), point_rect.inflate(4, 4)) - def mouse_drag(self, event, start_pos, end_pos): if self.select_rect is None and not self.rect.collidepoint(start_pos): return False # Drag did not originate in our panel @@ -369,8 +384,6 @@ surface.set_clip(self.rect) surface.blit(self.image, self.rect) surface.set_clip(self.rect.inflate(-self.border_width * 2, -self.border_width * 2)) - surface.blit(self.map_mask, self.rect) - surface.blit(self.map_points, self.rect) if self.select_rect is not None: pygame.draw.rect(surface, (200, 100, 0), self.select_rect, 1) surface.set_clip(old_clip) @@ -401,7 +414,6 @@ 'warship': 3, } - def __init__(self, planet): self._last_builder = None self.current_builder_btn = None Modified: particle.py =================================================================== --- particle.py 2007-11-19 07:55:50 UTC (rev 328) +++ particle.py 2007-11-24 08:18:22 UTC (rev 329) @@ -32,16 +32,18 @@ self.color = color 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.time_to_live = time_to_live * 1000 + self.timeout = game.time + time_to_live * 1000 self.opacity = self.image_alpha = opacity self.rect = pygame.Rect(0, 0, 0, 0) + self.image = None self.set_image() self.offscreen_img = None global particle_count particle_count += 1 def set_image(self): - if self.size >= 0.1: + if self.size >= 0.1 or self.image is None: img_key = int(self.size), self.color if img_key in self._image_cache: self.image = self._image_cache[img_key] @@ -57,25 +59,22 @@ pygame.draw.ellipse(self.image, self.color, self.image.get_rect()) self._image_cache[img_key] = self.image else: - self.image = pygame.Surface((0,0)) + self.image_alpha = 0 self.rect = self.image.get_rect(center=self.rect.center) def update(self): self.position += self.velocity - if self.accel_rate: - self.velocity *= self.accel_rate + self.velocity *= self.accel_rate if random.random() <= self.growth_rate: self.size += self.apparent_size self.set_image() - if self.frames > 0: - self.frames -= 1 - self.apparent_size, screen_pos = vector.to_screen(self.position) - self.rect.center = vector.to_tuple(screen_pos) - else: + self.apparent_size, screen_pos = vector.to_screen(self.position) + self.rect.center = vector.to_tuple(screen_pos) + if game.time > self.timeout: self.kill() def draw(self, surface): - alpha = self.frames * self.image_alpha / self.total_frames + alpha = (self.timeout - game.time) * self.image_alpha / self.time_to_live if alpha > 5: self.image.set_alpha(alpha) return surface.blit(self.image, self.rect) Modified: projectile.py =================================================================== --- projectile.py 2007-11-19 07:55:50 UTC (rev 328) +++ projectile.py 2007-11-24 08:18:22 UTC (rev 329) @@ -113,9 +113,6 @@ state['create_args'] = (self.charge, None) return state - def clear(self): - display.surface.fill((0,0,0), self.rect) - def update(self): # Position, velocity and heading are cached because they are frequently accessed self.position = vector.vector2(*self.body.getPosition()[:2]) @@ -369,9 +366,6 @@ state['create_args'] = (self.shooter.net_id, self.image_name, self.start_damage, vector.vector2()) return state - def clear(self): - display.surface.fill((0,0,0), self.rect) - def update(self): RoundBody.update(self) self.damage_value -= self.damage_loss Added: scheduler.py =================================================================== --- scheduler.py (rev 0) +++ scheduler.py 2007-11-24 08:18:22 UTC (rev 329) @@ -0,0 +1,113 @@ +## Eos, Dawn of Light -- A Space Opera +## Copyright (c) 2007 Casey Duncan and contributors +## See LICENSE.txt for licensing details + +# Periodic task scheduler +# $Id$ + +import sys +from heapq import heappush, heappop, heapreplace + +# Initialize the task queue with a fake task that will always +# be after any scheduled task so we can always assume the task +# queue has at least one item in it. +_taskq = [(sys.maxint,)] + +def schedule(callback, time=0, sprite=None, recurrance=None): + """Schedule the specified callback to be executed at the specified time. + If recurrance is specified, the callback will be rescheduled to occur + periodically every recurrance millis. + + You can specify a time value of 0 if you want the task to be called + immediately on the next call to dispatch. + + If sprite is specified, then the callback will only be dispatched + if the sprite is alive at the scheduled time. + """ + heappush(_taskq, (time, callback, sprite, recurrance)) + +def dispatch(time): + """Dispatch all scheduled items that are due to be executed on or + before time. + + Any exceptions from a scheduled callback will be propagated, halting + dispatching at that point. Callables not executed are left in the + task queue. + + Note this method is not currently thread-safe, though it could be + made so with a small loss in efficiency. + + >>> class Foo: + ... one_calls = 0 + ... two_calls = 0 + ... def one(self): + ... self.one_calls += 1 + ... def two(self): + ... self.two_calls += 1 + >>> class Sprite: + ... is_alive = True + ... def alive(self): + ... return self.is_alive + >>> _clear() + >>> o = Foo() + >>> s = Sprite() + >>> schedule(o.one, 2) + >>> schedule(o.two, 3, s, 2) + >>> dispatch(1) + >>> o.one_calls + 0 + >>> o.two_calls + 0 + >>> dispatch(2) + >>> o.one_calls + 1 + >>> o.two_calls + 0 + >>> dispatch(3) + >>> o.one_calls + 1 + >>> o.two_calls + 1 + >>> dispatch(4) + >>> o.one_calls + 1 + >>> o.two_calls + 1 + >>> dispatch(6) + >>> o.one_calls + 1 + >>> o.two_calls + 2 + >>> dispatch(4000) + >>> o.one_calls + 1 + >>> o.two_calls + 3 + >>> s.is_alive = False + >>> dispatch(4100) + >>> o.one_calls + 1 + >>> o.two_calls + 3 + """ + while _taskq[0][0] <= time: + nil, callback, sprite, recurrance = _taskq[0] + if sprite is None or sprite.alive(): + if recurrance is not None: + heapreplace(_taskq, (time + recurrance, callback, sprite, recurrance)) + else: + heappop(_taskq) + callback() + else: + heappop(_taskq) + +def _clear(): + global _taskq + _taskq = [] + +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: scheduler.py ___________________________________________________________________ Name: svn:keywords + Id Name: svn:eol-style + native Modified: vector.py =================================================================== --- vector.py 2007-11-19 07:55:50 UTC (rev 328) +++ vector.py 2007-11-24 08:18:22 UTC (rev 329) @@ -53,7 +53,7 @@ from_camera = position - camera.position apparent_size = camera.zoom * log10( 10.0 * camera.zoom_size_factor / (length(from_camera) + camera.zoom_size_factor) + 1.0) - return apparent_size**2, vector2(*camera.rect.center) + ( + return apparent_size**2, camera.screen_pos + ( from_camera * camera.zoom * apparent_size) def to_map(screen_pos, slop=50, max_iterations=100): Modified: vessel.py =================================================================== --- vessel.py 2007-11-19 07:55:50 UTC (rev 328) +++ vessel.py 2007-11-24 08:18:22 UTC (rev 329) @@ -20,6 +20,7 @@ import particle import message import sprite +import scheduler max_weapons = 5 _empty_rect = pygame.rect.Rect(0, 0, 0, 0) @@ -34,7 +35,6 @@ self.right_maneuver = False self.bw_maneuver = False self.turn = 0 # -1 = left, 0 = stop, 1 = right - self.show_status = False self.weapons = [False] * max_weapons self.target = None # body being targeted @@ -52,7 +52,6 @@ self.left_maneuver = keystate[K_a] self.right_maneuver = keystate[K_d] self.bw_maneuver = keystate[K_s] or keystate[K_DOWN] - self.show_status = keystate[K_RSHIFT] self.weapons[0] = keystate[K_SPACE] self.weapons[1] = keystate[K_LSHIFT] self.target = game.target.selected @@ -108,7 +107,6 @@ disable_factor = 5 # disabled if damage exceeds this factor * hull_mass system_damage = 0 # System damage accumulator system_damage_threshold = 10 # How much damage to disable a system - system_repair_time = 15000 # Time to repair systems max_speed = 0 max_energy = 0 energy_storage_mass = 0.02 # Mass per energy unit stored @@ -116,6 +114,7 @@ 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 + system_repair_time = 15000 # millis to repair a damaged system selected = False race = None @@ -155,12 +154,13 @@ self._damage_sys = [] self.weapons = [] self.set_heading(random.random() * fullcircle) - self._systems_to_repair = [] - self._repair_time = None self.damage_time = 0 self.race = race self.ai_name = ai self.incidental = str(incidental) not in ('0', 'False') + if game.map is not None: + game.map.vessels.add(self) + self._systems_to_repair = [] @classmethod def load(cls, config_file, **kw): @@ -262,16 +262,9 @@ body.RoundBody.update(self) if self.enabled: self.control.update() - if self.control.show_status: - self.show_status() for weapon, ctrl_state in zip(self.weapons, self.control.weapons): weapon.firing = ctrl_state self.update_systems() - game.map.minimap.update_vessel(self) - 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: # If we are overspeed, bleed off a little overspeed = vector.length(self.velocity) - self.max_speed @@ -285,18 +278,6 @@ 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 - message.send_status( - self, self, '%s Repaired' % system.name.title(), message.notice) - def draw_status(self): """Return an image for the vessel status""" @@ -490,6 +471,10 @@ self.status_time = max( self.status_time, time + self.status_timeout + self.status_fade_time * 2) + @property + def status_shown(self): + return game.time < self.status_time + def damage(self, value): """Apply damage to the vessel @@ -527,21 +512,36 @@ else: self.system_damage += value while self.system_damage > self.system_damage_threshold: - system = 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.damage_system(random.choice(self._sys)) self.system_damage -= self.system_damage_threshold - if game.local_player.vessel is self: - media.play_sound('power_down.wav', min_spacing=2) - if self.damage_smoke is None and not self.incidental: - 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 damage_system(self, system): + """Render a system inoperable due to damage and schedule repair""" + system.disable() + if not self._systems_to_repair: + scheduler.schedule(self.repair_system, game.time + self.system_repair_time, self) + self._systems_to_repair.append(system) + message.send_status(self, self, '%s Damaged' % system.name.title(), message.important) + if game.local_player.vessel is self: + media.play_sound('power_down.wav', min_spacing=2) + if self.damage_smoke is None and not self.incidental: + self.damage_smoke = particle.SmokeTrail(self) + def repair_system(self): + """Repair the earliest damaged system""" + if self._systems_to_repair: + system = self._systems_to_repair.pop() + system.enable() + message.send_status( + self, self, '%s Repaired' % system.name.title(), message.notice) + if self._systems_to_repair: + scheduler.schedule(self.repair_system, game.time + self.system_repair_time, self) + elif self.damage_smoke is not None: + self.damage_smoke.kill() + def use_energy(self, energy, partial=False): """Consume vessel energy. Return the amount of energy that is available. If partial is true, return whatever energy is available up to the amount @@ -620,6 +620,7 @@ name = 'base system' attr_name = None # Vessel attribute + vessel = None mass = 0 priority = sys.maxint enabled = True @@ -732,7 +733,7 @@ >>> s.level 1.0 """ - if self.vessel.control.show_status: + if self.vessel.status_shown: self.show_shield() if self.level < self.max_level and self.regeneration and self.enabled: # Regenerate shields This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cd...@us...> - 2007-11-19 07:55:54
|
Revision: 328 http://eos-game.svn.sourceforge.net/eos-game/?rev=328&view=rev Author: cduncan Date: 2007-11-18 23:55:50 -0800 (Sun, 18 Nov 2007) Log Message: ----------- Fix messages to local player vessel (i.e., system damage) Modified Paths: -------------- message.py Modified: message.py =================================================================== --- message.py 2007-11-13 07:29:12 UTC (rev 327) +++ message.py 2007-11-19 07:55:50 UTC (rev 328) @@ -83,7 +83,7 @@ def send(self, msg): """Send the message to its recipient queue""" - if msg.recipient in (self.local_player, all_players): + if msg.recipient in (self.local_player.vessel, self.local_player, all_players): self._queue_msg(self.local_queue, msg) def get_next_msg(self, recipient, msg_type=None, current_msg=None): @@ -117,7 +117,7 @@ >>> msgr.get_next_msg(other) """ for msg in self.local_queue: - if msg.recipient in (recipient, all_players) and ( + if msg.recipient in (recipient.vessel, 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) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cd...@us...> - 2007-11-13 07:29:16
|
Revision: 327 http://eos-game.svn.sourceforge.net/eos-game/?rev=327&view=rev Author: cduncan Date: 2007-11-12 23:29:12 -0800 (Mon, 12 Nov 2007) Log Message: ----------- - Update ai control only ever 100ms rather than every frame, except weapon controls. This is a big performance win and actually results in smoother flight. - Make turn control a float so the ai can compensate for less frequent updates. - Cress ai uses off-axis beam arc to compensate for loss of precision in turning. - Make animations fixed duration with set frame times. Previously the animation frames were tied to the screen flips, which meant they didn't look right at different framerates. - Pre-render fullerene animations for performance, also make them rotate faster for a more shimmery look. - Tweak to blit background nebula image a tad faster - Simplify timing code in main loop by using clock.get_time() rather than doing our own bookkeeping - Use sleep(0) in main loop for best compromise in framerate/smoothness Modified Paths: -------------- ai.py beam.py game.py media.py projectile.py stars.py vector.py Modified: ai.py =================================================================== --- ai.py 2007-11-09 09:14:15 UTC (rev 326) +++ ai.py 2007-11-13 07:29:12 UTC (rev 327) @@ -29,6 +29,8 @@ evade_max_health = 0.75 evade_damage_timeout = 2000 evade_max_distance = 350 + + update_interval = 100 # millis between updates def __init__(self, vessel, target=None, objective=None, sensor=None): Control.__init__(self) @@ -50,6 +52,7 @@ self.proximity_radius = self.vessel.collision_radius * 5 self.sensor = sensor self.target_time = 0 + self.next_update = 0 def seek_position(self, target_position, predict_ahead=2.0): """Return desired velocity towards a fixed position @@ -250,10 +253,11 @@ heading_diff += fullcircle turn_rate = self.vessel.turn_rate - if heading_diff > turn_rate / 4: - self.turn = 1 - elif heading_diff < -turn_rate / 4: - self.turn = -1 + max_turn_rate = self.vessel.directional_thrusters.max_turn_rate + if (heading_diff > turn_rate * (self.update_interval / 300.0) + or heading_diff < -turn_rate * (self.update_interval / 300.0)): + self.turn = (heading_diff / max_turn_rate) * 1000 / self.update_interval + self.turn = max(min(self.turn, 1), -1) else: self.turn = 0 if self.vessel.velocity != desired_velocity: @@ -342,12 +346,14 @@ 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() - self.steer(desired_heading, desired_velocity) + if game.time > self.next_update: + self.next_update = game.time + self.update_interval + self.acquire_target() + self.select_steerfunc() + # steering + desired_heading, desired_velocity = self.steerfunc() + desired_velocity += self.avoid_vessels() + self.steer(desired_heading, desired_velocity) # Fire all targeted weapons for i, weapon in enumerate(self.vessel.weapons): self.weapons[i] = weapon.targeted Modified: beam.py =================================================================== --- beam.py 2007-11-09 09:14:15 UTC (rev 326) +++ beam.py 2007-11-13 07:29:12 UTC (rev 327) @@ -54,9 +54,9 @@ self.ray.setCategoryBits(body.nothing) self.ray.parent = self self.ray.setCollideBits(body.everything & ~self.gunmount.category) - if self.arc > 0 and self.gunmount is game.local_player.vessel and game.target.target: + if self.arc > 0 and self.gunmount.control.target: # Track the selected target - track_angle = self.gunmount.bearing(game.target.target) + track_angle = self.gunmount.bearing(self.gunmount.control.target.sprite) if abs(track_angle) > self.arc: # limit angle to maximum arc track_angle /= track_angle / self.arc Modified: game.py =================================================================== --- game.py 2007-11-09 09:14:15 UTC (rev 326) +++ game.py 2007-11-13 07:29:12 UTC (rev 327) @@ -86,8 +86,7 @@ media.preload_images('data/*.png') media.preload_images('data/*.gif') media.create_images() - media.LargeExplosion.preload_images() - media.SmallExplosion.preload_images() + media.load_animations() if ship is None and race is None: stars = StarField(display.rect) @@ -224,12 +223,8 @@ friends = pygame.sprite.Group() enemies = pygame.sprite.Group() next_wave = 0 - this_frame_time = last_frame_time = 1000 / fps event_handler = event.MainHandler([panel.handler, event.playing_field_handler]) - if windowed: - sleep_time = 0.01 - else: - sleep_time = 0.001 + clock.tick() while 1: event_handler.handle_events() keystate = pygame.key.get_pressed() @@ -242,14 +237,12 @@ camera.update() sprite.layers.draw(display.surface) pygame.display.flip() - universe.quickStep(this_frame_time / 1000.0) + universe.quickStep(clock.get_time() / 1000.0) if server: server.step() last_time = time time += clock.tick() - sleep(sleep_time) - last_frame_time = this_frame_time - this_frame_time = min(time - last_time, last_frame_time * 3) + sleep(0) fps = clock.get_fps() or fps if debug and frame_no % 30 == 0: print body.body_count, len(sprite.layers), len(media._scaled_image_cache), fps Modified: media.py =================================================================== --- media.py 2007-11-09 09:14:15 UTC (rev 326) +++ media.py 2007-11-13 07:29:12 UTC (rev 327) @@ -152,6 +152,8 @@ colorkey = (0, 0, 0) initial_alpha = 255 fade_alpha = 0 + fps = 30 + cycle = False @classmethod def preload_images(cls): @@ -169,23 +171,23 @@ def __init__(self): assert self.images is not None, 'Animation not loaded, did you call preload_images()?' - self.image_iter = iter(self.images) + self.start_time = game.time def next_image(self, apparent_size=1.0): """Return the next animation frame or None if the animation is complete""" - global _scaled_image_cache - try: - image = self.image_iter.next() - except StopIteration: - return None - apparent_size = round(apparent_size, 1) - try: - return _scaled_image_cache[image, apparent_size] - except KeyError: - scaled_image = pygame.transform.rotozoom(image, 0, apparent_size) - #image.set_colorkey(colorkey, RLEACCEL) - _scaled_image_cache[image, apparent_size] = scaled_image - return scaled_image + frame = (game.time - self.start_time) / (1000 / self.fps) + if self.cycle: + frame = frame % len(self.images) + if frame < len(self.images): + global _scaled_image_cache + apparent_size = round(apparent_size, 1) + try: + return _scaled_image_cache[self.images[frame], apparent_size] + except KeyError: + scaled_image = pygame.transform.rotozoom(self.images[frame], 0, apparent_size) + #image.set_colorkey(colorkey, RLEACCEL) + _scaled_image_cache[self.images[frame], apparent_size] = scaled_image + return scaled_image class LargeExplosion(Animation): @@ -196,6 +198,115 @@ image_glob = 'data/proton-grenade/*.png' +class FullereneAnimation(Animation): + cycle = True + + rpm = 450 + fps = 45 + growth = 1.5 + size = 31 + max_shadows = 2 + particles = 3 + particle_radius = 5 + + @classmethod + def preload_images(cls): + """Render images for animation""" + shadows = [] + cls.images = [] + frames = cls.fps * 60 / cls.rpm + partsize = cls.particle_radius * 2 + rot_per_frame = vector.fullcircle / frames + image = pygame.Surface((cls.size, cls.size)) + image.fill((0,0,0)) + for run in range(2): + for rot in range(frames): + if len(shadows) > cls.max_shadows: + shadows = shadows[1:] + shadows = [pygame.transform.rotozoom(image, 0, cls.growth) + for image in shadows + [image]] + image = pygame.Surface((cls.size, cls.size)) + image.fill((0,0,0)) + image.set_colorkey((0,0,0)) + rect = image.get_rect() + for i in range(cls.particles): + direction_x, direction_y = vector.to_tuple( + vector.unit(rot * rot_per_frame + i * vector.fullcircle / cls.particles)) + part = (int(rect.centerx + direction_x * cls.particle_radius), + int(rect.centery + direction_y * cls.particle_radius)) + bg = random.randint(0, 255) + pygame.draw.circle(image, (255, bg, 255-bg), part, cls.particle_radius) + bg = random.randint(0, 255) + pygame.draw.circle(image, (255, bg, 255-bg), rect.center, cls.particle_radius) + if run: + rect = shadows[0].get_rect() + frame_img = pygame.Surface(rect.size, SRCALPHA, 32) + shade = 7 + for shadow in shadows: + shade = shade * 11 / 6 + shadow.set_colorkey(shadow.get_at((0, 0))) + shadow.set_alpha(shade) + frame_img.blit(shadow, shadow.get_rect(center=rect.center)) + frame_img.blit(image, image.get_rect(center=rect.center)) + frame_img.convert_alpha() + frame_img.set_alpha(255, RLEACCEL) + cls.images.append(frame_img) + + +class FullereneExplosion(FullereneAnimation): + cycle = False + + @classmethod + def preload_images(cls): + """Render images for animation""" + shadows = [] + cls.images = [] + frames = cls.fps * 60 / cls.rpm + rot_per_frame = vector.fullcircle / frames + image = pygame.Surface((cls.size, cls.size)) + image.fill((0,0,0)) + fade = 255 / frames + for run in range(2): + for rot in range(frames): + if len(shadows) > cls.max_shadows: + shadows = shadows[1:] + shadows = [pygame.transform.rotozoom(image, 0, cls.growth) + for image in shadows + [image]] + image = pygame.Surface((cls.size, cls.size)) + image.fill((0,0,0)) + image.set_colorkey((0,0,0)) + rect = image.get_rect() + bright = 255 - rot * fade + for i in range(cls.particles): + direction_x, direction_y = vector.to_tuple( + vector.unit(rot * rot_per_frame + i * vector.fullcircle / cls.particles)) + part = (int(rect.centerx + direction_x * cls.particle_radius), + int(rect.centery + direction_y * cls.particle_radius)) + bg = random.randint(0, bright) + pygame.draw.circle(image, (bright, bg, bright-bg), part, cls.particle_radius) + bg = random.randint(0, bright) + pygame.draw.circle(image, (bright, bg, bright-bg), rect.center, cls.particle_radius) + if run: + rect = shadows[0].get_rect() + frame_img = pygame.Surface(rect.size, SRCALPHA, 32) + shade = 7 + rot * 2 + for shadow in shadows: + shade = shade * 11 / 6 + shadow.set_colorkey(shadow.get_at((0, 0))) + shadow.set_alpha(shade) + frame_img.blit(shadow, shadow.get_rect(center=rect.center)) + frame_img.blit(image, image.get_rect(center=rect.center)) + frame_img.convert_alpha() + frame_img.set_alpha(255, RLEACCEL) + cls.images.append(frame_img) + + +def load_animations(): + LargeExplosion.preload_images() + SmallExplosion.preload_images() + FullereneAnimation.preload_images() + FullereneExplosion.preload_images() + def create_images(): def pointy_image(color): pointy = pygame.Surface((160, 190), SRCALPHA, 32) Modified: projectile.py =================================================================== --- projectile.py 2007-11-09 09:14:15 UTC (rev 326) +++ projectile.py 2007-11-13 07:29:12 UTC (rev 327) @@ -50,7 +50,7 @@ def update_system(self): if self.firing and self.shot is None: self.charge_start = game.time - self.shot = Fullerene(self.gunmount) + self.shot = Fullerene(self.gunmount, self.max_charge) if self.shot is not None: self.shot.set_position(self.gunmount.position + vector.unit(self.gunmount.heading) * self.offset) @@ -93,15 +93,10 @@ speed = 600 range = 600 base_damage = 1.0 - particles = 3 - rpm = 250 - growth = 1.5 - max_shadows = 2 - exploding = False layer = sprite.layers.effects exploding = False - def __init__(self, gunmount, net_id=None): + def __init__(self, gunmount, max_charge, net_id=None): self.radius = 1 if gunmount is None: RoundBody.__init__(self, net_id=net_id) @@ -109,18 +104,9 @@ RoundBody.__init__(self, gunmount.position, net_id=net_id) self.gunmount = gunmount self.charge = 0 + self.max_charge = max_charge self.distance = 0 - self.distance_per_frame = self.speed / game.fps - self.partangle = fullcircle/self.particles - self.rot_per_frame = self.rpm * fullcircle / (game.fps * 60) - self.rot = 0 - self.step = range(self.particles) - self.colormax = 255 - self.image = pygame.Surface((31, 31)) - self.image.fill((0,0,0)) - self.image.set_colorkey((0,0,0)) - self.rect = self.image.get_rect() - self.shadows = [] + self.animation = media.FullereneAnimation() def get_state(self): state = RoundBody.get_state(self) @@ -139,58 +125,28 @@ self.partsize = max(self.charge * .45, 1) self.apparent_size, screen_pos = vector.to_screen(self.position) - self.rect.center = vector.to_tuple(screen_pos) - if self.velocity: - self.distance += self.distance_per_frame - if self.range - self.distance < self.distance_per_frame * 10: - self.colormax = int(self.colormax * 0.85) - if self.distance >= self.range: - self.kill() - if len(self.shadows) > self.max_shadows: - self.shadows = self.shadows[1:] - self.shadows = [ - pygame.transform.rotozoom(image, 0, self.growth) - for image in self.shadows + [self.image]] - rect = self.image.get_rect() - self.image.fill((0,0,0)) - if not self.exploding: - partsize = self.partsize * self.apparent_size - bg = random.randint(0, self.colormax) - self.image.fill( - (self.colormax, bg, self.colormax-bg), (rect.center, (partsize, partsize))) - if partsize >= 1: - for i in self.step: - direction_x, direction_y = vector.to_tuple(vector.unit(self.rot)) - part = (rect.centerx + direction_x * partsize, - rect.centery + direction_y * partsize) - bg = random.randint(0, self.colormax) - pygame.draw.circle( - self.image, (self.colormax, bg, self.colormax-bg), part, partsize) - self.rot += self.partangle - self.rot += self.rot_per_frame + if self.velocity and vector.distance(self.start_position, self.position) > self.range: + self.kill() + if not self.gunmount.alive() and not self.exploding: + self.exploding = True + self.animation = media.FullereneExplosion() + image = self.animation.next_image(self.apparent_size * self.charge / self.max_charge) + if image is not None: + self.image = image + self.rect = self.image.get_rect(center=vector.to_tuple(screen_pos)) else: - if self.exploding > self.max_shadows + 2: - self.kill() - self.exploding += 1 + self.kill() def launch(self): self.set_velocity(vector.unit(self.gunmount.heading) * self.speed + self.gunmount.velocity) self.setup_collision(body.nothing, everything & ~self.gunmount.category) + self.start_position = self.gunmount.position media.play_sound('fullerene.wav', position=self.position) - - def draw(self, surface): - shade = 7 + self.exploding * 3 - for shadow in self.shadows: - shade = shade * 11 / 6 - shadow.set_colorkey(shadow.get_at((0, 0))) - shadow.set_alpha(shade) - surface.blit(shadow, shadow.get_rect(center=self.rect.center)) - surface.blit(self.image, self.image.get_rect(center=self.rect.center)) - return self.rect def collide(self, other, contacts): if not self.exploding: - self.exploding = 1 + self.exploding = True + self.animation = media.FullereneExplosion() self.set_velocity(other.velocity) other.damage((self.charge + self.base_damage)**2) if other.explosion is None: Modified: stars.py =================================================================== --- stars.py 2007-11-09 09:14:15 UTC (rev 326) +++ stars.py 2007-11-13 07:29:12 UTC (rev 327) @@ -57,6 +57,7 @@ self.nebula_image = pygame.transform.rotozoom( self.nebula_image, 30.0, float(rect.width) / float(camera.Camera.virtual_width)) + self.nebula_image.set_colorkey(self.nebula_image.get_at([0, 0])) self.nebula_image.set_alpha(255, pygame.RLEACCEL) rect = self.nebula_image.get_rect(center=rect.center) self.nebula_pos = [float(rect.left), float(rect.top)] Modified: vector.py =================================================================== --- vector.py 2007-11-09 09:14:15 UTC (rev 326) +++ vector.py 2007-11-13 07:29:12 UTC (rev 327) @@ -72,7 +72,6 @@ error = distance(screen_pos, approx_screen_pos) if error < slop: return last_pos - print approx_screen_pos, screen_pos, last_error, error if error > last_error: shift = (screen_pos - approx_screen_pos) * size approx_pos = last_pos This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cd...@us...> - 2007-11-09 09:14:17
|
Revision: 326 http://eos-game.svn.sourceforge.net/eos-game/?rev=326&view=rev Author: cduncan Date: 2007-11-09 01:14:15 -0800 (Fri, 09 Nov 2007) Log Message: ----------- Changes to lotus fullerene cannon: - New alpha blended eye-candy - Fullerene charges in view now, which helps with timing shots and aiming, plus it looks cool. - Fullerene draw energy as it charges rather than when fired - Set min energy higher Modified Paths: -------------- projectile.py vessels/naree/lotus Modified: projectile.py =================================================================== --- projectile.py 2007-11-08 22:47:41 UTC (rev 325) +++ projectile.py 2007-11-09 09:14:15 UTC (rev 326) @@ -43,24 +43,29 @@ self.max_charge_time = float(max_charge_time) self.efficiency = float(efficiency) self.charge_start = None - self.charge = 0 + self.shot = None self.gunmount = gunmount + self.offset = self.gunmount.collision_radius * 1.6 def update_system(self): - if (self.firing and (self.charge_start is None - or game.time - self.charge_start < self.max_charge_time * 1000)): - if self.charge_start is None: - 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: - 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 - self.charge_start = None + if self.firing and self.shot is None: + self.charge_start = game.time + self.shot = Fullerene(self.gunmount) + if self.shot is not None: + self.shot.set_position(self.gunmount.position + + vector.unit(self.gunmount.heading) * self.offset) + if self.firing: + old_charge_cost = (self.shot.charge + 1)**2 / self.efficiency + new_charge = min(self.shot.charge + self.charge_rate / game.fps, self.max_charge) + new_charge_cost = (new_charge + 1)**2 / self.efficiency + if self.gunmount.use_energy(new_charge_cost - old_charge_cost): + self.shot.charge = new_charge + elif self.shot.charge >= self.min_charge: + self.shot.launch() + self.shot = None + else: # Not charged enough to fire + self.shot.kill() + self.shot = None @property def targeted(self): @@ -72,14 +77,13 @@ or not target.category & everything & ~self.gunmount.category): return False target_dist = vector.distance(target.position, self.gunmount.position) - if self.firing: - if (self.charge >= self.max_charge / 1.5 + if self.shot is not None: + if (self.shot.charge >= self.max_charge / 1.5 and self.gunmount.bearing(target) < (diagonal / 4.0) and target_dist < Fullerene.range * 2): return False else: - return (game.time - self.charge_start < self.max_charge_time * 1000 - and self.gunmount.energy > 150) + return True else: return target_dist < Fullerene.range * 1.25 @@ -89,32 +93,34 @@ speed = 600 range = 600 base_damage = 1.0 - particles = 4 - rpm = 80 + particles = 3 + rpm = 250 + growth = 1.5 + max_shadows = 2 exploding = False layer = sprite.layers.effects + exploding = False - def __init__(self, charge, gunmount, net_id=None): - self.mass = charge - self.radius = max(charge, 1) + def __init__(self, gunmount, net_id=None): + self.radius = 1 if gunmount is None: RoundBody.__init__(self, net_id=net_id) else: - RoundBody.__init__( - self, gunmount.position, - vector.unit(gunmount.heading) * self.speed + gunmount.velocity, - collides_with=everything & ~gunmount.category, - net_id=net_id) - self.charge = charge + RoundBody.__init__(self, gunmount.position, net_id=net_id) + self.gunmount = gunmount + self.charge = 0 self.distance = 0 self.distance_per_frame = self.speed / game.fps - self.partsize = max(charge * .6, 2) self.partangle = fullcircle/self.particles self.rot_per_frame = self.rpm * fullcircle / (game.fps * 60) self.rot = 0 self.step = range(self.particles) self.colormax = 255 - media.play_sound('fullerene.wav', position=self.position) + self.image = pygame.Surface((31, 31)) + self.image.fill((0,0,0)) + self.image.set_colorkey((0,0,0)) + self.rect = self.image.get_rect() + self.shadows = [] def get_state(self): state = RoundBody.get_state(self) @@ -129,40 +135,63 @@ 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.radius = max(self.charge, 1) + self.partsize = max(self.charge * .45, 1) 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) - if self.distance >= self.range: - self.kill() + if self.velocity: + self.distance += self.distance_per_frame + if self.range - self.distance < self.distance_per_frame * 10: + self.colormax = int(self.colormax * 0.85) + if self.distance >= self.range: + self.kill() + if len(self.shadows) > self.max_shadows: + self.shadows = self.shadows[1:] + self.shadows = [ + pygame.transform.rotozoom(image, 0, self.growth) + for image in self.shadows + [self.image]] + rect = self.image.get_rect() + self.image.fill((0,0,0)) + if not self.exploding: + partsize = self.partsize * self.apparent_size + bg = random.randint(0, self.colormax) + self.image.fill( + (self.colormax, bg, self.colormax-bg), (rect.center, (partsize, partsize))) + if partsize >= 1: + for i in self.step: + direction_x, direction_y = vector.to_tuple(vector.unit(self.rot)) + part = (rect.centerx + direction_x * partsize, + rect.centery + direction_y * partsize) + bg = random.randint(0, self.colormax) + pygame.draw.circle( + self.image, (self.colormax, bg, self.colormax-bg), part, partsize) + self.rot += self.partangle + self.rot += self.rot_per_frame + else: + if self.exploding > self.max_shadows + 2: + self.kill() + self.exploding += 1 + def launch(self): + self.set_velocity(vector.unit(self.gunmount.heading) * self.speed + self.gunmount.velocity) + self.setup_collision(body.nothing, everything & ~self.gunmount.category) + media.play_sound('fullerene.wav', position=self.position) + def draw(self, surface): - partsize = self.partsize * self.apparent_size - radius = self.charge / 2 * self.apparent_size - bg = random.randint(0, self.colormax) - surface.fill((self.colormax, bg, self.colormax-bg), - (self.rect.center, (partsize, partsize))) - if radius >= 2: - rects = [self.rect] - for i in self.step: - direction_x, direction_y = vector.to_tuple(vector.unit(self.rot)) - part = (self.rect.centerx + direction_x * radius, - self.rect.centery + direction_y * radius, - partsize, partsize) - rects.append(part) - bg = random.randint(0, self.colormax) - surface.fill((self.colormax, bg, self.colormax-bg) , part) - self.rot += self.partangle - self.rot += self.rot_per_frame - return Rect(rects[0]).unionall(rects) - else: - return self.rect + shade = 7 + self.exploding * 3 + for shadow in self.shadows: + shade = shade * 11 / 6 + shadow.set_colorkey(shadow.get_at((0, 0))) + shadow.set_alpha(shade) + surface.blit(shadow, shadow.get_rect(center=self.rect.center)) + surface.blit(self.image, self.image.get_rect(center=self.rect.center)) + return self.rect def collide(self, other, contacts): - if self.alive(): - self.kill() + if not self.exploding: + self.exploding = 1 + self.set_velocity(other.velocity) other.damage((self.charge + self.base_damage)**2) if other.explosion is None: media.play_sound('hit.wav', volume=(self.charge/5), position=self.position) Modified: vessels/naree/lotus =================================================================== --- vessels/naree/lotus 2007-11-08 22:47:41 UTC (rev 325) +++ vessels/naree/lotus 2007-11-09 09:14:15 UTC (rev 326) @@ -30,7 +30,7 @@ [projectile.FullereneCannon] charge_rate: 12 -min_charge: 1.5 +min_charge: 4 max_charge: 11 max_charge_time: 4 efficiency: 0.95 This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cd...@us...> - 2007-11-08 22:47:45
|
Revision: 325 http://eos-game.svn.sourceforge.net/eos-game/?rev=325&view=rev Author: cduncan Date: 2007-11-08 14:47:41 -0800 (Thu, 08 Nov 2007) Log Message: ----------- Reduce fullerene shot detail at small apparent sizes Modified Paths: -------------- projectile.py Modified: projectile.py =================================================================== --- projectile.py 2007-11-08 22:47:14 UTC (rev 324) +++ projectile.py 2007-11-08 22:47:41 UTC (rev 325) @@ -141,21 +141,24 @@ def draw(self, surface): 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), (self.rect.center, (partsize, partsize))) - for i in self.step: - direction_x, direction_y = vector.to_tuple(vector.unit(self.rot)) - part = (self.rect.centerx + direction_x * radius, - self.rect.centery + direction_y * radius, - partsize, partsize) - rects.append(part) - bg = random.randint(0, self.colormax) - surface.fill((self.colormax, bg, self.colormax-bg) , part) - self.rot += self.partangle - self.rot += self.rot_per_frame - return Rect(rects[0]).unionall(rects) + if radius >= 2: + rects = [self.rect] + for i in self.step: + direction_x, direction_y = vector.to_tuple(vector.unit(self.rot)) + part = (self.rect.centerx + direction_x * radius, + self.rect.centery + direction_y * radius, + partsize, partsize) + rects.append(part) + bg = random.randint(0, self.colormax) + surface.fill((self.colormax, bg, self.colormax-bg) , part) + self.rot += self.partangle + self.rot += self.rot_per_frame + return Rect(rects[0]).unionall(rects) + else: + return self.rect def collide(self, other, contacts): if self.alive(): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cd...@us...> - 2007-11-08 22:47:16
|
Revision: 324 http://eos-game.svn.sourceforge.net/eos-game/?rev=324&view=rev Author: cduncan Date: 2007-11-08 14:47:14 -0800 (Thu, 08 Nov 2007) Log Message: ----------- - Build base level 2 on planets with >5 resource gen - Fix bug in explode() signature in Vessel Modified Paths: -------------- vessel.py Modified: vessel.py =================================================================== --- vessel.py 2007-11-07 08:22:34 UTC (rev 323) +++ vessel.py 2007-11-08 22:47:14 UTC (rev 324) @@ -568,12 +568,12 @@ else: return 0 - def explode(self): + def explode(self, sound='explode.wav'): if self is game.local_player.vessel: game.local_player.vessel = EscapePod(self) game.camera.follow(game.local_player.vessel) media.play_sound('launch.wav') - body.RoundBody.explode(self) + body.RoundBody.explode(self, sound) def kill(self): for system in self._sys: @@ -1249,7 +1249,9 @@ else: import base # Avoid circular imports # Landing cycle complete, build a base - self.planet.base = base.PlanetaryBase(self.planet, game.local_player) + + self.planet.base = base.PlanetaryBase(self.planet, game.local_player, + level=1 + (self.planet.resources >= 5)) message.send_status(self.planet, game.local_player, 'Base esablished on %s' % self.planet.name) self.vessel.kill() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cd...@us...> - 2007-11-07 08:22:35
|
Revision: 323 http://eos-game.svn.sourceforge.net/eos-game/?rev=323&view=rev Author: cduncan Date: 2007-11-07 00:22:34 -0800 (Wed, 07 Nov 2007) Log Message: ----------- Add a custom hit-and-run ai personality for the cress so it takes better advantage of its maneuverability and doesn't just hang out in the line of fire and get killed. Modified Paths: -------------- ai.py vessels/naree/cress Modified: ai.py =================================================================== --- ai.py 2007-11-07 08:17:21 UTC (rev 322) +++ ai.py 2007-11-07 08:22:34 UTC (rev 323) @@ -387,8 +387,8 @@ class AggroAI(BasicAI): - evade_min_health = 0.5 - evade_max_health = 0.7 + evade_min_health = 0.4 + evade_max_health = 0.5 def evade(self): """Head through the target""" @@ -408,6 +408,16 @@ self.steerfunc = self.standoff +class HitAndRun(BasicAI): + + evade_min_health = 0.7 + evade_max_health = 0.75 + evade_damage_timeout = 1200 + evade_max_distance = 250 + + evade = BasicAI.flee + + class AssaultAI(BasicAI): def select_steerfunc(self): Modified: vessels/naree/cress =================================================================== --- vessels/naree/cress 2007-11-07 08:17:21 UTC (rev 322) +++ vessels/naree/cress 2007-11-07 08:22:34 UTC (rev 323) @@ -11,7 +11,7 @@ max_energy: 200 standoff_distance: 125 cost: 125 -ai: Standoffish +ai: HitAndRun [vessel.Shield] max_level: 100 This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cd...@us...> - 2007-11-07 08:18:09
|
Revision: 322 http://eos-game.svn.sourceforge.net/eos-game/?rev=322&view=rev Author: cduncan Date: 2007-11-07 00:17:21 -0800 (Wed, 07 Nov 2007) Log Message: ----------- - Add player abstraction to store player game state, such as current vessel, race, category and owned planets - Human players can switch ships by targetting a friendly with "t" and pressing backspace/delete. the ai assumes control of the player's former vessel - When the player's ship is destroyed, a small escape pod is ejected which drifts through space. The escape pod is invulnerable, but cannot be controlled (it is in fact invisible to other players). this provides a simple avatar for the player before a new ship is selected. Note: these changes likely broke multiplayer, though I did try to refactor where I could. Modified Paths: -------------- ai.py base.py beam.py body.py event.py game.py net.py panel.py selection.py station.py vessel.py Added Paths: ----------- data/astronaut.png player.py Modified: ai.py =================================================================== --- ai.py 2007-11-04 06:03:37 UTC (rev 321) +++ ai.py 2007-11-07 08:17:21 UTC (rev 322) @@ -306,7 +306,7 @@ planets = sorted(game.map.planets, key=distance) # Try for the closest friendly planet first for planet in planets: - if planet.base is not None and planet.base.owner.is_friendly(self.vessel): + if planet.base is not None and self.vessel.is_friendly(planet.base.owner): self.objective.add(planet) return # Try for the closest neutral planet next @@ -487,11 +487,18 @@ else: BasicAI.collide(self, other, contacts) +def control(vessel, target=None, objective=None, sensor=None): + """Return the appropriate ai control instance for the specified vessel""" + if sensor is None and vessel.category is not None: + sensor = Sensor(vessel, 10000, body.everything & ~vessel.category) + sensor.disable() + ai_class = globals()[vessel.ai_name] + return ai_class(vessel, target, objective, sensor) class AIVessel(Vessel): """Vessel under ai control""" - def __init__(self, target=None, category=body.foe, objective=None, sensor=None, + def __init__(self, target=None, category=None, objective=None, sensor=None, ai='BasicAI', **kw): """ target -- Sprite to intercept and attack if foe. @@ -500,15 +507,13 @@ sensor -- Sensor object to be used (if omitted one is created just for this vessel) ai -- class name of ai personality. """ - 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() - ai_class = globals()[ai] - self.control = ai_class(self, target, objective, sensor) + Vessel.__init__(self, ai=ai, **kw) + if category is not None: + self.setup_collision(category, body.everything & ~body.shot) + self.control = control(self, target, objective, sensor) # Make sure we are behind the local player self.layer.to_back(self) + class Sensor: Modified: base.py =================================================================== --- base.py 2007-11-04 06:03:37 UTC (rev 321) +++ base.py 2007-11-07 08:17:21 UTC (rev 322) @@ -14,8 +14,9 @@ import map import message import panel +import sprite -class PlanetaryBase: +class PlanetaryBase(sprite.Sprite): """Planetary bases are owned by a player and can build using the planet's resources. Bases have a level which determines which tings can be built. In general bases can build three types of things: @@ -43,14 +44,18 @@ max_build_queue = 6 # Maximum number of queued builders def __init__(self, planet, owner, level=1, max_level=5): + sprite.Sprite.__init__(self) if isinstance(planet, str): self.planet = game.map.get_planet(planet_name) if self.planet is None: raise map.MapConfigError('Cannot create base, planet "%s" not found in map') else: self.planet = planet + if self.planet.base is not None: + self.planet.base.kill() self.planet.base = self self.owner = owner + owner.bases.add(self) self.rally_point = self.planet if max_level >= level: self.level = level Modified: beam.py =================================================================== --- beam.py 2007-11-04 06:03:37 UTC (rev 321) +++ beam.py 2007-11-07 08:17:21 UTC (rev 322) @@ -54,7 +54,7 @@ self.ray.setCategoryBits(body.nothing) 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: + if self.arc > 0 and self.gunmount is game.local_player.vessel and game.target.target: # Track the selected target track_angle = self.gunmount.bearing(game.target.target) if abs(track_angle) > self.arc: Modified: body.py =================================================================== --- body.py 2007-11-04 06:03:37 UTC (rev 321) +++ body.py 2007-11-07 08:17:21 UTC (rev 322) @@ -22,13 +22,9 @@ # bitmap to determine whether it interacts with another body # Objects only interact when the category bitmap of one # intersects the collide bitmap of the other. -friend = 1 -foe = 2 -shot = 4 -planet = 8 +shot = 1 nothing = 0 everything = 0x7fffffff -categories = (friend, foe) body_count = 0 # Counter for debugging @@ -147,8 +143,10 @@ self.collides_with = collides_with if game.local_player and self.category & game.local_player.category: self.offscreen_img = 'pointy-green' - elif self.category: + elif self.category and not self.incidental: self.offscreen_img = 'pointy-red' + else: + self.offscreen_img = None def __del__(self): if self.geom is not None and game.collision_space.query(self.geom): Added: data/astronaut.png =================================================================== (Binary files differ) Property changes on: data/astronaut.png ___________________________________________________________________ Name: svn:mime-type + image/png Modified: event.py =================================================================== --- event.py 2007-11-04 06:03:37 UTC (rev 321) +++ event.py 2007-11-07 08:17:21 UTC (rev 322) @@ -180,7 +180,7 @@ def select_nearest_friends(self): """Select friends nearest to player""" - selection.group.select_near_vessel(game.local_player, game.local_player.category) + selection.group.select_near_vessel(game.local_player.vessel, game.local_player.category) def constrain_selected_friends(self): """Constrain friend selection by vessel class""" @@ -205,8 +205,14 @@ def target_local_player(self): """Target your ship""" - game.target.select(game.local_player) + game.target.select(game.local_player.vessel) + def switch_local_player_vessel(self): + """Change ships""" + target = game.target.selected.sprite + if target: + game.local_player.switch_vessel(target) + def __init__(self): sprite.Sprite.__init__(self, sprite.layers.ui) key_map = { @@ -219,6 +225,7 @@ 'p': self.target_nearest_planet, 't': self.target_nearest_friend, 'y': self.target_local_player, + '\b': self.switch_local_player_vessel, } self.key_map = dict((ord(key), method) for key, method in key_map.items()) @@ -240,7 +247,8 @@ and not vessel.incidental): if event.button == 1: # Left-click, select - if vessel.is_friendly(game.local_player) and vessel is not game.local_player: + if vessel is not game.local_player.vessel and vessel.is_friendly( + game.local_player.vessel): selection.group.select(vessel) else: game.target.select(vessel) Modified: game.py =================================================================== --- game.py 2007-11-04 06:03:37 UTC (rev 321) +++ game.py 2007-11-07 08:17:21 UTC (rev 322) @@ -55,6 +55,7 @@ from media import load_image from map import Map from camera import Camera + import player global universe, collision_space, map, starfield global fps, avg_fps, clock, time, local_player, camera, ai global target, windowed, messenger, send_msg @@ -148,21 +149,20 @@ ship_choice.kill() time = 0 - # Establish player - local_player = vessel.KeyboardControl.new_player(race, ship) - local_player.number = random.choice([1, 2]) # XXX Fix for real multiplayer - camera = Camera(local_player) - target = selection.Target(local_player) + # Establish player and ship, for now randomly select player number 1 or 2 + local_player = player.HumanPlayer(random.choice([1, 2]), race, ship) + camera = Camera(local_player.vessel) + target = selection.Target(local_player.vessel) # Setup background and map starfield = StarField(display.rect) map = Map('sol') - + # Find the base and put the player next to it for planet in map.planets: if planet.base is not None and planet.base.owner is local_player: offset = vector.vector2(planet.collision_radius * 1.25, planet.collision_radius * 1.25) - local_player.set_position(planet.position + offset) + local_player.vessel.set_position(planet.position + offset) # Setup local messaging message.init(local_player) @@ -257,21 +257,21 @@ ## Temporary wave-based game play ## if not client: if time > next_wave: - target = local_player - target_race = target.race + target = local_player.vessel wave_race = random.choice([r for r in ['naree', 'rone', 'sc'] - if r != target_race]) + if r != local_player.race]) + wave_category = local_player.category<<2 if not target.alive(): target = random.choice(map.planets) position = vector.unit(random.random() * vector.fullcircle) * 1000 warship = random.random() * frame_no > 800 if warship and len(enemies) < max_fleet_size: if wave_race == 'rone': - ai = AIVessel.load('vessels/rone/draken') + ai = AIVessel.load('vessels/rone/draken', category=wave_category) elif wave_race == 'naree': - ai = AIVessel.load('vessels/naree/lotus') + ai = AIVessel.load('vessels/naree/lotus', category=wave_category) elif wave_race == 'sc': - ai = AIVessel.load('vessels/sc/pegasus') + ai = AIVessel.load('vessels/sc/pegasus', category=wave_category) ai.set_position(target.position + position) enemies.add(ai) else: @@ -281,20 +281,20 @@ while ((friendly or random.random() * frame_no > barrier) and len(friends) < max_fleet_size): if random.random() * frame_no < barrier * 6: - if target_race == 'rone': + if local_player.race == 'rone': ship = 'vessels/rone/drach' - elif target_race == 'naree': + elif local_player.race == 'naree': ship = 'vessels/naree/cress' - elif target_race == 'sc': + elif local_player.race == 'sc': ship = 'vessels/sc/striker' else: - if target_race == 'rone': + if local_player.race == 'rone': ship = 'vessels/rone/draken' - elif target_race == 'naree': + elif local_player.race == 'naree': ship = 'vessels/naree/lotus' - elif target_race == 'sc': + elif local_player.race == 'sc': ship = 'vessels/sc/pegasus' - friend = AIVessel.load(ship, objective=target, category=body.friend) + friend = AIVessel.load(ship, objective=target, category=local_player.category) friend.set_position(target.position - position) friends.add(friend) barrier *= 2 @@ -304,11 +304,11 @@ if warship and random.random() * frame_no < barrier: break if wave_race == 'rone': - ai = AIVessel.load('vessels/rone/drach') + ai = AIVessel.load('vessels/rone/drach', category=wave_category) elif wave_race == 'naree': - ai = AIVessel.load('vessels/naree/cress') + ai = AIVessel.load('vessels/naree/cress', category=wave_category) elif wave_race == 'sc': - ai = AIVessel.load('vessels/sc/striker') + ai = AIVessel.load('vessels/sc/striker', category=wave_category) ai.set_position(target.position + position) enemies.add(ai) warship = True Modified: net.py =================================================================== --- net.py 2007-11-04 06:03:37 UTC (rev 321) +++ net.py 2007-11-07 08:17:21 UTC (rev 322) @@ -87,8 +87,9 @@ net_id, ship, keystate = client_state player = game.objects.get(net_id) if player is None: - player = vessel.KeyboardControl.new_player(net_id=net_id, ship=ship) - player.setup_collision(body.foe, body.nothing) + player = Vessel.load(ship, net_id=net_id) + player.control = vessel.KeyboardControl() + player.setup_collision(game.local_player.category<<2, body.nothing) player.control.set_keystate(keystate) def step(self): @@ -145,7 +146,7 @@ return create_func(*args, **kw) def get_client_state(self, keystate): - return game.local_player.net_id, game.local_player.vessel_class, keystate + return game.local_player.vessel.net_id, game.local_player.vessel.vessel_class, keystate def set_game_state(self, game_state): # update objects Modified: panel.py =================================================================== --- panel.py 2007-11-04 06:03:37 UTC (rev 321) +++ panel.py 2007-11-07 08:17:21 UTC (rev 322) @@ -305,7 +305,7 @@ def update_vessel(self, vessel): if not vessel.incidental: - if vessel.is_friendly(game.local_player): + if vessel.is_friendly(game.local_player.vessel): if vessel.selected: color = (255, 255, 255) else: Added: player.py =================================================================== --- player.py (rev 0) +++ player.py 2007-11-07 08:17:21 UTC (rev 322) @@ -0,0 +1,67 @@ +## Eos, Dawn of Light -- A Space Opera +## Copyright (c) 2007 Casey Duncan and contributors +## See LICENSE.txt for licensing details + +# Player state +# $Id$ + +import glob +import random +import pygame +import game +import body +from vessel import Vessel, KeyboardControl +import ai +import selection + +class Player: + + number = None # Player number + race = None # Player race + category = None # Player category bitmap + vessel = None # Current player vessel + control = None # Player's vessel controls + bases = None # Bases owned by player + + +class HumanPlayer(Player): + """A player presumably controlled by a hunam""" + + def __init__(self, number, race, vessel_class): + self.number = number + self.race = race + self.category = 2**number + self.bases = pygame.sprite.Group() + self.control = KeyboardControl() + self.create_vessel(vessel_class) + + def create_vessel(self, vessel_class=None, net_id=None): + """Create a vessel for this player""" + config = random.choice(glob.glob('vessels/%s/%s' % (self.race or '*', vessel_class or '*'))) + self.race = config.split('/')[1] + self.vessel = Vessel.load(config, race=self.race, net_id=net_id) + self.vessel.setup_collision(self.category, body.nothing) + self.vessel.control = self.control + return self.vessel + + def switch_vessel(self, vessel): + """Make the specified vessel the player's current vessel. If the player + already has a current vessel, it is put under ai control + """ + if vessel.category == self.vessel.category and vessel is not self.vessel: + if isinstance(self.vessel, Vessel): + # Replace the player control with the appropriate ai control + self.vessel.control = ai.control( + self.vessel, target=game.target.selected.sprite, objective=vessel) + self.vessel.setup_collision(self.category, body.everything & ~body.shot) + else: + # We don't currently have a real vessel, just an avatar + self.vessel.kill() + vessel.setup_collision(self.category, body.nothing) + vessel.control = self.control + vessel.layer.to_front(vessel) + game.camera.follow(vessel) + game.target.kill() + game.target = selection.Target(vessel) # ick, make me better + self.vessel = vessel + Property changes on: player.py ___________________________________________________________________ Name: svn:keywords + Id Name: svn:eol-style + native Modified: selection.py =================================================================== --- selection.py 2007-11-04 06:03:37 UTC (rev 321) +++ selection.py 2007-11-07 08:17:21 UTC (rev 322) @@ -152,7 +152,7 @@ by_class = {} for spr in sprite.layers.vessels: if (isinstance(spr, Vessel) and spr.category & category and in_rect(spr.rect) - and spr is not game.local_player and not spr.incidental): + and spr is not game.local_player.vessel and not spr.incidental): self.selected.add(spr) if spr.vessel_class not in by_class: by_class[spr.vessel_class] = pygame.sprite.Group() @@ -170,7 +170,7 @@ for spr in sprite.layers.vessels: if (isinstance(spr, Vessel) and spr.category & category and in_rect(vector.to_tuple(spr.position)) - and spr is not game.local_player and not spr.incidental): + and spr is not game.local_player.vessel and not spr.incidental): self.selected.add(spr) if spr.vessel_class not in by_class: by_class[spr.vessel_class] = pygame.sprite.Group() Modified: station.py =================================================================== --- station.py 2007-11-04 06:03:37 UTC (rev 321) +++ station.py 2007-11-07 08:17:21 UTC (rev 322) @@ -123,12 +123,12 @@ self.collision_frame = 0 def update(self): - if vector.distance(game.local_player.position, self.planet.position) > self.distance: + if vector.distance(game.local_player.vessel.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 + game.local_player.vessel.heading) * self.distance self.geom.setPosition(vector.to_tuple3(self.position)) self.heading = vector.radians(self.planet.position - self.position) apparent_size, screen_pos = vector.to_screen(self.position) Modified: vessel.py =================================================================== --- vessel.py 2007-11-04 06:03:37 UTC (rev 321) +++ vessel.py 2007-11-07 08:17:21 UTC (rev 322) @@ -9,7 +9,6 @@ import body import ConfigParser import game -import glob import math import pygame import random @@ -58,24 +57,6 @@ self.weapons[1] = keystate[K_LSHIFT] self.target = game.target.selected - @classmethod - def new_player(cls, race=None, ship=None, net_id=None): - if ship is not None: - shipglob = 'vessels/*/%s' % ship - elif race is not None: - shipglob = 'vessels/%s/*' % race - else: - shipglob = 'vessels/*/*' - ships = glob.glob(shipglob) - assert len(ships) > 0, 'Invalid race/ship: %r/%r' % (race, ship) - ship = random.choice(ships) - race = ship.split('/')[1] - player = Vessel.load(ship, race=race, net_id=net_id) - player.setup_collision(body.friend, body.nothing) - player.control = KeyboardControl() - return player - - class VesselConfigError(Exception): """Vessel configuration file error""" @@ -178,6 +159,7 @@ self._repair_time = None self.damage_time = 0 self.race = race + self.ai_name = ai self.incidental = str(incidental) not in ('0', 'False') @classmethod @@ -551,7 +533,7 @@ message.send_status( self, self, '%s Damaged' % system.name.title(), message.important) self.system_damage -= self.system_damage_threshold - if game.local_player is self: + if game.local_player.vessel is self: media.play_sound('power_down.wav', min_spacing=2) if self.damage_smoke is None and not self.incidental: self.damage_smoke = particle.SmokeTrail(self) @@ -586,6 +568,13 @@ else: return 0 + def explode(self): + if self is game.local_player.vessel: + game.local_player.vessel = EscapePod(self) + game.camera.follow(game.local_player.vessel) + media.play_sound('launch.wav') + body.RoundBody.explode(self) + def kill(self): for system in self._sys: if hasattr(system, 'kill'): @@ -1274,6 +1263,30 @@ vector.distance(target.position, self.vessel.position) < target.collision_radius / 2) +class EscapePod(body.RoundBody): + + max_speed = 25 + layer = sprite.layers.vessels + collision_radius = 0 + + def __init__(self, launch_vessel): + body.RoundBody.__init__(self, + position=launch_vessel.position, + velocity=(launch_vessel.velocity + - vector.normal(-launch_vessel.velocity) * self.max_speed), + heading=launch_vessel.heading + vector.halfcircle, + image_name='astronaut.png') + self.turn_rate = random.gauss(0, 0.4) + media.play_sound('launch.wav') + self.category = launch_vessel.category + + def update(self): + if vector.length(self.velocity) > self.max_speed: + # If we are overspeed, bleed off a little + self.set_velocity(self.velocity * 0.98) + body.RoundBody.update(self) + + def resolve(dotted_name): """Return the object corresponding to the dotted module/object name pair specified. This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cd...@us...> - 2007-11-04 06:03:48
|
Revision: 321 http://eos-game.svn.sourceforge.net/eos-game/?rev=321&view=rev Author: cduncan Date: 2007-11-03 23:03:37 -0700 (Sat, 03 Nov 2007) Log Message: ----------- - Assault ships now called assault transports, since they double as transports - Add sc assault transport ship: aurora - Even out stats for assault vessels, improve turning thrust - Add ability to direct ships to arbitrary locations in space on the map and playing field Modified Paths: -------------- base.py body.py event.py game.py ideas/vessels.txt panel.py selection.py vector.py vessel.py vessels/naree/corde vessels/rone/kraken Added Paths: ----------- art/sc/aurora.xcf data/aurora.png vessels/sc/aurora Added: art/sc/aurora.xcf =================================================================== (Binary files differ) Property changes on: art/sc/aurora.xcf ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Modified: base.py =================================================================== --- base.py 2007-11-04 05:58:12 UTC (rev 320) +++ base.py 2007-11-04 06:03:37 UTC (rev 321) @@ -36,7 +36,7 @@ # base level required to build certain vessels vessel_level = { 'fighter': 1, - 'assault ship': 1, + 'assault transport': 1, 'warship': 2, } _owner2number = {} # Map of owner => last base number Modified: body.py =================================================================== --- body.py 2007-11-04 05:58:12 UTC (rev 320) +++ body.py 2007-11-04 06:03:37 UTC (rev 321) @@ -52,6 +52,7 @@ offscreen_img = None # image to point to offscreen sprite xplode_animation = media.LargeExplosion + explosion = None layer = () # Sprite group(s) to add body to rect = None @@ -94,7 +95,6 @@ self.rect = pygame.Rect((0, 0, self.radius*2, self.radius*2)) self.enabled = True self.on_screen = True - self.explosion = None self.apparent_size = 1.0 self.net_id = net_id self.image_name = image_name Added: data/aurora.png =================================================================== (Binary files differ) Property changes on: data/aurora.png ___________________________________________________________________ Name: svn:mime-type + image/png Modified: event.py =================================================================== --- event.py 2007-11-04 05:58:12 UTC (rev 320) +++ event.py 2007-11-04 06:03:37 UTC (rev 321) @@ -262,8 +262,14 @@ # Right-click, direct selection.group.set_target(planet) return True - # Nothing specific was clicked, just clear the current selected group - selection.group.select_none() + # Nothing specific was clicked + if event.button == 1: + # left-click in space, just clear the current selected group + selection.group.select_none() + elif event.button == 3: + # right-click in space, make a target marker and direct ships there + marker = selection.Marker(vector.to_map(vector.vector2(*event.pos))) + selection.group.set_target(marker) return True def mouse_drag(self, event, start_pos, end_pos): Modified: game.py =================================================================== --- game.py 2007-11-04 05:58:12 UTC (rev 320) +++ game.py 2007-11-04 06:03:37 UTC (rev 321) @@ -102,6 +102,7 @@ Vessel.load('vessels/naree/corde'), Vessel.load('vessels/sc/pegasus'), Vessel.load('vessels/sc/striker'), + Vessel.load('vessels/sc/aurora'), ] import math font = pygame.font.Font('fonts/forgottenfuturist/Forgotbi.ttf', 24) Modified: ideas/vessels.txt =================================================================== --- ideas/vessels.txt 2007-11-04 05:58:12 UTC (rev 320) +++ ideas/vessels.txt 2007-11-04 06:03:37 UTC (rev 321) @@ -73,11 +73,11 @@ Naree Cress: Lepton beam Human Striker: Burst-fire rockets -Secondary Weapon ----------------- +Secondary Weapon/Ability +------------------------ Rone Drach: Phase disruptors reduce shield regeneration and deflector screen efficiency -Naree Cress: Energy mines sap energy from enemy craft -Human Striker: Higgs well mines slow enemy movement +Naree Cress: Rush ability allows them to approach unhindered with immediacy +Human Striker: Plasmonic mines are invisible when not in motion, cause severe damage in an area upon contact. Warships ======== Modified: panel.py =================================================================== --- panel.py 2007-11-04 05:58:12 UTC (rev 320) +++ panel.py 2007-11-04 06:03:37 UTC (rev 321) @@ -358,6 +358,10 @@ # Right-click, direct selection.group.set_target(planet) return True + if event.button == 3: + # right-click in space, make a target marker and direct ships there + marker = selection.Marker(vector.vector2(*map_pos)) + selection.group.set_target(marker) return True def draw(self, surface): @@ -393,7 +397,7 @@ vessel_button_order = { 'fighter': 1, - 'assault ship': 2, + 'assault transport': 2, 'warship': 3, } Modified: selection.py =================================================================== --- selection.py 2007-11-04 05:58:12 UTC (rev 320) +++ selection.py 2007-11-04 06:03:37 UTC (rev 321) @@ -14,7 +14,7 @@ import sprite from vessel import Vessel import vector -import staticbody +import body class Target(sprite.Sprite): @@ -213,3 +213,19 @@ group = GroupSelector() # Singleton + +class Marker(body.RoundBody): + """Position marker for targetting arbitrary positions in space""" + + rect = pygame.Rect(0, 0, 0, 0) + velocity = vector.vector2() + heading = 0 + collision_radius = 0 + + def __init__(self, position): + sprite.Sprite.__init__(self) + self.position = position + + def draw(self): + return self.rect + Modified: vector.py =================================================================== --- vector.py 2007-11-04 05:58:12 UTC (rev 320) +++ vector.py 2007-11-04 06:03:37 UTC (rev 321) @@ -56,6 +56,30 @@ return apparent_size**2, vector2(*camera.rect.center) + ( from_camera * camera.zoom * apparent_size) +def to_map(screen_pos, slop=50, max_iterations=100): + """Return a vector of the approximate map coordinate for the given screen position. + Note that without knowing the apparent size at that screen position, there is + no unambiguous way to derive the exact map position. + """ + approx_pos = game.camera.position + size, approx_screen_pos = to_screen(approx_pos) + shift = (screen_pos - approx_screen_pos) * size + last_error = distance(screen_pos, approx_screen_pos) + while max_iterations > 0: + last_pos = approx_pos + approx_pos += shift + size, approx_screen_pos = to_screen(approx_pos) + error = distance(screen_pos, approx_screen_pos) + if error < slop: + return last_pos + print approx_screen_pos, screen_pos, last_error, error + if error > last_error: + shift = (screen_pos - approx_screen_pos) * size + approx_pos = last_pos + last_error = error + max_iterations -= 1 + return last_pos + # angle aliases fullcircle = pi * 2 halfcircle = pi Modified: vessel.py =================================================================== --- vessel.py 2007-11-04 05:58:12 UTC (rev 320) +++ vessel.py 2007-11-04 06:03:37 UTC (rev 321) @@ -1052,7 +1052,7 @@ self.vessel.body.addTorque((0, 0, self.vessel.control.turn * self.thrust)) else: # slow or stop turning with no input - if abs(turn_rate) < 0.25: + if abs(turn_rate) < 0.15: self.vessel.turn_rate = 0 elif turn_rate > 0: self.vessel.body.addTorque((0,0,-self.thrust)) Modified: vessels/naree/corde =================================================================== --- vessels/naree/corde 2007-11-04 05:58:12 UTC (rev 320) +++ vessels/naree/corde 2007-11-04 06:03:37 UTC (rev 321) @@ -2,7 +2,7 @@ [general] vessel_class: corde -vessel_type: assault ship +vessel_type: assault transport description: As a pacifist race, the Naree have had no use for a planetary assault vessel. When it became clear that one was required, they refitted one of their ubiquitous freighter hulls for the purpose. The Naree abhor barbaric infantry warfare, and instead utilize a powerful stasis technology which renders the indigenous populations or enemy forces inert indefinitely with little or no bloodshed. Naree scientists and engineers are currently working on a space-born application of this technology to curtail the senseless violent plague of destruction in the skies. hull_mass: 80 hull_length: 50 @@ -12,12 +12,11 @@ ai: AssaultAI cost: 400 -[vessel.Shield] -max_level: 1200 -regeneration: 10 +[vessel.Armor] +durability: 1200 [vessel.DirectionalThrusters] -thrust: 200 +thrust: 300 max_turn_rate: 1.0 [vessel.ManeuveringThrusters] Modified: vessels/rone/kraken =================================================================== --- vessels/rone/kraken 2007-11-04 05:58:12 UTC (rev 320) +++ vessels/rone/kraken 2007-11-04 06:03:37 UTC (rev 321) @@ -2,22 +2,21 @@ [general] vessel_class: kraken -vessel_type: assault ship +vessel_type: assault transport 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 +hull_mass: 80 +hull_length: 50 crew: 100 max_speed: 80 max_energy: 1000 ai: AssaultAI cost: 400 -[vessel.Shield] -max_level: 1500 -regeneration: 6 +[vessel.Armor] +durability: 1200 [vessel.DirectionalThrusters] -thrust: 200 +thrust: 300 max_turn_rate: 1.0 [vessel.ManeuveringThrusters] Added: vessels/sc/aurora =================================================================== --- vessels/sc/aurora (rev 0) +++ vessels/sc/aurora 2007-11-04 06:03:37 UTC (rev 321) @@ -0,0 +1,28 @@ +# $Id: lotus 271 2007-09-29 16:46:25Z cduncan $ + +[general] +vessel_class: aurora +vessel_type: assault transport +description: What the aurora lacks in form, it more than makes up for in function. Carrying a horde of marines and an immense quantity of equipment, ammunition and supplies, and aurora can make short work of opposing ground forces and erect a new base in short order. Marines in space are an impatient lot, however, and they've been know to partake in some extravehicular activities for "fun". +hull_mass: 80 +hull_length: 50 +crew: 100 +max_speed: 80 +max_energy: 1000 +ai: AssaultAI +cost: 400 + +[vessel.Armor] +durability: 1200 + +[vessel.DirectionalThrusters] +thrust: 300 +max_turn_rate: 1.0 + +[vessel.ManeuveringThrusters] +thrust: 100 + +[vessel.PlanetaryLander] + +[vessel.Engine] +thrust: 80 Property changes on: vessels/sc/aurora ___________________________________________________________________ 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-11-04 05:58:33
|
Revision: 320 http://eos-game.svn.sourceforge.net/eos-game/?rev=320&view=rev Author: cduncan Date: 2007-11-03 22:58:12 -0700 (Sat, 03 Nov 2007) Log Message: ----------- Improved ai behavior when near its stationary target Modified Paths: -------------- ai.py Modified: ai.py =================================================================== --- ai.py 2007-11-02 04:48:28 UTC (rev 319) +++ ai.py 2007-11-04 05:58:12 UTC (rev 320) @@ -177,7 +177,9 @@ if not target.velocity: # stationary target heading = vector.radians(to_target) - desired_dist = target.collision_radius * 1.25 + desired_dist = target.collision_radius + self.vessel.collision_radius * 5 + if distance < desired_dist: + return self.vessel.heading, vector.vector2() elif self.vessel.is_friendly(target): heading = target.heading desired_dist = self.vessel.collision_radius * 4 + target.collision_radius * 3 This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cd...@us...> - 2007-11-02 04:48:30
|
Revision: 319 http://eos-game.svn.sourceforge.net/eos-game/?rev=319&view=rev Author: cduncan Date: 2007-11-01 21:48:28 -0700 (Thu, 01 Nov 2007) Log Message: ----------- - Left click to select, right click to direct Modified Paths: -------------- event.py panel.py Modified: event.py =================================================================== --- event.py 2007-11-01 22:01:50 UTC (rev 318) +++ event.py 2007-11-02 04:48:28 UTC (rev 319) @@ -109,7 +109,7 @@ def mouse_up(self, event): """Handle mouse button release""" self.show_mouse() - click_pos = vector.to_tuple(self.mouse_down_pos) + click_pos = self.mouse_down_pos and vector.to_tuple(self.mouse_down_pos) or event.pos self.mouse_down_pos = None if self.mouse_handler is not None: if self.drag and self.mouse_handler.mouse_drag_end(event, click_pos, event.pos): @@ -228,6 +228,8 @@ return True def mouse_click(self, event, pos): + if event.button not in (1, 3): + return False # We only handle left and right clicks # First check for vessel click click_rect = pygame.Rect(0, 0, 10, 10) click_rect.center = pos @@ -235,19 +237,31 @@ vessels.reverse() for vessel in vessels: if (isinstance(vessel, Vessel) and vessel.rect.colliderect(click_rect) - and vessel is not game.local_player and not vessel.incidental): - if vessel.is_friendly(game.local_player): - selection.group.select(vessel) - else: - game.target.select(vessel) - return True + and not vessel.incidental): + if event.button == 1: + # Left-click, select + if vessel.is_friendly(game.local_player) and vessel is not game.local_player: + selection.group.select(vessel) + else: + game.target.select(vessel) + return True + elif event.button == 3: + # Right-click, direct + selection.group.set_target(vessel) + return True # No vessel was there, see about planets planets = [s for s in sprite.layers.background if isinstance(s, Planet)] planets.reverse() for planet in planets: if planet.rect.colliderect(click_rect): - game.target.select(planet) - return True + if event.button == 1: + # Left-click, select + game.target.select(planet) + return True + elif event.button == 3: + # Right-click, direct + selection.group.set_target(planet) + return True # Nothing specific was clicked, just clear the current selected group selection.group.select_none() return True Modified: panel.py =================================================================== --- panel.py 2007-11-01 22:01:50 UTC (rev 318) +++ panel.py 2007-11-02 04:48:28 UTC (rev 319) @@ -339,6 +339,8 @@ return True def mouse_click(self, event, pos): + if event.button not in (1, 3): + return False # We only handle left and right clicks if self.rect.collidepoint(pos): # Translate the position to map coodinates map_pos = ((pos[0] - self.rect.centerx) / self.map_scale, @@ -348,8 +350,14 @@ planet_rect.width = planet_rect.height = planet.collision_radius * 4 planet_rect.center = vector.to_tuple(planet.position) if planet_rect.collidepoint(map_pos): - game.target.select(planet) - break + if event.button == 1: + # Left-click, select + game.target.select(planet) + return True + elif event.button == 3: + # Right-click, direct + selection.group.set_target(planet) + return True return True def draw(self, surface): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cd...@us...> - 2007-11-01 22:01:58
|
Revision: 318 http://eos-game.svn.sourceforge.net/eos-game/?rev=318&view=rev Author: cduncan Date: 2007-11-01 15:01:50 -0700 (Thu, 01 Nov 2007) Log Message: ----------- - Add event handler apis and supporting code for higher level mouse events: click, double click and drag - Add click and drag selection support to playing field and minimap. - Add 'incidental' flag to body objects so they can be configured not to show on maps or be selectable (e.g., gnats). This removes a bunch of special case checking throughout. - Fix target timeout when setting the target of ai vessels explicitly. Also, always set the objective as well, to allow ai vessels to engage enemies on their way to their eventual target objective. - Fix rare div by zero bug in tracker Modified Paths: -------------- ai.py body.py camera.py event.py game.py panel.py selection.py vessel.py vessels/naree/gnat Modified: ai.py =================================================================== --- ai.py 2007-10-30 07:25:40 UTC (rev 317) +++ ai.py 2007-11-01 22:01:50 UTC (rev 318) @@ -22,7 +22,6 @@ class BasicAI(Control): """Basic AI Control""" - target_timeout = 5000 # Maximum distance to target when we have an objective target_max_distance_with_objective = 2000 @@ -286,21 +285,37 @@ vector.distance(self.vessel.position, self.sensor.closest_vessel.sprite.position) < self.target_max_distance_with_objective)): - self.target.add(self.sensor.closest_vessel) - self.target_time = game.time + self.target_timeout + self.set_target(self.sensor.closest_vessel) 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.objective: - # head for the objective - self.target.add(self.objective) - else: - # No objective, just head toward a planet - self.target.add(game.map.planets) + if not self.objective: + self.choose_objective() + # head for the objective looking for other targets + self.set_target(self.objective, timeout=0) - def set_target(self, target, timeout=None): + def choose_objective(self): + """Set the ship's objective, which by default is to head to the nearest + friendly or neutral planet + """ + def distance(planet): + return vector.distance(self.vessel.position, planet.position) + planets = sorted(game.map.planets, key=distance) + # Try for the closest friendly planet first + for planet in planets: + if planet.base is not None and planet.base.owner.is_friendly(self.vessel): + self.objective.add(planet) + return + # Try for the closest neutral planet next + for planet in planets: + if planet.base is None: + self.objective.add(planet) + return + # Just head for the closest planet at all + self.objective.add(planets[0]) + + def set_target(self, target, timeout=3000): """Set the target for the ai, timing out in timeout seconds at which time it will acquire another target """ @@ -591,16 +606,16 @@ # This is a new frame, start a new sensor sweep self._detected = [] self._closest_dist = sys.maxint - self._closest_type = None + self._closest_incidental = False self.closest_vessel.empty() self.last_sweep = game.frame_no distance = vector.distance(self.vessel.position, other.position) self._detected.append((distance, other)) if (isinstance(other, Vessel) and distance < self._closest_dist - or self._closest_type == 'missile' and other.vessel_type != 'missle'): + or self._closest_incidental and other.incidental): self.closest_vessel.sprite = other self._closest_dist = distance - self._closest_type = other.vessel_type + self._closest_incidental = other.incidental @property def detected(self): @@ -679,7 +694,7 @@ top += x * y bot_left += x * x bot_right += y * y - correlation = abs(top / math.sqrt(bot_left * bot_right)) + correlation = abs(top / math.sqrt(bot_left * bot_right or float('nan'))) endx, endy = self.samples[-1] v2 = vector.vector2(endx - startx, endy - starty) / ( len(self.samples) * self.sample_time) * time_ahead Modified: body.py =================================================================== --- body.py 2007-10-30 07:25:40 UTC (rev 317) +++ body.py 2007-11-01 22:01:50 UTC (rev 318) @@ -42,6 +42,7 @@ 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 + incidental = False # Incidental bodies cannot be selected, do not show on maps, etc category = nothing # category for collision interaction collides_with = nothing # category bitmap this body collides with Modified: camera.py =================================================================== --- camera.py 2007-10-30 07:25:40 UTC (rev 317) +++ camera.py 2007-11-01 22:01:50 UTC (rev 318) @@ -58,8 +58,8 @@ def acquire_target(self): # Select a new target - for ship in sprite.layers.vessels.sprites(): - if (ship.alive() and getattr(ship, 'vessel_type', None) not in ('missile', 'station')): + for ship in sprite.layers.vessels: + if ship.alive() and not ship.incidental: self.follow(ship) break Modified: event.py =================================================================== --- event.py 2007-10-30 07:25:40 UTC (rev 317) +++ event.py 2007-11-01 22:01:50 UTC (rev 318) @@ -13,6 +13,9 @@ import vector import panel import selection +import sprite +from vessel import Vessel +from staticbody import Planet class Handler: @@ -26,6 +29,18 @@ def mouse_down(self, event): """Handle mouse button press""" + + def mouse_click(self, event, pos): + """Single mouse click at pos, event contains mouse button info""" + + def mouse_double_click(self, event, pos): + """Double mouse click at pos, event contains mouse button info""" + + def mouse_drag(self, event, start_pos, end_pos): + """Mouse drag in progress from start_pos to end_pos, event contains mouse button info""" + + def mouse_drag_end(self, event, start_pos, end_pos): + """Mouse drag completed from tart_pos to end_pos, event contains mouse button info""" def key_down(self, event): """Handle key press""" @@ -35,6 +50,8 @@ """Top-level event handler""" mouse_timeout = 3000 # Time to hide mouse if not moved or clicked + double_click_time = 750 # max millis between click to be considered a double + mouse_slop = 5 # Amount mouse is allowed to move to differential between clicks and drags def __init__(self, handlers=()): """Dispatches pygame events to the handler's methods""" @@ -48,6 +65,11 @@ self.handlers = list(handlers) self.mouse_visible = pygame.mouse.set_visible(not display.fullscreen) self.mouse_hide_time = 0 + self.last_click_time = 0 + self.last_click_pos = None + self.mouse_down_pos = None + self.mouse_handler = None + self.drag = False self.running = True def handle_events(self): @@ -68,22 +90,58 @@ def mouse_move(self, event): """Handle mouse movement""" self.show_mouse() + if event.buttons and not self.drag and self.mouse_down_pos is not None: + # See if the user has moved enough to begin a drag + self.drag = (vector.distance(vector.vector2(*event.pos), self.mouse_down_pos) + > self.mouse_slop) + if self.drag and self.mouse_down_pos is not None: + if self.mouse_handler is not None and self.mouse_handler.mouse_drag( + event, vector.to_tuple(self.mouse_down_pos), event.pos): + return + elif self.mouse_handler is None: + for handler in self.handlers: + if handler.mouse_drag(event, vector.to_tuple(self.mouse_down_pos), event.pos): + self.mouse_handler = handler + return for handler in self.handlers: handler.mouse_move(event) def mouse_up(self, event): """Handle mouse button release""" self.show_mouse() + click_pos = vector.to_tuple(self.mouse_down_pos) + self.mouse_down_pos = None + if self.mouse_handler is not None: + if self.drag and self.mouse_handler.mouse_drag_end(event, click_pos, event.pos): + self.drag = False + self.last_click_time = 0 + return + if game.time - self.last_click_time < self.double_click_time: + was_double_click = not self.drag + self.last_click_time = 0 + else: + was_double_click = False + self.last_click_time = game.time + if was_double_click and self.mouse_handler.mouse_double_click(event, click_pos): + return + if not self.drag and self.mouse_handler.mouse_click(event, click_pos): + return for handler in self.handlers: + if not self.drag and handler.mouse_click(event, click_pos): + break if handler.mouse_up(event): break def mouse_down(self, event): """Handle mouse button press""" self.show_mouse() + self.mouse_down_pos = vector.vector2(*event.pos) + self.drag = False for handler in self.handlers: if handler.mouse_down(event): + self.mouse_handler = handler break + self.mouse_handler = None def quit(self, event=None): self.running = False @@ -106,9 +164,12 @@ game.exit() -class GameCommandsHandler(Handler): - """Global game command event handler""" +class PlayingFieldHandler(Handler, sprite.Sprite): + """Game playing field event handler""" + select_rect = None + rect = pygame.Rect(0, 0, 0, 0) + def zoom_in(self): """Zoom in""" game.camera.zoom_in() @@ -147,6 +208,7 @@ game.target.select(game.local_player) def __init__(self): + sprite.Sprite.__init__(self, sprite.layers.ui) key_map = { '=': self.zoom_in, '-': self.zoom_out, @@ -165,6 +227,47 @@ self.key_map[event.key]() return True + def mouse_click(self, event, pos): + # First check for vessel click + click_rect = pygame.Rect(0, 0, 10, 10) + click_rect.center = pos + vessels = [s for s in sprite.layers.vessels if isinstance(s, Vessel)] + vessels.reverse() + for vessel in vessels: + if (isinstance(vessel, Vessel) and vessel.rect.colliderect(click_rect) + and vessel is not game.local_player and not vessel.incidental): + if vessel.is_friendly(game.local_player): + selection.group.select(vessel) + else: + game.target.select(vessel) + return True + # No vessel was there, see about planets + planets = [s for s in sprite.layers.background if isinstance(s, Planet)] + planets.reverse() + for planet in planets: + if planet.rect.colliderect(click_rect): + game.target.select(planet) + return True + # Nothing specific was clicked, just clear the current selected group + selection.group.select_none() + return True + + def mouse_drag(self, event, start_pos, end_pos): + self.select_rect = pygame.Rect( + min(start_pos[0], end_pos[0]), min(start_pos[1], end_pos[1]), + abs(start_pos[0] - end_pos[0]), abs(start_pos[1] - end_pos[1])) + return True - - + def mouse_drag_end(self, event, start_pos, end_pos): + selection.group.select_none() + selection.group.select_screen_rect(game.local_player.category, self.select_rect) + self.select_rect = None + + def draw(self, surface): + if self.select_rect is not None: + pygame.draw.rect(surface, (100, 100, 200), self.select_rect, 1) + return self.select_rect + return self.rect + +playing_field_handler = PlayingFieldHandler() + Modified: game.py =================================================================== --- game.py 2007-10-30 07:25:40 UTC (rev 317) +++ game.py 2007-11-01 22:01:50 UTC (rev 318) @@ -224,7 +224,7 @@ enemies = pygame.sprite.Group() next_wave = 0 this_frame_time = last_frame_time = 1000 / fps - event_handler = event.MainHandler([event.GameCommandsHandler(), panel.handler]) + event_handler = event.MainHandler([panel.handler, event.playing_field_handler]) if windowed: sleep_time = 0.01 else: Modified: panel.py =================================================================== --- panel.py 2007-10-30 07:25:40 UTC (rev 317) +++ panel.py 2007-11-01 22:01:50 UTC (rev 318) @@ -18,6 +18,7 @@ import widget import sprite import media +import selection class Panel(sprite.Sprite, event.Handler): """User interface panel. Panels contain controls. Enabled panels receive @@ -268,6 +269,7 @@ BottomPanel.__init__(self, width, height, align_right=True) self.tab = PanelTab(self, align_right=True) self.controls.add(self.tab) + self.select_rect = None def create_image(self): image = pygame.Surface(self.rect.size, pygame.SRCALPHA, 32) @@ -302,7 +304,7 @@ self.tab.rect.right = self.rect.right def update_vessel(self, vessel): - if vessel.vessel_type != 'missile': + if not vessel.incidental: if vessel.is_friendly(game.local_player): if vessel.selected: color = (255, 255, 255) @@ -315,11 +317,51 @@ pos_x + self.map_rect.centerx, pos_y + self.map_rect.centery, 2, 2) self.map_points.fill(color, point_rect) self.map_mask.fill((0, 0, 0, 120), point_rect.inflate(4, 4)) + + def mouse_drag(self, event, start_pos, end_pos): + if self.select_rect is None and not self.rect.collidepoint(start_pos): + return False # Drag did not originate in our panel + self.select_rect = pygame.Rect( + min(start_pos[0], end_pos[0]), min(start_pos[1], end_pos[1]), + abs(start_pos[0] - end_pos[0]), abs(start_pos[1] - end_pos[1])) + return True + def mouse_drag_end(self, event, start_pos, end_pos): + if self.select_rect is not None: + selection.group.select_none() + # Translate the select rect to map coordinates + (left, top), (width, height) = self.select_rect.topleft, self.select_rect.size + rect = pygame.Rect((left - self.rect.centerx) / self.map_scale, + (top - self.rect.centery) / self.map_scale, + width / self.map_scale, height / self.map_scale) + selection.group.select_map_rect(game.local_player.category, rect) + self.select_rect = None + return True + + def mouse_click(self, event, pos): + if self.rect.collidepoint(pos): + # Translate the position to map coodinates + map_pos = ((pos[0] - self.rect.centerx) / self.map_scale, + (pos[1] - self.rect.centery) / self.map_scale) + planet_rect = pygame.Rect(0, 0, 0, 0) + for planet in game.map.planets: + planet_rect.width = planet_rect.height = planet.collision_radius * 4 + planet_rect.center = vector.to_tuple(planet.position) + if planet_rect.collidepoint(map_pos): + game.target.select(planet) + break + return True + def draw(self, surface): + old_clip = surface.get_clip() + surface.set_clip(self.rect) surface.blit(self.image, self.rect) + surface.set_clip(self.rect.inflate(-self.border_width * 2, -self.border_width * 2)) surface.blit(self.map_mask, self.rect) surface.blit(self.map_points, self.rect) + if self.select_rect is not None: + pygame.draw.rect(surface, (200, 100, 0), self.select_rect, 1) + surface.set_clip(old_clip) return self.rect @@ -640,11 +682,28 @@ def mouse_down(self, event): """Handle mouse button press""" - handled = False for panel in self.panels: if panel.mouse_down(event): return panel + + def mouse_click(self, event, pos): + """Handle click events""" + for panel in self.panels: + if panel.mouse_click(event, pos): + return panel + + def mouse_drag(self, event, start_pos, end_pos): + """Handle drag events""" + for panel in self.panels: + if panel.mouse_drag(event, start_pos, end_pos): + return panel + def mouse_drag_end(self, event, start_pos, end_pos): + """Handle drag events""" + for panel in self.panels: + if panel.mouse_drag_end(event, start_pos, end_pos): + return panel + def key_down(self, event): """Handle key press""" for panel in self.panels: Modified: selection.py =================================================================== --- selection.py 2007-10-30 07:25:40 UTC (rev 317) +++ selection.py 2007-11-01 22:01:50 UTC (rev 318) @@ -53,8 +53,7 @@ try: while 1: next = self._nearest_vessel_iter.next() - if next is not self.selected.sprite and ( - getattr(next, 'vessel_type', None) != 'missile'): + if next is not self.selected.sprite and not next.incidental: # Ensure we always select a new target self.select(next) break @@ -141,13 +140,19 @@ if clear_class_iter: self.by_class_iter = None - def select_rect(self, category, rect): - """Select all vessels matching the category inside the given rect""" + def select(self, vessel): + """Select a single vessel""" + self.select_none() + self.selected.add(vessel) + vessel.selected = True + + def select_screen_rect(self, category, rect): + """Select all vessels matching the category inside the given rect in screen coordinates""" in_rect = rect.colliderect by_class = {} for spr in sprite.layers.vessels: if (isinstance(spr, Vessel) and spr.category & category and in_rect(spr.rect) - and spr is not game.local_player and spr.vessel_type != 'missile'): + and spr is not game.local_player and not spr.incidental): self.selected.add(spr) if spr.vessel_class not in by_class: by_class[spr.vessel_class] = pygame.sprite.Group() @@ -158,6 +163,24 @@ else: self.by_class = None + def select_map_rect(self, category, rect): + """Select all vessels matching the category inside the given rect in map coordinates""" + in_rect = rect.collidepoint + by_class = {} + for spr in sprite.layers.vessels: + if (isinstance(spr, Vessel) and spr.category & category + and in_rect(vector.to_tuple(spr.position)) + and spr is not game.local_player and not spr.incidental): + self.selected.add(spr) + if spr.vessel_class not in by_class: + by_class[spr.vessel_class] = pygame.sprite.Group() + by_class[spr.vessel_class].add(spr) + spr.selected = True + if by_class: + self.by_class_iter = itertools.cycle(by_class.iteritems()) + else: + self.by_class = None + def select_near_vessel(self, vessel, category): """Select vessels in an expanding area surround the vessel""" if game.time > self._select_timeout: @@ -167,8 +190,8 @@ size = self._last_select_size = self._last_select_size*2 or self.initial_select_size size += vessel.collision_radius * 2 rect = pygame.Rect(0, 0, size, size) - rect.center = vessel.rect.center - self.select_rect(category, rect) + rect.center = vector.to_tuple(vessel.position) + self.select_map_rect(category, rect) def constrain_by_class(self): """Constrain an existing selection to a single vessel class. Repeated @@ -182,12 +205,11 @@ self.selected.add(vessel) def set_target(self, target): - """Set the target for all selected vessels. If the target is a planet - or a friendly ship, it is also set as the objective for all + """Set the target and objective for all selected vessels. """ for vessel in self.selected: vessel.control.set_target(target) - if isinstance(target, staticbody.Planet) or vessel.is_friendly(target): - vessel.control.objective.add(target) + vessel.control.objective.add(target) group = GroupSelector() # Singleton + Modified: vessel.py =================================================================== --- vessel.py 2007-10-30 07:25:40 UTC (rev 317) +++ vessel.py 2007-11-01 22:01:50 UTC (rev 318) @@ -145,7 +145,8 @@ 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, - standoff_distance=0, race=None, ai=None, cost=None, config_file=None, net_id=None): + standoff_distance=0, race=None, ai=None, cost=None, config_file=None, net_id=None, + incidental=False): if image_name is None and vessel_class: image_name = vessel_class + '.png' if image_name is not None: @@ -177,6 +178,7 @@ self._repair_time = None self.damage_time = 0 self.race = race + self.incidental = str(incidental) not in ('0', 'False') @classmethod def load(cls, config_file, **kw): @@ -527,7 +529,7 @@ 5 """ self.damage_time = game.time - if self.vessel_type != 'missile': + if not self.incidental: self.show_status() for s in self._damage_sys: value = s.damage(value) @@ -551,7 +553,7 @@ self.system_damage -= self.system_damage_threshold if game.local_player is self: media.play_sound('power_down.wav', min_spacing=2) - if self.damage_smoke is None and self.vessel_type != 'missile': + if self.damage_smoke is None and not self.incidental: self.damage_smoke = particle.SmokeTrail(self) if (self.hull_damage > self.disable_factor * self.hull_mass * 0.75 and self.explosion is None): Modified: vessels/naree/gnat =================================================================== --- vessels/naree/gnat 2007-10-30 07:25:40 UTC (rev 317) +++ vessels/naree/gnat 2007-11-01 22:01:50 UTC (rev 318) @@ -8,6 +8,7 @@ hull_length: 2 max_speed: 300 max_energy: 50 +incidental: True ai: GnatAI [vessel.Shield] This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cd...@us...> - 2007-10-30 07:25:42
|
Revision: 317 http://eos-game.svn.sourceforge.net/eos-game/?rev=317&view=rev Author: cduncan Date: 2007-10-30 00:25:40 -0700 (Tue, 30 Oct 2007) Log Message: ----------- - Add remaining planets to sol map, with symmetric arch layout - Can't land on "inhospitable" planets with no resources - Allow initial base and player positions to be specified on the map - Randomly choose initial starting position for player (now done in a tacky way, will need refactoring for real multiplayer) Modified Paths: -------------- base.py content/maps/sol game.py panel.py staticbody.py vessel.py Added Paths: ----------- data/jupiter.png data/neptune.png data/saturn.png data/titan.png data/triton.png data/uranus.png Modified: base.py =================================================================== --- base.py 2007-10-28 07:20:31 UTC (rev 316) +++ base.py 2007-10-30 07:25:40 UTC (rev 317) @@ -13,6 +13,7 @@ import vector import map import message +import panel class PlanetaryBase: """Planetary bases are owned by a player and can build using the planet's @@ -59,6 +60,8 @@ % (level, max_level)) self.number = self._owner2number[owner] = self._owner2number.get(owner, 0) + 1 self.build_queue = [] + if owner is game.local_player: + panel.BasePanel(planet) def update(self): if self.builder is None and self.build_queue: Modified: content/maps/sol =================================================================== --- content/maps/sol 2007-10-28 07:20:31 UTC (rev 316) +++ content/maps/sol 2007-10-30 07:25:40 UTC (rev 317) @@ -1,3 +1,5 @@ +# $Id$ + [general] name: Sol description: Home system of the Solar Coalition @@ -2,29 +4,67 @@ +[planet:Mercury] +image: mercury.png +x:-9000 +y: 4500 +resources: 3 + +[planet:Venus] +image: venus.png +x:-8000 +y: 2000 + [planet:Earth] image: earth-east.png,earth-west.png -x: 250 -y: -50 +x:-6000 +y:-500 resources: 10 +player: 1 +base_level: 2 [planet:Moon] image: moon.png -x: -500 -y: -1000 +x:-7000 +y: 0 resources: 4 [planet:Mars] image: mars.png -x: 7500 -y: 2200 +x:-2500 +y: -3300 resources: 8 -[planet:Venus] -image: venus.png -x: -6000 -y: -1500 +[planet:Jupiter] +image: jupiter.png +x: 2500 +y: -3300 +resources: 8 -[planet:Mercury] -image: mercury.png -x: -9000 -y: -2200 +[planet:Saturn] +image: saturn.png +x: 6000 +y: -500 +player: 2 +base_level: 2 +resources: 10 + +[planet:Titan] +image: titan.png +x: 7000 +y: 0 +resources: 4 + +[planet:Uranus] +image: uranus.png +x: 8000 +y: 2000 + +[planet:Triton] +image: triton.png +x: 9000 +y: 4500 resources: 3 + +[planet:Neptune] +image: neptune.png +x: 10000 +y: 5000 Added: data/jupiter.png =================================================================== (Binary files differ) Property changes on: data/jupiter.png ___________________________________________________________________ Name: svn:mime-type + image/png Added: data/neptune.png =================================================================== (Binary files differ) Property changes on: data/neptune.png ___________________________________________________________________ Name: svn:mime-type + image/png Added: data/saturn.png =================================================================== (Binary files differ) Property changes on: data/saturn.png ___________________________________________________________________ Name: svn:mime-type + image/png Added: data/titan.png =================================================================== (Binary files differ) Property changes on: data/titan.png ___________________________________________________________________ Name: svn:mime-type + image/png Added: data/triton.png =================================================================== (Binary files differ) Property changes on: data/triton.png ___________________________________________________________________ Name: svn:mime-type + image/png Added: data/uranus.png =================================================================== (Binary files differ) Property changes on: data/uranus.png ___________________________________________________________________ Name: svn:mime-type + image/png Modified: game.py =================================================================== --- game.py 2007-10-28 07:20:31 UTC (rev 316) +++ game.py 2007-10-30 07:25:40 UTC (rev 317) @@ -149,21 +149,20 @@ # Establish player local_player = vessel.KeyboardControl.new_player(race, ship) + local_player.number = random.choice([1, 2]) # XXX Fix for real multiplayer camera = Camera(local_player) target = selection.Target(local_player) # Setup background and map starfield = StarField(display.rect) map = Map('sol') + + # Find the base and put the player next to it + for planet in map.planets: + if planet.base is not None and planet.base.owner is local_player: + offset = vector.vector2(planet.collision_radius * 1.25, planet.collision_radius * 1.25) + local_player.set_position(planet.position + offset) - import panel, base - 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) - # Setup local messaging message.init(local_player) Modified: panel.py =================================================================== --- panel.py 2007-10-28 07:20:31 UTC (rev 316) +++ panel.py 2007-10-30 07:25:40 UTC (rev 317) @@ -470,7 +470,7 @@ desired_width = self.info_rect.width * 2.0 planet_img = media.image(planet.image_name) planet_img = pygame.transform.rotozoom(planet_img, - 0, desired_width / max(planet_img.get_rect().size)) + 90, desired_width / max(planet_img.get_rect().size)) image.blit(planet_img, (0, self.info_rect.height / 4)) title_img = self.planet_font.render(planet.name, True, (255, 255, 255)) title_rect = title_img.get_rect(left=4, top=rect.top + 3) Modified: staticbody.py =================================================================== --- staticbody.py 2007-10-28 07:20:31 UTC (rev 316) +++ staticbody.py 2007-10-30 07:25:40 UTC (rev 317) @@ -22,7 +22,8 @@ base = None # Established planetary base layer = sprite.layers.background - def __init__(self, name, image, x=0, y=0, resources=0, net_id=None): + def __init__(self, name, image, x=0, y=0, resources=0, player=None, base_level=None, + max_base_level=5, net_id=None): RoundBody.__init__(self, position=(int(x), int(y)), heading=-vector.rightangle, net_id=net_id) self.body.disable() # immobile @@ -31,6 +32,12 @@ self.collision_radius = self.radius = media.image(self.image_name).get_rect().width / 2 self.resources = float(resources) self.update() + if player is not None and base_level is not None: + import base + # XXX Hack, this needs to be better for real multiplayer + if game.local_player.number == int(player): + self.base = base.PlanetaryBase( + self, game.local_player, int(base_level), int(max_base_level)) def get_state(self): state = RoundBody.get_state(self) Modified: vessel.py =================================================================== --- vessel.py 2007-10-28 07:20:31 UTC (rev 316) +++ vessel.py 2007-10-30 07:25:40 UTC (rev 317) @@ -21,7 +21,6 @@ import particle import message import sprite -import staticbody max_weapons = 5 _empty_rect = pygame.rect.Rect(0, 0, 0, 0) @@ -1242,26 +1241,31 @@ if (self.enabled and self.firing and self.targeted 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 + if self.planet.resources: + 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 + else: + message.send_status(self.vessel, game.local_player, + 'Cannot land on %s, planet is inhospitable' % self.planet.name) + self.vessel.control.target.empty() 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 + import base # 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 targeted(self): + import staticbody # Avoid circular import 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 This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cd...@us...> - 2007-10-28 07:20:37
|
Revision: 316 http://eos-game.svn.sourceforge.net/eos-game/?rev=316&view=rev Author: cduncan Date: 2007-10-28 00:20:31 -0700 (Sun, 28 Oct 2007) Log Message: ----------- - AI mothership becomes more general "objective" which is used to specify the ship's mission object. Unlike for target, the vessel ai never changes this value. - The objective overrides the immediate target when the latter is a certain distance away. - Add AssaultAI personality which enables the ai to take planets with assault ships. - Add resources to other sol planets (though a redesign of the map is in the works) - Add a new game commands event handler which abstracts out game-level keyboard (and later mouse) commands. This will enable later control remapping too. - New keyboard commands: 't': target nearest friend 'y': target yourself [return]: Direct selected friends to target Modified Paths: -------------- ai.py base.py bay.py content/maps/sol event.py game.py selection.py sounds/land.wav vessel.py vessels/naree/corde vessels/rone/kraken Modified: ai.py =================================================================== --- ai.py 2007-10-26 18:45:46 UTC (rev 315) +++ ai.py 2007-10-28 07:20:31 UTC (rev 316) @@ -17,18 +17,21 @@ import sprite from vector import diagonal, halfcircle, rightangle, fullcircle from vessel import Control, Vessel, DirectionalThrusters +import staticbody class BasicAI(Control): """Basic AI Control""" target_timeout = 5000 + # Maximum distance to target when we have an objective + target_max_distance_with_objective = 2000 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): + def __init__(self, vessel, target=None, objective=None, sensor=None): Control.__init__(self) self.vessel = vessel # override vessel collision handler with our own @@ -40,9 +43,9 @@ target = target.sprite if target.alive(): self.target.add(target) - self.mothership = GroupSingle() - if mothership: - self.mothership.add(mothership) + self.objective = GroupSingle() + if objective: + self.objective.add(objective) self.steerfunc = self.standoff self.close_vessels = Group() self.proximity_radius = self.vessel.collision_radius * 5 @@ -268,13 +271,21 @@ self.bw_maneuver = False def acquire_target(self): - """Select the approriate target vessel""" + """auto-select the approriate target""" + if (self.target and self.objective and vector.distance( + self.vessel.position, self.target.sprite.position) > + self.target_max_distance_with_objective + and self.target not in self.objective): + self.target.empty() 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: + if (self.sensor.closest_vessel and (not self.objective or + vector.distance(self.vessel.position, + self.sensor.closest_vessel.sprite.position) < + self.target_max_distance_with_objective)): self.target.add(self.sensor.closest_vessel) self.target_time = game.time + self.target_timeout self.sensor.disable() @@ -282,13 +293,23 @@ 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) + if self.objective: + # head for the objective + self.target.add(self.objective) else: - # No mothership, just head toward a planet + # No objective, just head toward a planet self.target.add(game.map.planets) + def set_target(self, target, timeout=None): + """Set the target for the ai, timing out in timeout seconds + at which time it will acquire another target + """ + self.target.add(target) + if timeout is not None: + self.target_time = game.time + timeout + else: + self.target_time = sys.maxint + def select_steerfunc(self): """Select the appropirate steering function""" if (self.vessel.health < self.evade_min_health @@ -311,8 +332,8 @@ desired_velocity += self.avoid_vessels() self.steer(desired_heading, desired_velocity) # Fire all targeted weapons - for i in range(len(self.vessel.weapons)): - self.weapons[i] = self.vessel.weapons[i].targeted + for i, weapon in enumerate(self.vessel.weapons): + self.weapons[i] = weapon.targeted def collide(self, other, contacts): """Detect contact with other vessels to avoid stacking @@ -340,7 +361,7 @@ evade_max_health = 0.7 def evade(self): - """Turne away from the target""" + """Turn away from the target""" heading, seek_velocity = self.pursue() velocity = vector.unit( vector.radians(seek_velocity) + rightangle) * self.vessel.max_speed * 10 @@ -367,13 +388,32 @@ return heading, velocity * self.orbit def select_steerfunc(self): - return self.standoff + self.steerfunc = self.standoff +class AssaultAI(BasicAI): + + def select_steerfunc(self): + target = self.target.sprite + if isinstance(target, staticbody.Planet): + self.steerfunc = self.seek + elif self.vessel.is_friendly(target) or self.vessel.health > .75: + self.steerfunc = self.pursue + else: + self.steerfunc = self.flee + + def acquire_target(self): + if isinstance(self.objective.sprite, staticbody.Planet): + # When targetting a planet, always go there + self.target.add(self.objective) + else: + BasicAI.acquire_target(self) + + class GnatAI(BasicAI): """Gnat AI control""" - is_returning = False # Is returning to the mothership + is_returning = False # Is returning to the objective had_target = False def steer(self, desired_heading, desired_velocity): @@ -390,18 +430,18 @@ 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) + # Return to the objective if our energy is expended + self.target.add(self.objective) 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) + elif (not self.target or self.vessel.is_friendly(self.target.sprite)) and self.objective: + # Target whatever the objective is targeting + self.target.add(self.objective.sprite.control.target) if not self.target: - if self.mothership: - # head for the mothership - self.target.add(self.mothership) + if self.objective: + # head for the objective + self.target.add(self.objective) else: - # Commit suicide if our mothership dies + # Commit suicide if our objective dies self.vessel.explode('hit.wav') self.target.add(game.map.planets) @@ -421,9 +461,9 @@ 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) + if self.is_returning and other in self.objective: + if hasattr(self.objective.sprite, 'fighter_bay'): + self.objective.sprite.fighter_bay.dock(self.vessel) self.is_returning = False self.had_target = False self.target.empty() @@ -434,15 +474,22 @@ class AIVessel(Vessel): """Vessel under ai control""" - def __init__(self, target=None, category=body.foe, mothership=None, sensor=None, + def __init__(self, target=None, category=body.foe, objective=None, sensor=None, ai='BasicAI', **kw): + """ + target -- Sprite to intercept and attack if foe. + category -- Vessel category, determines friends and foes. + objective -- Long term objective sprite or base + sensor -- Sensor object to be used (if omitted one is created just for this vessel) + ai -- class name of ai personality. + """ 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() ai_class = globals()[ai] - self.control = ai_class(self, target, mothership, sensor) + self.control = ai_class(self, target, objective, sensor) # Make sure we are behind the local player self.layer.to_back(self) Modified: base.py =================================================================== --- base.py 2007-10-26 18:45:46 UTC (rev 315) +++ base.py 2007-10-28 07:20:31 UTC (rev 316) @@ -170,7 +170,8 @@ """Construct vessel""" if not self.vessel_built: vessel = ai.AIVessel.load( - self.config_file, target=self.base.rally_point, category=self.base.owner.category) + self.config_file, target=self.base.rally_point, objective=self.base.rally_point, + category=self.base.owner.category) vessel.set_position(self.base.planet.position) vessel.set_heading(random.random() * vector.fullcircle) self.vessel_built = True Modified: bay.py =================================================================== --- bay.py 2007-10-26 18:45:46 UTC (rev 315) +++ bay.py 2007-10-28 07:20:31 UTC (rev 316) @@ -85,7 +85,7 @@ self.vessel, 1000, body.everything & ~self.vessel.category) fighter = ai.AIVessel.load(self.fighter_config, target=self.vessel.control.target, - mothership=self.vessel, + objective=self.vessel, sensor=self.sensor, category=self.vessel.category) self.fighters.add(fighter) Modified: content/maps/sol =================================================================== --- content/maps/sol 2007-10-26 18:45:46 UTC (rev 315) +++ content/maps/sol 2007-10-28 07:20:31 UTC (rev 316) @@ -12,12 +12,13 @@ image: moon.png x: -500 y: -1000 -resources: 2.5 +resources: 4 [planet:Mars] image: mars.png x: 7500 y: 2200 +resources: 8 [planet:Venus] image: venus.png @@ -28,3 +29,4 @@ image: mercury.png x: -9000 y: -2200 +resources: 3 Modified: event.py =================================================================== --- event.py 2007-10-26 18:45:46 UTC (rev 315) +++ event.py 2007-10-28 07:20:31 UTC (rev 316) @@ -20,19 +20,15 @@ def mouse_move(self, event): """Handle mouse movement""" - raise NotImplementedError def mouse_up(self, event): """Handle mouse button release""" - raise NotImplementedError def mouse_down(self, event): """Handle mouse button press""" - raise NotImplementedError def key_down(self, event): """Handle key press""" - raise NotImplementedError class MainHandler(Handler): @@ -101,18 +97,6 @@ game.camera.center() game.starfield.set_rect(display.rect) panel.handler.hide_all(force=True) - elif event.key == K_f: - selection.group.select_near_vessel(game.local_player, game.local_player.category) - elif event.key == K_TAB: - selection.group.constrain_by_class() - elif event.key == K_e: - game.target.select_nearest(~game.local_player.category) - elif event.key == K_p: - game.target.select_next_planet() - elif event.key == K_MINUS: - game.camera.zoom_out() - elif event.key == K_EQUALS: - game.camera.zoom_in() else: for handler in self.handlers: if handler.key_down(event): @@ -121,3 +105,66 @@ def quit(self, event=None): game.exit() + +class GameCommandsHandler(Handler): + """Global game command event handler""" + + def zoom_in(self): + """Zoom in""" + game.camera.zoom_in() + + def zoom_out(self): + """Zoom out""" + game.camera.zoom_out() + + def select_nearest_friends(self): + """Select friends nearest to player""" + selection.group.select_near_vessel(game.local_player, game.local_player.category) + + def constrain_selected_friends(self): + """Constrain friend selection by vessel class""" + selection.group.constrain_by_class() + + def set_selected_friends_target(self): + """Command selected friends to the current target""" + if game.target.selected: + selection.group.set_target(game.target.selected.sprite) + + def target_nearest_enemy(self): + """Target nearest enemy""" + game.target.select_nearest(~game.local_player.category) + + def target_nearest_planet(self): + """Target nearest planet""" + game.target.select_next_planet() + + def target_nearest_friend(self): + """Target nearest friend""" + game.target.select_nearest(game.local_player.category) + + def target_local_player(self): + """Target your ship""" + game.target.select(game.local_player) + + def __init__(self): + key_map = { + '=': self.zoom_in, + '-': self.zoom_out, + 'f': self.select_nearest_friends, + '\t': self.constrain_selected_friends, + '\r': self.set_selected_friends_target, + 'e': self.target_nearest_enemy, + 'p': self.target_nearest_planet, + 't': self.target_nearest_friend, + 'y': self.target_local_player, + } + self.key_map = dict((ord(key), method) for key, method in key_map.items()) + + def key_down(self, event): + if event.key in self.key_map: + self.key_map[event.key]() + return True + + + + Modified: game.py =================================================================== --- game.py 2007-10-26 18:45:46 UTC (rev 315) +++ game.py 2007-10-28 07:20:31 UTC (rev 316) @@ -225,7 +225,7 @@ enemies = pygame.sprite.Group() next_wave = 0 this_frame_time = last_frame_time = 1000 / fps - event_handler = event.MainHandler([panel.handler]) + event_handler = event.MainHandler([event.GameCommandsHandler(), panel.handler]) if windowed: sleep_time = 0.01 else: @@ -294,7 +294,7 @@ ship = 'vessels/naree/lotus' elif target_race == 'sc': ship = 'vessels/sc/pegasus' - friend = AIVessel.load(ship, mothership=target, category=body.friend) + friend = AIVessel.load(ship, objective=target, category=body.friend) friend.set_position(target.position - position) friends.add(friend) barrier *= 2 Modified: selection.py =================================================================== --- selection.py 2007-10-26 18:45:46 UTC (rev 315) +++ selection.py 2007-10-28 07:20:31 UTC (rev 316) @@ -14,6 +14,7 @@ import sprite from vessel import Vessel import vector +import staticbody class Target(sprite.Sprite): @@ -146,7 +147,7 @@ by_class = {} for spr in sprite.layers.vessels: if (isinstance(spr, Vessel) and spr.category & category and in_rect(spr.rect) - and spr.vessel_type != 'missile'): + and spr is not game.local_player and spr.vessel_type != 'missile'): self.selected.add(spr) if spr.vessel_class not in by_class: by_class[spr.vessel_class] = pygame.sprite.Group() @@ -179,5 +180,14 @@ for vessel in vessels: vessel.selected = True self.selected.add(vessel) + + def set_target(self, target): + """Set the target for all selected vessels. If the target is a planet + or a friendly ship, it is also set as the objective for all + """ + for vessel in self.selected: + vessel.control.set_target(target) + if isinstance(target, staticbody.Planet) or vessel.is_friendly(target): + vessel.control.objective.add(target) group = GroupSelector() # Singleton Modified: sounds/land.wav =================================================================== (Binary files differ) Modified: vessel.py =================================================================== --- vessel.py 2007-10-26 18:45:46 UTC (rev 315) +++ vessel.py 2007-10-28 07:20:31 UTC (rev 316) @@ -1215,8 +1215,8 @@ self.energy_flow = float(energy_flow) def update_system(self): - if self.vessel.control.mothership: - mothership = self.vessel.control.mothership.sprite + if self.vessel.control.objective: + mothership = self.vessel.control.objective.sprite if (self.vessel.energy < self.vessel.max_energy and hasattr(mothership, 'fighter_bay') and self.vessel in mothership.fighter_bay): @@ -1239,7 +1239,7 @@ self.landing_end = None def update_system(self): - if (self.enabled and self.firing and self.targetted and self.landing_end is None): + if (self.enabled and self.firing and self.targeted and self.landing_end is None): # Start landing cycle self.planet = self.vessel.control.target.sprite self.landing_end = game.time + self.landing_time @@ -1261,7 +1261,7 @@ self.vessel.kill() @property - def targetted(self): + def targeted(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 Modified: vessels/naree/corde =================================================================== --- vessels/naree/corde 2007-10-26 18:45:46 UTC (rev 315) +++ vessels/naree/corde 2007-10-28 07:20:31 UTC (rev 316) @@ -9,7 +9,7 @@ crew: 100 max_speed: 80 max_energy: 1000 -ai: EvaderAI +ai: AssaultAI cost: 400 [vessel.Shield] Modified: vessels/rone/kraken =================================================================== --- vessels/rone/kraken 2007-10-26 18:45:46 UTC (rev 315) +++ vessels/rone/kraken 2007-10-28 07:20:31 UTC (rev 316) @@ -9,7 +9,7 @@ crew: 100 max_speed: 80 max_energy: 1000 -ai: EvaderAI +ai: AssaultAI cost: 400 [vessel.Shield] This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cd...@us...> - 2007-10-26 18:45:49
|
Revision: 315 http://eos-game.svn.sourceforge.net/eos-game/?rev=315&view=rev Author: cduncan Date: 2007-10-26 11:45:46 -0700 (Fri, 26 Oct 2007) Log Message: ----------- Fix white planet pointy Modified Paths: -------------- media.py Modified: media.py =================================================================== --- media.py 2007-10-26 18:45:20 UTC (rev 314) +++ media.py 2007-10-26 18:45:46 UTC (rev 315) @@ -222,7 +222,7 @@ set_image('pointy-planet-blue', planet_pointy_image((55, 80, 216))) set_image('pointy-planet-green', planet_pointy_image((0, 155, 38))) set_image('pointy-planet-red', planet_pointy_image((216, 36, 30))) - set_image('pointy-planet-white', pointy_image((255, 255, 255))) + set_image('pointy-planet-white', planet_pointy_image((255, 255, 255))) _sound_cache = {} _sound_last_played = {} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cd...@us...> - 2007-10-26 18:45:24
|
Revision: 314 http://eos-game.svn.sourceforge.net/eos-game/?rev=314&view=rev Author: cduncan Date: 2007-10-26 11:45:20 -0700 (Fri, 26 Oct 2007) Log Message: ----------- - Use white planet pointies for selected planet, dynamically update pointy color - Select planets in distance order when using "p" Modified Paths: -------------- base.py selection.py staticbody.py Modified: base.py =================================================================== --- base.py 2007-10-26 06:26:59 UTC (rev 313) +++ base.py 2007-10-26 18:45:20 UTC (rev 314) @@ -50,10 +50,6 @@ self.planet = planet self.planet.base = self self.owner = owner - if owner is game.local_player: - planet.offscreen_img = 'pointy-planet-green' - else: - planet.offscreen_img = 'pointy-planet-red' self.rally_point = self.planet if max_level >= level: self.level = level Modified: selection.py =================================================================== --- selection.py 2007-10-26 06:26:59 UTC (rev 313) +++ selection.py 2007-10-26 18:45:20 UTC (rev 314) @@ -13,6 +13,7 @@ import media import sprite from vessel import Vessel +import vector class Target(sprite.Sprite): @@ -71,8 +72,10 @@ self.sensor.enable() def select_next_planet(self): + def distance(planet): + return vector.distance(planet.position, game.camera.position) if self._planet_iter is None: - self._planet_iter = itertools.cycle(game.map.planets) + self._planet_iter = itertools.cycle(sorted(game.map.planets, key=distance)) self.sensor.disable() self.select(self._planet_iter.next()) Modified: staticbody.py =================================================================== --- staticbody.py 2007-10-26 06:26:59 UTC (rev 313) +++ staticbody.py 2007-10-26 18:45:20 UTC (rev 314) @@ -28,7 +28,6 @@ self.body.disable() # immobile self.name = name self.image_name = random.choice(image.split(',')) - self.offscreen_img = 'pointy-planet-blue' self.collision_radius = self.radius = media.image(self.image_name).get_rect().width / 2 self.resources = float(resources) self.update() @@ -37,9 +36,17 @@ state = RoundBody.get_state(self) state['create_args'] = (self.name, self.image_name) return state - + def update(self): RoundBody.update(self) if self.base is not None: self.base.update() + if self in game.target.selected: + self.offscreen_img = 'pointy-planet-white' + elif self.base is None: + self.offscreen_img = 'pointy-planet-blue' + elif self.base.owner.category & game.local_player.category: + self.offscreen_img = 'pointy-planet-green' + else: + self.offscreen_img = 'pointy-planet-red' 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. |
From: <cd...@us...> - 2007-10-26 06:22:58
|
Revision: 312 http://eos-game.svn.sourceforge.net/eos-game/?rev=312&view=rev Author: cduncan Date: 2007-10-25 23:22:56 -0700 (Thu, 25 Oct 2007) Log Message: ----------- Fix tab placement Modified Paths: -------------- panel.py Modified: panel.py =================================================================== --- panel.py 2007-10-26 06:20:53 UTC (rev 311) +++ panel.py 2007-10-26 06:22:56 UTC (rev 312) @@ -568,7 +568,7 @@ self.hotkey_font = pygame.font.Font(self.hotkey_font, 72) self.render() if not align_right: - rect = self.image.get_rect(left=self.panel_left) + rect = self.image.get_rect(left=PanelTab.panel_left) PanelTab.panel_left = rect.right + 6 else: rect = self.image.get_rect(right=display.rect.right) 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:20:58
|
Revision: 311 http://eos-game.svn.sourceforge.net/eos-game/?rev=311&view=rev Author: cduncan Date: 2007-10-25 23:20:53 -0700 (Thu, 25 Oct 2007) Log Message: ----------- Use psyco if available Modified Paths: -------------- eos.py Modified: eos.py =================================================================== --- eos.py 2007-10-24 07:47:45 UTC (rev 310) +++ eos.py 2007-10-26 06:20:53 UTC (rev 311) @@ -9,6 +9,13 @@ import optparse import pygame +try: + import psyco +except ImportError: + pass +else: + psyco.full() + def run(): parser = optparse.OptionParser() parser.add_option('-s', '--server', This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cd...@us...> - 2007-10-24 07:47:49
|
Revision: 310 http://eos-game.svn.sourceforge.net/eos-game/?rev=310&view=rev Author: cduncan Date: 2007-10-24 00:47:45 -0700 (Wed, 24 Oct 2007) Log Message: ----------- Formative script to generate explosion animations. Considering how crude it is the results are excellent IMO. Added Paths: ----------- splode.py Added: splode.py =================================================================== --- splode.py (rev 0) +++ splode.py 2007-10-24 07:47:45 UTC (rev 310) @@ -0,0 +1,173 @@ +import sys +import pygame +from pygame.locals import * +import random +import time +import itertools + +pygame.init() +screen = pygame.display.set_mode((800,600)) + +class Particle: + + def __init__(self, color, pos, vel=(0,0), drag=0, initial_size=1, initial_expand=0.1, accel=1.5, accel_time=8, deccel=1.05, deccel_time=80): + self.velx, self.vely = vel + self.x = float(pos[0]) + self.y = float(pos[1]) + self.drag = 1.0 - drag + self.size = float(initial_size) + self.expand = initial_expand + self.rect = pygame.Rect(pos[0], pos[1], initial_size, initial_size) + self.orig_color = color + self.accel = accel + self.accel_time = accel_time + self.deccel = deccel + self.deccel_time = deccel_time + + def update(self): + width = min(int(self.size), 500) + self.image = pygame.Surface((width, width), SRCALPHA, 32) + self.x += self.velx + self.y += self.vely + self.rect = self.image.get_rect(centerx=self.x, centery=self.y) + self.velx *= self.drag + self.vely *= self.drag + color = [max(min(int(c * self.expand), 255), 0) for c in self.orig_color] + pygame.draw.circle(self.image, color, self.image.get_rect().center, int(width / 2)) + self.size += self.expand + if self.accel_time: + self.expand *= self.accel + self.accel_time -= 1 + elif self.deccel_time: + self.expand /= self.deccel + self.deccel_time -= 1 + +class Particle: + + def __init__(self, color, pos, vel=(0,0), drag=0, initial_size=1, max_size=1, size_interval=2, fade_start=sys.maxint, fade_end=sys.maxint): + self.velx, self.vely = vel + self.x = float(pos[0]) + self.y = float(pos[1]) + self.drag = drag + self.size = float(initial_size) + self.max_size = float(max_size) + self.size_interval = float(size_interval) + self.fade_start = fade_start + self.fade_end = fade_end + self.frame = 0 + self.rect = pygame.Rect(pos[0], pos[1], initial_size, initial_size) + self.color = list(color) + + def update(self): + width = int(self.size) + self.image = pygame.Surface((width, width), SRCALPHA, 32) + self.x += self.velx + self.y += self.vely + self.rect = self.image.get_rect(centerx=self.x, centery=self.y) + self.velx -= self.velx * self.drag + self.vely -= self.vely * self.drag + if self.frame < self.fade_start: + color = [max(min(int(c), 255), 0) for c in self.color] + else: + fade = float(self.fade_end - self.frame) / float(self.fade_end - self.fade_start) + color = [max(min(int(c * fade), 255), 0) for c in self.color] + pygame.draw.circle(self.image, color, self.image.get_rect().center, int(width / 2)) + self.frame += 1 + if self.size < self.max_size: + self.size += (self.max_size - self.size) / self.size_interval + +particles = [] +rect = pygame.Rect(0,0,0,0) + + +for i in range(50): + red=random.randint(100,200) + green=random.randint(50, red) + blue=random.randint(50, green) + p = Particle( + color=(red, green, blue, 10), + pos=(200,200), #(200 + random.gauss(40, 15), 200 + random.gauss(40, 15)), + vel=(random.gauss(0, 4), random.gauss(0, 4)), + drag=random.gauss(0.15, .01), + max_size=random.gauss(150, 10), + size_interval=random.uniform(35,80), + fade_start=random.gauss(5,1), + fade_end=100) + particles.append(p) + rect.union_ip(p.rect) + +for i in range(250): + red=random.randint(150,255) + green=random.randint(20, red) + blue=random.randint(0, green) + p = Particle( + color=(red, green, blue, 50), + pos=(200,200), #(200 + random.gauss(40, 15), 200 + random.gauss(40, 15)), + vel=(random.gauss(0, 4), random.gauss(0, 4)), + drag=random.gauss(0.35, .02), + max_size=random.gauss(40, 10), + size_interval=random.uniform(5,70), + fade_start=random.gauss(5,1), + fade_end=70) + particles.append(p) + rect.union_ip(p.rect) + + +for i in range(500): + bright=random.gauss(200,20) + p = Particle( + color=(bright, bright, bright, 150), + pos=(200,200), + vel=(random.gauss(0, 3), random.gauss(0, 3)), + drag=0.1, + initial_size=1, + max_size=random.gauss(5, 1), + size_interval=random.uniform(60,100), + fade_start=random.gauss(15,12), + fade_end=random.gauss(80,5)) + particles.append(p) + rect.union_ip(p.rect) + +for i in range(250): + red=random.randint(150,255) + green=random.randint(20, red) + blue=random.randint(0, green) + p = Particle( + color=(red, green, blue, 50), + pos=(200,200), #(200 + random.gauss(40, 15), 200 + random.gauss(40, 15)), + vel=(random.gauss(0, 4), random.gauss(0, 4)), + drag=random.gauss(0.35, .01), + max_size=random.gauss(40, 10), + size_interval=random.uniform(5,70), + fade_start=random.gauss(5,1), + fade_end=random.gauss(70, 10)) + particles.append(p) + rect.union_ip(p.rect) + +frames = [] +slomo = False +for i in range(200): + image = pygame.Surface(rect.size, SRCALPHA, 32) + for p in particles: + p.update() + image.blit(p.image, p.rect) + rect.union_ip(p.rect) + frames.append(image) + for event in pygame.event.get(): + if event.type == QUIT or ( + event.type == KEYDOWN and event.key == K_ESCAPE): + raise SystemExit +screen_center = screen.get_rect().center +for image in itertools.cycle(frames): + screen.fill((0, 0, 0)) + #image = pygame.transform.rotozoom(image, 0, .5) + screen.blit(image, image.get_rect()) + pygame.display.flip() + if slomo: + time.sleep(0.1) + for event in pygame.event.get(): + if event.type == QUIT or ( + event.type == KEYDOWN and event.key == K_ESCAPE): + raise SystemExit + if event.type == KEYDOWN and event.key == K_SPACE: + slomo = not slomo Property changes on: splode.py ___________________________________________________________________ Name: svn:keywords + Id 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-23 06:30:50
|
Revision: 309 http://eos-game.svn.sourceforge.net/eos-game/?rev=309&view=rev Author: cduncan Date: 2007-10-22 23:30:48 -0700 (Mon, 22 Oct 2007) Log Message: ----------- - Different offscreen pointies for planets - Select planets using "p" to cycle through (click-select is next) Modified Paths: -------------- base.py body.py event.py media.py selection.py staticbody.py Modified: base.py =================================================================== --- base.py 2007-10-21 18:35:47 UTC (rev 308) +++ base.py 2007-10-23 06:30:48 UTC (rev 309) @@ -50,6 +50,10 @@ self.planet = planet self.planet.base = self self.owner = owner + if owner is game.local_player: + planet.offscreen_img = 'pointy-planet-green' + else: + planet.offscreen_img = 'pointy-planet-red' self.rally_point = self.planet if max_level >= level: self.level = level Modified: body.py =================================================================== --- body.py 2007-10-21 18:35:47 UTC (rev 308) +++ body.py 2007-10-23 06:30:48 UTC (rev 309) @@ -25,6 +25,7 @@ friend = 1 foe = 2 shot = 4 +planet = 8 nothing = 0 everything = 0x7fffffff categories = (friend, foe) @@ -92,7 +93,7 @@ self.enabled = True self.on_screen = True self.explosion = None - self.apparent_size = None + self.apparent_size = 1.0 self.net_id = net_id self.image_name = image_name game.objects.add(self) @@ -186,11 +187,11 @@ self.velocity = vector.vector2(*self.body.getLinearVel()[:2]) self.heading = self.get_heading() # place self relative to the camera at proper zoom level - apparent_size, screen_pos = vector.to_screen(self.position) + self.apparent_size, screen_pos = vector.to_screen(self.position) if self.explosion is None: - self.image = media.image(self.image_name, apparent_size, self.heading) + self.image = media.image(self.image_name, self.apparent_size, self.heading) else: - image = self.explosion.next_image(apparent_size) + image = self.explosion.next_image(self.apparent_size) if image is not None: self.image = image else: @@ -201,6 +202,7 @@ 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: event.py =================================================================== --- event.py 2007-10-21 18:35:47 UTC (rev 308) +++ event.py 2007-10-23 06:30:48 UTC (rev 309) @@ -39,7 +39,6 @@ """Top-level event handler""" mouse_timeout = 3000 # Time to hide mouse if not moved or clicked - base_keys = [K_1, K_2, K_3, K_4, K_5, K_6, K_7, K_8, K_9] def __init__(self, handlers=()): """Dispatches pygame events to the handler's methods""" @@ -108,6 +107,8 @@ selection.group.constrain_by_class() elif event.key == K_e: game.target.select_nearest(~game.local_player.category) + elif event.key == K_p: + game.target.select_next_planet() elif event.key == K_MINUS: game.camera.zoom_out() elif event.key == K_EQUALS: Modified: media.py =================================================================== --- media.py 2007-10-21 18:35:47 UTC (rev 308) +++ media.py 2007-10-23 06:30:48 UTC (rev 309) @@ -208,6 +208,21 @@ set_image('pointy-blue', pointy_image((55, 80, 216))) set_image('pointy-green', pointy_image((0, 155, 38))) set_image('pointy-red', pointy_image((216, 36, 30))) + set_image('pointy-white', pointy_image((255, 255, 255))) + def planet_pointy_image(color): + pointy = pygame.Surface((160, 290), SRCALPHA, 32) + pygame.draw.polygon(pointy, color, [(0, 130), (80, 0), (160, 130)]) + color2 = pygame.color.subtract(color, 60) + pygame.draw.polygon(pointy, color2, [(0, 130), (80, 0), (160, 130)], 15) + pygame.draw.circle(pointy, color2, (80, 175), 85) + pygame.draw.circle(pointy, (0, 0, 0, 0), (80, 190), 85) + pygame.draw.circle(pointy, color2, (80, 185), 60) + pygame.draw.circle(pointy, color, (80, 185), 45) + return pygame.transform.rotozoom(pointy, 0, 0.1) + set_image('pointy-planet-blue', planet_pointy_image((55, 80, 216))) + set_image('pointy-planet-green', planet_pointy_image((0, 155, 38))) + set_image('pointy-planet-red', planet_pointy_image((216, 36, 30))) + set_image('pointy-planet-white', pointy_image((255, 255, 255))) _sound_cache = {} _sound_last_played = {} Modified: selection.py =================================================================== --- selection.py 2007-10-21 18:35:47 UTC (rev 308) +++ selection.py 2007-10-23 06:30:48 UTC (rev 309) @@ -17,7 +17,7 @@ class Target(sprite.Sprite): - size_factor = 1.8 + size_factor = 1.5 color = (255, 255, 255) timeout = 2000 sensor = None @@ -28,7 +28,8 @@ self._select_timeout = sys.maxint self._sensor_off_frame = None self.rect = pygame.rect.Rect(0, 0, 0, 0) - self._nearest_iter = None + self._nearest_vessel_iter = None + self._planet_iter = None self.sensor = ai.Sensor(source_vessel, range) self.sensor.disable() @@ -46,10 +47,10 @@ def select_nearest(self, category, cycle=True): """Select the nearest body that is in the category.""" - if self._nearest_iter is not None and self._last_category == category: + if self._nearest_vessel_iter is not None and self._last_category == category: try: while 1: - next = self._nearest_iter.next() + next = self._nearest_vessel_iter.next() if next is not self.selected.sprite and ( getattr(next, 'vessel_type', None) != 'missile'): # Ensure we always select a new target @@ -58,25 +59,33 @@ return except StopIteration: if cycle: - self._nearest_iter = (body for body in self.sensor.detected if body.alive()) + self._nearest_vessel_iter = (body for body in self.sensor.detected + if body.alive()) nearest = self.select_nearest(category, cycle=False) else: - self._nearest_iter = None + self._nearest_vessel_iter = None self.sensor.setDetect(category) self._last_category = category # Enable the sensor for the next few frames self._sensor_off_frame = game.frame_no + 3 self.sensor.enable() + def select_next_planet(self): + if self._planet_iter is None: + self._planet_iter = itertools.cycle(game.map.planets) + self.sensor.disable() + self.select(self._planet_iter.next()) + def update(self): if game.time > self._select_timeout: # forget about all previously selected targets - self._nearest_iter = None + self._nearest_vessel_iter = None + self._planet_iter = None self._select_timeout = sys.maxint if self.sensor.enabled: if self.sensor.closest_vessel: # See what our sensor detected - self._nearest_iter = (body for body in self.sensor.detected if body.alive()) + self._nearest_vessel_iter = (body for body in self.sensor.detected if body.alive()) self.select_nearest(self._last_category) self.sensor.disable() # Stop detection elif game.frame_no > self._sensor_off_frame: @@ -88,13 +97,14 @@ if target is not None: # To avoid lagging behind the target, we update our rect here # which guarantees that the target's rect is already updated - if hasattr(target, 'collision_radius'): - width = target.collision_radius * 2 * self.size_factor - else: - width = max(target.rect.size) * self.size_factor + width = target.collision_radius * 3 * target.apparent_size + if width <= max(target.rect.size): + # compensate for smaller collision radii + width *= 2 + width = max(width, 3) rect.center = target.rect.center rect.size = (width, width) - line_len = max(self.rect.width / 10, 4) + line_len = min(max(self.rect.width / 10, 3), 75) pygame.draw.line(surface, self.color, (rect.left, rect.centery), (rect.left + line_len, rect.centery)) pygame.draw.line(surface, self.color, Modified: staticbody.py =================================================================== --- staticbody.py 2007-10-21 18:35:47 UTC (rev 308) +++ staticbody.py 2007-10-23 06:30:48 UTC (rev 309) @@ -23,11 +23,12 @@ layer = sprite.layers.background def __init__(self, name, image, x=0, y=0, resources=0, net_id=None): - RoundBody.__init__(self, position=(int(x), int(y)), heading=-vector.rightangle, net_id=net_id) + RoundBody.__init__(self, position=(int(x), int(y)), heading=-vector.rightangle, + net_id=net_id) self.body.disable() # immobile self.name = name self.image_name = random.choice(image.split(',')) - self.offscreen_img = 'pointy-blue' + self.offscreen_img = 'pointy-planet-blue' self.collision_radius = self.radius = media.image(self.image_name).get_rect().width / 2 self.resources = float(resources) self.update() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cd...@us...> - 2007-10-21 18:35:49
|
Revision: 308 http://eos-game.svn.sourceforge.net/eos-game/?rev=308&view=rev Author: cduncan Date: 2007-10-21 11:35:47 -0700 (Sun, 21 Oct 2007) Log Message: ----------- Add a fixed sleep interval per frame (more if in windowed mode) to even out the framerate especially when other processes are burning lots of cpu (*cough* pandora *cough*). Even though this reduces the maximum framerate possible, it makes things noticably smoother to me overall. Modified Paths: -------------- game.py Modified: game.py =================================================================== --- game.py 2007-10-21 08:57:23 UTC (rev 307) +++ game.py 2007-10-21 18:35:47 UTC (rev 308) @@ -226,6 +226,10 @@ next_wave = 0 this_frame_time = last_frame_time = 1000 / fps event_handler = event.MainHandler([panel.handler]) + if windowed: + sleep_time = 0.01 + else: + sleep_time = 0.0015 while 1: event_handler.handle_events() keystate = pygame.key.get_pressed() @@ -243,8 +247,7 @@ server.step() last_time = time time += clock.tick() - if windowed and time - last_time < 33: - sleep(0.02) + sleep(sleep_time) last_frame_time = this_frame_time this_frame_time = min(time - last_time, last_frame_time * 3) fps = clock.get_fps() or fps This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cd...@us...> - 2007-10-21 08:57:25
|
Revision: 307 http://eos-game.svn.sourceforge.net/eos-game/?rev=307&view=rev Author: cduncan Date: 2007-10-21 01:57:23 -0700 (Sun, 21 Oct 2007) Log Message: ----------- Base the star movement solely on the camera, instead of approximating it from the camera target's movement. This eliminates the need to special-case panning Modified Paths: -------------- camera.py stars.py Modified: camera.py =================================================================== --- camera.py 2007-10-21 08:40:25 UTC (rev 306) +++ camera.py 2007-10-21 08:57:23 UTC (rev 307) @@ -45,11 +45,7 @@ def update(self): if not self.target.alive(): self.acquire_target() - old_offset = self.offset self.offset *= self.centering - panning = old_offset - self.offset - if vector.length(panning) >= 1: - game.starfield.move(vector.to_tuple(panning)) accel = self.target.velocity - self.last_velocity self.offset += -vector.normal(accel) * (vector.length(accel) / self.adhesion)**2 self.last_velocity = self.target.velocity Modified: stars.py =================================================================== --- stars.py 2007-10-21 08:40:25 UTC (rev 306) +++ stars.py 2007-10-21 08:57:23 UTC (rev 307) @@ -25,6 +25,7 @@ big_star_count = 30 parallax_depths = 20 parallax_factor = 0.95 + last_camera_pos = None def __init__(self, rect): sprite.Sprite.__init__(self, sprite.layers.background) @@ -131,5 +132,7 @@ self.nebula_pos[1] += vec[1] * 0.15 def update(self): - star_velocity = game.camera.zoom * -game.camera.target.velocity / game.fps - self.move(vector.to_tuple(star_velocity)) + if self.last_camera_pos is not None: + star_velocity = game.camera.zoom * (self.last_camera_pos - game.camera.position) + self.move(vector.to_tuple(star_velocity)) + self.last_camera_pos = game.camera.position This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cd...@us...> - 2007-10-21 08:40:27
|
Revision: 306 http://eos-game.svn.sourceforge.net/eos-game/?rev=306&view=rev Author: cduncan Date: 2007-10-21 01:40:25 -0700 (Sun, 21 Oct 2007) Log Message: ----------- Stars now pan properly with the camera Modified Paths: -------------- camera.py Modified: camera.py =================================================================== --- camera.py 2007-10-21 05:41:31 UTC (rev 305) +++ camera.py 2007-10-21 08:40:25 UTC (rev 306) @@ -45,7 +45,11 @@ def update(self): if not self.target.alive(): self.acquire_target() + old_offset = self.offset self.offset *= self.centering + panning = old_offset - self.offset + if vector.length(panning) >= 1: + game.starfield.move(vector.to_tuple(panning)) accel = self.target.velocity - self.last_velocity self.offset += -vector.normal(accel) * (vector.length(accel) / self.adhesion)**2 self.last_velocity = self.target.velocity This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |