/** * @brief Destroys a weapon. * * @param w Weapon to destroy. * @param layer Layer to which the weapon belongs. */ static void weapon_destroy( Weapon* w, WeaponLayer layer ) { int i; Weapon** wlayer; int *nlayer; Pilot *pilot_target; /* Decrement target lockons if needed */ if (outfit_isSeeker(w->outfit)) { pilot_target = pilot_get( w->target ); if (pilot_target != NULL) pilot_target->lockons--; } /* Stop playing sound if beam weapon. */ if (outfit_isBeam(w->outfit)) { sound_stop( w->voice ); sound_playPos(w->outfit->u.bem.sound_off, w->solid->pos.x, w->solid->pos.y, w->solid->vel.x, w->solid->vel.y); } switch (layer) { case WEAPON_LAYER_BG: wlayer = wbackLayer; nlayer = &nwbackLayer; break; case WEAPON_LAYER_FG: wlayer = wfrontLayer; nlayer = &nwfrontLayer; break; default: WARN("Unknown weapon layer!"); } for (i=0; (wlayer[i] != w) && (i < *nlayer); i++); /* get to the curent position */ if (i >= *nlayer) { WARN("Trying to destroy weapon not found in stack!"); return; } weapon_free(wlayer[i]); wlayer[i] = NULL; (*nlayer)--; for ( ; i < (*nlayer); i++) wlayer[i] = wlayer[i+1]; }
/** * @brief Deactivates the afterburner. */ void pilot_afterburnOver (Pilot *p) { if (p == NULL) return; if (p->afterburner == NULL) return; if (p->afterburner->state == PILOT_OUTFIT_ON) { p->afterburner->state = PILOT_OUTFIT_OFF; pilot_rmFlag(p,PILOT_AFTERBURNER); pilot_calcStats( p ); /* @todo Make this part of a more dynamic activated outfit sound system. */ sound_playPos(p->afterburner->outfit->u.afb.sound_off, p->solid->pos.x, p->solid->pos.y, p->solid->vel.x, p->solid->vel.y); } }
/** * @brief Weapon hit the pilot. * * @param w Weapon involved in the collision. * @param p Pilot that got hit. * @param layer Layer to which the weapon belongs. * @param pos Position of the hit. */ static void weapon_hit( Weapon* w, Pilot* p, WeaponLayer layer, Vector2d* pos ) { Pilot *parent; int spfx; double damage; DamageType dtype; WeaponLayer spfx_layer; int s; /* Get general details. */ parent = pilot_get(w->parent); damage = w->strength * outfit_damage(w->outfit); dtype = outfit_damageType(w->outfit); /* Play sound if they have it. */ s = outfit_soundHit(w->outfit); if (s != -1) w->voice = sound_playPos( s, w->solid->pos.x, w->solid->pos.y, w->solid->vel.x, w->solid->vel.y); /* Have pilot take damage and get real damage done. */ damage = pilot_hit( p, w->solid, w->parent, dtype, damage ); /* Get the layer. */ spfx_layer = (p==player) ? SPFX_LAYER_FRONT : SPFX_LAYER_BACK; /* Choose spfx. */ if (p->shield > 0.) spfx = outfit_spfxShield(w->outfit); else spfx = outfit_spfxArmour(w->outfit); /* Add sprite, layer depends on whether player shot or not. */ spfx_add( spfx, pos->x, pos->y, VX(p->solid->vel), VY(p->solid->vel), spfx_layer ); /* Inform AI that it's been hit. */ weapon_hitAI( p, parent, damage ); /* no need for the weapon particle anymore */ weapon_destroy(w,layer); }
/** * @brief Activate the afterburner. */ void pilot_afterburn (Pilot *p) { double afb_mod; if (p == NULL) return; if (pilot_isFlag(p, PILOT_HYP_PREP) || pilot_isFlag(p, PILOT_HYPERSPACE) || pilot_isFlag(p, PILOT_LANDING) || pilot_isFlag(p, PILOT_TAKEOFF) || pilot_isDisabled(p) || pilot_isFlag(p, PILOT_COOLDOWN)) return; /* Not under manual control if is player. */ if (pilot_isFlag( p, PILOT_MANUAL_CONTROL ) && pilot_isFlag( p, PILOT_PLAYER )) return; /** @todo fancy effect? */ if (p->afterburner == NULL) return; /* The afterburner only works if its efficiency is high enough. */ if (pilot_heatEfficiencyMod( p->afterburner->heat_T, p->afterburner->outfit->u.afb.heat_base, p->afterburner->outfit->u.afb.heat_cap ) < 0.3) return; if (p->afterburner->state == PILOT_OUTFIT_OFF) { p->afterburner->state = PILOT_OUTFIT_ON; p->afterburner->stimer = outfit_duration( p->afterburner->outfit ); pilot_setFlag(p,PILOT_AFTERBURNER); pilot_calcStats( p ); /* @todo Make this part of a more dynamic activated outfit sound system. */ sound_playPos(p->afterburner->outfit->u.afb.sound_on, p->solid->pos.x, p->solid->pos.y, p->solid->vel.x, p->solid->vel.y); } if (pilot_isPlayer(p)) { afb_mod = MIN( 1., player.p->afterburner->outfit->u.afb.mass_limit / player.p->solid->mass ); spfx_shake( afb_mod * player.p->afterburner->outfit->u.afb.rumble * SHAKE_MAX ); } }
/** * @brief Updates all the weapons in the layer. * * @param dt Current delta tick. * @param layer Layer to update. */ static void weapons_updateLayer( const double dt, const WeaponLayer layer ) { Weapon **wlayer; int *nlayer; Weapon *w; int i; int spfx; int s; /* Choose layer. */ switch (layer) { case WEAPON_LAYER_BG: wlayer = wbackLayer; nlayer = &nwbackLayer; break; case WEAPON_LAYER_FG: wlayer = wfrontLayer; nlayer = &nwfrontLayer; break; default: WARN("Unknown weapon layer!"); } i = 0; while (i < *nlayer) { w = wlayer[i]; switch (w->outfit->type) { /* most missiles behave the same */ case OUTFIT_TYPE_AMMO: case OUTFIT_TYPE_TURRET_AMMO: if (w->lockon > 0.) /* decrement lockon */ w->lockon -= dt; limit_speed( &w->solid->vel, w->outfit->u.amm.speed, dt ); w->timer -= dt; if (w->timer < 0.) { spfx = -1; /* See if we need armour death sprite. */ if (outfit_isProp(w->outfit, OUTFIT_PROP_WEAP_BLOWUP_ARMOUR)) spfx = outfit_spfxArmour(w->outfit); /* See if we need shield death sprite. */ else if (outfit_isProp(w->outfit, OUTFIT_PROP_WEAP_BLOWUP_SHIELD)) spfx = outfit_spfxShield(w->outfit); /* Add death sprite if needed. */ if (spfx != -1) { spfx_add( spfx, w->solid->pos.x, w->solid->pos.y, w->solid->vel.x, w->solid->vel.y, SPFX_LAYER_BACK ); /* presume back. */ /* Add sound if explodes and has it. */ s = outfit_soundHit(w->outfit); if (s != -1) w->voice = sound_playPos(s, w->solid->pos.x, w->solid->pos.y, w->solid->vel.x, w->solid->vel.y); } weapon_destroy(w,layer); break; } break; case OUTFIT_TYPE_BOLT: case OUTFIT_TYPE_TURRET_BOLT: w->timer -= dt; if (w->timer < 0.) { spfx = -1; /* See if we need armour death sprite. */ if (outfit_isProp(w->outfit, OUTFIT_PROP_WEAP_BLOWUP_ARMOUR)) spfx = outfit_spfxArmour(w->outfit); /* See if we need shield death sprite. */ else if (outfit_isProp(w->outfit, OUTFIT_PROP_WEAP_BLOWUP_SHIELD)) spfx = outfit_spfxShield(w->outfit); /* Add death sprite if needed. */ if (spfx != -1) { spfx_add( spfx, w->solid->pos.x, w->solid->pos.y, w->solid->vel.x, w->solid->vel.y, SPFX_LAYER_BACK ); /* presume back. */ /* Add sound if explodes and has it. */ s = outfit_soundHit(w->outfit); if (s != -1) w->voice = sound_playPos(s, w->solid->pos.x, w->solid->pos.y, w->solid->vel.x, w->solid->vel.y); } weapon_destroy(w,layer); break; } else if (w->timer < w->falloff) w->strength = w->timer / w->falloff; break; /* Beam weapons handled a part. */ case OUTFIT_TYPE_BEAM: case OUTFIT_TYPE_TURRET_BEAM: w->timer -= dt; if (w->timer < 0.) { weapon_destroy(w,layer); break; } /* We use the lockon to tell when we have to create explosions. */ w->lockon -= dt; if (w->lockon < 0.) { if (w->lockon < -1.) w->lockon = 0.100; else w->lockon = -1.; } break; default: WARN("Weapon of type '%s' has no update implemented yet!", w->outfit->name); break; } /* Out of bounds, loop is over. */ if (i >= *nlayer) break; /* Only increment if weapon wasn't deleted. */ if (w == wlayer[i]) { weapon_update(w,dt,layer); if ((i < *nlayer) && (w == wlayer[i])) i++; } } }
/** * @brief Creates a new weapon. * * @param outfit Outfit which spawned the weapon. * @param dir Direction the shooter is facing. * @param pos Position of the shooter. * @param vel Velocity of the shooter. * @param parent Shooter ID. * @param target Target ID of the shooter. * @return A pointer to the newly created weapon. */ static Weapon* weapon_create( const Outfit* outfit, const double dir, const Vector2d* pos, const Vector2d* vel, const unsigned int parent, const unsigned int target ) { Vector2d v; double mass, rdir; Pilot *pilot_target; double x,y, t, dist; Weapon* w; /* Create basic features */ w = malloc(sizeof(Weapon)); memset(w, 0, sizeof(Weapon)); w->faction = pilot_get(parent)->faction; /* non-changeable */ w->parent = parent; /* non-changeable */ w->target = target; /* non-changeable */ w->outfit = outfit; /* non-changeable */ w->update = weapon_update; w->status = WEAPON_STATUS_OK; w->strength = 1.; switch (outfit->type) { /* Bolts treated together */ case OUTFIT_TYPE_BOLT: case OUTFIT_TYPE_TURRET_BOLT: /* Only difference is the direction of fire */ if ((outfit->type == OUTFIT_TYPE_TURRET_BOLT) && (w->parent!=w->target) && (w->target != 0)) { /* Must have valid target */ pilot_target = pilot_get(w->target); if (pilot_target == NULL) rdir = dir; else { /* Get the distance */ dist = vect_dist( pos, &pilot_target->solid->pos ); /* Aim. */ if (dist > outfit->u.blt.range*1.2) { x = pilot_target->solid->pos.x - pos->x; y = pilot_target->solid->pos.y - pos->y; } else { /* Try to predict where the enemy will be. */ /* Time for shots to reach that distance */ t = dist / (w->outfit->u.blt.speed + VMOD(*vel)); /* Position is calculated on where it should be */ x = (pilot_target->solid->pos.x + pilot_target->solid->vel.x*t) - (pos->x + vel->x*t); y = (pilot_target->solid->pos.y + pilot_target->solid->vel.y*t) - (pos->y + vel->y*t); } /* Set angle to face. */ rdir = ANGLE(x, y); } } else /* fire straight */ rdir = dir; rdir += RNG_2SIGMA() * outfit->u.blt.accuracy/2. * 1./180.*M_PI; if (rdir < 0.) rdir += 2.*M_PI; else if (rdir >= 2.*M_PI) rdir -= 2.*M_PI; mass = 1; /* Lasers are presumed to have unitary mass */ vectcpy( &v, vel ); vect_cadd( &v, outfit->u.blt.speed*cos(rdir), outfit->u.blt.speed*sin(rdir)); w->timer = outfit->u.blt.range / outfit->u.blt.speed; w->falloff = w->timer - outfit->u.blt.falloff / outfit->u.blt.speed; w->solid = solid_create( mass, rdir, pos, &v ); w->voice = sound_playPos( w->outfit->u.blt.sound, w->solid->pos.x, w->solid->pos.y, w->solid->vel.x, w->solid->vel.y); break; /* Beam weapons are treated together. */ case OUTFIT_TYPE_BEAM: case OUTFIT_TYPE_TURRET_BEAM: if ((outfit->type == OUTFIT_TYPE_TURRET_BEAM) && (w->parent!=w->target)) { pilot_target = pilot_get(target); rdir = (pilot_target == NULL) ? dir : vect_angle(pos, &pilot_target->solid->pos); } else rdir = dir; if (rdir < 0.) rdir += 2.*M_PI; else if (rdir >= 2.*M_PI) rdir -= 2.*M_PI; mass = 1.; /**< Needs a mass. */ w->solid = solid_create( mass, rdir, pos, NULL ); w->think = think_beam; w->timer = outfit->u.bem.duration; w->voice = sound_playPos( w->outfit->u.bem.sound, w->solid->pos.x, w->solid->pos.y, w->solid->vel.x, w->solid->vel.y); break; /* Treat seekers together. */ case OUTFIT_TYPE_AMMO: case OUTFIT_TYPE_TURRET_AMMO: if (w->outfit->type == OUTFIT_TYPE_TURRET_AMMO) { pilot_target = pilot_get(w->target); if (pilot_target == NULL) rdir = dir; else { /* Get the distance */ dist = vect_dist( pos, &pilot_target->solid->pos ); /* Aim. */ /* Try to predict where the enemy will be. */ /* Time for shots to reach that distance */ if (outfit->u.amm.thrust == 0.) t = dist / (w->outfit->u.amm.speed + VMOD(*vel)); else t = dist / w->outfit->u.amm.speed; /* Position is calculated on where it should be */ x = (pilot_target->solid->pos.x + pilot_target->solid->vel.x*t) - (pos->x + vel->x*t); y = (pilot_target->solid->pos.y + pilot_target->solid->vel.y*t) - (pos->y + vel->y*t); /* Set angle to face. */ rdir = ANGLE(x, y); } } else { rdir = dir; } if (outfit->u.amm.accuracy != 0.) { rdir += RNG_2SIGMA() * outfit->u.amm.accuracy/2. * 1./180.*M_PI; if ((rdir > 2.*M_PI) || (rdir < 0.)) rdir = fmod(rdir, 2.*M_PI); } if (rdir < 0.) rdir += 2.*M_PI; else if (rdir >= 2.*M_PI) rdir -= 2.*M_PI; /* If thrust is 0. we assume it starts out at speed. */ vectcpy( &v, vel ); if (outfit->u.amm.thrust == 0.) vect_cadd( &v, cos(rdir) * w->outfit->u.amm.speed, sin(rdir) * w->outfit->u.amm.speed ); /* Set up ammo details. */ mass = w->outfit->mass; w->lockon = outfit->u.amm.lockon; w->timer = outfit->u.amm.duration; w->solid = solid_create( mass, rdir, pos, &v ); if (w->outfit->u.amm.thrust != 0.) weapon_setThrust( w, w->outfit->u.amm.thrust * mass ); /* Handle seekers. */ if (w->outfit->u.amm.ai > 0) { w->think = think_seeker; /* AI is the same atm. */ /* If they are seeking a pilot, increment lockon counter. */ pilot_target = pilot_get(target); if (pilot_target != NULL) pilot_target->lockons++; } /* Play sound. */ w->voice = sound_playPos(w->outfit->u.amm.sound, w->solid->pos.x, w->solid->pos.y, w->solid->vel.x, w->solid->vel.y); break; /* just dump it where the player is */ default: WARN("Weapon of type '%s' has no create implemented yet!", w->outfit->name); w->solid = solid_create( 1., dir, pos, vel ); break; } /* Set life to timer. */ w->life = w->timer; return w; }