From: <cd...@us...> - 2007-01-26 08:24:46
|
Revision: 60 http://eos-game.svn.sourceforge.net/eos-game/?rev=60&view=rev Author: cduncan Date: 2007-01-26 00:24:45 -0800 (Fri, 26 Jan 2007) Log Message: ----------- Vessel objects can now be instantiated by config files that describe the vessel parameters and all of its systems. for example: v = Vessel.load('vessels/naree/cress') Will create a new vessel configured according to the config file. Modified Paths: -------------- body.py vessel.py Added Paths: ----------- vessels/ vessels/naree/ vessels/naree/cress vessels/naree/gnat vessels/naree/lotus Property Changed: ---------------- vessel.py Modified: body.py =================================================================== --- body.py 2007-01-25 09:30:55 UTC (rev 59) +++ body.py 2007-01-26 08:24:45 UTC (rev 60) @@ -35,7 +35,7 @@ body = None # ode dynamics body geom = None # ode collision geom - def __init__(self, position, velocity=None): + def __init__(self, position=None, velocity=None): """Create the body in the given position vector which can be either a Vector2D or a tuple. An optional initial velocity may be specified if omitted the object @@ -47,7 +47,8 @@ mass.setSphereTotal(self.mass, self.radius) mass.adjust(self.mass) self.body.setMass(mass) - self.body.setPosition((position[0], position[1], 0)) + if position is not None: + self.body.setPosition((position[0], position[1], 0)) if velocity is not None: self.body.setLinearVel((velocity[0], velocity[1], 0)) if self.category or self.collides_with: @@ -61,7 +62,7 @@ self.position = PositionVector(self.body) self.velocity = VelocityVector(self.body) # on screen size and positioning - self.rect = pygame.Rect([position[0], position[1], 0, 0]) + self.rect = pygame.Rect([self.position.x, self.position.y, 0, 0]) @property def force(self): Modified: vessel.py =================================================================== --- vessel.py 2007-01-25 09:30:55 UTC (rev 59) +++ vessel.py 2007-01-26 08:24:45 UTC (rev 60) @@ -8,6 +8,7 @@ import sys import math import random +import ConfigParser import game import pygame from body import RoundBody @@ -35,6 +36,10 @@ pass +class VesselConfigError(Exception): + """Vessel configuration file error""" + + class Vessel(RoundBody): """A controlled body @@ -56,9 +61,6 @@ Systems that have a graphical representation should subclass Sprite. """ - class_name = '' # Name of vessel class - image_name = 'ship.png' - hull_mass = 1 # Mass of hull in tons hull_damage = 0 # Current damage to hull disable_factor = 20 # disabled if damage exceeds this factor * hull_mass system_damage = 0 # System damage accumulator @@ -67,20 +69,103 @@ control = Controls() - def __init__(self, position, velocity=None, heading=None, systems=[]): - RoundBody.__init__(self, position, velocity) - self.vessel_img = RotatedImage(self.image_name) + def __init__(self, vessel_name=None, vessel_class='', vessel_type='', description='', + image_name='ship.png', hull_mass=1, hull_length=1, crew=0, max_speed=0): + RoundBody.__init__(self) + self.vessel_name = vessel_name + self.vessel_class = vessel_class + self.vessel_type = vessel_type + self.description = description + self.hull_mass = float(hull_mass) + self.hull_length = float(hull_length) + self.crew = int(crew) + self.max_speed = float(max_speed) + self.vessel_img = RotatedImage(image_name) self._sys = [] self._damage_sys = [] - if heading is not None: - self.heading = float(heading) - else: - self.heading = random.random() * fullcircle + self.heading = random.random() * fullcircle self.turning = 0 - for s in systems: - self.add_system(s) + @classmethod + def load(cls, config_file): + """Create a vessel from a config file. config_file is either a readable + file-like object or a file name. + + The config file is in ConfigParser format and contains a [general] + section that defines the overall parameters for the vessel. These are + passed as arguments to the class constructor. Additional sections + describe the vessel systems. The section names are in the format + [modulename.SystemClass] for each system. The values under each + system section are passed to the constructor for the class specified + by the section name. Each instantiated system is then added to the + vessel and the completed vessel is returned. See below for an example. + + >>> config = ''' + ... [general] + ... vessel_class: test + ... description: A test vessel + ... hull_mass: 42 + ... hull_length: 3.1415926 + ... + ... [vessel.Engine] + ... thrust: 55 + ... ''' + >>> import StringIO + >>> v = Vessel.load(StringIO.StringIO(config)) + >>> v.vessel_class + 'test' + >>> v.description + 'A test vessel' + >>> v.hull_mass + 42.0 + >>> v.hull_length == 3.1415926 + True + >>> s = list(v) + >>> len(s) + 1 + >>> s[0].__class__.__name__ + 'Engine' + >>> s[0].thrust + 55.0 + """ + if isinstance(config_file, str): + config_file = open(config_file, 'rt') + file_name = getattr(config_file, 'name', repr(config_file)) + parser = ConfigParser.SafeConfigParser() + parser.readfp(config_file) + params = dict(parser.items('general')) + vessel = cls(**params) + for section in parser.sections(): + if section == 'general': + continue + class_path = section.split('.') + if len(class_path) != 2: + raise VesselConfigError( + 'Invalid section name [%s] in vessel config file %s' + % (section, file_name)) + mod_name, class_name = class_path + if mod_name not in sys.modules: + raise VesselConfigError( + 'Cannot find module %s for section [%s] in vessel config file %s. ' + 'Check the module name and be sure the vessel.py module imports it.' + % (mod_name, section, file_name)) + if not hasattr(sys.modules[mod_name], class_name): + raise VesselConfigError( + 'Cannot find vessel system class %s in module %s for section [%s] in ' + 'vessel config file %s' % (class_name, mod_name, section, file_name)) + sys_constructor = getattr(sys.modules[mod_name], class_name) + try: + params = dict(parser.items(section)) + system = sys_constructor(vessel, **params) + except Exception: + raise VesselConfigError, ( + 'Exception creating system from section [%s] of vessel config file %s.' + % (section, file_name)), sys.exc_info()[-1] + vessel.add_system(system) + return vessel + def update(self): + RoundBody.update(self) self.control.update() for system in self._sys: system.update_system() @@ -96,7 +181,7 @@ >>> game.init() >>> class TestSys: mass = 5 - >>> v = Vessel((0,0)) + >>> v = Vessel() >>> v.mass = 1 >>> len(list(v)) 0 @@ -105,7 +190,7 @@ >>> len(list(v)) 1 >>> v.mass - 6 + 6.0 """ if self.allow_system(system): self._sys.append(system) @@ -123,21 +208,22 @@ >>> game.init() >>> class TestSys: mass = 10 >>> sys = [TestSys(), TestSys()] - >>> v = Vessel((0,0), systems=sys) + >>> v = Vessel() + >>> v.add_system(sys[0]); v.add_system(sys[1]) >>> len(list(v)) 2 >>> v.mass - 21 + 21.0 >>> v.remove_system(sys[1]) >>> len(list(v)) 1 >>> v.mass - 11 + 11.0 >>> v.remove_system(sys[0]) >>> len(list(v)) 0 >>> v.mass - 1 + 1.0 >>> v.remove_system(sys[0]) Traceback (most recent call last): ... @@ -153,7 +239,8 @@ >>> game.init() >>> class TestSys: mass = 7 - >>> v = Vessel((0,0), systems=[TestSys()]) + >>> v = Vessel() + >>> v.add_system(TestSys()) >>> v.hull_mass = 5 >>> v.calc_mass() >>> v.mass @@ -181,7 +268,8 @@ ... return d / 2 ... >>> d = DamageSys() - >>> v = Vessel((0,0), systems=[d]) + >>> v = Vessel() + >>> v.add_system(d) >>> v.damage(10) >>> d.level 5 @@ -299,7 +387,7 @@ >>> game.init() >>> game.avg_fps = 30 - >>> s = Shield(Vessel((0,0)), 10.0, 30.0) + >>> s = Shield(Vessel(), 10.0, 30.0) >>> s.level == s.max_level True >>> s.update_system() @@ -391,7 +479,7 @@ """Update vessel turning based on control input >>> game.init() - >>> v = Vessel((0,0)) + >>> v = Vessel() >>> dt = DirectionalThrusters(v, 10, 2) >>> v.add_system(dt) >>> v.control.turn = 1 @@ -425,7 +513,7 @@ def __init__(self, vessel, thrust): self.vessel = vessel self.thrust = float(thrust) - self.mass = thrust * self.mass_factor + self.mass = self.thrust * self.mass_factor self.enabled = True def disable(self): @@ -438,7 +526,8 @@ """Translate control state into thrusting actions >>> game.init() - >>> v = Vessel((0,0), heading=0) + >>> v = Vessel() + >>> v.heading = 0 >>> mt = ManeuveringThrusters(v, 1) >>> v.control.fw_maneuver = True >>> mt.update_system() @@ -447,7 +536,8 @@ >>> v.force.radians == v.heading True - >>> v = Vessel((0,0), heading=0) + >>> v = Vessel() + >>> v.heading = 0 >>> mt = ManeuveringThrusters(v, 1) >>> v.control.fw_maneuver = False >>> v.control.right_maneuver = True @@ -457,7 +547,8 @@ >>> v.force.radians == v.heading + rightangle True - >>> v = Vessel((0,0), heading=0) + >>> v = Vessel() + >>> v.heading = 0 >>> mt = ManeuveringThrusters(v, 1) >>> v.control.right_maneuver = False >>> v.control.left_maneuver = True @@ -467,7 +558,8 @@ >>> v.force.radians == v.heading + halfcircle + rightangle True - >>> v = Vessel((0,0), heading=0) + >>> v = Vessel() + >>> v.heading = 0 >>> mt = ManeuveringThrusters(v, 1) >>> v.control.left_maneuver = False >>> v.control.bw_maneuver = True @@ -499,7 +591,7 @@ def __init__(self, vessel, thrust): self.vessel = vessel self.thrust = float(thrust) - self.mass = self.base_mass + thrust * self.mass_factor + self.mass = self.base_mass + self.thrust * self.mass_factor self.enabled = True def disable(self): @@ -512,7 +604,8 @@ """Translate control state into thrust actions >>> game.init() - >>> v = Vessel((0,0), heading=0) + >>> v = Vessel() + >>> v.heading = 0 >>> e = Engine(v, 10) >>> v.control.thrust = False Property changes on: vessel.py ___________________________________________________________________ Name: svn:keywords + Id Added: vessels/naree/cress =================================================================== --- vessels/naree/cress (rev 0) +++ vessels/naree/cress 2007-01-26 08:24:45 UTC (rev 60) @@ -0,0 +1,24 @@ +# $Id$ + +[general] +vessel_class: cress +vessel_type: fighter +description: A ubiquitous presence in Naree space, the cress is employed equally in both patrol and interceptor roles. Eschewing cumbersome armor entirely in favor of powerful shield regenerators, it's lightweight nano-molecular hull belies its efficacy in battle. Quantum differential drive provides motive power and unparalleled agility for a craft of its size. Capable of independent supralight travel, the cress excels in long range escort and scouting capacities. Its lepton particle beam armament imparts a lasting impression on those who have felt its sting. +hull_mass: 5 +hull_length: 10 +crew: 1 +max_speed: 1000 + +[vessel.Shield] +max_level: 100 +regeneration: 10 + +[vessel.DirectionalThrusters] +thrust: 1 +max_turn_rate: 0.07 + +[vessel.ManeuveringThrusters] +thrust: 200 + +[vessel.Engine] +thrust: 150 Property changes on: vessels/naree/cress ___________________________________________________________________ Name: svn:keywords + Id Name: svn:eol-style + native Added: vessels/naree/gnat =================================================================== --- vessels/naree/gnat (rev 0) +++ vessels/naree/gnat 2007-01-26 08:24:45 UTC (rev 60) @@ -0,0 +1,20 @@ +# $Id$ + +[general] +vessel_class: gnat +vessel_type: missile +description: The Naree can ill afford to squander their pilots in delicate stub fighters, nor can they afford to waste resources on crude "fire and forget" weapons. The gnat was designed as an alternative to both, a reuseable weapon effective in both defense and offense. The gnat is a self contained autonomous unmanned craft wielding a laser cannon. Gnats are launched by a system resembling a cross between a figher bay and a torpedo tube that can be carried by medium-sized warships. Launched in groups the gnats can de directed to swarm hostile targets inflicting damage while using their agility and advanced flight control to avoid being destroyed. Once their energy is depleted, they are automatically programmed to return to their host ship to recharge. In defensive mode, gnats act as mobile point-defense weapons, staying near their host and intercepting incoming threats. +hull_mass: 1 +hull_length: 3 + +[vessel.Shield] +max_level: 20 +regeneration: 0 + +[vessel.DirectionalThrusters] +thrust: 0.5 +max_turn_rate: 0.1 + +[vessel.ManeuveringThrusters] +thrust: 1 + Property changes on: vessels/naree/gnat ___________________________________________________________________ Name: svn:keywords + Id Name: svn:eol-style + native Added: vessels/naree/lotus =================================================================== --- vessels/naree/lotus (rev 0) +++ vessels/naree/lotus 2007-01-26 08:24:45 UTC (rev 60) @@ -0,0 +1,28 @@ +# $id$ + +[general] +vessel_class: lotus +vessel_type: warship +description: Answering the call in defense of the Naree, the lotus is the very definition of a multi-role warship. Armed with the potent fullerene pulse cannon, the lotus is equally adept leading an assault as it is supporting a fleet of larger ships. For defense against enemy fighters and missile threats, the lotus deploys a gnat bay, making it a pint sized carrier platform. An onboard dark matter core provides sufficient energy for extended missions into deep space or engagements with stubborn adversaries. Its diamond matrix armor and stout energy shields protect the crew and ship systems. +hull_mass: 25 +hull_length: 20 +crew: 3 +max_speed: 800 + +[vessel.Shield] +max_level: 300 +regeneration: 8 + +# [vessel.Armor] +# durability: 100 +# dissipation: 10 + +[vessel.DirectionalThrusters] +thrust: 3 +max_turn_rate: 0.07 + +[vessel.ManeuveringThrusters] +thrust: 350 + +[vessel.Engine] +thrust: 500 Property changes on: vessels/naree/lotus ___________________________________________________________________ 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-03-04 07:49:06
|
Revision: 119 http://eos-game.svn.sourceforge.net/eos-game/?rev=119&view=rev Author: cduncan Date: 2007-03-03 23:49:00 -0800 (Sat, 03 Mar 2007) Log Message: ----------- - Third race is now know as the Rone - Add two Rone ships, the Drach fighter and Draken warship - Switch enemy ais to Rone ships - Add ship explosion sound, tone down hit sound - Add plasma cannon weapon used by both new ships. More weapons are in the works. - Add armor defensive system used by both new ships. Still needs some sort of visual feedback on damage. - Add gnat launch sound. Modified Paths: -------------- bay.py body.py game.py ideas/races.txt projectile.py sounds/hit.wav vector.py vessel.py vessels/naree/lotus Added Paths: ----------- data/drach.png data/draken.png data/plasma.png sounds/explode.wav sounds/launch.wav vessels/rone/ vessels/rone/drach vessels/rone/draken Modified: bay.py =================================================================== --- bay.py 2007-03-03 18:00:45 UTC (rev 118) +++ bay.py 2007-03-04 07:49:00 UTC (rev 119) @@ -49,6 +49,7 @@ fighter.push(Vector2D.unit(self.vessel.heading + halfcircle) * self.launch_force) fighter.update() self.last_launch = game.time + media.play_sound('launch.wav', position=self.vessel.position) @property def targeted(self): Modified: body.py =================================================================== --- body.py 2007-03-03 18:00:45 UTC (rev 118) +++ body.py 2007-03-04 07:49:00 UTC (rev 119) @@ -95,12 +95,9 @@ self.category = category self.collides_with = collides_with if game.local_player and self.category & game.local_player.category: - pointy = 'pointy-green.gif' + self.offscreen_img = media.RotatedImage('pointy-green.gif') elif self.category: - pointy = 'pointy-red.gif' - else: - pointy = 'pointy-blue.gif' - self.offscreen_img = media.RotatedImage(pointy) + self.offscreen_img = media.RotatedImage('pointy-red.gif') @property def force(self): @@ -204,6 +201,7 @@ """Flaming death""" if self.explosion is None: self.explosion = iter(self.xplode_animation.images) + media.play_sound('explode.wav', position=self.position) self.disable() def kill(self): Added: data/drach.png =================================================================== (Binary files differ) Property changes on: data/drach.png ___________________________________________________________________ Name: svn:mime-type + image/png Added: data/draken.png =================================================================== (Binary files differ) Property changes on: data/draken.png ___________________________________________________________________ Name: svn:mime-type + image/png Added: data/plasma.png =================================================================== (Binary files differ) Property changes on: data/plasma.png ___________________________________________________________________ Name: svn:mime-type + image/png Modified: game.py =================================================================== --- game.py 2007-03-03 18:00:45 UTC (rev 118) +++ game.py 2007-03-04 07:49:00 UTC (rev 119) @@ -45,6 +45,7 @@ global universe, collision_space, screen, screen_rect, background, planets global sprites, fps, avg_fps, clock, time, players, local_player, camera, ai # Initialize pygame and setup main screen + pygame.mixer.pre_init(11025) pygame.init() # Get the available display modes and use the best one if fullscreen: @@ -138,10 +139,10 @@ x = target.position.x + position.x y = target.position.y + position.y for i in xrange(1): - ai = AIVessel.load('vessels/naree/lotus', target=target) + ai = AIVessel.load('vessels/rone/draken', target=target) ai.position.x, ai.position.y = x, y for i in xrange(2): - ai = AIVessel.load('vessels/naree/cress', target=target) + ai = AIVessel.load('vessels/rone/drach', target=target) ai.position.x, ai.position.y = x, y ai_interval = max(ai_interval * .9, 5) frame_no += 1 Modified: ideas/races.txt =================================================================== --- ideas/races.txt 2007-03-03 18:00:45 UTC (rev 118) +++ ideas/races.txt 2007-03-04 07:49:00 UTC (rev 119) @@ -30,7 +30,7 @@ becoming expended - Knows about humans and is awaiting contact, does not feel threatened by them (or anyone) -- At war with the Yeqon, though too politically impotent to go on the +- At war with the Rone, though too politically impotent to go on the offensive, and do not admit to themselves the obvious threat they pose. Currently holding them at bay in a stalemate. - More dependant on energy resources @@ -48,13 +48,13 @@ - Kti (A wise Nareen who wishes to befriend humanity, your guide in the Naree storyline) - Matma (A skeptical Nareen who thinks the humans are secretly pawns of the - Yeqon) + Rone) - Dalini (The "mother" of the Naree (although not exactly female), their spiritual and political leader. Strong on philosophy, weak on action. Views other races with some indifference which could also be viewed as arrogance) -Yeqon +Rone ===== - Highly aggressive - Matriarcy, the females dominate positions of power and make all decisions. @@ -73,10 +73,10 @@ Main Characters --------------- -- Pazuzu (corps commander (male), your superior in the Yeqon storyline) -- Raiju (Yeqon military commander (female), one of the few Yeqon females that +- Pazuzu (corps commander (male), your superior in the Rone storyline) +- Raiju (Rone military commander (female), one of the few Rone females that actually engage directly in combat operations. A deadly adversary) -- Zegtji (Yeqon Queen. Bent on the destruction of the Naree, and later +- Zegtji (Rone Queen. Bent on the destruction of the Naree, and later incensed by the decadent humans) Modified: projectile.py =================================================================== --- projectile.py 2007-03-03 18:00:45 UTC (rev 118) +++ projectile.py 2007-03-04 07:49:00 UTC (rev 119) @@ -139,5 +139,118 @@ def collide(self, other, contacts): if self.alive(): self.kill() - media.play_sound('hit.wav', volume=(self.charge/5), position=self.position) 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) + + +class PlasmaCannon(vessel.Weapon): + + name = 'plasma cannon' + priority = 0 + base_mass = 2 + mass_factor = 0.1 # Mass per (range / 100) + damage + + def __init__(self, gunmount, range, cool_down, damage): + self.gunmount = gunmount + self.range = float(range) + self.cool_down = float(cool_down) + self.damage_value = float(damage) + self.mass = self.base_mass + ((self.range / 100) + self.damage_value) * self.mass_factor + self.last_fire = 0 + self.plasma_img = media.RotatedImage('plasma.png') + + def update_system(self): + if self.firing and game.time - self.last_fire > self.cool_down: + Shot( + shooter=self.gunmount, + image=self.plasma_img.rotated(self.gunmount.heading), + damage=self.damage_value, + velocity=Vector2D.unit(self.gunmount.heading) * ( + 900 - self.damage_value * 10), + range=self.range, + fade_range=self.range/4, + damage_loss=self.damage_value * 0.6) + media.play_sound('plasma-cannon-small.wav', position=self.gunmount.position) + self.last_fire = game.time + + @property + def targeted(self): + if not self.gunmount.control.target: + return False + else: + target = self.gunmount.control.target.sprite + if (target.category is None + or not target.category & everything & ~self.gunmount.category): + return False + if target.position.distance(self.gunmount.position) > self.range: + return False + heading_diff = self.gunmount.heading - (target.position - self.gunmount.position).radians + if heading_diff > halfcircle: + heading_diff -= fullcircle + elif heading_diff < -halfcircle: + heading_diff += fullcircle + return abs(heading_diff) < (diagonal / 3) + + +class Shot(RoundBody): + """Generic projectile shot""" + + def __init__(self, shooter, image, damage, velocity, range, + fade_range=None, damage_loss=0): + """Generic projectile shot + + shooter -- body that fired the shot + image -- shot sprite image + damage -- damage value on hit + velocity -- velocity vector of shot not including shooter velocity) + range -- distance shot travels before disappearing + fade_range -- range when shot image begins to fade (cosmetic) + damage_loss -- Reduction in damage at max range + """ + RoundBody.__init__(self, + shooter.position, shooter.velocity + velocity, + collides_with=everything & ~shooter.category + ) + self.shooter = shooter + self.start_position = shooter.position + self.image = image + self.rect = image.get_rect(center=shooter.rect.center) + self.damage_value = self.start_damage = damage + self.range = range + if fade_range is not None: + self.fade_range = fade_range + else: + self.fade_range = range * 0.9 + if damage_loss: + # calculate damage loss per frame + flight_time = range / self.velocity.length + self.damage_loss = damage_loss / flight_time / game.avg_fps + else: + self.damage_loss = 0 + + def clear(self): + game.screen.fill((0,0,0), self.rect) + + def update(self): + RoundBody.update(self) + self.damage_value -= self.damage_loss + distance = self.start_position.distance(self.position) + if distance > self.range: + self.kill() + elif distance > self.fade_range: + opacity = 255 * (self.range - distance) / (self.range - self.fade_range) + self.image.set_alpha(int(opacity)) + else: + self.image.set_alpha(255) + + def collide(self, other, contacts): + if self.alive(): + self.kill() + if self.damage_value > 0: + other.damage(self.damage_value) + if other.explosion is None: + media.play_sound('hit.wav', + volume=self.damage_value / self.start_damage, + position=self.position) + Added: sounds/explode.wav =================================================================== (Binary files differ) Property changes on: sounds/explode.wav ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Modified: sounds/hit.wav =================================================================== (Binary files differ) Added: sounds/launch.wav =================================================================== (Binary files differ) Property changes on: sounds/launch.wav ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Modified: vector.py =================================================================== --- vector.py 2007-03-03 18:00:45 UTC (rev 118) +++ vector.py 2007-03-04 07:49:00 UTC (rev 119) @@ -224,7 +224,7 @@ return self._normal def copy(self): - """Return a new vectory equal to self + """Return a new vector equal to self >>> v = Vector2D(5, 6) >>> v2 = v.copy() Modified: vessel.py =================================================================== --- vessel.py 2007-03-03 18:00:45 UTC (rev 118) +++ vessel.py 2007-03-04 07:49:00 UTC (rev 119) @@ -201,6 +201,7 @@ 'Exception creating system from section [%s] of vessel config file %s: %s' % (section, file_name, err)), sys.exc_info()[-1] vessel.add_system(system) + print vessel, vessel.mass return vessel def update(self): @@ -534,6 +535,108 @@ return value +class Armor(System): + """Armor defensive system + + Armor has a durability, heat level and dissipation rate per second. As + armor takes damage, it's heat level is increased. Every second, it's heat + level decreases by the dissipation rate. If the heat level of the armor + reaches it's durability level, then additional damage pierces the armor, + damaging the ship's hull. The durability of the armor is also reduced by + the damage amount until eventually the armor is disabled. Armor is passive + system, thus cannot be disabled until its durability reaches zero. + + Reactive armor will regenerate lost durability over time, but the rate of + recovery is typically quite slow. + """ + + name = 'armor' + mass_factor = 0.01 # Mass per durability unit per ship length unit + + def __init__(self, vessel, durability, dissipation, regeneration=0): + self.vessel = vessel + self.durability = float(durability) + self.dissipation = self.max_dissipate = float(dissipation) + self.regeneration = float(regeneration) + self.mass = self.durability * vessel.hull_length * self.mass_factor + self.heat = 0.0 + + @property + def enabled(self): + """Only disable if armor is fully depleted + + >>> game.init() + >>> a = Armor(Vessel(), 1, 1) + >>> a.enabled + True + >>> a.durability = 0 + >>> a.enabled + False + """ + return self.durability > 0 + + def disable(self): + pass + + def enable(self): + pass + + def update_system(self): + """Update heat level + + >>> game.init() + >>> a = Armor(Vessel(), 1, 1) + >>> a.heat + 0.0 + >>> a.update_system() + >>> a.heat + 0.0 + >>> a.heat = 2 + >>> a.update_system() + >>> a.heat + 1.0 + >>> a.update_system() + >>> a.heat + 0.0 + >>> a.update_system() + >>> a.heat + 0.0 + """ + if self.heat > 0: + self.heat = max(self.heat - self.dissipation / game.avg_fps, 0.0) + if self.regeneration and self.dissipation < self.max_dissipate: + self.dissipation = min( + self.dissipation + self.regeneration, self.max_dissipate) + + def damage(self, value): + """Apply damage to armor, return damage not absorbed + + >>> game.init() + >>> a = Armor(Vessel(), 1, 1) + >>> a.heat, a.durability + (0.0, 1.0) + >>> a.damage(1.0) + 0 + >>> a.heat + 1.0 + >>> a.damage(1.0) + 1.0 + >>> a.heat + 2.0 + >>> a.damage(2.0) + 2.0 + >>> a.heat + 4.0 + """ + self.heat += value + if self.heat > self.durability: + damage = min(self.heat - self.durability, value) + self.durability = max(self.durability - damage, 0) + return damage + else: + return 0 + + class DirectionalThrusters(System): """Simple directional thrust system""" Modified: vessels/naree/lotus =================================================================== --- vessels/naree/lotus 2007-03-03 18:00:45 UTC (rev 118) +++ vessels/naree/lotus 2007-03-04 07:49:00 UTC (rev 119) @@ -3,7 +3,7 @@ [general] vessel_class: lotus vessel_type: warship -description: Answering the call in defense of the Naree, the lotus is the very definition of a multi-role warship. Armed with the potent fullerene pulse cannon, the lotus is equally adept leading an assault as it is supporting a fleet of larger ships. For defense against enemy fighters and missile threats, the lotus deploys a gnat bay, making it a pint sized carrier platform. An onboard dark matter core provides sufficient energy for extended missions into deep space or engagements with stubborn adversaries. Its diamond matrix armor and stout energy shields protect the crew and ship systems. +description: Answering the call in defense of the Naree, the lotus is the very definition of a multi-role warship. Armed with the potent fullerene pulse cannon, the lotus is equally adept leading an assault as it is supporting a fleet of larger ships. For defense against enemy fighters and missile threats, the lotus deploys a gnat bay, making it a pint sized carrier platform. An onboard dark matter core provides sufficient energy for extended missions into deep space or engagements with stubborn adversaries. Its stout energy shields protect the crew and ship systems. hull_mass: 25 hull_length: 20 crew: 3 Added: vessels/rone/drach =================================================================== --- vessels/rone/drach (rev 0) +++ vessels/rone/drach 2007-03-04 07:49:00 UTC (rev 119) @@ -0,0 +1,29 @@ +# $Id: cress 98 2007-02-10 17:22:08Z cduncan $ + +[general] +vessel_class: drach +vessel_type: fighter +description: The Drach is the first strike weapon of choice for the Rune. Deadly Drach squadrons can handily flank enemy vessels to deliver a hail of plasma fire at close range. At longer range their proton grenades pack a deadly punch. Their small size and rigid armor make them a challenge to gun down before they unload their deadly arsenal. +hull_mass: 6 +hull_length: 4 +crew: 2 +max_speed: 560 + +[vessel.Armor] +durability: 120 +dissipation: 6 + +[vessel.DirectionalThrusters] +thrust: 30 +max_turn_rate: 3.2 + +[vessel.ManeuveringThrusters] +thrust: 35 + +[vessel.Engine] +thrust: 65 + +[projectile.PlasmaCannon] +range: 370 +cool_down: 200 +damage: 20 Property changes on: vessels/rone/drach ___________________________________________________________________ Name: svn:eol-style + native Added: vessels/rone/draken =================================================================== --- vessels/rone/draken (rev 0) +++ vessels/rone/draken 2007-03-04 07:49:00 UTC (rev 119) @@ -0,0 +1,29 @@ +# $Id: lotus 116 2007-02-22 08:52:18Z cduncan $ + +[general] +vessel_class: draken +vessel_type: warship +description: The draken class warship is the backbone of the Rone fleet, ably filling multiple combat roles. Its plasma cannon array provides ample firepower in close combat. Powerful flak bombs are employed to dispense with fighter threats. The draken's defensive armor protects its vital systems and allows for a reduced electromagnetic signature. This greatly reduces the effective range that sensors can detect draken fleets, giving them the element of surprise. +hull_mass: 20 +hull_length: 17 +crew: 12 +max_speed: 485 + +[vessel.Armor] +durability: 270 +dissipation: 16 + +[vessel.DirectionalThrusters] +thrust: 190 +max_turn_rate: 2.8 + +[vessel.ManeuveringThrusters] +thrust: 100 + +[vessel.Engine] +thrust: 150 + +[projectile.PlasmaCannon] +range: 500 +cool_down: 300 +damage: 40 Property changes on: vessels/rone/draken ___________________________________________________________________ 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-03-14 05:20:14
|
Revision: 132 http://eos-game.svn.sourceforge.net/eos-game/?rev=132&view=rev Author: cduncan Date: 2007-03-13 22:20:10 -0700 (Tue, 13 Mar 2007) Log Message: ----------- - major game speed adjustment. All ship max speeds slowed significantly. Relative speeds of different ships remains about the same though. To offset this, I have increased the speed of the star background which keeps things from feeling too slow. This makes navigation less hectic and allows you to stay with friendlies better. - Also removed annoying ai flee behavior and replcaed it with a slightly more effective evade. Still more work to do here. - Reduce damage of drach proton grenade for better balance. Modified Paths: -------------- ai.py stars.py vessels/naree/cress vessels/naree/gnat vessels/naree/lotus vessels/rone/drach vessels/rone/draken Modified: ai.py =================================================================== --- ai.py 2007-03-13 08:10:16 UTC (rev 131) +++ ai.py 2007-03-14 05:20:10 UTC (rev 132) @@ -187,7 +187,7 @@ True """ seek_velocity = self.seek() - velocity = Vector2D.unit(seek_velocity.radians + diagonal) * self.vessel.max_speed + velocity = Vector2D.unit(seek_velocity.radians + diagonal * 1.5) * self.vessel.max_speed return seek_velocity.radians, velocity def avoid_vessels(self): @@ -257,12 +257,6 @@ else: # No mothership, just head toward a planet self.target.add(game.planets) - elif (self.steerfunc is not self.evade - and isinstance(self.target.sprite, Vessel) - and self.vessel.vessel_type not in ('warship', 'missile') - and self.vessel.health < 0.33 - and self.target.sprite.position.distance(self.vessel.position) < 300): - self.steerfunc = self.flee elif game.time - self.vessel.damage_time < 1500: self.steerfunc = self.evade elif (self.vessel.health > 0.66 Modified: stars.py =================================================================== --- stars.py 2007-03-13 08:10:16 UTC (rev 131) +++ stars.py 2007-03-14 05:20:10 UTC (rev 132) @@ -54,7 +54,7 @@ leftbound, topbound = self.rect.left, self.rect.top rightbound, botbound = self.rect.right, self.rect.bottom width, height = self.rect.width, self.rect.height - speed = 0.66 + speed = 0.80 left = self.star_count / self.parallax_depths for star_rect in self.stars: left -= 1 Modified: vessels/naree/cress =================================================================== --- vessels/naree/cress 2007-03-13 08:10:16 UTC (rev 131) +++ vessels/naree/cress 2007-03-14 05:20:10 UTC (rev 132) @@ -7,7 +7,7 @@ hull_mass: 5 hull_length: 5 crew: 1 -max_speed: 600 +max_speed: 400 [vessel.Shield] max_level: 100 Modified: vessels/naree/gnat =================================================================== --- vessels/naree/gnat 2007-03-13 08:10:16 UTC (rev 131) +++ vessels/naree/gnat 2007-03-14 05:20:10 UTC (rev 132) @@ -6,7 +6,7 @@ description: The Naree can ill afford to squander their pilots in delicate stub fighters, nor can they afford to waste resources on crude "fire and forget" weapons. The gnat was designed as an alternative to both, a reuseable weapon effective in both defense and offense. The gnat is a self contained autonomous unmanned craft wielding a laser cannon. Gnats are launched by a system resembling a cross between a figher bay and a torpedo tube that can be carried by medium-sized warships. Launched in groups the gnats can de directed to swarm hostile targets inflicting damage while using their agility and advanced flight control to avoid being destroyed. Once their energy is depleted, they are automatically programmed to return to their host ship to recharge. In defensive mode, gnats act as mobile point-defense weapons, staying near their host and intercepting incoming threats. hull_mass: 1 hull_length: 2 -max_speed: 800 +max_speed: 505 [vessel.Shield] max_level: 40 Modified: vessels/naree/lotus =================================================================== --- vessels/naree/lotus 2007-03-13 08:10:16 UTC (rev 131) +++ vessels/naree/lotus 2007-03-14 05:20:10 UTC (rev 132) @@ -7,7 +7,7 @@ hull_mass: 25 hull_length: 20 crew: 3 -max_speed: 520 +max_speed: 350 [vessel.Shield] max_level: 300 Modified: vessels/rone/drach =================================================================== --- vessels/rone/drach 2007-03-13 08:10:16 UTC (rev 131) +++ vessels/rone/drach 2007-03-14 05:20:10 UTC (rev 132) @@ -7,7 +7,7 @@ hull_mass: 6 hull_length: 4 crew: 2 -max_speed: 560 +max_speed: 370 [vessel.Armor] durability: 120 @@ -28,7 +28,7 @@ range: 375 fade_range: 100 cool_down: 300 -damage: 22 +damage: 15 velocity: 500 radius: 20 image: proton-grenade.png Modified: vessels/rone/draken =================================================================== --- vessels/rone/draken 2007-03-13 08:10:16 UTC (rev 131) +++ vessels/rone/draken 2007-03-14 05:20:10 UTC (rev 132) @@ -7,7 +7,7 @@ hull_mass: 20 hull_length: 17 crew: 12 -max_speed: 485 +max_speed: 315 [vessel.Armor] durability: 270 This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cd...@us...> - 2007-03-20 22:39:47
|
Revision: 143 http://eos-game.svn.sourceforge.net/eos-game/?rev=143&view=rev Author: cduncan Date: 2007-03-20 15:35:56 -0700 (Tue, 20 Mar 2007) Log Message: ----------- - Roughly double defensive strength of drakens and lotuses - Increase manuverability of gnats, but offset that with greater speed - Increase fullerene velocity and ai accuracy using it Modified Paths: -------------- projectile.py vessel.py vessels/naree/gnat vessels/naree/lotus vessels/rone/draken Modified: projectile.py =================================================================== --- projectile.py 2007-03-20 22:24:35 UTC (rev 142) +++ projectile.py 2007-03-20 22:35:56 UTC (rev 143) @@ -62,14 +62,9 @@ or not target.category & everything & ~self.gunmount.category): return False target_dist = target.position.distance(self.gunmount.position) - target_angle = (self.gunmount.position - target.position).radians - heading_diff = target.heading - self.gunmount.heading - if heading_diff > halfcircle: - heading_diff -= fullcircle - elif heading_diff < -halfcircle: - heading_diff += fullcircle if self.firing: - if (self.charge >= self.max_charge / 3 and abs(heading_diff) < (diagonal / 2) + if (self.charge >= self.max_charge / 2 + and self.gunmount.bearing(target) < (diagonal / 8.0) and target_dist < Fullerene.range * 2): return False else: @@ -80,9 +75,9 @@ class Fullerene(RoundBody): - speed = 450 + speed = 550 range = 450 - base_damage = 2 + base_damage = 1.0 particles = 4 rpm = 80 exploding = False Modified: vessel.py =================================================================== --- vessel.py 2007-03-20 22:24:35 UTC (rev 142) +++ vessel.py 2007-03-20 22:35:56 UTC (rev 143) @@ -623,7 +623,7 @@ """ name = 'armor' - mass_factor = 0.01 # Mass per durability unit per ship length unit + mass_factor = 0.005 # Mass per durability unit per ship length unit def __init__(self, vessel, durability, dissipation, regeneration=0): pygame.sprite.Sprite.__init__(self, game.sprites) Modified: vessels/naree/gnat =================================================================== --- vessels/naree/gnat 2007-03-20 22:24:35 UTC (rev 142) +++ vessels/naree/gnat 2007-03-20 22:35:56 UTC (rev 143) @@ -6,7 +6,7 @@ description: The Naree can ill afford to squander their pilots in delicate stub fighters, nor can they afford to waste resources on crude "fire and forget" weapons. The gnat was designed as an alternative to both, a reuseable weapon effective in both defense and offense. The gnat is a self contained autonomous unmanned craft wielding a laser cannon. Gnats are launched by a system resembling a cross between a figher bay and a torpedo tube that can be carried by medium-sized warships. Launched in groups the gnats can de directed to swarm hostile targets inflicting damage while using their agility and advanced flight control to avoid being destroyed. Once their energy is depleted, they are automatically programmed to return to their host ship to recharge. In defensive mode, gnats act as mobile point-defense weapons, staying near their host and intercepting incoming threats. hull_mass: 1 hull_length: 2 -max_speed: 505 +max_speed: 600 [vessel.Shield] max_level: 40 @@ -17,10 +17,10 @@ max_turn_rate: 5 [vessel.ManeuveringThrusters] -thrust: 18 +thrust: 20 [vessel.Engine] -thrust: 18 +thrust: 20 [beam.BeamWeapon] range: 100 Modified: vessels/naree/lotus =================================================================== --- vessels/naree/lotus 2007-03-20 22:24:35 UTC (rev 142) +++ vessels/naree/lotus 2007-03-20 22:35:56 UTC (rev 143) @@ -10,8 +10,8 @@ max_speed: 350 [vessel.Shield] -max_level: 300 -regeneration: 8 +max_level: 600 +regeneration: 14 # [vessel.Armor] # durability: 100 Modified: vessels/rone/draken =================================================================== --- vessels/rone/draken 2007-03-20 22:24:35 UTC (rev 142) +++ vessels/rone/draken 2007-03-20 22:35:56 UTC (rev 143) @@ -10,7 +10,7 @@ max_speed: 315 [vessel.Armor] -durability: 270 +durability: 540 dissipation: 16 [vessel.DirectionalThrusters] This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cd...@us...> - 2007-03-22 20:09:04
|
Revision: 151 http://eos-game.svn.sourceforge.net/eos-game/?rev=151&view=rev Author: cduncan Date: 2007-03-22 12:59:06 -0700 (Thu, 22 Mar 2007) Log Message: ----------- - Reduce damage from friendly fire - Limit fleet sizes to 12 on each side - Increase gap between waves by 1 second - Significantly reduce vessel max speeds again by a fixed amount. This lets you stay in the battle more easily, stay near the planet and keep together. Modified Paths: -------------- game.py projectile.py stars.py vessels/naree/cress vessels/naree/gnat vessels/naree/lotus vessels/rone/drach vessels/rone/draken Modified: game.py =================================================================== --- game.py 2007-03-22 06:16:05 UTC (rev 150) +++ game.py 2007-03-22 19:59:06 UTC (rev 151) @@ -16,6 +16,7 @@ fps = 32 # Desired fps avg_fps = fps # Running average of actual fps ai_interval = 70 # Release AI every N seconds +max_fleet_size = 12 # Maximum # of vessels for each side universe = None # ode world collision_space = None # ode collision space screen = None # pygame screen @@ -134,6 +135,7 @@ global universe, collision_space, screen, background global sprites, fps, avg_fps, clock, time, frame_no global ai_interval + friends = pygame.sprite.Group() enemies = pygame.sprite.Group() next_wave = 0 while handle_events(): @@ -159,13 +161,14 @@ warship = netsync.random.random() * frame_no > 800 if target is local_player: media.play_soundtrack(large=warship) - if warship: + if warship and len(enemies) < max_fleet_size: ai = AIVessel.load('vessels/naree/lotus') ai.position.x, ai.position.y = x, y enemies.add(ai) barrier = 300 friendly = warship - while friendly or netsync.random.random() * frame_no > barrier: + while (friendly or netsync.random.random() * frame_no > barrier + and len(friends) < max_fleet_size): if netsync.random.random() * frame_no < barrier * 6: ship = 'vessels/rone/drach' else: @@ -173,10 +176,11 @@ friend = AIVessel.load(ship, mothership=target, category=body.friend) friend.position.x = target.position.x - position.x friend.position.y = target.position.y - position.y + friends.add(friend) barrier *= 2 friendly = False barrier = 200 - while 1: + while len(enemies) < max_fleet_size: if warship and netsync.random.random() * frame_no < barrier: break ai = AIVessel.load('vessels/naree/cress') @@ -187,5 +191,5 @@ next_wave = time + ai_interval * 1000 ai_interval = max(ai_interval * .9, 5) elif not enemies: - next_wave = min(time + 4000, next_wave) + next_wave = min(time + 5000, next_wave) frame_no += 1 Modified: projectile.py =================================================================== --- projectile.py 2007-03-22 06:16:05 UTC (rev 150) +++ projectile.py 2007-03-22 19:59:06 UTC (rev 151) @@ -275,12 +275,17 @@ def collide(self, other, contacts): if self.alive(): self.kill() - if self.damage_value > 0: - other.damage(self.damage_value) - if other.explosion is None: - media.play_sound('hit.wav', - volume=self.damage_value / self.start_damage, - position=self.position) + if self.shooter.is_friendly(other): + # Reduce damage for friendly fire + damage = self.damage_value / 3 + else: + damage = self.damage_value + if damage > 0: + other.damage(damage) + if other.explosion is None: + media.play_sound('hit.wav', + volume=self.damage_value / self.start_damage, + position=self.position) class ExplodingShot(Shot): """Shot that explodes on collision""" Modified: stars.py =================================================================== --- stars.py 2007-03-22 06:16:05 UTC (rev 150) +++ stars.py 2007-03-22 19:59:06 UTC (rev 151) @@ -54,7 +54,7 @@ leftbound, topbound = self.rect.left, self.rect.top rightbound, botbound = self.rect.right, self.rect.bottom width, height = self.rect.width, self.rect.height - speed = 0.80 + speed = 0.90 left = self.star_count / self.parallax_depths for star_rect in self.stars: left -= 1 Modified: vessels/naree/cress =================================================================== --- vessels/naree/cress 2007-03-22 06:16:05 UTC (rev 150) +++ vessels/naree/cress 2007-03-22 19:59:06 UTC (rev 151) @@ -7,7 +7,7 @@ hull_mass: 5 hull_length: 5 crew: 1 -max_speed: 400 +max_speed: 250 [vessel.Shield] max_level: 100 Modified: vessels/naree/gnat =================================================================== --- vessels/naree/gnat 2007-03-22 06:16:05 UTC (rev 150) +++ vessels/naree/gnat 2007-03-22 19:59:06 UTC (rev 151) @@ -6,7 +6,7 @@ description: The Naree can ill afford to squander their pilots in delicate stub fighters, nor can they afford to waste resources on crude "fire and forget" weapons. The gnat was designed as an alternative to both, a reuseable weapon effective in both defense and offense. The gnat is a self contained autonomous unmanned craft wielding a laser cannon. Gnats are launched by a system resembling a cross between a figher bay and a torpedo tube that can be carried by medium-sized warships. Launched in groups the gnats can de directed to swarm hostile targets inflicting damage while using their agility and advanced flight control to avoid being destroyed. Once their energy is depleted, they are automatically programmed to return to their host ship to recharge. In defensive mode, gnats act as mobile point-defense weapons, staying near their host and intercepting incoming threats. hull_mass: 1 hull_length: 2 -max_speed: 600 +max_speed: 450 [vessel.Shield] max_level: 40 Modified: vessels/naree/lotus =================================================================== --- vessels/naree/lotus 2007-03-22 06:16:05 UTC (rev 150) +++ vessels/naree/lotus 2007-03-22 19:59:06 UTC (rev 151) @@ -7,7 +7,7 @@ hull_mass: 25 hull_length: 20 crew: 3 -max_speed: 350 +max_speed: 200 [vessel.Shield] max_level: 600 Modified: vessels/rone/drach =================================================================== --- vessels/rone/drach 2007-03-22 06:16:05 UTC (rev 150) +++ vessels/rone/drach 2007-03-22 19:59:06 UTC (rev 151) @@ -7,7 +7,7 @@ hull_mass: 6 hull_length: 4 crew: 2 -max_speed: 370 +max_speed: 220 [vessel.Armor] durability: 120 Modified: vessels/rone/draken =================================================================== --- vessels/rone/draken 2007-03-22 06:16:05 UTC (rev 150) +++ vessels/rone/draken 2007-03-22 19:59:06 UTC (rev 151) @@ -7,7 +7,7 @@ hull_mass: 20 hull_length: 17 crew: 12 -max_speed: 315 +max_speed: 165 [vessel.Armor] durability: 540 This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cd...@us...> - 2007-03-24 05:50:47
|
Revision: 155 http://eos-game.svn.sourceforge.net/eos-game/?rev=155&view=rev Author: cduncan Date: 2007-03-23 22:50:45 -0700 (Fri, 23 Mar 2007) Log Message: ----------- Speed things up again, splitting the difference between the current and previous paces Modified Paths: -------------- vessels/naree/cress vessels/naree/lotus vessels/rone/drach vessels/rone/draken Modified: vessels/naree/cress =================================================================== --- vessels/naree/cress 2007-03-24 01:37:26 UTC (rev 154) +++ vessels/naree/cress 2007-03-24 05:50:45 UTC (rev 155) @@ -7,7 +7,7 @@ hull_mass: 5 hull_length: 5 crew: 1 -max_speed: 250 +max_speed: 300 [vessel.Shield] max_level: 100 Modified: vessels/naree/lotus =================================================================== --- vessels/naree/lotus 2007-03-24 01:37:26 UTC (rev 154) +++ vessels/naree/lotus 2007-03-24 05:50:45 UTC (rev 155) @@ -7,7 +7,7 @@ hull_mass: 25 hull_length: 20 crew: 3 -max_speed: 200 +max_speed: 250 [vessel.Shield] max_level: 600 Modified: vessels/rone/drach =================================================================== --- vessels/rone/drach 2007-03-24 01:37:26 UTC (rev 154) +++ vessels/rone/drach 2007-03-24 05:50:45 UTC (rev 155) @@ -7,7 +7,7 @@ hull_mass: 6 hull_length: 4 crew: 2 -max_speed: 220 +max_speed: 270 [vessel.Armor] durability: 120 Modified: vessels/rone/draken =================================================================== --- vessels/rone/draken 2007-03-24 01:37:26 UTC (rev 154) +++ vessels/rone/draken 2007-03-24 05:50:45 UTC (rev 155) @@ -7,7 +7,7 @@ hull_mass: 20 hull_length: 17 crew: 12 -max_speed: 165 +max_speed: 215 [vessel.Armor] durability: 540 This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cd...@us...> - 2007-04-07 08:59:45
|
Revision: 171 http://eos-game.svn.sourceforge.net/eos-game/?rev=171&view=rev Author: cduncan Date: 2007-04-07 01:59:41 -0700 (Sat, 07 Apr 2007) Log Message: ----------- - Implement vessel energy. - Rebalance ship energy, damage, etc., In particular give the cress a lot more punch - Add graphical health/energy meters under each ship. For now they are always visible, and also used for the player's ship. Probably these will be visible on demand and we'll have separate gauges for the player. Modified Paths: -------------- bay.py beam.py frontend.py projectile.py vessel.py vessels/naree/cress vessels/naree/gnat vessels/naree/lotus vessels/rone/drach vessels/rone/draken Modified: bay.py =================================================================== --- bay.py 2007-04-05 06:52:18 UTC (rev 170) +++ bay.py 2007-04-07 08:59:41 UTC (rev 171) @@ -66,18 +66,21 @@ priority = 1 mass = 5 - def __init__(self, vessel_mount, bomb_factory, launch_velocity, max_ttl, reload_time): + def __init__(self, vessel_mount, bomb_factory, launch_velocity, max_ttl, reload_time, + energy_use): self.vessel = vessel_mount self.bomb_factory = vessel.resolve(bomb_factory) self.launch_velocity = float(launch_velocity) self.reload_time = float(reload_time) * 1000 + self.energy_use = int(energy_use) self.max_ttl = float(max_ttl) * 1000 self.range = self.launch_velocity * float(max_ttl) self.last_launch = 0 def update_system(self): if (self.enabled and self.firing - and game.time > self.last_launch + self.reload_time): + and game.time > self.last_launch + self.reload_time + and self.vessel.use_energy(self.energy_use)): bomb = self.bomb_factory(self.vessel, self, Vector2D.unit(self.vessel.heading) * self.launch_velocity, self.max_ttl) Modified: beam.py =================================================================== --- beam.py 2007-04-05 06:52:18 UTC (rev 170) +++ beam.py 2007-04-07 08:59:41 UTC (rev 171) @@ -20,12 +20,13 @@ mass_factor = 0.1 # Mass per unit damage - def __init__(self, gunmount, range, damage, width=1, color='255,255,255'): + def __init__(self, gunmount, range, damage, efficiency, width=1, color='255,255,255'): pygame.sprite.Sprite.__init__(self, game.sprites) self.gunmount = gunmount self.range = float(range) self.length = self.range self.beam_damage = float(damage) + self.efficiency = float(efficiency) self.mass = self.beam_damage * self.mass_factor self.width = int(width) self.color = tuple(int(c) for c in color.split(',')) @@ -40,6 +41,8 @@ self.ray.setCollideBits(body.everything & ~self.gunmount.category) self.ray.parent = self self.ray.set(self.gunmount.position, Vector2D.unit(self.gunmount.heading)) + self.firing = self.firing and self.gunmount.use_energy( + self.beam_damage / self.efficiency / game.fps) def draw(self, surface): if self.firing and self.enabled: @@ -63,5 +66,5 @@ self.length = max(contact[0].getContactGeomParams()[2], 30) self.targeted = True if self.firing and self.enabled: - other.damage(self.beam_damage / game.avg_fps) + other.damage(self.beam_damage / game.fps) Modified: frontend.py =================================================================== --- frontend.py 2007-04-05 06:52:18 UTC (rev 170) +++ frontend.py 2007-04-07 08:59:41 UTC (rev 171) @@ -167,7 +167,7 @@ def key_down(self, event): """Handle key press""" if event.key == K_ESCAPE: - self.finished = True + self.quit() def quit(self, event=None): self.finished = True Modified: projectile.py =================================================================== --- projectile.py 2007-04-05 06:52:18 UTC (rev 170) +++ projectile.py 2007-04-07 08:59:41 UTC (rev 171) @@ -23,7 +23,7 @@ priority = 0 mass = 10 - def __init__(self, gunmount, charge_rate, min_charge, max_charge, max_charge_time): + def __init__(self, gunmount, charge_rate, min_charge, max_charge, max_charge_time, efficiency): """Fullerene pulse cannon, a variable charge energy projectile gunmount -- body we are mounted to @@ -36,6 +36,7 @@ self.min_charge = float(min_charge) self.max_charge = float(max_charge) self.max_charge_time = float(max_charge_time) + self.efficiency = float(efficiency) self.charge_start = None self.charge = 0 self.gunmount = gunmount @@ -47,8 +48,9 @@ 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: - Fullerene(self.charge, self.gunmount) - self.charge = 0 + if self.gunmount.use_energy((self.charge+1)**2 / self.efficiency): + Fullerene(self.charge, self.gunmount) + self.charge = 0 elif not self.firing: self.charge = 0 self.charge_start = None @@ -64,7 +66,7 @@ return False target_dist = target.position.distance(self.gunmount.position) if self.firing: - if (self.charge >= self.max_charge / 2 + if (self.charge >= self.max_charge / 1.5 and self.gunmount.bearing(target) < (diagonal / 4.0) and target_dist < Fullerene.range * 2): return False @@ -147,13 +149,14 @@ mass_factor = 0.1 # Mass per (range / 100) + damage def __init__(self, gunmount, range, velocity, cool_down, image, damage, - radius=None, damage_loss=0.0, sound=None, shot='projectile.Shot', + efficiency, radius=None, damage_loss=0.0, sound=None, shot='projectile.Shot', fade_range=None, mount_points=1, mount_positions=None): self.gunmount = gunmount self.range = float(range) self.velocity = float(velocity) self.cool_down = float(cool_down) self.damage_value = float(damage) + self.efficiency = float(efficiency) self.damage_loss = float(damage_loss) self.velocity = float(velocity) self.mass = self.base_mass + ((self.range / 100) + self.damage_value) * self.mass_factor @@ -180,7 +183,8 @@ self.next_position = 0 def update_system(self): - if self.enabled and self.firing and game.time - self.last_fire > self.cool_down: + if (self.enabled and self.firing and game.time - self.last_fire > self.cool_down + and self.gunmount.use_energy(self.damage_value / self.efficiency)): shot_position = self.gunmount.position.copy() if self.mount_positions is not None: angle, length = self.mount_positions[self.next_position] Modified: vessel.py =================================================================== --- vessel.py 2007-04-05 06:52:18 UTC (rev 170) +++ vessel.py 2007-04-07 08:59:41 UTC (rev 171) @@ -95,11 +95,13 @@ system_damage = 0 # System damage accumulator system_damage_threshold = 10 # How much damage to disable a system max_speed = 0 + max_energy = 0 + energy_storage_mass = 0.02 # Mass per energy unit stored control = Control() 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): + image_name=None, hull_mass=1, hull_length=1, crew=0, max_speed=0, max_energy=0): if image_name is None: if vessel_class: image_name = vessel_class + '.png' @@ -116,6 +118,7 @@ self.hull_length = float(hull_length) self.hull_mass = float(hull_mass) self.crew = int(crew) + self.energy = self.max_energy = float(max_energy) self.max_speed = float(max_speed) self._sys = [] self._damage_sys = [] @@ -210,7 +213,6 @@ params = dict(parser.items(section)) system = sys_constructor(vessel, **params) except Exception, err: - print `sys_constructor`, params raise VesselConfigError, ( 'Exception creating system from section [%s] of vessel config file %s: %s' % (section, file_name, err)), sys.exc_info()[-1] @@ -240,6 +242,37 @@ self.image = self.vessel_img.rotated(self.heading) self.rect = self.image.get_rect(center=self.rect.center) + def draw(self, surface): + rect = body.RoundBody.draw(self, surface) + if self.explosion is None: + energy_frac = float(self.energy) / self.max_energy + graph_rect = pygame.Rect(0, 0, 37, 3) + graph_rect.midtop = self.rect.centerx, self.rect.centery + self.radius * 1.5 + graph_bar = pygame.Rect(graph_rect) + health = self.health + graph_bar.width = int(health * graph_bar.width) + if self.health > 0.7: + color = (0, 255, 0) + elif self.health > 0.45: + color = (255, 255, 0) + elif self.health > 0.2: + color = (255, 127, 0) + else: + color = (255, 0, 0) + surface.fill((0, 0, 0), graph_rect) + surface.fill(color, graph_bar) + pygame.draw.rect(surface, (50, 50, 50), graph_rect, 1) + graph_rect.top = graph_rect.bottom + 1 + graph_bar.top = graph_bar.bottom + 1 + graph_bar.width = int(energy_frac * graph_bar.width) + surface.fill((0, 0, 0), graph_rect) + surface.fill((50, 50, 255), graph_bar) + pygame.draw.rect(surface, (0, 0, 125), graph_rect, 1) + return rect.union(graph_rect) + else: + return rect + + def add_system(self, system): """Add a vessel system @@ -322,6 +355,7 @@ >>> v.calc_mass() """ self.mass = sum(s.mass for s in self._sys) + self.hull_mass + self.mass += self.max_energy * self.energy_storage_mass massobj = self.body.getMass() massobj.adjust(self.mass) self.body.setMass(massobj) @@ -370,6 +404,22 @@ system = netsync.random.choice(self._sys) system.disable() self.system_damage -= self.system_damage_threshold + + def use_energy(self, energy, partial=False): + """Consume vessel energy. Return the amount of energy that is available. + If parital is true, return whatever energy is available up to the amount + specified, otherwise return all of what was specified or zero if that amount + is not available. + """ + if self.energy >= energy: + self.energy -= energy + return energy + elif partial: + energy = self.energy + self.energy = 0 + return energy + else: + return 0 @property def health(self): @@ -546,8 +596,11 @@ if self.vessel.control.shield: self.show_shield() if self.level < self.max_level and self.regeneration and self.enabled: - self.level = min( + # Regenerate shields + new_level = min( self.level + self.regeneration / game.avg_fps, self.max_level) + energy = self.vessel.use_energy(new_level - self.level, partial=True) + self.level += energy if self.regeneration < self.max_regeneration: # Recover regeneration rate if disrupted self.regeneration = min( @@ -870,24 +923,27 @@ class Engine(System): - """Basic thrust engine""" + """Basic thrust engine and generator""" name = 'engine' attr_name = 'engine' base_mass = 1 mass_factor = 0.03 # mass per unit thrust - speed_boost = 1.5 + energy_factor = 0.4 # energy generation per unit thrust + speed_boost = 1.6 def __init__(self, vessel, thrust): self.vessel = vessel self.thrust = float(thrust) * 100 + self.generation = float(thrust) * self.energy_factor self.mass = self.base_mass + float(thrust) * self.mass_factor self.max_speed = self.vessel.max_speed self.enabled = True def disable(self): - self.max_speed /= 2 + self.max_speed *= 0.75 self.thrust /= 2 + self.generation /= 2 self.enabled = False def enable(self): @@ -915,9 +971,11 @@ >>> v.force.radians == v.heading True """ + boost = False if self.vessel.control.thrust: if self.vessel.control.fw_maneuver: # Thrust + forward maneuver == boost + boost = True self.vessel.max_speed = self.max_speed * self.speed_boost elif self.vessel.max_speed > self.max_speed: # overspeed, bleed off a little @@ -928,7 +986,11 @@ # overspeed, bleed off a little # this avoids a sudden decceleration when we let off boost self.vessel.max_speed = max(self.vessel.max_speed * 0.98, self.max_speed) + if not boost: + self.vessel.energy = min( + self.vessel.energy + self.generation / game.fps, self.vessel.max_energy) + def resolve(dotted_name): """Return the object corresponding to the dotted module/object name pair specified. Modified: vessels/naree/cress =================================================================== --- vessels/naree/cress 2007-04-05 06:52:18 UTC (rev 170) +++ vessels/naree/cress 2007-04-07 08:59:41 UTC (rev 171) @@ -8,6 +8,7 @@ hull_length: 5 crew: 1 max_speed: 300 +max_energy: 200 [vessel.Shield] max_level: 100 @@ -25,6 +26,7 @@ [beam.BeamWeapon] range=200 -damage=55 +damage=95 +efficiency: 0.97 width=2 color: 100, 255, 100 Modified: vessels/naree/gnat =================================================================== --- vessels/naree/gnat 2007-04-05 06:52:18 UTC (rev 170) +++ vessels/naree/gnat 2007-04-07 08:59:41 UTC (rev 171) @@ -7,6 +7,7 @@ hull_mass: 1 hull_length: 2 max_speed: 450 +max_energy: 50 [vessel.Shield] max_level: 40 @@ -25,4 +26,5 @@ [beam.BeamWeapon] range: 100 damage: 25 +efficiency: 0.98 color: 255, 80, 120 Modified: vessels/naree/lotus =================================================================== --- vessels/naree/lotus 2007-04-05 06:52:18 UTC (rev 170) +++ vessels/naree/lotus 2007-04-07 08:59:41 UTC (rev 171) @@ -8,6 +8,7 @@ hull_length: 20 crew: 3 max_speed: 250 +max_energy: 600 [vessel.Shield] max_level: 600 @@ -18,7 +19,7 @@ # dissipation: 10 [vessel.DirectionalThrusters] -thrust: 160 +thrust: 190 max_turn_rate: 2.5 [vessel.ManeuveringThrusters] @@ -32,6 +33,7 @@ min_charge: 0.4 max_charge: 9 max_charge_time: 4 +efficiency: 0.95 [bay.FighterBay] fighter_config: vessels/naree/gnat Modified: vessels/rone/drach =================================================================== --- vessels/rone/drach 2007-04-05 06:52:18 UTC (rev 170) +++ vessels/rone/drach 2007-04-07 08:59:41 UTC (rev 171) @@ -8,6 +8,7 @@ hull_length: 4 crew: 2 max_speed: 270 +max_energy: 250 [vessel.Armor] durability: 120 @@ -29,6 +30,7 @@ fade_range: 100 cool_down: 300 damage: 15 +efficiency: 0.80 velocity: 500 radius: 20 image: proton-grenade.png @@ -41,6 +43,7 @@ range: 200 cool_down: 200 damage: 3 +efficiency: 0.90 velocity: 800 radius: 4 image: phase-disruptor.png Modified: vessels/rone/draken =================================================================== --- vessels/rone/draken 2007-04-05 06:52:18 UTC (rev 170) +++ vessels/rone/draken 2007-04-07 08:59:41 UTC (rev 171) @@ -8,13 +8,14 @@ hull_length: 17 crew: 12 max_speed: 215 +max_energy: 800 [vessel.Armor] durability: 540 dissipation: 16 [vessel.DirectionalThrusters] -thrust: 190 +thrust: 210 max_turn_rate: 2.8 [vessel.ManeuveringThrusters] @@ -28,6 +29,7 @@ fade_range: 125 cool_down: 300 damage: 40 +efficiency: 0.85 damage_loss: 25 velocity: 500 sound: plasma-cannon-small.wav @@ -36,5 +38,6 @@ [bay.BombBay] bomb_factory: projectile.FlakBomb launch_velocity: 250 +energy_use: 200 reload_time: 3 max_ttl: 2 This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cd...@us...> - 2007-04-17 05:38:45
|
Revision: 176 http://eos-game.svn.sourceforge.net/eos-game/?rev=176&view=rev Author: cduncan Date: 2007-04-16 22:38:40 -0700 (Mon, 16 Apr 2007) Log Message: ----------- Rebalance: - Decrease energy usage of flak bomb, increase turn thrust a bit - Decrease max energy of lotus Modified Paths: -------------- vessels/naree/lotus vessels/rone/draken Modified: vessels/naree/lotus =================================================================== --- vessels/naree/lotus 2007-04-17 05:37:05 UTC (rev 175) +++ vessels/naree/lotus 2007-04-17 05:38:40 UTC (rev 176) @@ -8,7 +8,7 @@ hull_length: 20 crew: 3 max_speed: 250 -max_energy: 600 +max_energy: 500 [vessel.Shield] max_level: 600 Modified: vessels/rone/draken =================================================================== --- vessels/rone/draken 2007-04-17 05:37:05 UTC (rev 175) +++ vessels/rone/draken 2007-04-17 05:38:40 UTC (rev 176) @@ -15,7 +15,7 @@ dissipation: 16 [vessel.DirectionalThrusters] -thrust: 210 +thrust: 220 max_turn_rate: 2.8 [vessel.ManeuveringThrusters] @@ -38,6 +38,6 @@ [bay.BombBay] bomb_factory: projectile.FlakBomb launch_velocity: 250 -energy_use: 200 +energy_use: 150 reload_time: 3 max_ttl: 2 This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cd...@us...> - 2007-05-02 05:57:38
|
Revision: 210 http://eos-game.svn.sourceforge.net/eos-game/?rev=210&view=rev Author: cduncan Date: 2007-05-01 22:57:36 -0700 (Tue, 01 May 2007) Log Message: ----------- - Separate collision radius from ode body radius. The former is derived from the sprite size, the latter from the vessel config. - Make projectile ranges reasonable, in particular reduce Rone range to reduce their advantage over the Naree - Rebalance ship speed/manueverability to give the Naree a larger advantage, making the cress more survivable and fun. I believe it's more capable than the drach, but it's less user-friendly requiring more finesse - Give the cress an energy generation boost. Dogfighting with the drach it was clear this was its achilles heal, especially since the cress' energy store is much smaller and it's shields consume energy when recharging. Modified Paths: -------------- body.py projectile.py vessel.py vessels/naree/cress vessels/naree/gnat vessels/naree/lotus vessels/rone/drach vessels/rone/draken Modified: body.py =================================================================== --- body.py 2007-05-02 05:43:32 UTC (rev 209) +++ body.py 2007-05-02 05:57:36 UTC (rev 210) @@ -33,7 +33,8 @@ """ mass = 0.1 # Mass in tons - radius = 0.1 # radius in meters + 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 category = nothing # category for collision interaction @@ -105,12 +106,12 @@ self.body.setLinearVel((velocity[0], velocity[1], 0)) self.velocity = vector.vector2(*velocity) - def setup_collision(self, category, collides_with): + def setup_collision(self, category, collides_with, radius=None): """Configure the collision geom to determine what objects we collide with (collides_with) and what objects collide with us (category) """ if self.geom is None: - self.geom = ode.GeomSphere(game.collision_space, self.radius) + self.geom = ode.GeomSphere(game.collision_space, self.collision_radius or self.radius) self.geom.setBody(self.body) # Move with the ode body self.geom.parent = self # attach ourself for callback purposes self.geom.setCategoryBits(category) Modified: projectile.py =================================================================== --- projectile.py 2007-05-02 05:43:32 UTC (rev 209) +++ projectile.py 2007-05-02 05:57:36 UTC (rev 210) @@ -253,7 +253,7 @@ collides_with=everything & ~shooter.category ) self.shooter = shooter - self.start_position = vector.vector2(shooter.position) + self.start_position = shooter.position self.set_image(image) self.rect = image.get_rect(center=shooter.rect.center) self.damage_value = self.start_damage = damage @@ -276,7 +276,6 @@ RoundBody.update(self) self.damage_value -= self.damage_loss distance = vector.distance(self.start_position, self.position) - print distance if distance > self.range: self.kill() elif distance > self.fade_range: @@ -336,7 +335,7 @@ """ shards = 50 shard_velocity = 500 - shard_range = 100 + shard_range = 50 shard_damage = 3.0 min_time = 0.6 @@ -344,7 +343,7 @@ bomb_img = media.RotatedImage('flak-bomb.png') Shot.__init__(self, shooter, image=bomb_img.rotated(shooter.heading), - damage=75, velocity=velocity, range=1000) + damage=75, velocity=velocity, range=750) self.max_time = game.time + max_ttl self.min_time = game.time + self.min_time * 1000 media.play_sound('flak-bomb.wav', position=shooter.position) Modified: vessel.py =================================================================== --- vessel.py 2007-05-02 05:43:32 UTC (rev 209) +++ vessel.py 2007-05-02 05:57:36 UTC (rev 210) @@ -128,13 +128,14 @@ image_name = 'ship.png' self.vessel_img = RotatedImage(image_name) img_rect = self.vessel_img.get_rect() - self.radius = max(img_rect.width, img_rect.height) / 2 + self.hull_length = float(hull_length) + self.radius = self.hull_length + self.collision_radius = max(img_rect.size) / 2.25 body.RoundBody.__init__(self) self.vessel_name = vessel_name self.vessel_class = vessel_class self.vessel_type = vessel_type self.description = description - self.hull_length = float(hull_length) self.hull_mass = float(hull_mass) self.crew = int(crew) self.energy = self.max_energy = float(max_energy) @@ -334,7 +335,7 @@ opacity = 255 status_image.set_alpha(opacity) status_rect = status_image.get_rect( - centerx=self.rect.centerx, top=self.rect.centery + self.radius * 1.5) + centerx=self.rect.centerx, top=self.rect.centery + self.collision_radius + 15) surface.blit(status_image, status_rect) return rect.union(status_rect) else: @@ -853,14 +854,13 @@ name = 'directional thrusters' attr_name = 'directional_thrusters' mass = 0.5 - mass_factor = 50 # Number of tons required to half turn acceleration def __init__(self, vessel, thrust, max_turn_rate): """thrust is in radians per second per second for a one ton ship max_turn_rate is in radians per second """ self.vessel = vessel - self.thrust = self.full_thrust = float(thrust) * 1000 + self.thrust = self.full_thrust = float(thrust) * 10000 self.max_turn_rate = self.full_turn_rate = float(max_turn_rate) self.enabled = True @@ -914,7 +914,7 @@ def __init__(self, vessel, thrust): self.vessel = vessel - self.thrust = float(thrust) * 100 + self.thrust = float(thrust) self.mass = float(thrust) * self.mass_factor self.enabled = True @@ -989,7 +989,7 @@ attr_name = 'engine' base_mass = 1 mass_factor = 0.03 # mass per unit thrust - energy_factor = 0.4 # energy generation per unit thrust + energy_factor = 0.8 # energy generation per unit thrust speed_boost = 1.6 def __init__(self, vessel, thrust): Modified: vessels/naree/cress =================================================================== --- vessels/naree/cress 2007-05-02 05:43:32 UTC (rev 209) +++ vessels/naree/cress 2007-05-02 05:57:36 UTC (rev 210) @@ -7,7 +7,7 @@ hull_mass: 5 hull_length: 5 crew: 1 -max_speed: 150 +max_speed: 160 max_energy: 200 [vessel.Shield] @@ -15,14 +15,14 @@ regeneration: 18 [vessel.DirectionalThrusters] -thrust: 30 -max_turn_rate: 3 +thrust: 0.6 +max_turn_rate: 4 [vessel.ManeuveringThrusters] -thrust: 50 +thrust: 35 [vessel.Engine] -thrust: 80 +thrust: 55 [beam.BeamWeapon] range=200 Modified: vessels/naree/gnat =================================================================== --- vessels/naree/gnat 2007-05-02 05:43:32 UTC (rev 209) +++ vessels/naree/gnat 2007-05-02 05:57:36 UTC (rev 210) @@ -14,14 +14,14 @@ regeneration: 0 [vessel.DirectionalThrusters] -thrust: 1 +thrust: 0.1 max_turn_rate: 5 [vessel.ManeuveringThrusters] -thrust: 20 +thrust: 10 [vessel.Engine] -thrust: 20 +thrust: 10 [beam.BeamWeapon] range: 100 Modified: vessels/naree/lotus =================================================================== --- vessels/naree/lotus 2007-05-02 05:43:32 UTC (rev 209) +++ vessels/naree/lotus 2007-05-02 05:57:36 UTC (rev 210) @@ -19,14 +19,14 @@ # dissipation: 10 [vessel.DirectionalThrusters] -thrust: 190 -max_turn_rate: 2.5 +thrust: 19 +max_turn_rate: 2.6 [vessel.ManeuveringThrusters] -thrust: 90 +thrust: 45 [vessel.Engine] -thrust: 150 +thrust: 75 [projectile.FullereneCannon] charge_rate: 10 Modified: vessels/rone/drach =================================================================== --- vessels/rone/drach 2007-05-02 05:43:32 UTC (rev 209) +++ vessels/rone/drach 2007-05-02 05:57:36 UTC (rev 210) @@ -5,7 +5,7 @@ vessel_type: fighter description: The Drach is the first strike weapon of choice for the Rune. Deadly Drach squadrons can handily flank enemy vessels to deliver a hail of plasma fire at close range. At longer range their proton grenades pack a deadly punch. Their small size and rigid armor make them a challenge to gun down before they unload their deadly arsenal. hull_mass: 6 -hull_length: 4 +hull_length: 6 crew: 2 max_speed: 135 max_energy: 250 @@ -15,7 +15,7 @@ dissipation: 6 [vessel.DirectionalThrusters] -thrust: 30 +thrust: 0.7 max_turn_rate: 3.2 [vessel.ManeuveringThrusters] @@ -26,7 +26,7 @@ [projectile.Cannon:0] shot: projectile.ExplodingShot -range: 375 +range: 275 fade_range: 100 cool_down: 300 damage: 15 @@ -40,7 +40,7 @@ shot: projectile.PhaseDisruptorShot mount_points: 2 mount_positions: (-rightangle, 11), (rightangle, 11) -range: 200 +range: 175 cool_down: 200 damage: 3 efficiency: 0.90 Modified: vessels/rone/draken =================================================================== --- vessels/rone/draken 2007-05-02 05:43:32 UTC (rev 209) +++ vessels/rone/draken 2007-05-02 05:57:36 UTC (rev 210) @@ -5,7 +5,7 @@ vessel_type: warship description: The draken class warship is the backbone of the Rone fleet, ably filling multiple combat roles. Its plasma cannon array provides ample firepower in close combat. Powerful flak bombs are employed to dispense with fighter threats. The draken's defensive armor protects its vital systems and allows for a reduced electromagnetic signature. This greatly reduces the effective range that sensors can detect draken fleets, giving them the element of surprise. hull_mass: 20 -hull_length: 17 +hull_length: 18 crew: 12 max_speed: 107 max_energy: 1000 @@ -15,30 +15,30 @@ dissipation: 16 [vessel.DirectionalThrusters] -thrust: 220 +thrust: 10 max_turn_rate: 2.8 [vessel.ManeuveringThrusters] -thrust: 100 +thrust: 50 [vessel.Engine] -thrust: 150 +thrust: 75 [projectile.Cannon] -range: 500 +range: 400 radius: 15 fade_range: 125 -cool_down: 300 +cool_down: 350 damage: 40 efficiency: 0.85 -damage_loss: 25 -velocity: 500 +damage_loss: 35 +velocity: 400 sound: plasma-cannon-small.wav image: plasma.png [bay.BombBay] bomb_factory: projectile.FlakBomb -launch_velocity: 250 +launch_velocity: 150 energy_use: 150 reload_time: 3 max_ttl: 2 This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cd...@us...> - 2007-05-04 07:11:22
|
Revision: 211 http://eos-game.svn.sourceforge.net/eos-game/?rev=211&view=rev Author: cduncan Date: 2007-05-04 00:11:19 -0700 (Fri, 04 May 2007) Log Message: ----------- - Reduce lotus/drach turn thrust to make the inertia noticeable again - Reduce drach weapon efficiency to better balance against the naree Modified Paths: -------------- vessels/naree/lotus vessels/rone/drach Modified: vessels/naree/lotus =================================================================== --- vessels/naree/lotus 2007-05-02 05:57:36 UTC (rev 210) +++ vessels/naree/lotus 2007-05-04 07:11:19 UTC (rev 211) @@ -19,8 +19,8 @@ # dissipation: 10 [vessel.DirectionalThrusters] -thrust: 19 -max_turn_rate: 2.6 +thrust: 10 +max_turn_rate: 2.7 [vessel.ManeuveringThrusters] thrust: 45 Modified: vessels/rone/drach =================================================================== --- vessels/rone/drach 2007-05-02 05:57:36 UTC (rev 210) +++ vessels/rone/drach 2007-05-04 07:11:19 UTC (rev 211) @@ -15,7 +15,7 @@ dissipation: 6 [vessel.DirectionalThrusters] -thrust: 0.7 +thrust: 0.55 max_turn_rate: 3.2 [vessel.ManeuveringThrusters] @@ -30,7 +30,7 @@ fade_range: 100 cool_down: 300 damage: 15 -efficiency: 0.80 +efficiency: 0.65 velocity: 500 radius: 20 image: proton-grenade.png @@ -40,10 +40,10 @@ shot: projectile.PhaseDisruptorShot mount_points: 2 mount_positions: (-rightangle, 11), (rightangle, 11) -range: 175 +range: 150 cool_down: 200 damage: 3 -efficiency: 0.90 +efficiency: 0.25 velocity: 800 radius: 4 image: phase-disruptor.png This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cd...@us...> - 2007-06-19 08:17:10
|
Revision: 235 http://eos-game.svn.sourceforge.net/eos-game/?rev=235&view=rev Author: cduncan Date: 2007-06-19 01:17:07 -0700 (Tue, 19 Jun 2007) Log Message: ----------- Fix weapon system naming for damage/repair messages Modified Paths: -------------- bay.py beam.py projectile.py vessel.py vessels/naree/cress vessels/naree/gnat vessels/rone/drach vessels/rone/draken Modified: bay.py =================================================================== --- bay.py 2007-06-19 07:36:22 UTC (rev 234) +++ bay.py 2007-06-19 08:17:07 UTC (rev 235) @@ -133,9 +133,10 @@ priority = 1 mass = 5 + name = 'bomb bay' def __init__(self, vessel_mount, bomb_factory, launch_velocity, max_ttl, reload_time, - energy_use): + energy_use, name=None): self.vessel = vessel_mount self.bomb_factory = vessel.resolve(bomb_factory) self.launch_velocity = float(launch_velocity) @@ -144,6 +145,8 @@ self.max_ttl = float(max_ttl) * 1000 self.range = self.launch_velocity * float(max_ttl) self.last_launch = 0 + if name is not None: + self.name = name def update_system(self): if (self.enabled and self.firing Modified: beam.py =================================================================== --- beam.py 2007-06-19 07:36:22 UTC (rev 234) +++ beam.py 2007-06-19 08:17:07 UTC (rev 235) @@ -23,7 +23,7 @@ sound_interval = 750 # time between sound plays def __init__(self, gunmount, range, damage, efficiency, arc_degrees=0, - width=1, color='255,255,255', sound=None): + width=1, color='255,255,255', sound=None, name=None): pygame.sprite.Sprite.__init__(self, game.new_sprites) self.gunmount = gunmount self.range = float(range) @@ -42,6 +42,8 @@ self.draw_line = pygame.draw.aaline else: self.draw_line = pygame.draw.line + if name is not None: + self.name = name def update_system(self): if self.ray is None: Modified: projectile.py =================================================================== --- projectile.py 2007-06-19 07:36:22 UTC (rev 234) +++ projectile.py 2007-06-19 08:17:07 UTC (rev 235) @@ -22,7 +22,7 @@ class FullereneCannon(vessel.Weapon): - name = 'fullerene pulse cannon' + name = 'fullerene cannon' priority = 0 mass = 10 @@ -161,7 +161,7 @@ def __init__(self, gunmount, range, velocity, cool_down, image, damage, efficiency, radius=None, damage_loss=0.0, sound=None, shot='projectile.Shot', - fade_range=None, mount_points=1, mount_positions=None): + fade_range=None, mount_points=1, mount_positions=None, name=None): self.gunmount = gunmount self.range = float(range) self.velocity = float(velocity) @@ -193,6 +193,8 @@ self.mount_positions = None self.next_position = 0 self.ray = None + if name is not None: + self.name = name def update_system(self): if (self.enabled and self.firing and game.time - self.last_fire > self.cool_down Modified: vessel.py =================================================================== --- vessel.py 2007-06-19 07:36:22 UTC (rev 234) +++ vessel.py 2007-06-19 08:17:07 UTC (rev 235) @@ -1000,7 +1000,7 @@ class Engine(System): """Basic thrust engine and generator""" - name = 'engine' + name = 'engines' attr_name = 'engine' base_mass = 1 mass_factor = 0.03 # mass per unit thrust Modified: vessels/naree/cress =================================================================== --- vessels/naree/cress 2007-06-19 07:36:22 UTC (rev 234) +++ vessels/naree/cress 2007-06-19 08:17:07 UTC (rev 235) @@ -25,6 +25,7 @@ thrust: 55 [beam.BeamWeapon] +name: lepton beam range=200 damage=95 efficiency: 0.97 Modified: vessels/naree/gnat =================================================================== --- vessels/naree/gnat 2007-06-19 07:36:22 UTC (rev 234) +++ vessels/naree/gnat 2007-06-19 08:17:07 UTC (rev 235) @@ -25,6 +25,7 @@ energy_flow: 15 [beam.BeamWeapon] +name: point laser range: 100 damage: 50 efficiency: 0.98 Modified: vessels/rone/drach =================================================================== --- vessels/rone/drach 2007-06-19 07:36:22 UTC (rev 234) +++ vessels/rone/drach 2007-06-19 08:17:07 UTC (rev 235) @@ -25,6 +25,7 @@ thrust: 65 [projectile.Cannon:0] +name: proton cannon shot: projectile.ExplodingShot range: 275 fade_range: 100 @@ -37,6 +38,7 @@ sound: proton-grenade.wav [projectile.Cannon:1] +name: phase disruptor shot: projectile.PhaseDisruptorShot mount_points: 2 mount_positions: (-rightangle, 11), (rightangle, 11) Modified: vessels/rone/draken =================================================================== --- vessels/rone/draken 2007-06-19 07:36:22 UTC (rev 234) +++ vessels/rone/draken 2007-06-19 08:17:07 UTC (rev 235) @@ -25,6 +25,7 @@ thrust: 75 [projectile.Cannon] +name: plasma cannon range: 400 radius: 15 fade_range: 125 @@ -37,6 +38,7 @@ image: plasma.png [bay.BombBay] +name: flak bomb launcher bomb_factory: projectile.FlakBomb launch_velocity: 150 energy_use: 150 This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cd...@us...> - 2007-09-10 05:19:07
|
Revision: 242 http://eos-game.svn.sourceforge.net/eos-game/?rev=242&view=rev Author: cduncan Date: 2007-09-09 22:18:59 -0700 (Sun, 09 Sep 2007) Log Message: ----------- - New race: sc (human) with two ships, the pegasus warship and striker fighter the latter artwork is not 100% complete (nose needs work), but the basic idea is there. The striker has burst-fire rockets (unguided) and the pegasus has a neutronium driver spread-fire cannon and guided dagger missiles. Both sport combination deflector screens and armor for defense. - Make ai standoff distance configurable rather than based on ship size. - Increase ship ship avoidance distance, to try and prevent bunching, this still needs more work. Modified Paths: -------------- ai.py game.py projectile.py sounds/hit.wav vessel.py vessels/naree/cress vessels/naree/lotus vessels/rone/drach vessels/rone/draken Added Paths: ----------- art/pegasus.xcf art/sc-missile.xcf data/burst-rocket.png data/dagger-missile.png data/neutronium-shot.png data/pegasus.png data/striker.png sounds/burst-rocket.wav sounds/dagger-missile.wav sounds/neutronium-driver.wav sounds/power_down.wav vessels/sc/ vessels/sc/pegasus vessels/sc/striker Modified: ai.py =================================================================== --- ai.py 2007-08-29 00:56:05 UTC (rev 241) +++ ai.py 2007-09-10 05:18:59 UTC (rev 242) @@ -164,7 +164,7 @@ approach = self.predict_intercept(target, self_predict=0.4) return vector.radians(approach), vector.clamp(approach * game.fps, self.vessel.max_speed) - def standoff(self, distance_factor=12): + def standoff(self): """Pursue a target, keeping our distance""" target = self.target.sprite position = self.vessel.position @@ -173,21 +173,21 @@ # close in to stationary target return vector.radians(target.position - position), target.velocity approach = self.predict_intercept(target) - if vector.length(approach) > target.collision_radius * distance_factor: + if self.vessel.is_friendly(target): + heading = target.heading + distance = self.vessel.collision_radius * 4 + target.collision_radius * 2 + else: + distance = (self.vessel.collision_radius + target.collision_radius + + self.vessel.standoff_distance) + heading = vector.radians(target.position - position) + if vector.length(approach) > distance * 1.5: # Far from target, catch up as fast as we can return vector.radians(approach), vector.clamp( approach * game.fps, self.vessel.max_speed) else: - # close to target, keep a distance based on target size - if self.vessel.is_friendly(target): - # Match heading with friendly target - heading = target.heading - else: - # Face enemy target head-on - heading = vector.radians(target.position - position) + # close to target, keep a distance return heading, vector.clamp(-(approach * - (self.vessel.collision_radius * distance_factor - vector.length(approach))), - self.vessel.max_speed) + (distance - vector.length(approach))), self.vessel.max_speed) def evade(self): """Return desired velocity away from where we predict @@ -235,7 +235,7 @@ if too_close: center /= len(too_close) return vector.normal(self.vessel.position - center) * ( - self.vessel.max_speed * (mass * 2.0 / self.vessel.mass)) + self.vessel.max_speed * (mass * 4.0 / self.vessel.mass)) else: return center @@ -247,7 +247,6 @@ heading_diff += fullcircle turn_rate = self.vessel.turn_rate - max_turn_rate = self.vessel.directional_thrusters.max_turn_rate if heading_diff > turn_rate / 4: self.turn = 1 elif heading_diff < -turn_rate / 4: @@ -541,6 +540,77 @@ pass +class Tracker: + """Object that tracks the course of a ship and can predict its + future location + """ + + def __init__(self, target, sample_time=.250, max_samples=40): + """Track the target vessel, sampling its location every sample_time + seconds, keeping at most max_samples. + """ + self.target = sprite.GroupSingle(target) + self.samples = [] + self.next_sample = 0 + self.max_samples = max_samples + self.sample_time = sample_time + self.update() + + def update(self): + """Take a sample of the location of the target if the time since the + last sample is over the sample time. Otherwise do nothing. + Return True if a sample was actually taken, False if not. + """ + if game.time > self.next_sample and self.target: + self.next_sample = game.time + self.sample_time * 1000 + self.samples.append(vector.to_tuple(self.target.sprite.position)) + while len(self.samples) > self.max_samples: + self.samples.pop(0) + return True + else: + return False + + def predict(self, time_ahead): + """Return the predicted location for the target at time_ahead + seconds in the future. If the target no longer exists, + return None. + """ + if not self.target: + return None + target = self.target.sprite + v1 = target.velocity * min(time_ahead, self.sample_time * 3) + if len(self.samples) <= 3: + # with very few samples, just extrapolate ahead given + # the target's current heading and velocity, limiting + # time_ahead to a small value to minimize overshoots + return target.position + v1 + # Determine the linear correlation of the samples + # to see how to extrapolate the target's position + top = 0.0 + bot_left = 0.0 + bot_right = 0.0 + startx, starty = self.samples[0] + for x, y in self.samples: + top += x * y + bot_left += x * x + bot_right += y * y + correlation = abs(top / math.sqrt(bot_left * bot_right)) + endx, endy = self.samples[-1] + v2 = vector.vector2(endx - startx, endy - starty) / ( + len(self.samples) * self.sample_time) * time_ahead + if correlation > 0.5: + # We have a linear path, interpolate between extrapolating + # along this path and extrapolating on the current velocity + return target.position + (v2 * correlation) + (v1 * (1 - correlation)) + else: + # The path is very non-linear, average the prediction with + # the center point of the location samples to hedge against + # a future non-linear trajectory + hedge = vector.vector2(*self.samples[0]) + vector.vector2( + endx - startx, endy - starty) / 2 + return target.position + (v1 + v2 + hedge) / 3 + + if __name__ == '__main__': """Run tests if executed directly""" import sys, doctest Added: art/pegasus.xcf =================================================================== (Binary files differ) Property changes on: art/pegasus.xcf ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: art/sc-missile.xcf =================================================================== (Binary files differ) Property changes on: art/sc-missile.xcf ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: data/burst-rocket.png =================================================================== (Binary files differ) Property changes on: data/burst-rocket.png ___________________________________________________________________ Name: svn:mime-type + image/png Added: data/dagger-missile.png =================================================================== (Binary files differ) Property changes on: data/dagger-missile.png ___________________________________________________________________ Name: svn:mime-type + image/png Added: data/neutronium-shot.png =================================================================== (Binary files differ) Property changes on: data/neutronium-shot.png ___________________________________________________________________ Name: svn:mime-type + image/png Added: data/pegasus.png =================================================================== (Binary files differ) Property changes on: data/pegasus.png ___________________________________________________________________ Name: svn:mime-type + image/png Added: data/striker.png =================================================================== (Binary files differ) Property changes on: data/striker.png ___________________________________________________________________ Name: svn:mime-type + image/png Modified: game.py =================================================================== --- game.py 2007-08-29 00:56:05 UTC (rev 241) +++ game.py 2007-09-10 05:18:59 UTC (rev 242) @@ -130,6 +130,8 @@ Vessel.load('vessels/rone/drach'), Vessel.load('vessels/naree/lotus'), Vessel.load('vessels/naree/cress'), + Vessel.load('vessels/sc/pegasus'), + Vessel.load('vessels/sc/striker'), ] import math font = pygame.font.Font('fonts/forgottenfuturist/Forgotbi.ttf', 24) @@ -270,7 +272,8 @@ if time > next_wave: target = netsync.random.choice(players) target_race = target.race - wave_race = netsync.random.choice([r for r in ['naree', 'rone'] if r != target_race]) + wave_race = netsync.random.choice([r for r in ['naree', 'rone', 'sc'] + if r != target_race]) if not target.alive(): target = netsync.random.choice(map.planets) position = vector.unit(netsync.random.random() * vector.fullcircle) * 1000 @@ -280,6 +283,8 @@ ai = AIVessel.load('vessels/rone/draken') elif wave_race == 'naree': ai = AIVessel.load('vessels/naree/lotus') + elif wave_race == 'sc': + ai = AIVessel.load('vessels/sc/pegasus') ai.set_position(target.position + position) enemies.add(ai) else: @@ -293,11 +298,15 @@ ship = 'vessels/rone/drach' elif target_race == 'naree': ship = 'vessels/naree/cress' + elif target_race == 'sc': + ship = 'vessels/sc/striker' else: if target_race == 'rone': ship = 'vessels/rone/draken' elif target_race == 'naree': ship = 'vessels/naree/lotus' + elif target_race == 'sc': + ship = 'vessels/sc/pegasus' friend = AIVessel.load(ship, mothership=target, category=body.friend) friend.set_position(target.position - position) friends.add(friend) @@ -311,6 +320,8 @@ ai = AIVessel.load('vessels/rone/drach') elif wave_race == 'naree': ai = AIVessel.load('vessels/naree/cress') + elif wave_race == 'sc': + ai = AIVessel.load('vessels/sc/striker') ai.set_position(target.position + position) enemies.add(ai) warship = True Modified: projectile.py =================================================================== --- projectile.py 2007-08-29 00:56:05 UTC (rev 241) +++ projectile.py 2007-09-10 05:18:59 UTC (rev 242) @@ -76,7 +76,8 @@ and target_dist < Fullerene.range * 2): return False else: - return game.time - self.charge_start < self.max_charge_time * 1000 + return (game.time - self.charge_start < self.max_charge_time * 1000 + and self.gunmount.energy > 150) else: return target_dist < Fullerene.range * 1.25 @@ -160,14 +161,14 @@ mass_factor = 0.1 # Mass per (range / 100) + damage def __init__(self, gunmount, range, velocity, cool_down, image, damage, - efficiency, radius=None, damage_loss=0.0, sound=None, shot='projectile.Shot', + efficiency=None, radius=None, damage_loss=0.0, sound=None, shot='projectile.Shot', fade_range=None, mount_points=1, mount_positions=None, name=None): self.gunmount = gunmount self.range = float(range) self.velocity = float(velocity) self.cool_down = float(cool_down) self.damage_value = float(damage) - self.efficiency = float(efficiency) + self.efficiency = efficiency and float(efficiency) self.damage_loss = float(damage_loss) self.velocity = float(velocity) self.mass = self.base_mass + ((self.range / 100) + self.damage_value) * self.mass_factor @@ -181,32 +182,43 @@ self.shot_factory = vessel.resolve(shot) self.shot_sound = sound self.shot_fade_range = fade_range - self.mount_points = int(mount_points) - if mount_positions is not None: - self.mount_positions = eval(mount_positions) + self.setup_mount_points(mount_points, mount_positions) + self.ray = None + if name is not None: + self.name = name + + def setup_mount_points(self, count, positions): + self.mount_points = int(count) + if positions is not None: + self.mount_positions = eval(positions) if len(self.mount_positions) != self.mount_points: raise vessel.VesselConfigError( "Value for mount_points (%s) doesn't match" - " number of match_positions configured (%s)" + " number of mount_positions configured (%s)" % (self.mount_points, len(self.mount_positions))) else: self.mount_positions = None self.next_position = 0 - self.ray = None - if name is not None: - self.name = name + + def update_system(self): + if self.firing: + self.fire() - def update_system(self): - if (self.enabled and self.firing and game.time - self.last_fire > self.cool_down + def next_shot_position(self): + """Return the position that the next shot is fired from""" + shot_position = self.gunmount.position + if self.mount_positions is not None: + angle, length = self.mount_positions[self.next_position] + self.next_position = (self.next_position + 1) % self.mount_points + shot_position += vector.unit(angle + self.gunmount.heading) * length + return shot_position + + def fire(self): + if (self.enabled and game.time - self.last_fire > self.cool_down and self.gunmount.use_energy(self.damage_value / self.efficiency)): - shot_position = self.gunmount.position - if self.mount_positions is not None: - angle, length = self.mount_positions[self.next_position] - shot_position += vector.unit(angle + self.gunmount.heading) * length - self.next_position = (self.next_position + 1) % self.mount_points self.shot_factory( shooter=self.gunmount, - position=shot_position, + position=self.next_shot_position(), image=self.shot_img.rotated(self.gunmount.heading), radius=self.radius, damage=self.damage_value, @@ -219,7 +231,7 @@ media.play_sound(self.shot_sound, position=self.gunmount.position) @property - def targeted(self): + def targeted(self, slop=diagonal / 3): if self.ray is None: # Create a ray geom to determine beam collision self.ray = ode.GeomRay(game.collision_space, self.range * 1.1) @@ -247,17 +259,68 @@ heading_diff -= fullcircle elif heading_diff < -halfcircle: heading_diff += fullcircle - return abs(heading_diff) < (diagonal / 3) + return abs(heading_diff) < slop def collide(self, other, contact): self.ray.targeted = True +class SpreadCannon(Cannon): + """Cannon that shoots multiple shots in a triangular spread""" + + base_mass = 3 + + def __init__(self, gunmount, range, velocity, cool_down, image, shot_damage, shot_count, + spread_angle, capacity, regeneration_time, radius=None, damage_loss=0.0, sound=None, + shot='projectile.Shot', fade_range=None, mount_points=1, mount_positions=None, name=None): + Cannon.__init__(self, gunmount, range, velocity, cool_down, image, shot_damage, + radius=radius, damage_loss=damage_loss, sound=sound, shot=shot, fade_range=fade_range, + mount_points=mount_points, mount_positions=mount_positions, name=name) + self.shot_count = int(shot_count) + self.spread_angle = float(spread_angle) / 360.0 * vector.fullcircle + self.max_capacity = self.remaining = int(capacity) + self.regen_time = float(regeneration_time) * 1000 + self.last_fire = 0 + self.next_regen = None + + def update_system(self): + if self.remaining < self.max_capacity: + if self.next_regen is not None: + if game.time > self.next_regen: + self.remaining += 1 + self.next_regen = game.time + self.regen_time + else: + self.next_regen = game.time + self.regen_time + else: + self.next_regen = None + Cannon.update_system(self) + + def fire(self): + if self.enabled and self.remaining and game.time - self.last_fire > self.cool_down: + angle = self.gunmount.heading - (self.spread_angle / 2) + increment = self.spread_angle / self.shot_count + for i in range(self.shot_count): + self.shot_factory( + shooter=self.gunmount, + position=self.next_shot_position(), + image=self.shot_img.rotated(angle), + radius=self.radius, + damage=self.damage_value, + velocity=vector.unit(angle) * self.velocity, + range=self.range, + fade_range=self.shot_fade_range, + damage_loss=self.damage_loss) + angle += increment + self.last_fire = game.time + if self.shot_sound is not None: + media.play_sound(self.shot_sound, position=self.gunmount.position) + + class Shot(RoundBody): """Generic projectile shot""" - def __init__(self, shooter, image, damage, velocity, range, - position=None, fade_range=None, damage_loss=0, radius=1): + def __init__(self, shooter, image, damage, velocity, position=None, range=None, max_ttl=None, + fade_range=None, damage_loss=0, radius=1, mass=0.1): """Generic projectile shot shooter -- body that fired the shot @@ -265,25 +328,28 @@ damage -- damage value on hit velocity -- velocity vector of shot not including shooter velocity) range -- distance shot travels before disappearing + max_ttl -- Maximum time before shot is gone (alternative to range) fade_range -- range when shot image begins to fade (cosmetic) damage_loss -- Reduction in damage at max range radius -- shot collision radius """ self.radius = radius + self.mass = mass RoundBody.__init__(self, position or shooter.position, shooter.velocity + velocity, collides_with=everything & ~shooter.category ) self.shooter = shooter - self.start_position = shooter.position + self.start_position = position or shooter.position self.set_image(image) self.rect = image.get_rect(center=shooter.rect.center) self.damage_value = self.start_damage = damage self.range = range - if fade_range is not None: - self.fade_range = fade_range + if max_ttl is not None: + self.max_time = game.time + max_ttl else: - self.fade_range = range * 0.9 + self.max_time = None + self.fade_range = fade_range if damage_loss: # calculate damage loss per frame flight_time = range / vector.length(self.velocity) @@ -297,14 +363,19 @@ def update(self): RoundBody.update(self) self.damage_value -= self.damage_loss - distance = vector.distance(self.start_position, self.position) - if distance > self.range: - self.kill() - elif distance > self.fade_range: - opacity = 255 * (self.range - distance) / (self.range - self.fade_range) - self.image.set_alpha(int(opacity)) - else: - self.image.set_alpha(255) + if self.range is not None: + distance = vector.distance(self.start_position, self.position) + if distance > self.range: + self.out_of_range() + elif self.fade_range is not None and distance > self.fade_range: + opacity = 255 * (self.range - distance) / (self.range - self.fade_range) + self.image.set_alpha(int(opacity)) + else: + self.image.set_alpha(255) + if self.max_time is not None and game.time > self.max_time: + self.out_of_range() + + out_of_range = RoundBody.kill def collide(self, other, contacts): if self.alive(): @@ -405,3 +476,162 @@ self.shard_velocity, self.shard_velocity / 3), range=self.shard_range, damage_loss=-30.0) + + +class MissileLauncher(Cannon): + + base_mass = 3 + priority = 1 + + def __init__(self, gunmount, max_ttl, max_velocity, mass, thrust, cool_down, damage, radius, + capacity, missile_factory, regeneration_time, + image, sound=None, mount_points=1, mount_positions=None, arc_degrees=0, + max_turn_rate=None, length=None, name=None): + self.gunmount = gunmount + self.missile_max_ttl = float(max_ttl) * 1000 + self.max_velocity = float(max_velocity) + self.cool_down = float(cool_down) + self.missile_factory = vessel.resolve(missile_factory) + self.damage_value = float(damage) + self.mass = self.base_mass + ( + (self.missile_max_ttl / 100) + self.damage_value) * self.mass_factor + self.missile_mass = float(mass) + self.missile_thrust = float(thrust) + self.missile_radius = float(radius) + self.missile_length = length and float(length) + self.missile_max_turn_rate = max_turn_rate and float(max_turn_rate) + self.range = self.missile_max_ttl * self.max_velocity / 2000 # for targetting + self.max_capacity = self.remaining = float(capacity) + self.last_fire = 0 + self.next_regen = None + self.regen_time = float(regeneration_time) * 1000 + self.arc = float(arc_degrees) / 360.0 * vector.fullcircle + self.missile_img = media.RotatedImage(image) + self.sound = sound + self.setup_mount_points(mount_points, mount_positions) + self.ray = None + if name is not None: + self.name = name + + def update_system(self): + if self.remaining < self.max_capacity: + if self.next_regen is not None: + if game.time > self.next_regen: + self.remaining += 1 + self.next_regen = game.time + self.regen_time + else: + self.next_regen = game.time + self.regen_time + else: + self.next_regen = None + Cannon.update_system(self) + + def fire(self): + if self.enabled and self.remaining and game.time - self.last_fire > self.cool_down: + target = self.gunmount.control.target.sprite + position = self.next_shot_position() + if self.arc > 0 and target: + # Track the selected target + track_angle = vector.radians(target.position - position) - self.gunmount.heading + if track_angle > halfcircle: + track_angle -= fullcircle + elif track_angle < -halfcircle: + track_angle += fullcircle + if abs(track_angle) > self.arc: + # limit angle to maximum arc + track_angle /= track_angle / self.arc + else: + track_angle = 0 + + self.missile_factory( + shooter=self.gunmount, + heading=self.gunmount.heading + track_angle, + position=position, + image=self.missile_img, + damage=self.damage_value, + max_velocity=self.max_velocity, + mass=self.missile_mass, + length=self.missile_length, + thrust=self.missile_thrust, + max_turn_rate=self.missile_max_turn_rate, + radius=self.missile_radius, + max_ttl=self.missile_max_ttl, + target=target) + self.last_fire = game.time + self.remaining -= 1 + if self.sound is not None: + media.play_sound(self.sound, position=self.gunmount.position) + + +class Rocket(ExplodingShot): + """Unguided, dumb self propelled rocket""" + + def __init__(self, shooter, heading, image, damage, mass, length, thrust, max_turn_rate, + max_velocity, max_ttl, position=None, radius=1, target=None): + ExplodingShot.__init__( + self, shooter, image.rotated(heading), damage, vector.vector2(), position, + max_ttl=max_ttl, radius=radius, mass=mass) + self.set_heading(heading) + self.max_velocity = float(max_velocity) + self.thrust_vector = vector.unit(heading) * float(thrust) * 100 + self.image = self.src_image # XXX Bug + + def update(self): + ExplodingShot.update(self) + if self.explosion is None and vector.length(self.get_velocity()) < self.max_velocity: + self.push(self.thrust_vector) + + def out_of_range(self): + self.explode('hit.wav') + + +class Missile(ExplodingShot): + """Guided, self propelled missile""" + + def __init__(self, shooter, heading, image, damage, mass, length, thrust, max_turn_rate, + max_velocity, max_ttl, position=None, radius=1, target=None): + ExplodingShot.__init__( + self, shooter, image.rotated(heading), damage, vector.vector2(), position, + max_ttl=max_ttl, radius=radius, mass=mass) + self.target = pygame.sprite.GroupSingle(target) + self.set_heading(heading) + self.max_velocity = float(max_velocity) + self.max_turn_rate = float(max_turn_rate) + self.thrust = float(thrust) * 100 + self.image = self.src_image # XXX Bug + self.rot_image = image + self.tracker = ai.Tracker(target) + + def update(self): + self.set_image(self.rot_image.rotated(self.heading)) + ExplodingShot.update(self) + if self.explosion is None: + tracking = self.tracker.update() + turn = 0 + if tracking: + target = self.target.sprite + speed = vector.length(self.velocity) + if speed: + travel_time = vector.length(target.position - self.position) / speed + target_ahead = self.tracker.predict(travel_time) + self_ahead = self.position + self.velocity * (travel_time / 5) + #target_ahead = target.position + target.velocity * travel_time + else: + target_ahead = target.position + self_ahead = self.position + #mag, pos = vector.to_screen(target_ahead) + #game.screen.fill((255, 255, 0), (vector.to_tuple(pos), (3, 3))) + #mag, pos = vector.to_screen(self_ahead) + #game.screen.fill((0, 0, 255), (vector.to_tuple(pos), (4, 4))) + #pygame.display.flip() + angle = vector.radians(target_ahead - self_ahead) - self.heading + if angle > halfcircle: + angle -= fullcircle + elif angle < -halfcircle: + angle += fullcircle + self.turn_rate = max(min(angle * min(2 / travel_time, 4), self.max_turn_rate), -self.max_turn_rate) + self.push(vector.unit(self.heading) * self.thrust) + if vector.length(self.velocity) > self.max_velocity: + self.set_velocity(vector.clamp(self.velocity, self.max_velocity)) + + def out_of_range(self): + self.explode('hit.wav') Added: sounds/burst-rocket.wav =================================================================== (Binary files differ) Property changes on: sounds/burst-rocket.wav ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: sounds/dagger-missile.wav =================================================================== (Binary files differ) Property changes on: sounds/dagger-missile.wav ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Modified: sounds/hit.wav =================================================================== (Binary files differ) Added: sounds/neutronium-driver.wav =================================================================== (Binary files differ) Property changes on: sounds/neutronium-driver.wav ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: sounds/power_down.wav =================================================================== (Binary files differ) Property changes on: sounds/power_down.wav ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Modified: vessel.py =================================================================== --- vessel.py 2007-08-29 00:56:05 UTC (rev 241) +++ vessel.py 2007-09-10 05:18:59 UTC (rev 242) @@ -146,7 +146,8 @@ control = Control() def __init__(self, vessel_name=None, vessel_class='', vessel_type='', description='', - image_name=None, hull_mass=1, hull_length=1, crew=0, max_speed=0, max_energy=0, race=None): + image_name=None, hull_mass=1, hull_length=1, crew=0, max_speed=0, max_energy=0, + standoff_distance=0, race=None): if image_name is None and vessel_class: image_name = vessel_class + '.png' if image_name is not None: @@ -157,6 +158,7 @@ self.collision_radius = 0 self.hull_length = float(hull_length) self.radius = self.hull_length + self.standoff_distance = float(standoff_distance) body.RoundBody.__init__(self) self.vessel_name = vessel_name self.vessel_class = vessel_class @@ -520,6 +522,8 @@ 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: + media.play_sound('power_down.wav', position=self.position) if self.damage_smoke is None and self.vessel_type != 'missile': self.damage_smoke = particle.SmokeTrail(self) if (self.hull_damage > self.disable_factor * self.hull_mass * 0.75 and @@ -547,7 +551,11 @@ def health(self): """Return our health rating between 0 and 1""" if self._damage_sys: - return sum(system.health for system in self._damage_sys) / len(self._damage_sys) + damage_sys_healths = [system.health for system in self._damage_sys + if hasattr(system, 'health')] + return sum(damage_sys_healths) / len(damage_sys_healths) + else: + return 0 def kill(self): for system in self._sys: @@ -651,7 +659,7 @@ name = 'energy shield' attr_name = 'shield' mass = 0 - priority = 0 # Shields get hit first + priority = 1 regen_recovery = 5.0 # Time regeneration recovers after disruption @@ -772,6 +780,71 @@ self.regeneration *= multiplier +class DeflectorScreen(System): + """Deflector screen defensive system + + Deflector screens reduce damage to other systems in proportion to the + energy level of the vessel. Once activated, the deflector draws + a steady amount of energy. Over time, as the energy level depletes, + the effectiveness of the deflector screen decreases. The deflector + also has an maximum damage protection per time over which it does + not reduce damage further. + """ + + name = 'deflector screen' + attr_name = 'shield' + efficiency_recovery = 4.0 + priority = 0 # Screens get hit first + + def __init__(self, vessel, energy_drain, max_protection, deactivation_time): + self.vessel = vessel + self.energy_drain = float(energy_drain) + self.efficiency = 1.0 + self.max_protection = self.protection = float(max_protection) + self.deactivation_time = float(deactivation_time) * 1000 + self.last_activation = 0 + self.activated = False + + def update_system(self): + if self.enabled: + self.activated = self.activated and ( + game.time - self.last_activation < self.deactivation_time) + if self.activated: + self.vessel.use_energy( + self.energy_drain / (self.efficiency * game.fps), partial=True) + if self.efficiency < 1.0: + self.efficiency += min( + self.efficiency + (1.0 / (game.fps * self.efficiency_recovery)), 1.0) + if self.protection < self.max_protection: + self.protection = min(self.protection + self.max_protection / game.fps, + self.max_protection) + + def disable(self): + self.protection = 0 + self.enabled = False + + def damage(self, value): + """Absorb damage, return value not absorbed""" + if self.enabled: + self.activated = True + self.last_activation = game.time + if value > self.protection: + undeflected = value - self.protection + deflected = self.protection + else: + undeflected = 0 + deflected = value + self.protection = max(self.protection - deflected, 0) + return undeflected + deflected - ( + deflected * self.vessel.energy / self.vessel.max_energy) + else: + return value + + def disrupt(self, multiplier): + """Disrupt deflector efficiency rate by multiplier""" + self.efficiency *= multiplier + + class Armor(System, pygame.sprite.Sprite): """Armor defensive system @@ -790,9 +863,12 @@ name = 'armor' mass_factor = 0.005 # Mass per durability unit per ship length unit - def __init__(self, vessel, durability, dissipation, regeneration=0): + def __init__(self, vessel, durability, dissipation=0, image=None, regeneration=0): pygame.sprite.Sprite.__init__(self, game.new_sprites) - self.armor_img = RotatedImage('%s-armor.png' % vessel.vessel_class, enhanced=False) + if image is not None: + self.armor_img = RotatedImage(image, enhanced=False) + else: + self.armor_img = None self.vessel = vessel self.durability = float(durability) self.dissipation = self.max_dissipate = float(dissipation) @@ -801,7 +877,7 @@ self.heat = 0.0 def draw(self, surface): - if self.heat > 0 and self.vessel.explosion is None: + if self.armor_img is not None and self.heat > 0 and self.vessel.explosion is None: image = self.armor_img.rotated(self.vessel.heading) apparent_size, pos = vector.to_screen(self.vessel.position) image = media.scale_image(image, apparent_size * 0.99, colorkey=True) Modified: vessels/naree/cress =================================================================== --- vessels/naree/cress 2007-08-29 00:56:05 UTC (rev 241) +++ vessels/naree/cress 2007-09-10 05:18:59 UTC (rev 242) @@ -9,6 +9,7 @@ crew: 1 max_speed: 160 max_energy: 200 +standoff_distance: 125 [vessel.Shield] max_level: 100 Modified: vessels/naree/lotus =================================================================== --- vessels/naree/lotus 2007-08-29 00:56:05 UTC (rev 241) +++ vessels/naree/lotus 2007-09-10 05:18:59 UTC (rev 242) @@ -4,20 +4,17 @@ vessel_class: lotus vessel_type: warship description: Answering the call in defense of the Naree, the lotus is the very definition of a multi-role warship. Armed with the potent fullerene pulse cannon, the lotus is equally adept leading an assault as it is supporting a fleet of larger ships. For defense against enemy fighters and missile threats, the lotus deploys a gnat bay, making it a pint sized carrier platform. An onboard dark matter core provides sufficient energy for extended missions into deep space or engagements with stubborn adversaries. Its stout energy shields protect the crew and ship systems. -hull_mass: 25 +hull_mass: 18 hull_length: 20 crew: 3 max_speed: 125 -max_energy: 250 +max_energy: 500 +standoff_distance: 400 [vessel.Shield] max_level: 600 regeneration: 14 -# [vessel.Armor] -# durability: 100 -# dissipation: 10 - [vessel.DirectionalThrusters] thrust: 100 max_turn_rate: 2.7 Modified: vessels/rone/drach =================================================================== --- vessels/rone/drach 2007-08-29 00:56:05 UTC (rev 241) +++ vessels/rone/drach 2007-09-10 05:18:59 UTC (rev 242) @@ -3,16 +3,18 @@ [general] vessel_class: drach vessel_type: fighter -description: The Drach is the first strike weapon of choice for the Rune. Deadly Drach squadrons can handily flank enemy vessels to deliver a hail of plasma fire at close range. At longer range their proton grenades pack a deadly punch. Their small size and rigid armor make them a challenge to gun down before they unload their deadly arsenal. +description: The Drach is the first strike weapon of choice for the Rune. Deadly Drach squadrons can handily flank enemy vessels to batter them with their powerful proton grenades. Winglet mounted phase disruptors reduce the effectiveness of enemy shields and deflector screens, leaving them vulnerable to attack. Their small size and rigid armor make them a challenge to gun down before they unload their deadly arsenal. hull_mass: 6 hull_length: 6 crew: 2 max_speed: 135 max_energy: 250 +standoff_distance: 150 [vessel.Armor] durability: 120 dissipation: 6 +image: drach-armor.png [vessel.DirectionalThrusters] thrust: 5.5 Modified: vessels/rone/draken =================================================================== --- vessels/rone/draken 2007-08-29 00:56:05 UTC (rev 241) +++ vessels/rone/draken 2007-09-10 05:18:59 UTC (rev 242) @@ -9,10 +9,12 @@ crew: 12 max_speed: 107 max_energy: 1000 +standoff_distance: 250 [vessel.Armor] durability: 540 dissipation: 16 +image: draken-armor.png [vessel.DirectionalThrusters] thrust: 80 Added: vessels/sc/pegasus =================================================================== --- vessels/sc/pegasus (rev 0) +++ vessels/sc/pegasus 2007-09-10 05:18:59 UTC (rev 242) @@ -0,0 +1,64 @@ +# $Id: lotus 116 2007-02-22 08:52:18Z cduncan $ + +[general] +vessel_class: pegasus +vessel_type: warship +description: The pegasus is a warship, uh huh. +hull_mass: 20 +hull_length: 21 +crew: 8 +max_speed: 95 +max_energy: 200 +standoff_distance: 500 + +[vessel.DeflectorScreen] +energy_drain: 58 +max_protection: 400 +deactivation_time: 3.5 + +[vessel.Armor] +durability: 350 + +[vessel.DirectionalThrusters] +thrust: 75 +max_turn_rate: 2.2 + +[vessel.ManeuveringThrusters] +thrust: 50 + +[vessel.Engine] +thrust: 65 + +[projectile.SpreadCannon] +name: neutronium driver +shot_count: 15 +spread_angle: 12 +range: 250 +radius: 10 +fade_range: 150 +cool_down: 1200 +shot_damage: 7 +velocity: 750 +capacity: 50 +regeneration_time: 1.7 +sound: neutronium-driver.wav +image: neutronium-shot.png + +[projectile.MissileLauncher] +name: Dagger Missile Launcher +missile_factory: projectile.Missile +image: dagger-missile.png +sound: dagger-missile.wav +mount_points: 1 +mount_positions: (0, 20), +cool_down: 1300 +max_ttl: 2 +damage: 50 +max_velocity: 300 +thrust: 10 +mass: 0.3 +length: 2 +max_turn_rate: 5.0 +radius: 5 +capacity: 20 +regeneration_time: 3 Property changes on: vessels/sc/pegasus ___________________________________________________________________ Name: svn:eol-style + native Added: vessels/sc/striker =================================================================== --- vessels/sc/striker (rev 0) +++ vessels/sc/striker 2007-09-10 05:18:59 UTC (rev 242) @@ -0,0 +1,48 @@ +# $Id: lotus 116 2007-02-22 08:52:18Z cduncan $ + +[general] +vessel_class: striker +vessel_type: fighter +description: The striker is the primary Solar Coalition fighter. Although technologically inferior to its rivals, the striker exemplifies the human ability to leverage whatever strengths they have to tactical advantage. The striker's burst rockets unleash a fury of fire upon their adversaries and are extremely effective at range. Their ablative armor is complemented by minaturized deflector screen generators. Once only available on larger ships, these defensive screens are extremely effective at deflecting damage. Their only drawback is their immense appetite for energy. +hull_mass: 7 +hull_length: 7 +crew: 2 +max_speed: 120 +max_energy: 150 +standoff_distance: 300 + +[vessel.DeflectorScreen] +energy_drain: 52 +max_protection: 150 +deactivation_time: 2 + +[vessel.Armor] +durability: 50 + +[vessel.DirectionalThrusters] +thrust: 5 +max_turn_rate: 3.0 + +[vessel.ManeuveringThrusters] +thrust: 30 + +[vessel.Engine] +thrust: 50 + +[projectile.MissileLauncher] +name: Burst Rocket Launcher +missile_factory: projectile.Rocket +image: burst-rocket.png +sound: burst-rocket.wav +mount_points: 6 +mount_positions: (-rightangle, 20), (rightangle, 20), (-rightangle, 16), (rightangle, 16), (-rightangle, 12), (rightangle, 12) +arc_degrees: 15 +cool_down: 200 +max_ttl: 1.5 +damage: 11 +max_velocity: 1000 +thrust: .40 +mass: 0.1 +radius: 25 +capacity: 100 +regeneration_time: 1 Property changes on: vessels/sc/striker ___________________________________________________________________ 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-09-13 07:38:23
|
Revision: 251 http://eos-game.svn.sourceforge.net/eos-game/?rev=251&view=rev Author: cduncan Date: 2007-09-13 00:38:19 -0700 (Thu, 13 Sep 2007) Log Message: ----------- Balance weapons a bit: - Pegasus gets less ammo and slower regen - lotus fullerenes are faster and longer range - draken plasma shots are longer range and lose less damage Modified Paths: -------------- vessels/naree/lotus vessels/rone/draken vessels/sc/pegasus Modified: vessels/naree/lotus =================================================================== --- vessels/naree/lotus 2007-09-12 04:25:19 UTC (rev 250) +++ vessels/naree/lotus 2007-09-13 07:38:19 UTC (rev 251) @@ -9,7 +9,7 @@ crew: 3 max_speed: 125 max_energy: 500 -standoff_distance: 400 +standoff_distance: 550 [vessel.Shield] max_level: 600 Modified: vessels/rone/draken =================================================================== --- vessels/rone/draken 2007-09-12 04:25:19 UTC (rev 250) +++ vessels/rone/draken 2007-09-13 07:38:19 UTC (rev 251) @@ -28,13 +28,13 @@ [projectile.Cannon] name: plasma cannon -range: 400 +range: 500 radius: 15 fade_range: 125 cool_down: 325 damage: 40 efficiency: 0.85 -damage_loss: 35 +damage_loss: 30 velocity: 400 sound: plasma-cannon-small.wav image: plasma.png Modified: vessels/sc/pegasus =================================================================== --- vessels/sc/pegasus 2007-09-12 04:25:19 UTC (rev 250) +++ vessels/sc/pegasus 2007-09-13 07:38:19 UTC (rev 251) @@ -40,7 +40,7 @@ shot_damage: 7 velocity: 750 capacity: 50 -regeneration_time: 1.7 +regeneration_time: 3.6 sound: neutronium-driver.wav image: neutronium-shot.png @@ -61,5 +61,5 @@ length: 2 max_turn_rate: 5.0 radius: 5 -capacity: 20 -regeneration_time: 3 +capacity: 15 +regeneration_time: 5 This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <cd...@us...> - 2007-09-29 16:46:29
|
Revision: 271 http://eos-game.svn.sourceforge.net/eos-game/?rev=271&view=rev Author: cduncan Date: 2007-09-29 09:46:25 -0700 (Sat, 29 Sep 2007) Log Message: ----------- - Vessels can now be built from the base panel - Fix panel slide positioning bug - Add different ai "personalities". These are still a wip, but worthwhile even as-is Modified Paths: -------------- ai.py base.py content/maps/sol event.py game.py media.py panel.py particle.py staticbody.py vessel.py vessels/naree/cress vessels/naree/lotus vessels/rone/drach vessels/rone/draken vessels/sc/pegasus vessels/sc/striker Added Paths: ----------- widget.py Modified: ai.py =================================================================== --- ai.py 2007-09-27 23:06:58 UTC (rev 270) +++ ai.py 2007-09-29 16:46:25 UTC (rev 271) @@ -180,8 +180,8 @@ else: desired_dist = (self.vessel.collision_radius + target.collision_radius + self.vessel.standoff_distance) - heading = (vector.radians(to_target) * desired_dist * 2 - + vector.radians(approach) * distance) / (desired_dist * 2 + distance) + heading = (vector.radians(to_target) * desired_dist * 4 + + vector.radians(approach) * distance) / (desired_dist * 4 + distance) if distance > desired_dist: # Far from target, catch up as fast as we can return vector.radians(approach), vector.clamp( @@ -377,6 +377,42 @@ self.last_update = time.time() +class EvaderAI(BasicAI): + + evade_min_health = 0.5 + evade_max_health = 0.7 + + def evade(self): + """Turne away from the target""" + heading, seek_velocity = self.pursue() + velocity = vector.unit( + vector.radians(seek_velocity) + rightangle) * self.vessel.max_speed * 10 + return vector.radians(velocity), velocity + + +class AggroAI(BasicAI): + + evade_min_health = 0.5 + evade_max_health = 0.7 + + def evade(self): + """Head through the target""" + heading = self.target.sprite.heading + halfcircle + return heading, vector.unit(heading) * self.vessel.max_speed * 10 + + +class Standoffish(BasicAI): + + orbit = vector.vector2(1, -0.25) + + def standoff(self): + heading, velocity = BasicAI.standoff(self) + return heading, velocity * self.orbit + + def select_steerfunc(self): + return self.standoff + + class GnatAI(BasicAI): """Gnat AI control""" @@ -408,7 +444,8 @@ # head for the mothership self.target.add(self.mothership) else: - # No mothership, just head toward a planet + # Commit suicide if our mothership dies + self.vessel.explode('hit.wav') self.target.add(game.map.planets) def select_steerfunc(self): Modified: base.py =================================================================== --- base.py 2007-09-27 23:06:58 UTC (rev 270) +++ base.py 2007-09-29 16:46:25 UTC (rev 271) @@ -6,10 +6,13 @@ # $Id$ import ConfigParser +import random +import game import ai import vessel import vector import map +import message class PlanetaryBase: """Planetary bases are owned by a player and can build using the planet's @@ -29,6 +32,12 @@ """ builder = None # What we are building + # base level required to build certain vessels + vessel_level = { + 'fighter': 1, + 'warship': 2, + } + _owner2number = {} # Map of owner => last base number def __init__(self, planet, owner, level=1, max_level=5): if isinstance(planet, str): @@ -46,12 +55,29 @@ else: raise map.MapConfigError('Expected base level (%s) <= max_level (%s)' % (level, max_level)) + self.number = self._owner2number[owner] = self._owner2number.get(owner, 0) + 1 def update(self): if self.builder is not None: - self.builder.build(self.planet.use_resources()) + self.builder.work(self.planet.resources / game.fps) if self.builder.is_finished(): self.builder = None + + def can_build_vessel(self, config_file): + """Return True if the base can build the vessel from the config file, + False if not + """ + path, config = vessel.get_config(config_file) + vtype = config.get('general', 'vessel_type') + return (vtype in self.vessel_level and self.vessel_level[vtype] <= self.level + and config.has_option('general', 'cost')) + + def build_vessel(self, config_file): + """Start building the vessel if we can build it and nothing else + is being built + """ + if self.builder is None and self.can_build_vessel(config_file): + self.builder = VesselBuilder(self, config_file) class Builder: @@ -121,8 +147,8 @@ def __init__(self, base, config_file): self.config_file = config_file file, config = vessel.get_config(config_file) - cost = float(config.get('build', 'cost')) - Builder.__init__(base, cost) + cost = float(config.get('general', 'cost')) + Builder.__init__(self, base, cost) self.vessel_built = False def done(self): Modified: content/maps/sol =================================================================== --- content/maps/sol 2007-09-27 23:06:58 UTC (rev 270) +++ content/maps/sol 2007-09-29 16:46:25 UTC (rev 271) @@ -1,11 +1,12 @@ [general] name: Sol -description: Home system of the Human race +description: Home system of the Solar Coalition [planet:Earth] image: earth-east.png,earth-west.png x: 250 y: -50 +resources: 10 [planet:Moon] image: moon.png Modified: event.py =================================================================== --- event.py 2007-09-27 23:06:58 UTC (rev 270) +++ event.py 2007-09-29 16:46:25 UTC (rev 271) @@ -11,12 +11,34 @@ import game import vector + class Handler: + """Event handler base class""" + + 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): """Top-level event handler""" - mouse_timeout = 3000 # Time to hide mouse if not moved + mouse_timeout = 3000 # Time to hide mouse if not moved or clicked - def __init__(self, hide_mouse=False): + def __init__(self, handlers=(), hide_mouse=False): + """Dispatches pygame events to the handler's methods""" self.event_handlers = { MOUSEMOTION: self.mouse_move, MOUSEBUTTONUP: self.mouse_up, @@ -24,31 +46,49 @@ KEYDOWN: self.key_down, QUIT: self.quit } + self.handlers = list(handlers) self.hide_mouse = hide_mouse self.mouse_visible = pygame.mouse.set_visible(not hide_mouse) self.mouse_hide_time = 0 + self.running = True def handle_events(self): - """Dispatch pending events to the appropriate handlers""" + """Dispatch pending events to the appropriate handler methods""" for event in pygame.event.get(): if event.type in self.event_handlers: self.event_handlers[event.type](event) if self.hide_mouse and self.mouse_visible and game.time > self.mouse_hide_time: self.mouse_visible = pygame.mouse.set_visible(False) + return self.running - def mouse_move(self, event): - """Handle mouse movement""" + def show_mouse(self): + """Keep mouse visible until timeout""" if self.hide_mouse and not self.mouse_visible: self.mouse_hide_time = game.time + self.mouse_timeout self.mouse_visible = pygame.mouse.set_visible(True) + + def mouse_move(self, event): + """Handle mouse movement""" + self.show_mouse() + for handler in self.handlers: + handler.mouse_move(event) def mouse_up(self, event): """Handle mouse button release""" - pass + self.show_mouse() + for handler in self.handlers: + if handler.mouse_up(event): + break def mouse_down(self, event): """Handle mouse button press""" - pass + self.show_mouse() + for handler in self.handlers: + if handler.mouse_down(event): + break + + def quit(self, event=None): + self.running = False def key_down(self, event): """Handle key press""" Modified: game.py =================================================================== --- game.py 2007-09-27 23:06:58 UTC (rev 270) +++ game.py 2007-09-29 16:46:25 UTC (rev 271) @@ -186,10 +186,12 @@ camera = Camera(local_player) target = selection.Target(local_player) - import panel + import panel, base global base_panel + earth = map.get_planet('Earth') + earth.base = base.PlanetaryBase(earth, local_player, level=2) base_panel = panel.BasePanel() - base_panel.set_planet(map.get_planet('Earth')) + base_panel.set_planet(earth) # Setup local messaging message.init(local_player) @@ -242,6 +244,7 @@ import body import media import event + import panel global universe, screen, background, debug global sprites, new_sprites, fps, avg_fps, clock, time, frame_no global ai_interval, windowed, camera, map, ui_sprites @@ -249,7 +252,7 @@ enemies = pygame.sprite.Group() next_wave = 0 this_frame_time = last_frame_time = 1000 / fps - event_handler = event.Handler(hide_mouse=not windowed) + event_handler = event.MainHandler([panel.handler], hide_mouse=not windowed) while 1: event_handler.handle_events() keystate = pygame.key.get_pressed() Modified: media.py =================================================================== --- media.py 2007-09-27 23:06:58 UTC (rev 270) +++ media.py 2007-09-29 16:46:25 UTC (rev 271) @@ -22,6 +22,18 @@ def load_image(name): return optimize_image(pygame.image.load('data/%s' % name)) +def load_and_fit_image(name, rect): + """Load image and scale to fit inside rect""" + img = pygame.image.load('data/%s' % name) + img_rect = img.get_rect() + width, height = img_rect.size + if width > rect.width or height > rect.height: + scale = min(float(rect.width) / float(img_rect.width), + float(rect.height) / float(img_rect.height)) + else: + scale = 1.0 + return scale_image(img, scale) + def optimize_image(image): #if not game.screen.get_flags() & HWSURFACE: # transparent = image.get_colorkey() Modified: panel.py =================================================================== --- panel.py 2007-09-27 23:06:58 UTC (rev 270) +++ panel.py 2007-09-29 16:46:25 UTC (rev 271) @@ -5,22 +5,29 @@ # User interface panels # $Id$ +import glob import pygame +from pygame.locals import * import game import sprite import vector import camera +import event +import vessel +import widget -class Panel(pygame.sprite.Sprite): +class Panel(pygame.sprite.Sprite, event.Handler): """User interface panel. Panels contain controls. Enabled panels receive events that occur inside them and dispatch the appropriate control actions. """ + panel_hotkey = None # Key to show/hide panel slide_time = 0.25 border_color = (40, 40, 60, 150) background_color = (0, 0, 40, 150) def __init__(self, enabled=True, *controls): + """Create a panel and add it to the panel handler""" pygame.sprite.Sprite.__init__(self) self.image = self.create_image() self.enabled = enabled @@ -31,6 +38,7 @@ if control.hotkey is not None) self.clicked_control = None self.slide_end_frame = None + handler.add_panel(self) def create_image(self): """Return a panel image that is a background for the panel controls. @@ -38,6 +46,10 @@ """ return None + def collides_with(self, pos): + """Return true if pos is inside the panel""" + return self.rect.collidepoint(*pos) + def control_at(self, pos): """Return the control object at the postion specified or None if none""" for control in self.controls.sprites(): @@ -62,11 +74,20 @@ surface.blit(self.image, self.rect) self.controls.draw(surface) return self.rect + + def localize_event(self, event): + """Return a new event with coordinates local to the panel""" + event_info = dict(event.dict) # copy event data into a new dict + if 'pos' in event_info: + gx, gy = event_info['pos'] + event_info['pos'] = (gx - self.rect.left, gy - self.rect.top) + return pygame.event.Event(event.type, event_info) def mouse_move(self, event): """Mouse movement. All panels receive all mouse movement events, so each panel needs to decide if the event is relevant to it """ + event = self.localize_event(event) pos_before = (event.pos[0] - event.rel[0], event.pos[1] - event.rel[1]) control_before = self.control_at(pos_before) control_now = self.control_at(event.pos) @@ -77,25 +98,43 @@ control_now.mouse_enter(self, event) def mouse_up(self, event): - """Handle mouse button release inside panel""" - if (self.clicked_control is not None - and self.clicked_control is self.control_at(event.pos)): - self.clicked_control.mouse_up(self, event) - self.clicked_control = None + """Handle mouse button release inside panel, return True if the + panel handled the event, false if not + """ + event = self.localize_event(event) + if self.clicked_control is not None: + if self.clicked_control is self.control_at(event.pos): + self.clicked_control.mouse_up(self, event) + self.clicked_control = None + return True + else: + return False def mouse_down(self, event): - """Handle mouse button press inside panel""" + """Handle mouse button press inside panel, return True if the + panel handled the event, false if not + """ + event = self.localize_event(event) control = self.control_at(event.pos) self.clicked_control = control if control is not None: control.mouse_down(self, event) + return True + else: + return False def key_down(self, event): """Key press. All panels receive all keypress events, so each panel must decide if the event is relevant to it. If the panel processes the key press, return True, else return False """ - if event.key in self.control_keymap: + if event.key == self.panel_hotkey: + if not self.enabled: + self.show() + else: + self.hide() + return True + elif self.enabled and event.key in self.control_keymap: control = self.control_keymap[event.key] return control.key_down(event) else: @@ -113,19 +152,26 @@ self.slide_dest.right = pos[1] frame_count = int(game.fps * self.slide_time) self.slide_vel = vector.vector2( - (pos[0] - self.rect[0]) / frame_count, (pos[1] - self.rect[1]) / frame_count) + float(pos[0] - self.rect[0]) / frame_count, float(pos[1] - self.rect[1]) / frame_count) self.slide_pos = vector.vector2(float(self.rect[0]), float(self.rect[1])) self.slide_end_frame = game.frame_no + frame_count def slide(self): + old_pos = self.slide_pos self.slide_pos += self.slide_vel self.rect.left, self.rect.top = vector.to_tuple(self.slide_pos) + # Create a simulated mouse move event and send it to the event handler + # Even though we are moving, not the mouse, the effect is the same + mm_event = pygame.event.Event(pygame.MOUSEMOTION, + pos=pygame.mouse.get_pos(), buttons=pygame.mouse.get_pressed(), + rel=vector.to_tuple(-self.slide_vel)) + self.mouse_move(mm_event) class BottomPanel(Panel): """Panel at bottom of screen""" - border_frac = 0.02 # Fractional border width + border_frac = 0.015 # Fractional border width def __init__(self, width, height, align_right=False, *controls): self.rect = pygame.Rect(0, 0, width, height) @@ -153,7 +199,8 @@ class MiniMap(BottomPanel): """Tactical mini-map display""" - frac_width = 0.25 + panel_hotkey = K_m + frac_width = 0.28 def __init__(self, map): width = game.screen_rect.width * self.frac_width @@ -227,6 +274,11 @@ planet_font_size = 32 info_font_size = 22 + button_off_color = (0, 0, 0, 50) + button_highlight_color = (100, 100, 160, 130) + button_on_color = (100, 100, 160, 130) + box_border_color = (50, 50, 70) + def __init__(self): BottomPanel.__init__(self, game.screen_rect.width * self.frac_width, game.screen_rect.height * self.frac_height) @@ -242,17 +294,62 @@ self.info_rect = pygame.Rect(self.border_width, self.border_width, self.rect.height - self.border_width * 2, self.rect.height - self.border_width * 2) image.fill(self.background_color, self.info_rect) + pygame.draw.rect(image, self.box_border_color, self.info_rect.inflate(2, 2), 1) + self.build_rect = pygame.Rect(self.info_rect) + self.build_rect.right = self.rect.width - self.border_width + image.fill(self.background_color, self.build_rect) + pygame.draw.rect(image, self.box_border_color, self.build_rect.inflate(2, 2), 1) + self.progress_rect = pygame.Rect( + self.info_rect.right + self.border_width * 2, + self.rect.height / 4 + self.border_width, + self.build_rect.left - self.info_rect.right - self.border_width * 4, + self.rect.height / 15) + image.fill(self.background_color, self.progress_rect) + pygame.draw.rect(image, self.box_border_color, self.progress_rect.inflate(2, 2), 1) + image.set_alpha(0, pygame.RLEACCEL) return image + def create_build_buttons(self): + """Create button widgets for building vessels, etc. in the base""" + self.controls.empty() + if self.planet.base is not None: + btn_size = self.build_rect.height / 3 + btn_rect = Rect(self.build_rect.left, self.build_rect.top, + btn_size, btn_size) + for config_path in glob.glob('vessels/%s/*' % game.local_player.race): + if self.planet.base.can_build_vessel(config_path): + ignored, vessel_config = vessel.get_config(config_path) + if vessel_config.has_option('general', 'image_name'): + image = vessel_config.get('general', 'image_name') + else: + image = '%s.png' % vessel_config.get('general', 'vessel_class') + button = widget.IconButton(image, btn_rect, + off_color=self.background_color, + highlight_color=self.button_highlight_color, + on_color=self.button_on_color, + render_callback=self.draw_buttons, + action=self.planet.base.build_vessel, action_args=[config_path]) + btn_rect = btn_rect.move(btn_rect.width, 0) + if btn_rect.left > self.build_rect.right: + btn_rect.left = self.build_rect.left + btn_rect.top = btn_rect.bottom + self.controls.add(button) + self.draw_buttons() + + def draw_buttons(self): + """Refresh button section of panel""" + self.image.fill(self.background_color, self.build_rect) # Erase button area + self.controls.draw(self.image) + self.image.set_alpha(0, pygame.RLEACCEL) + def set_planet(self, planet): self.planet = planet self.info_image = image = pygame.Surface(self.info_rect.size, pygame.SRCALPHA, 32) rect = image.get_rect() - desired_width = self.info_rect.width / 1.5 - #planet_img = pygame.transform.rotozoom(planet.src_image, - # 0, desired_width / max(planet.src_image.get_rect().size)) - #image.blit(planet_img, planet_img.get_rect(centerx=rect.centerx, bottom=rect.bottom - 4)) - image.blit(planet.src_image, (0, 0)) + desired_width = self.info_rect.width * 2.0 + planet_img = pygame.transform.rotozoom(planet.src_image, + 0, desired_width / max(planet.src_image.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) if planet.base is not None: @@ -263,16 +360,63 @@ info_img = self.info_font.render(base_info, True, (255, 255, 255)) info_rect = info_img.get_rect(left=4, top=title_rect.bottom) image.blit(info_img, info_rect) - info_img = self.info_font.render('Resources: %0.1f' % planet.resource_gen, + info_img = self.info_font.render('Resources: %0.1f' % planet.resources, True, (255, 255, 255)) info_rect = info_img.get_rect(left=4, top=info_rect.bottom) image.blit(info_img, info_rect) image.set_alpha(0, pygame.RLEACCEL) + self.create_build_buttons() def draw(self, surface): surface.blit(self.image, self.rect) if self.info_image is not None: self.info_rect.centery = self.rect.centery surface.blit(self.info_image, self.info_rect) + if self.planet.base is not None and self.planet.base.builder is not None: + bar = pygame.Rect(self.progress_rect) + bar.width = int(bar.width * self.planet.base.builder.fraction_complete()) + bar.top += self.rect.top + surface.fill((200, 150, 0), bar) return self.rect - + + +class PanelHandler(event.Handler): + """Event handler that delegates to the appropriate panel""" + + def __init__(self, panels=(), next_handler=None): + self.panels = [] + + def add_panel(self, panel): + if panel not in self.panels: + self.panels.append(panel) + + def mouse_move(self, event): + """Handle mouse movement""" + for panel in self.panels: + if panel.enabled: + panel.mouse_move(event) + + def mouse_up(self, event): + """Handle mouse button release""" + for panel in self.panels: + if panel.enabled: + if panel.mouse_up(event): + return panel + + def mouse_down(self, event): + """Handle mouse button press""" + handled = False + for panel in self.panels: + if panel.enabled: + if panel.mouse_down(event): + return panel + + def key_down(self, event): + """Handle key press""" + handled = False + for panel in self.panels: + if panel.enabled: + if panel.key_down(event): + return panel + +handler = PanelHandler() # Singleton Modified: particle.py =================================================================== --- particle.py 2007-09-27 23:06:58 UTC (rev 270) +++ particle.py 2007-09-29 16:46:25 UTC (rev 271) @@ -68,7 +68,7 @@ self.set_image() if self.frames > 0: self.frames -= 1 - apparent_size, screen_pos = vector.to_screen(self.position) + self.apparent_size, screen_pos = vector.to_screen(self.position) self.rect.center = vector.to_tuple(screen_pos) else: self.kill() Modified: staticbody.py =================================================================== --- staticbody.py 2007-09-27 23:06:58 UTC (rev 270) +++ staticbody.py 2007-09-29 16:46:25 UTC (rev 271) @@ -20,7 +20,7 @@ mass = 1000000 base = None # Established planetary base - def __init__(self, name, image, x=0, y=0, resource_gen=0): + def __init__(self, name, image, x=0, y=0, resources=0): RoundBody.__init__(self, (int(x), int(y))) # Position, velocity and heading are cached because they are frequently accessed self.position = vector.vector2(*self.body.getPosition()[:2]) @@ -32,9 +32,8 @@ self.offscreen_img = RotatedImage('pointy-blue.gif') self.rect = self.image.get_rect() self.collision_radius = self.radius = self.rect.width / 2 - self.resource_gen = float(resource_gen) + self.resources = float(resources) self.apparent_size = None - self.resources = 0 def update(self): # place self relative to the camera at proper zoom level @@ -47,13 +46,6 @@ else: self.rect.center = vector.to_tuple(screen_pos) self.on_screen = self.rect.colliderect(game.screen_rect) - self.resources = self.resource_gen / game.fps if self.base is not None: self.base.update() - - def use_resources(self, amount=None): - if amount is None or amount > self.resources: - amount = self.resources - self.resources -= amount - return amount Modified: vessel.py =================================================================== --- vessel.py 2007-09-27 23:06:58 UTC (rev 270) +++ vessel.py 2007-09-29 16:46:25 UTC (rev 271) @@ -149,7 +149,7 @@ 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, config_file=None): + standoff_distance=0, race=None, ai=None, cost=None, config_file=None): if image_name is None and vessel_class: image_name = vessel_class + '.png' if image_name is not None: Modified: vessels/naree/cress =================================================================== --- vessels/naree/cress 2007-09-27 23:06:58 UTC (rev 270) +++ vessels/naree/cress 2007-09-29 16:46:25 UTC (rev 271) @@ -10,6 +10,8 @@ max_speed: 160 max_energy: 200 standoff_distance: 125 +cost: 125 +ai: Standoffish [vessel.Shield] max_level: 100 Modified: vessels/naree/lotus =================================================================== --- vessels/naree/lotus 2007-09-27 23:06:58 UTC (rev 270) +++ vessels/naree/lotus 2007-09-29 16:46:25 UTC (rev 271) @@ -10,6 +10,8 @@ max_speed: 125 max_energy: 500 standoff_distance: 550 +ai: EvaderAI +cost: 300 [vessel.Shield] max_level: 600 Modified: vessels/rone/drach =================================================================== --- vessels/rone/drach 2007-09-27 23:06:58 UTC (rev 270) +++ vessels/rone/drach 2007-09-29 16:46:25 UTC (rev 271) @@ -10,6 +10,8 @@ max_speed: 135 max_energy: 250 standoff_distance: 150 +ai: AggroAI +cost: 100 [vessel.Armor] durability: 120 Modified: vessels/rone/draken =================================================================== --- vessels/rone/draken 2007-09-27 23:06:58 UTC (rev 270) +++ vessels/rone/draken 2007-09-29 16:46:25 UTC (rev 271) @@ -9,7 +9,9 @@ crew: 12 max_speed: 107 max_energy: 1000 -standoff_distance: 250 +standoff_distance: 300 +ai: AggroAI +cost: 240 [vessel.Armor] durability: 540 Modified: vessels/sc/pegasus =================================================================== --- vessels/sc/pegasus 2007-09-27 23:06:58 UTC (rev 270) +++ vessels/sc/pegasus 2007-09-29 16:46:25 UTC (rev 271) @@ -10,6 +10,8 @@ max_speed: 95 max_energy: 400 standoff_distance: 500 +ai: Standoffish +cost: 270 [vessel.DeflectorScreen] energy_drain: 85 Modified: vessels/sc/striker =================================================================== --- vessels/sc/striker 2007-09-27 23:06:58 UTC (rev 270) +++ vessels/sc/striker 2007-09-29 16:46:25 UTC (rev 271) @@ -10,6 +10,8 @@ max_speed: 120 max_energy: 150 standoff_distance: 350 +ai: Standoffish +cost: 110 [vessel.DeflectorScreen] energy_drain: 52 Added: widget.py =================================================================== --- widget.py (rev 0) +++ widget.py 2007-09-29 16:46:25 UTC (rev 271) @@ -0,0 +1,108 @@ +## Eos, Dawn of Light -- A Space Opera +## Copyright (c) 2007 Casey Duncan and contributors +## See LICENSE.txt for licensing details + +# User interface widgets +# $Id$ + +import pygame +import game +import media + + +class Button(pygame.sprite.Sprite): + """Basic button widget base class""" + + def __init__(self, rect, action=None, *action_args): + pygame.sprite.Sprite.__init__(self) + self.rect = rect + self.action = action + self.action_args = action_args + self.highlighted = False + self.pressed = False + self.render() + + def render(self): + """Render the composed button image""" + raise NotImplementedError + + def collides_with(self, pos): + """Return true if pos is inside the button""" + return self.rect.collidepoint(*pos) + + ## Event Handlers ## + + def mouse_enter(self, ui, event): + if not event.buttons[0]: + self.highlighted = True + self.render() + elif ui.clicked_control is self: + self.highlighted = True + self.pressed = True + self.render() + + def mouse_leave(self, ui, event): + self.highlighted = False + self.pressed = False + self.render() + + def mouse_down(self, ui, event): + self.highlighted = True + self.pressed = True + self.render() + + def mouse_up(self, ui, event): + self.pressed = False + self.render() + if self.action is not None: + self.action(*self.action_args) + + +class IconButton(Button): + """Button labeled with an icon""" + + icon_padding = 0.15 + + def __init__(self, icon, rect, off_color=(40, 40, 40), + on_color=(255, 255, 255, 100), highlight_color=(150, 150, 150), + text='', render_callback=None, action=None, action_args=()): + self.padding = max(int((sum(rect.size) / 2) * (self.icon_padding / 2)), 1) + self.icon_rect = rect.inflate((-self.padding * 2, -self.padding * 2)) + self.icon = media.load_and_fit_image(icon, self.icon_rect) + self.text = text + self.on_color = on_color + self.off_color = off_color + self.highlight_color = highlight_color + self.render_callback = render_callback + Button.__init__(self, rect, action, *action_args) + + def render(self): + self.image = pygame.Surface(self.rect.size, pygame.SRCALPHA, 32) + #self.image.fill(self.off_color) + self.image.blit(self.icon, self.icon.get_rect(center=self.image.get_rect().center)) + rect = [(0,0), self.rect.size] + if self.highlighted and not self.pressed: + pygame.draw.rect(self.image, self.highlight_color, + rect, self.padding * 3 / 4) + if self.pressed: + shader = pygame.Surface(self.rect.size, pygame.SRCALPHA, 32) + shader.fill(self.on_color) + self.image.blit(shader, rect) + if self.render_callback is not None: + self.render_callback() + +if __name__ == '__main__': + # widget tests + import event + pygame.init() + screen = pygame.display.set_mode((1024, 768)) + button = IconButton('outpost.png', pygame.Rect(200, 200, 47, 47)) + event_handler = event.MainHandler() + controls = pygame.sprite.OrderedUpdates([button]) + while event_handler.handle_events(): + screen.fill((0, 0, 0)) + controls.draw(screen) + pygame.display.flip() + + + Property changes on: widget.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-18 04:53:57
|
Revision: 294 http://eos-game.svn.sourceforge.net/eos-game/?rev=294&view=rev Author: cduncan Date: 2007-10-17 21:53:54 -0700 (Wed, 17 Oct 2007) Log Message: ----------- - Render new high quality 32 bpp rgba sprites for the Rone draken/drach - Add some shading/contrast to the pegasus sprite - Finish striker sprite, add better nose and cockpit detail - Fix shield animation for Naree, back off opacity a bit - Add glowing effect for SC deflector screens - Add friendly ship selection using "f" to select and [tab] to constrain selection to different vessel classes Modified Paths: -------------- data/drach-armor.png data/drach.png data/draken-armor.png data/draken.png data/pegasus.png data/plasma.png data/striker.png event.py panel.py selection.py vessel.py vessels/naree/cress vessels/naree/gnat vessels/naree/lotus vessels/sc/pegasus vessels/sc/striker Added Paths: ----------- art/striker.xcf data/pegasus-deflector.png Added: art/striker.xcf =================================================================== (Binary files differ) Property changes on: art/striker.xcf ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Modified: data/drach-armor.png =================================================================== (Binary files differ) Modified: data/drach.png =================================================================== (Binary files differ) Modified: data/draken-armor.png =================================================================== (Binary files differ) Modified: data/draken.png =================================================================== (Binary files differ) Added: data/pegasus-deflector.png =================================================================== (Binary files differ) Property changes on: data/pegasus-deflector.png ___________________________________________________________________ Name: svn:mime-type + image/png Modified: data/pegasus.png =================================================================== (Binary files differ) Modified: data/plasma.png =================================================================== (Binary files differ) Modified: data/striker.png =================================================================== (Binary files differ) Modified: event.py =================================================================== --- event.py 2007-10-18 04:20:45 UTC (rev 293) +++ event.py 2007-10-18 04:53:54 UTC (rev 294) @@ -12,6 +12,7 @@ import display import vector import panel +import selection class Handler: @@ -96,11 +97,15 @@ """Handle key press""" if event.key == K_ESCAPE: self.quit() - if event.key == K_f and event.mod in (KMOD_LCTRL, KMOD_RCTRL): + elif event.key == K_f and event.mod in (KMOD_LCTRL, KMOD_RCTRL): display.toggle_fullscreen() 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_MINUS: Modified: panel.py =================================================================== --- panel.py 2007-10-18 04:20:45 UTC (rev 293) +++ panel.py 2007-10-18 04:53:54 UTC (rev 294) @@ -303,7 +303,10 @@ def update_vessel(self, vessel): if vessel.vessel_type != 'missile': if vessel.is_friendly(game.local_player): - color = (0, 255, 0) + 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) Modified: selection.py =================================================================== --- selection.py 2007-10-18 04:20:45 UTC (rev 293) +++ selection.py 2007-10-18 04:53:54 UTC (rev 294) @@ -6,11 +6,13 @@ # $Id$ import sys +import itertools import ai import game import pygame import media import sprite +from vessel import Vessel class Target(pygame.sprite.Sprite): @@ -104,4 +106,65 @@ elif rect: rect.size = 0, 0 return rect - + + +class GroupSelector: + + timeout = 2000 + initial_select_size = 500 + + def __init__(self): + self.selected = pygame.sprite.Group() + self.by_class_iter = None + self._select_timeout = 0 + self._last_select_size = 0 + + def select_none(self, clear_class_iter=True): + """De-select all selected vessels""" + for vessel in self.selected: + vessel.selected = False + self.selected.empty() + 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""" + in_rect = rect.colliderect + by_class = {} + for spr in sprite.layers.ai_vessels: + if (isinstance(spr, Vessel) and spr.category & category and in_rect(spr.rect) + 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() + 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: + self._last_select_size = 0 + self._select_timeout = game.time + self.timeout + self.select_none() + 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) + + def constrain_by_class(self): + """Constrain an existing selection to a single vessel class. Repeated + calls cycle through all currently selected vessel classes + """ + if self.by_class_iter is not None: + vessel_class, vessels = self.by_class_iter.next() + self.select_none(clear_class_iter=False) + for vessel in vessels: + vessel.selected = True + self.selected.add(vessel) + +group = GroupSelector() # Singleton Modified: vessel.py =================================================================== --- vessel.py 2007-10-18 04:20:45 UTC (rev 293) +++ vessel.py 2007-10-18 04:53:54 UTC (rev 294) @@ -73,9 +73,7 @@ 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) - player.layer = sprite.layers.human_vessels - player.layer.add(player) + player = Vessel.load(ship, race=race, layer=sprite.layers.human_vessels) player.net_id = net_id player.setup_collision(body.friend, body.nothing) player.control = KeyboardControl() @@ -141,6 +139,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 + selected = False race = None @@ -154,7 +153,7 @@ if image_name is not None: self.vessel_img = RotatedImage(image_name) img_rect = self.vessel_img.get_rect() - self.collision_radius = min(img_rect.size) / 3 + self.collision_radius = max(img_rect.size) / 3 else: self.collision_radius = 0 self.config_file = config_file @@ -162,6 +161,7 @@ self.radius = self.hull_length self.standoff_distance = float(standoff_distance) body.RoundBody.__init__(self) + self.select_img = media.load_image('select.png') self.vessel_name = vessel_name self.vessel_class = vessel_class self.vessel_type = vessel_type @@ -184,7 +184,7 @@ self.state_history = [] @classmethod - def load(cls, config_file, **kw): + def load(cls, config_file, layer=None, **kw): """Create a vessel from a config file. config_file is either a readable file-like object or a file name. Additional keyword arguments are passed to the vessel constructor and override values of the same name in the @@ -231,6 +231,9 @@ params = dict(config.items('general')) params.update(kw) vessel = cls(config_file=config_file, **params) + if layer is not None: + vessel.layer = layer + layer.add(vessel) for section in config.sections(): if section == 'general': continue @@ -359,6 +362,25 @@ def draw(self, surface): rect = body.RoundBody.draw(self, surface) + if self.selected and self.on_screen and self.apparent_size is not None: + size = max(self.collision_radius * 4 * self.apparent_size, 10) + rect = pygame.Rect(0, 0, size, size) + rect.center = self.rect.center + white = (255, 255, 255) + segsize = rect.width / 4 + inset = rect.width / 11 + pygame.draw.aalines(surface, white, 0, + [(rect.left, rect.top+segsize), (rect.left+inset, rect.top+inset), + (rect.left+segsize, rect.top)], 0) + pygame.draw.aalines(surface, white, 0, + [(rect.left, rect.bottom-segsize), (rect.left+inset, rect.bottom-inset), + (rect.left+segsize, rect.bottom)], 0) + pygame.draw.aalines(surface, white, 0, + [(rect.right-segsize, rect.top), (rect.right-inset, rect.top+inset), + (rect.right, rect.top+segsize)], 0) + pygame.draw.aalines(surface, white, 0, + [(rect.right-segsize, rect.bottom), (rect.right-inset, rect.bottom-inset), + (rect.right-inset-1, rect.bottom-inset+1), (rect.right, rect.bottom-segsize)], 0) if game.time <= self.status_time and self.explosion is None: status_image = self.draw_status() time = self.status_time - game.time @@ -667,19 +689,18 @@ regen_recovery = 5.0 # Time regeneration recovers after disruption # Cosmetic - opacity = 80 # 0-255 + opacity = 70 # 0-255 flicker = 12 # variance in opacity fadeout = 200 # millis shields fade when turned off timeout = 1500 # millis shields stay active - def __init__(self, vessel, max_level, regeneration): + def __init__(self, vessel, max_level, regeneration, image=None): pygame.sprite.Sprite.__init__(self, vessel.groups()) self.vessel = vessel self.max_level = self.level = float(max_level) self.regeneration = self.max_regeneration = float(regeneration) - shield_img_file = '%s-shield.png' % vessel.vessel_class - if os.path.exists(shield_img_file): - self.rot_images = RotatedImage(shield_img_file, enhanced=False) + if image is not None: + self.rot_images = RotatedImage(image, enhanced=False) else: self.rot_images = None self.time = 0 @@ -734,8 +755,7 @@ def draw(self, surface): if self.time >= game.time and self.level and self.rot_images is not None: - image = self.rot_images.rotated( - math.radians(game.time / 30)) + image = self.rot_images.rotated(math.radians(game.time / 30)) apparent_size, pos = vector.to_screen(self.vessel.position) image = media.scale_image(image, apparent_size, colorkey=True) flicker = int(self.flicker + self.flicker * (self.max_level / self.level)) @@ -803,7 +823,7 @@ efficiency_recovery = 4.0 priority = 0 # Screens get hit first - def __init__(self, vessel, energy_drain, max_protection, deactivation_time): + def __init__(self, vessel, energy_drain, max_protection, deactivation_time, image=None): self.vessel = vessel self.energy_drain = float(energy_drain) self.efficiency = 1.0 @@ -811,6 +831,11 @@ self.deactivation_time = float(deactivation_time) * 1000 self.last_activation = 0 self.activated = False + self.no_deflector_img = self.vessel.vessel_img + if image is not None: + self.deflector_img = RotatedImage(image) + else: + self.deflector_img = None def update_system(self): if self.enabled: @@ -819,9 +844,15 @@ if self.activated: self.vessel.use_energy( self.energy_drain / (self.efficiency * game.fps), partial=True) + if self.deflector_img is not None: + self.vessel.vessel_img = self.deflector_img + else: + self.vessel.vessel_img = self.no_deflector_img if self.efficiency < 1.0: self.efficiency += min( self.efficiency + (1.0 / (game.fps * self.efficiency_recovery)), 1.0) + else: + self.vessel.vessel_img = self.no_deflector_img if self.protection < self.max_protection: self.protection = min(self.protection + self.max_protection / game.fps, self.max_protection) @@ -887,7 +918,7 @@ if self.armor_img is not None and self.heat > 0 and self.vessel.explosion is None: image = self.armor_img.rotated(self.vessel.heading) apparent_size, pos = vector.to_screen(self.vessel.position) - image = media.scale_image(image, apparent_size * 0.99, colorkey=True) + image = media.scale_image(image, apparent_size, colorkey=True) rect = image.get_rect(center=self.vessel.rect.center) if self.durability: level = min(self.heat / self.durability, 1.0) Modified: vessels/naree/cress =================================================================== --- vessels/naree/cress 2007-10-18 04:20:45 UTC (rev 293) +++ vessels/naree/cress 2007-10-18 04:53:54 UTC (rev 294) @@ -16,6 +16,7 @@ [vessel.Shield] max_level: 100 regeneration: 18 +image: cress-shield.png [vessel.DirectionalThrusters] thrust: 6 Modified: vessels/naree/gnat =================================================================== --- vessels/naree/gnat 2007-10-18 04:20:45 UTC (rev 293) +++ vessels/naree/gnat 2007-10-18 04:53:54 UTC (rev 294) @@ -13,6 +13,7 @@ [vessel.Shield] max_level: 40 regeneration: 6 +image: gnat-shield.png [vessel.DirectionalThrusters] thrust: 1 Modified: vessels/naree/lotus =================================================================== --- vessels/naree/lotus 2007-10-18 04:20:45 UTC (rev 293) +++ vessels/naree/lotus 2007-10-18 04:53:54 UTC (rev 294) @@ -16,6 +16,7 @@ [vessel.Shield] max_level: 600 regeneration: 14 +image: lotus-shield.png [vessel.DirectionalThrusters] thrust: 100 Modified: vessels/sc/pegasus =================================================================== --- vessels/sc/pegasus 2007-10-18 04:20:45 UTC (rev 293) +++ vessels/sc/pegasus 2007-10-18 04:53:54 UTC (rev 294) @@ -3,7 +3,7 @@ [general] vessel_class: pegasus vessel_type: warship -description: After experiencing combat with the Rone and Naree, it was clear that the striker was insufficient to counter their more capable fighter craft. The pegasus was developed as a robust adjunct to the striker fleets, providing both long-range strike capabilities with its dagger missiles and close range hitting power from its neutronium driver cannon. The defense systems are a scaled up version of those found on the striker with more robust deflector screens and hull armor. +description: After experiencing combat with the Rone and Naree, it was clear that the striker was insufficient to counter their more capable fighter craft and warships. The pegasus was developed as a robust adjunct to the striker fleets, providing both long-range strike capabilities with its dagger missiles and close range hitting power from its neutronium driver cannon. The defense systems are a scaled up version of those found on the striker with more robust deflector screens and hull armor. hull_mass: 20 hull_length: 21 crew: 8 @@ -17,6 +17,7 @@ energy_drain: 85 max_protection: 300 deactivation_time: 4 +image: pegasus-deflector.png [vessel.Armor] durability: 250 Modified: vessels/sc/striker =================================================================== --- vessels/sc/striker 2007-10-18 04:20:45 UTC (rev 293) +++ vessels/sc/striker 2007-10-18 04:53:54 UTC (rev 294) @@ -17,6 +17,7 @@ energy_drain: 52 max_protection: 150 deactivation_time: 2 +image: striker-deflector.png [vessel.Armor] durability: 50 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-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. |