/** * @brief Starts autonav. */ void player_autonavStart (void) { /* Not under manual control or disabled. */ if (pilot_isFlag( player.p, PILOT_MANUAL_CONTROL ) || pilot_isDisabled(player.p)) return; if ((player.p->nav_hyperspace == -1) && (player.p->nav_planet== -1)) return; else if ((player.p->nav_planet != -1) && !player_getHypPreempt()) { player_autonavPnt( cur_system->planets[ player.p->nav_planet ]->name ); return; } if (player.p->fuel < player.p->fuel_consumption) { player_message(_("\arNot enough fuel to jump for autonav.")); return; } if (pilot_isFlag( player.p, PILOT_NOJUMP)) { player_message(_("\arHyperspace drive is offline.")); return; } if (!player_autonavSetup()) return; player.autonav = AUTONAV_JUMP_APPROACH; }
/** * @brief Aborts autonav. * * @param reason Human-readable string describing abort condition. */ void player_autonavAbort( const char *reason ) { /* No point if player is beyond aborting. */ if ((player.p==NULL) || pilot_isFlag(player.p, PILOT_HYPERSPACE)) return; /* Cooldown (handled later) may be script-initiated and we don't * want to make it player-abortable while under manual control. */ if (pilot_isFlag( player.p, PILOT_MANUAL_CONTROL )) return; if (player_isFlag(PLAYER_AUTONAV)) { if (reason != NULL) player_message(_("\arAutonav aborted: %s!"), reason); else player_message(_("\arAutonav aborted!")); player_rmFlag(PLAYER_AUTONAV); /* Get rid of acceleration. */ player_accelOver(); /* Break possible hyperspacing. */ if (pilot_isFlag(player.p, PILOT_HYP_PREP)) { pilot_hyperspaceAbort(player.p); player_message(_("\apAborting hyperspace sequence.")); } /* Reset time compression. */ player_autonavEnd(); } }
/** * @brief Opens the communication dialogue with a pilot. * * @param pilot Pilot to communicate with. * @return 0 on success. */ int comm_openPilot( unsigned int pilot ) { const char *msg; unsigned int wid; /* Get the pilot. */ comm_pilot = pilot_get( pilot ); if (comm_pilot == NULL) return -1; /* Must not be disabled. */ if (pilot_isFlag(comm_pilot, PILOT_DISABLED)) { player_message("%s does not respond.", comm_pilot->name); return 0; } /* Check to see if pilot wants to communicate. */ msg = comm_getString( "comm_no" ); if (msg != NULL) { player_message( msg ); return 0; } /* Set up for the comm_get* functions. */ ai_setPilot( comm_pilot ); /* Create the generic comm window. */ wid = comm_open( ship_loadCommGFX( comm_pilot->ship ), comm_pilot->faction, pilot_isHostile(comm_pilot) ? -1 : pilot_isFriendly(comm_pilot) ? 1 : 0, pilot_isFlag(comm_pilot, PILOT_BRIBED), comm_pilot->name ); /* Add special buttons. */ window_addButton( wid, -20, 20 + BUTTON_HEIGHT + 20, BUTTON_WIDTH, BUTTON_HEIGHT, "btnGreet", "Greet", NULL ); if (!pilot_isFlag(comm_pilot, PILOT_BRIBED) && /* Not already bribed. */ ((faction_getPlayer( comm_pilot->faction ) < 0) || /* Hostile. */ pilot_isHostile(comm_pilot))) window_addButton( wid, -20, 20 + 2*BUTTON_HEIGHT + 40, BUTTON_WIDTH, BUTTON_HEIGHT, "btnBribe", "Bribe", comm_bribePilot ); else window_addButton( wid, -20, 20 + 2*BUTTON_HEIGHT + 40, BUTTON_WIDTH, BUTTON_HEIGHT, "btnRequest", "Refuel", comm_requestFuel ); /* Run hooks if needed. */ pilot_runHook( comm_pilot, PILOT_HOOK_HAIL ); return 0; }
/** * @brief Updates the pilot stats after mass change. * * @param pilot Pilot to update his mass. */ void pilot_updateMass( Pilot *pilot ) { double mass, factor; /* Set limit. */ mass = pilot->solid->mass; if ((pilot->stats.engine_limit > 0.) && (mass > pilot->stats.engine_limit)) factor = pilot->stats.engine_limit / mass; else factor = 1.; pilot->thrust = factor * pilot->thrust_base * mass; pilot->turn = factor * pilot->turn_base; pilot->speed = factor * pilot->speed_base; /* limit the maximum speed if limiter is active */ if (pilot_isFlag(pilot, PILOT_HASSPEEDLIMIT)) { pilot->speed = pilot->speed_limit - pilot->thrust / (mass * 3.); /* Sanity: speed must never go negative. */ if (pilot->speed < 0.) { /* If speed DOES go negative, we have to lower thrust. */ pilot->thrust = 3 * pilot->speed_limit * mass; pilot->speed = 0.; } } /* Need to recalculate electronic warfare mass change. */ pilot_ewUpdateStatic( pilot ); }
/** * @brief Has a pilot attempt to board another pilot. * * @param p Pilot doing the boarding. * @return 1 if target was boarded. */ int pilot_board( Pilot *p ) { Pilot *target; /* Make sure target is sane. */ target = pilot_get(p->target); if (target == NULL) { DEBUG("NO TARGET"); return 0; } /* Check if can board. */ if (!pilot_isDisabled(target)) return 0; else if (vect_dist(&p->solid->pos, &target->solid->pos) > target->ship->gfx_space->sw * PILOT_SIZE_APROX ) return 0; else if ((pow2(VX(p->solid->vel)-VX(target->solid->vel)) + pow2(VY(p->solid->vel)-VY(target->solid->vel))) > (double)pow2(MAX_HYPERSPACE_VEL)) return 0; else if (pilot_isFlag(target,PILOT_BOARDED)) return 0; /* Set the boarding flag. */ pilot_setFlag(target, PILOT_BOARDED); pilot_setFlag(p, PILOT_BOARDING); /* Set time it takes to board. */ p->ptimer = 3.; return 1; }
/** * @brief Handles input to the map overlay. */ int ovr_input( SDL_Event *event ) { int mx, my; double x, y; /* We only want mouse events. */ if (event->type != SDL_MOUSEBUTTONDOWN) return 0; /* Player must not be NULL. */ if (player_isFlag(PLAYER_DESTROYED) || (player.p == NULL)) return 0; /* Player must not be dead. */ if (pilot_isFlag(player.p, PILOT_DEAD)) return 0; /* Mouse targeting only uses left and right buttons. */ if (event->button.button != SDL_BUTTON_LEFT && event->button.button != SDL_BUTTON_RIGHT) return 0; /* Translate from window to screen. */ mx = event->button.x; my = event->button.y; gl_windowToScreenPos( &mx, &my, mx, my ); /* Translate to space coords. */ x = ((double)mx - SCREEN_W/2.) * ovr_res; y = ((double)my - SCREEN_H/2.) * ovr_res; return input_clickPos( event, x, y, 1., 10. * ovr_res, 15. * ovr_res ); }
/** * @brief Disables a given active outfit. * * @param p Pilot whose outfit we are disabling. * @return Whether the outfit was actually disabled. */ int pilot_outfitOff( Pilot *p, PilotOutfitSlot *o ) { double c; /* Must not be disabled or cooling down. */ if ((pilot_isDisabled(p)) || (pilot_isFlag(p, PILOT_COOLDOWN))) return 0; if (outfit_isAfterburner( o->outfit )) /* Afterburners */ pilot_afterburnOver( p ); else if (outfit_isBeam( o->outfit )) { /* Beams use stimer to represent minimum time until shutdown. */ o->stimer = -1; } else { c = outfit_cooldown( o->outfit ); if (o->stimer != INFINITY) o->stimer = c - (c * o->stimer / outfit_duration( o->outfit )); else o->stimer = c; o->state = PILOT_OUTFIT_COOLDOWN; } return 1; }
/** * @brief Opens the small ingame menu. */ void menu_small (void) { unsigned int wid; /* Check if menu should be openable. */ if ((player == NULL) || player_isFlag(PLAYER_DESTROYED) || pilot_isFlag(player,PILOT_DEAD) || comm_isOpen() || dialogue_isOpen() || /* Shouldn't open over dialogues. */ (menu_isOpen(MENU_MAIN) || menu_isOpen(MENU_SMALL) || menu_isOpen(MENU_DEATH) )) return; wid = window_create( "Menu", -1, -1, MENU_WIDTH, MENU_HEIGHT ); window_setCancel( wid, menu_small_close ); window_addButton( wid, 20, 20 + BUTTON_HEIGHT*2 + 20*2, BUTTON_WIDTH, BUTTON_HEIGHT, "btnResume", "Resume", menu_small_close ); window_addButton( wid, 20, 20 + BUTTON_HEIGHT + 20, BUTTON_WIDTH, BUTTON_HEIGHT, "btnOptions", "Options", menu_options_button ); window_addButton( wid, 20, 20, BUTTON_WIDTH, BUTTON_HEIGHT, "btnExit", "Exit", menu_small_exit ); menu_Open(MENU_SMALL); }
/** * @fn void player_board (void) * * @brief Attempt to board the player's target. * * Creates the window on success. */ void player_board (void) { Pilot *p; unsigned int wdw; char c; HookParam hparam[2]; if (player.p->target==PLAYER_ID) { /* We don't try to find far away targets, only nearest and see if it matches. * However, perhaps looking for first boardable target within a certain range * could be more interesting. */ player_targetNearest(); p = pilot_get(player.p->target); if ((!pilot_isDisabled(p) && !pilot_isFlag(p,PILOT_BOARDABLE)) || pilot_isFlag(p,PILOT_NOBOARD)) { player_targetClear(); player_message("\erYou need a target to board first!"); return; }
/** * @brief Check to see if a pilot is in sensor range of another. * * @param p Pilot who is trying to check to see if other is in sensor range. * @param target Target of p to check to see if is in sensor range. * @return 1 if they are in range, 0 if they aren't and -1 if they are detected fuzzily. */ int pilot_inRangePilot( const Pilot *p, const Pilot *target ) { double d, sense; /* Special case player or omni-visible. */ if ((pilot_isPlayer(p) && pilot_isFlag(target, PILOT_VISPLAYER)) || pilot_isFlag(target, PILOT_VISIBLE)) return 1; /* Get distance. */ d = vect_dist2( &p->solid->pos, &target->solid->pos ); sense = sensor_curRange * p->ew_detect; if (d * target->ew_evasion < sense) return 1; else if (d * target->ew_hide < sense) return -1; return 0; }
/** * @brief Runs an escort command on all of a pilot's escorts. * * @param parent Pilot who is giving orders. * @param cmd Order to give. * @param param Parameter for order. * @return 0 on success, 1 if no orders given. */ static int escort_command( Pilot *parent, int cmd, int param ) { int i, n; lua_State *L; Pilot *e; char *buf; if (parent->nescorts == 0) return 1; n = 0; for (i=0; i<parent->nescorts; i++) { e = pilot_get( parent->escorts[i] ); if (e == NULL) /* Most likely died. */ continue; /* Check if command makes sense. */ if ((cmd == ESCORT_RETURN) && !pilot_isFlag(e, PILOT_CARRIED)) continue; n++; /* Amount of escorts left. */ /* Prepare ai. */ ai_setPilot( e ); /* Set up stack. */ L = e->ai->L; switch (cmd) { case ESCORT_ATTACK: buf = "e_attack"; break; case ESCORT_HOLD: buf = "e_hold"; break; case ESCORT_RETURN: buf = "e_return"; break; case ESCORT_CLEAR: buf = "e_clear"; break; } lua_getglobal(L, buf); if (param >= 0) lua_pushnumber(L, param); /* Run command. */ if (lua_pcall(L, (param >= 0) ? 1 : 0, 0, 0)) WARN("Pilot '%s' ai -> '%s': %s", e->name, buf, lua_tostring(L,-1)); } return !n; }
/** * @brief Aborts regular interstellar autonav, but not in-system autonav. * * @param reason Human-readable string describing abort condition. */ void player_autonavAbortJump( const char *reason ) { /* No point if player is beyond aborting. */ if ((player.p==NULL) || pilot_isFlag(player.p, PILOT_HYPERSPACE)) return; if (!player_isFlag(PLAYER_AUTONAV) || ((player.autonav != AUTONAV_JUMP_APPROACH) && (player.autonav != AUTONAV_JUMP_BRAKE))) return; /* It's definitely not in-system autonav. */ player_autonavAbort(reason); }
/** * @brief Starts autonav. */ void player_autonavStart (void) { /* Not under manual control. */ if (pilot_isFlag( player.p, PILOT_MANUAL_CONTROL )) return; if (player.p->nav_hyperspace == -1) return; if (player.p->fuel < HYPERSPACE_FUEL) { player_message("\erNot enough fuel to jump for autonav."); return; }
/** * @brief Disables a given active outfit. * * @param p Pilot whose outfit we are disabling. * @return Whether the outfit was actually disabled. */ int pilot_outfitOff( Pilot *p, PilotOutfitSlot *o ) { /* Must not be disabled or cooling down. */ if ((pilot_isDisabled(p)) || (pilot_isFlag(p, PILOT_COOLDOWN))) return 0; if (outfit_isAfterburner( o->outfit )) /* Afterburners */ pilot_afterburnOver( p ); else { o->stimer = outfit_cooldown( o->outfit ); o->state = PILOT_OUTFIT_COOLDOWN; } return 1; }
/** * @brief Checks whether the speed should be reset due to damage or missile locks. * * @return 1 if the speed should be reset. */ int player_autonavShouldResetSpeed (void) { double failpc, shield, armour; int i, n; Pilot **pstk; int hostiles, will_reset; if (!player_isFlag(PLAYER_AUTONAV)) return 0; hostiles = 0; will_reset = 0; failpc = conf.autonav_reset_speed; shield = player.p->shield / player.p->shield_max; armour = player.p->armour / player.p->armour_max; pstk = pilot_getAll( &n ); for (i=0; i<n; i++) { if ((pstk[i]->id != PLAYER_ID) && pilot_isHostile( pstk[i] ) && pilot_inRangePilot( player.p, pstk[i] ) && !pilot_isDisabled( pstk[i] ) && !pilot_isFlag( pstk[i], PILOT_BRIBED )) { hostiles = 1; break; } } if (hostiles) { if (failpc > .995) { will_reset = 1; player.autonav_timer = MAX( player.autonav_timer, 0. ); } else if ((shield < lasts && shield < failpc) || armour < lasta) { will_reset = 1; player.autonav_timer = MAX( player.autonav_timer, 2. ); } } lasts = shield; lasta = armour; if (will_reset || (player.autonav_timer > 0)) { player_autonavResetSpeed(); return 1; } return 0; }
/** * @brief Starts autonav. */ void player_autonavStart (void) { /* Not under manual control. */ if (pilot_isFlag( player.p, PILOT_MANUAL_CONTROL )) return; if ((player.p->nav_hyperspace == -1) && (player.p->nav_planet== -1)) return; else if ((player.p->nav_planet != -1) && !player_getHypPreempt()) { player_autonavPnt( cur_system->planets[ player.p->nav_planet ]->name ); return; } if (player.p->fuel < player.p->fuel_consumption) { player_message("\erNot enough fuel to jump for autonav."); return; }
/** * @brief Performs an appropriate action when a pilot is clicked. * * @param pilot Index of the pilot. * @return Whether the click was used. */ int input_clickedPilot( unsigned int pilot ) { Pilot *p; if (pilot == PLAYER_ID) return 0; p = pilot_get(pilot); if (pilot == player.p->target && input_isDoubleClick( (void*)p )) { if (pilot_isDisabled(p) || pilot_isFlag(p, PILOT_BOARDABLE)) player_board(); else player_hail(); } else player_targetSet( pilot ); input_clicked( (void*)p ); return 1; }
/** * @brief Makes the pilot shoot. * * @param p The pilot which is shooting. * @param level Level of the shot. * @return The number of shots fired. */ int pilot_shoot( Pilot* p, int level ) { PilotWeaponSet *ws; int ret; /* Get active set. */ ws = pilot_weapSet( p, p->active_set ); /* Fire weapons. */ if (ws->type == WEAPSET_TYPE_CHANGE) { /* Must be a change set or a weaponset. */ ret = pilot_weapSetFire( p, ws, level ); /* Firing weapons aborts active cooldown. */ if (pilot_isFlag(p, PILOT_COOLDOWN) && ret) pilot_cooldownEnd(p, NULL); return ret; } return 0; }
/** * @brief Updates the pilot's weapon sets. * * @param p Pilot to update. */ void pilot_weapSetUpdate( Pilot* p ) { PilotWeaponSet *ws; int i; /* Must not be doing hyperspace procedures. */ if (pilot_isFlag( p, PILOT_HYP_BEGIN)) return; for (i=0; i<PILOT_WEAPON_SETS; i++) { ws = &p->weapon_sets[i]; if (ws->slots == NULL) continue; /* Weapons must get "fired" every turn. */ if (ws->type == WEAPSET_TYPE_WEAPON) { if (ws->active) pilot_weapSetFire( p, ws, -1 ); } } }
/** * @brief Prepares the player to enter autonav. * * @return 1 on success, 0 on failure (disabled, etc.) */ static int player_autonavSetup (void) { /* Not under manual control or disabled. */ if (pilot_isFlag( player.p, PILOT_MANUAL_CONTROL ) || pilot_isDisabled(player.p)) return 0; /* Autonav is mutually-exclusive with other autopilot methods. */ player_restoreControl( PINPUT_AUTONAV, NULL ); player_message(_("\apAutonav initialized.")); if (!player_isFlag(PLAYER_AUTONAV)) { tc_base = player_isFlag(PLAYER_DOUBLESPEED) ? 2. : 1.; tc_mod = tc_base; if (conf.compression_mult >= 1.) player.tc_max = MIN( conf.compression_velocity / solid_maxspeed(player.p->solid, player.p->speed, player.p->thrust), conf.compression_mult ); else player.tc_max = conf.compression_velocity / solid_maxspeed(player.p->solid, player.p->speed, player.p->thrust); /* Safe cap. */ player.tc_max = MAX( 1., player.tc_max ); } /* Sane values. */ tc_rampdown = 0; tc_down = 0.; lasts = player.p->shield / player.p->shield_max; lasta = player.p->armour / player.p->armour_max; /* Set flag and tc_mod just in case. */ player_setFlag(PLAYER_AUTONAV); pause_setSpeed( tc_mod ); /* Make sure time acceleration starts immediately. */ player.autonav_timer = 0.; return 1; }
/** * @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 Opens the information menu. */ void menu_info( int window ) { int w, h; /* Not under manual control. */ if (pilot_isFlag( player.p, PILOT_MANUAL_CONTROL )) return; /* Open closes when previously opened. */ if (menu_isOpen(MENU_INFO) || dialogue_isOpen()) { info_close( 0, NULL ); return; } /* Dimensions. */ w = 600; h = 500; /* Create the window. */ info_wid = window_create( "Info", -1, -1, w, h ); window_setCancel( info_wid, info_close ); /* Create tabbed window. */ info_windows = window_addTabbedWindow( info_wid, -1, -1, -1, -1, "tabInfo", INFO_WINDOWS, info_names ); /* Open the subwindows. */ info_openMain( info_windows[ INFO_WIN_MAIN ] ); info_openShip( info_windows[ INFO_WIN_SHIP ] ); info_openWeapons( info_windows[ INFO_WIN_WEAP ] ); info_openCargo( info_windows[ INFO_WIN_CARGO ] ); info_openMissions( info_windows[ INFO_WIN_MISN ] ); info_openStandings( info_windows[ INFO_WIN_STAND ] ); menu_Open(MENU_INFO); /* Set active window. */ window_tabWinSetActive( info_wid, "tabInfo", CLAMP( 0, 5, window ) ); }
/** * @brief Renders an individual weapon. * * @param w Weapon to render. * @param dt Current delta tick. */ static void weapon_render( Weapon* w, const double dt ) { double x,y, cx,cy, gx,gy; glTexture *gfx; double z; glColour c = { .r=1., .g=1., .b=1. }; switch (w->outfit->type) { /* Weapons that use sprites. */ case OUTFIT_TYPE_AMMO: case OUTFIT_TYPE_TURRET_AMMO: case OUTFIT_TYPE_BOLT: case OUTFIT_TYPE_TURRET_BOLT: gfx = outfit_gfx(w->outfit); /* Alpha based on strength. */ c.a = w->strength; /* Outfit spins around. */ if (outfit_isProp(w->outfit, OUTFIT_PROP_WEAP_SPIN)) { /* Check timer. */ w->anim -= dt; if (w->anim < 0.) { w->anim = outfit_spin(w->outfit); /* Increment sprite. */ w->sprite++; if (w->sprite >= gfx->sx*gfx->sy) w->sprite = 0; } /* Render. */ if (outfit_isBolt(w->outfit) && w->outfit->u.blt.gfx_end) gl_blitSpriteInterpolate( gfx, w->outfit->u.blt.gfx_end, w->timer / w->life, w->solid->pos.x, w->solid->pos.y, w->sprite % (int)gfx->sx, w->sprite / (int)gfx->sx, &c ); else gl_blitSprite( gfx, w->solid->pos.x, w->solid->pos.y, w->sprite % (int)gfx->sx, w->sprite / (int)gfx->sx, &c ); } /* Outfit faces direction. */ else { if (outfit_isBolt(w->outfit) && w->outfit->u.blt.gfx_end) gl_blitSpriteInterpolate( gfx, w->outfit->u.blt.gfx_end, w->timer / w->life, w->solid->pos.x, w->solid->pos.y, w->sx, w->sy, &c ); else gl_blitSprite( gfx, w->solid->pos.x, w->solid->pos.y, w->sx, w->sy, &c ); } break; /* Beam weapons. */ case OUTFIT_TYPE_BEAM: case OUTFIT_TYPE_TURRET_BEAM: gfx = outfit_gfx(w->outfit); /* Zoom. */ gl_cameraZoomGet( &z ); /* Position. */ gl_cameraGet( &cx, &cy ); gui_getOffset( &gx, &gy ); x = (w->solid->pos.x - cx)*z + gx; y = (w->solid->pos.y - cy)*z + gy; /* Set up the matrix. */ glMatrixMode(GL_PROJECTION); glPushMatrix(); glTranslated( x, y, 0. ); glRotated( 270. + w->solid->dir / M_PI * 180., 0., 0., 1. ); /* Preparatives. */ glEnable(GL_TEXTURE_2D); glBindTexture( GL_TEXTURE_2D, gfx->texture); glShadeModel(GL_SMOOTH); /* Actual rendering. */ glBegin(GL_QUAD_STRIP); /* Start faded. */ ACOLOUR(cWhite, 0.); glTexCoord2d( w->anim, 0. ); glVertex2d( -gfx->sh/2.*z, 0. ); glTexCoord2d( w->anim, 1. ); glVertex2d( +gfx->sh/2.*z, 0. ); /* Full strength. */ COLOUR(cWhite); glTexCoord2d( w->anim + 10. / gfx->sw, 0. ); glVertex2d( -gfx->sh/2.*z, 10.*z ); glTexCoord2d( w->anim + 10. / gfx->sw, 1. ); glVertex2d( +gfx->sh/2.*z, 10.*z ); glTexCoord2d( w->anim + 0.8*w->outfit->u.bem.range / gfx->sw, 0. ); glVertex2d( -gfx->sh/2.*z, 0.8*w->outfit->u.bem.range*z ); glTexCoord2d( w->anim + 0.8*w->outfit->u.bem.range / gfx->sw, 1. ); glVertex2d( +gfx->sh/2.*z, 0.8*w->outfit->u.bem.range*z ); /* Fades out. */ ACOLOUR(cWhite, 0.); glTexCoord2d( w->anim + w->outfit->u.bem.range / gfx->sw, 0. ); glVertex2d( -gfx->sh/2.*z, w->outfit->u.bem.range*z ); glTexCoord2d( w->anim + w->outfit->u.bem.range / gfx->sw, 1. ); glVertex2d( +gfx->sh/2.*z, w->outfit->u.bem.range*z ); glEnd(); /* GL_QUAD_STRIP */ /* Do the beam movement. */ w->anim -= 5. * dt; if (w->anim <= -gfx->sw) w->anim += gfx->sw; /* Clean up. */ glDisable(GL_TEXTURE_2D); glShadeModel(GL_FLAT); glPopMatrix(); /* GL_PROJECTION */ gl_checkErr(); break; default: WARN("Weapon of type '%s' has no render implemented yet!", w->outfit->name); break; } } /** * @brief Checks to see if the weapon can hit the pilot. * * @param w Weapon to check if hits pilot. * @param p Pilot to check if is hit by weapon. * @return 1 if can be hit, 0 if can't. */ static int weapon_checkCanHit( Weapon* w, Pilot *p ) { Pilot *parent; /* Can't hit invincible stuff. */ if (pilot_isFlag(p, PILOT_INVINCIBLE)) return 0; /* Can never hit same faction. */ if (p->faction == w->faction) return 0; /* Go "through" dead pilots. */ if (pilot_isFlag(p, PILOT_DEAD)) return 0; /* Player behaves differently. */ if (w->faction == FACTION_PLAYER) { /* Always hit without safety. */ if (!weapon_safety) return 1; /* Always hit target. */ else if (w->target == p->id) return 1; /* Always hit hostiles. */ else if (pilot_isFlag(p, PILOT_HOSTILE)) return 1; /* Always hit unbribed enemies. */ else if (!pilot_isFlag(p, PILOT_BRIBED) && areEnemies(w->faction, p->faction)) return 1; /* Miss rest - can be neutral/ally. */ else return 0; } /* Let hostiles hit player. */ if (p->faction == FACTION_PLAYER) { parent = pilot_get(w->parent); if (parent != NULL) { if (pilot_isFlag(parent, PILOT_BRIBED)) return 0; if (pilot_isFlag(parent, PILOT_HOSTILE)) return 1; } } /* Hit non-allies. */ if (areEnemies(w->faction, p->faction)) return 1; return 0; }
/** * @brief Handles a weapon set press. * * @param p Pilot the weapon set belongs to. * @param id ID of the weapon set. * @param type Is +1 if it's a press or -1 if it's a release. */ void pilot_weapSetPress( Pilot* p, int id, int type ) { int i, l, on, n; PilotWeaponSet *ws; ws = pilot_weapSet(p,id); /* Case no outfits. */ if (ws->slots == NULL) return; /* Handle fire groups. */ switch (ws->type) { case WEAPSET_TYPE_CHANGE: /* On press just change active weapon set to whatever is available. */ if (type > 0) { if (id != p->active_set) pilot_weapSetUpdateOutfits( p, ws ); p->active_set = id; } break; case WEAPSET_TYPE_WEAPON: /* Activation philosophy here is to turn on while pressed and off * when it's not held anymore. */ if (type > 0) ws->active = 1; else if (type < 0) ws->active = 0; break; case WEAPSET_TYPE_ACTIVE: /* The behaviour here is more complex. What we do is consider a group * to be entirely off if not all outfits are either on or cooling down. * In the case it's deemed to be off, all outfits that are off get turned * on, otherwise all outfits that are on are turrned to cooling down. */ /* Only care about presses. */ if (type < 0) break; /* Must not be disabled or cooling down. */ if ((pilot_isDisabled(p)) || (pilot_isFlag(p, PILOT_COOLDOWN))) return; /* Decide what to do. */ on = 1; l = array_size(ws->slots); for (i=0; i<l; i++) { if (ws->slots[i].slot->state == PILOT_OUTFIT_OFF) { on = 0; break; } } /* Turn them off. */ n = 0; if (on) { for (i=0; i<l; i++) { if (ws->slots[i].slot->state != PILOT_OUTFIT_ON) continue; n += pilot_outfitOff( p, ws->slots[i].slot ); } } /* Turn them on. */ else { for (i=0; i<l; i++) { if (ws->slots[i].slot->state != PILOT_OUTFIT_OFF) continue; if (outfit_isAfterburner(ws->slots[i].slot->outfit)) pilot_afterburn( p ); else { ws->slots[i].slot->state = PILOT_OUTFIT_ON; ws->slots[i].slot->stimer = outfit_duration( ws->slots[i].slot->outfit ); } n++; } } /* Must recalculate stats. */ if (n > 0) pilot_calcStats( p ); break; } }
/** * @brief Renders the starry background. * * @param dt Current delta tick. */ void background_renderStars( const double dt ) { (void) dt; unsigned int i; GLfloat hh, hw, h, w; GLfloat x, y, m, b; GLfloat brightness; double z; double sx, sy; int shade_mode; int j, n; /* * gprof claims it's the slowest thing in the game! */ /* Do some scaling for now. */ z = cam_getZoom(); z = 1. * (1. - conf.zoom_stars) + z * conf.zoom_stars; gl_matrixPush(); gl_matrixTranslate( SCREEN_W/2., SCREEN_H/2. ); gl_matrixScale( z, z ); if (!paused && (player.p != NULL) && !player_isFlag(PLAYER_DESTROYED) && !player_isFlag(PLAYER_CREATING)) { /* update position */ /* Calculate some dimensions. */ w = (SCREEN_W + 2.*STAR_BUF); w += conf.zoom_stars * (w / conf.zoom_far - 1.); h = (SCREEN_H + 2.*STAR_BUF); h += conf.zoom_stars * (h / conf.zoom_far - 1.); hw = w/2.; hh = h/2.; if ((star_x > SCREEN_W) || (star_y > SCREEN_H)) { sx = ceil( star_x / SCREEN_W ); sy = ceil( star_y / SCREEN_H ); n = MAX( sx, sy ); star_x /= (double)n; star_y /= (double)n; } else n = 1; /* Calculate new star positions. */ for (j=0; j < n; j++) { for (i=0; i < nstars; i++) { /* calculate new position */ b = 1./(9. - 10.*star_colour[8*i+3]); star_vertex[4*i+0] = star_vertex[4*i+0] + star_x*b; star_vertex[4*i+1] = star_vertex[4*i+1] + star_y*b; /* check boundaries */ if (star_vertex[4*i+0] > hw) star_vertex[4*i+0] -= w; else if (star_vertex[4*i+0] < -hw) star_vertex[4*i+0] += w; if (star_vertex[4*i+1] > hh) star_vertex[4*i+1] -= h; else if (star_vertex[4*i+1] < -hh) star_vertex[4*i+1] += h; } } /* Upload the data. */ gl_vboSubData( star_vertexVBO, 0, nstars * 4 * sizeof(GLfloat), star_vertex ); } /* Decide on shade mode. */ shade_mode = 0; if ((player.p != NULL) && !player_isFlag(PLAYER_DESTROYED) && !player_isFlag(PLAYER_CREATING)) { if (pilot_isFlag(player.p,PILOT_HYPERSPACE) && /* hyperspace fancy effects */ (player.p->ptimer < HYPERSPACE_STARS_BLUR)) { glShadeModel(GL_SMOOTH); shade_mode = 1; /* lines will be based on velocity */ m = HYPERSPACE_STARS_BLUR-player.p->ptimer; m /= HYPERSPACE_STARS_BLUR; m *= HYPERSPACE_STARS_LENGTH; x = m*cos(VANGLE(player.p->solid->vel)); y = m*sin(VANGLE(player.p->solid->vel)); } else if (dt_mod > 3.) { glShadeModel(GL_SMOOTH); shade_mode = 1; /* lines will be based on velocity */ m = (dt_mod-3.)*VMOD(player.p->solid->vel)/10.; x = m*cos(VANGLE(player.p->solid->vel)); y = m*sin(VANGLE(player.p->solid->vel)); } if (shade_mode) { /* Generate lines. */ for (i=0; i < nstars; i++) { brightness = star_colour[8*i+3]; star_vertex[4*i+2] = star_vertex[4*i+0] + x*brightness; star_vertex[4*i+3] = star_vertex[4*i+1] + y*brightness; } /* Upload new data. */ gl_vboSubData( star_vertexVBO, 0, nstars * 4 * sizeof(GLfloat), star_vertex ); } } /* Render. */ gl_vboActivate( star_vertexVBO, GL_VERTEX_ARRAY, 2, GL_FLOAT, 2 * sizeof(GLfloat) ); gl_vboActivate( star_colourVBO, GL_COLOR_ARRAY, 4, GL_FLOAT, 4 * sizeof(GLfloat) ); if (shade_mode) { glDrawArrays( GL_LINES, 0, nstars ); glDrawArrays( GL_POINTS, 0, nstars ); /* This second pass is when the lines are very short that they "lose" intensity. */ glShadeModel(GL_FLAT); } else { glDrawArrays( GL_POINTS, 0, nstars ); } /* Clear star movement. */ star_x = 0.; star_y = 0.; /* Disable vertex array. */ gl_vboDeactivate(); /* Pop matrix. */ gl_matrixPop(); /* Check for errors. */ gl_checkErr(); }
/** * @brief Updates a camera following a pilot. */ static void cam_updatePilot( Pilot *follow, double dt ) { Pilot *target; double diag2, a, r, dir, k; double x,y, dx,dy, mx,my, targ_x,targ_y, bias_x,bias_y, vx,vy; /* Get target. */ if (!pilot_isFlag(follow, PILOT_HYPERSPACE) && (follow->target != follow->id)) target = pilot_get( follow->target ); else target = NULL; /* Real diagonal might be a bit too harsh since it can cut out the ship, * we'll just use the largest of the two. */ /*diag2 = pow2(SCREEN_W) + pow2(SCREEN_H);*/ /*diag2 = pow2( MIN(SCREEN_W, SCREEN_H) );*/ diag2 = 100*100; x = follow->solid->pos.x; y = follow->solid->pos.y; /* Compensate player movement. */ mx = x - old_X; my = y - old_Y; camera_X += mx; camera_Y += my; /* Set old position. */ old_X = x; old_Y = y; /* No bias by default. */ bias_x = 0.; bias_y = 0.; /* Bias towards target. */ if (target != NULL) { bias_x += target->solid->pos.x - x; bias_y += target->solid->pos.y - y; } /* Bias towards velocity and facing. */ vx = follow->solid->vel.x*1.5; vy = follow->solid->vel.y*1.5; dir = angle_diff( atan2(vy,vx), follow->solid->dir); dir = (M_PI - fabs(dir)) / M_PI; /* Normalize. */ vx *= dir; vy *= dir; bias_x += vx; bias_y += vy; /* Limit bias. */ if (pow2(bias_x)+pow2(bias_y) > diag2/2.) { a = atan2( bias_y, bias_x ); r = sqrt(diag2)/2.; bias_x = r*cos(a); bias_y = r*sin(a); } /* Compose the target. */ targ_x = x + bias_x; targ_y = y + bias_y; /* Head towards target. */ k = 0.5*dt/dt_mod; dx = (targ_x-camera_X)*k; dy = (targ_y-camera_Y)*k; background_moveStars( -(mx+dx), -(my+dy) ); /* Update camera. */ camera_X += dx; camera_Y += dy; /* DEBUG. */ #if 0 glColor4d( 1., 1., 1., 1. ); glBegin(GL_LINES); gl_gameToScreenCoords( &x, &y, x, y ); glVertex2d( x, y ); gl_gameToScreenCoords( &x, &y, camera_X, camera_Y ); glVertex2d( x, y ); glEnd(); /* GL_LINES */ #endif /* Update zoom. */ cam_updatePilotZoom( follow, target, dt ); }
/** * @brief Handles a click event. */ static void input_clickevent( SDL_Event* event ) { unsigned int pid; int mx, my, mxr, myr, pntid, jpid; int rx, ry, rh, rw, res; int autonav; double x, y, zoom, px, py; double ang, angp, mouseang; HookParam hparam[2]; /* Generate hook. */ hparam[0].type = HOOK_PARAM_NUMBER; hparam[0].u.num = event->button.button; hparam[1].type = HOOK_PARAM_SENTINEL; hooks_runParam( "mouse", hparam ); #if !SDL_VERSION_ATLEAST(2,0,0) /* Handle zoom. */ if (event->button.button == SDL_BUTTON_WHEELUP) { input_clickZoom( 1.1 ); return; } else if (event->button.button == SDL_BUTTON_WHEELDOWN) { input_clickZoom( 0.9 ); return; } #endif /* !SDL_VERSION_ATLEAST(2,0,0) */ /* Player must not be NULL. */ if ((player.p == NULL) || player_isFlag(PLAYER_DESTROYED)) return; /* Player must not be dead. */ if (pilot_isFlag(player.p, PILOT_DEAD)) return; /* Middle mouse enables mouse flying. */ if (event->button.button == SDL_BUTTON_MIDDLE) { player_toggleMouseFly(); return; } /* Mouse targeting only uses left and right buttons. */ if (event->button.button != SDL_BUTTON_LEFT && event->button.button != SDL_BUTTON_RIGHT) return; autonav = (event->button.button == SDL_BUTTON_RIGHT) ? 1 : 0; px = player.p->solid->pos.x; py = player.p->solid->pos.y; gl_windowToScreenPos( &mx, &my, event->button.x, event->button.y ); if ((mx <= 15 || my <= 15 ) || (my >= gl_screen.h - 15 || mx >= gl_screen.w - 15)) { /* Border targeting is handled as a special case, as it uses angles, * not coordinates. */ x = (mx - (gl_screen.w / 2.)) + px; y = (my - (gl_screen.h / 2.)) + py; mouseang = atan2(py - y, px - x); angp = pilot_getNearestAng( player.p, &pid, mouseang, 1 ); ang = system_getClosestAng( cur_system, &pntid, &jpid, x, y, mouseang ); if ((ABS(angle_diff(mouseang, angp)) > M_PI / 64) || ABS(angle_diff(mouseang, ang)) < ABS(angle_diff(mouseang, angp))) pid = PLAYER_ID; /* Pilot angle is too great, or planet/jump is closer. */ if (ABS(angle_diff(mouseang, ang)) > M_PI / 64 ) jpid = pntid = -1; /* Asset angle difference is too great. */ if (!autonav && pid != PLAYER_ID) { if (input_clickedPilot(pid)) return; } else if (pntid >= 0) { /* Planet is closest. */ if (input_clickedPlanet(pntid, autonav)) return; } else if (jpid >= 0) { /* Jump point is closest. */ if (input_clickedJump(jpid, autonav)) return; } /* Fall-through and handle as a normal click. */ } /* Radar targeting requires raw coordinates. */ mxr = event->button.x; myr = gl_screen.rh - event->button.y; gui_radarGetPos( &rx, &ry ); gui_radarGetDim( &rw, &rh ); if ((mxr > rx && mxr <= rx + rw ) && (myr > ry && myr <= ry + rh )) { /* Radar */ zoom = 1.; gui_radarGetRes( &res ); x = (mxr - (rx + rw / 2.)) * res + px; y = (myr - (ry + rh / 2.)) * res + py; if (input_clickPos( event, x, y, zoom, 10. * res, 15. * res )) return; } /* Visual (on-screen) */ gl_screenToGameCoords( &x, &y, (double)mx, (double)my ); zoom = res = 1. / cam_getZoom(); input_clickPos( event, x, y, zoom, 10. * res, 15. * res ); return; }
/** * @brief Handles a click event. */ static void input_clickevent( SDL_Event* event ) { unsigned int pid; Pilot *p; int mx, my, mxr, myr, pntid, jpid; int rx, ry, rh, rw, res; double x, y, m, r, rp, d, dp, px, py; double ang, angp, mouseang; Planet *pnt; JumpPoint *jp; HookParam hparam[2]; /* Generate hook. */ hparam[0].type = HOOK_PARAM_NUMBER; hparam[0].u.num = event->button.button; hparam[1].type = HOOK_PARAM_SENTINAL; hooks_runParam( "mouse", hparam ); /* Handle zoom. */ if (event->button.button == SDL_BUTTON_WHEELUP) { input_clickZoom( 1.1 ); return; } else if (event->button.button == SDL_BUTTON_WHEELDOWN) { input_clickZoom( 0.9 ); return; } /* Middle mouse enables mouse flying. */ if (event->button.button == SDL_BUTTON_MIDDLE) { player_toggleMouseFly(); return; } /* Mouse targetting is left only. */ if (event->button.button != SDL_BUTTON_LEFT) return; /* Player must not be NULL. */ if (player_isFlag(PLAYER_DESTROYED) || (player.p == NULL)) return; px = player.p->solid->pos.x; py = player.p->solid->pos.y; gl_windowToScreenPos( &mx, &my, event->button.x, event->button.y ); gl_screenToGameCoords( &x, &y, (double)mx, (double)my ); if ((mx <= 15 || my <= 15 ) || (my >= gl_screen.h - 15 || mx >= gl_screen.w - 15)) { /* Border */ x = (mx - (gl_screen.w / 2.)) + px; y = (my - (gl_screen.h / 2.)) + py; mouseang = atan2(py - y, px - x); angp = pilot_getNearestAng( player.p, &pid, mouseang, 1 ); ang = system_getClosestAng( cur_system, &pntid, &jpid, x, y, mouseang ); if ((ABS(angle_diff(mouseang, angp)) > M_PI / 64) || ABS(angle_diff(mouseang, ang)) < ABS(angle_diff(mouseang, angp))) pid = PLAYER_ID; /* Pilot angle is too great, or planet/jump is closer. */ if (ABS(angle_diff(mouseang, ang)) > M_PI / 64 ) jpid = pntid = -1; /* Asset angle difference is too great. */ } else { /* Radar targeting requires raw coordinates. */ mxr = event->button.x; myr = gl_screen.rh - event->button.y; gui_radarGetPos( &rx, &ry ); gui_radarGetDim( &rw, &rh ); if ((mxr > rx && mxr <= rx + rw ) && (myr > ry && myr <= ry + rh )) { /* Radar */ m = 1; gui_radarGetRes( &res ); x = (mxr - (rx + rw / 2.)) * res + px; y = (myr - (ry + rh / 2.)) * res + py; } else /* Visual (on-screen) */ m = res = 1. / cam_getZoom(); dp = pilot_getNearestPos( player.p, &pid, x, y, 1 ); d = system_getClosest( cur_system, &pntid, &jpid, x, y ); rp = MAX( 1.5 * PILOT_SIZE_APROX * pilot_get(pid)->ship->gfx_space->sw / 2 * m, 10. * res); if (pntid >=0) { /* Planet is closer. */ pnt = cur_system->planets[ pntid ]; r = MAX( 1.5 * pnt->radius, 100. ); } else if (jpid >= 0) { jp = &cur_system->jumps[ jpid ]; r = MAX( 1.5 * jp->radius, 100. ); } else { r = 0.; } /* Reject pilot if it's too far or a valid asset is closer. */ if (dp > pow2(rp) || (d < pow2(r) && dp < pow2(rp) && dp > d)) pid = PLAYER_ID; if (d > pow2(r)) /* Planet or jump point is too far. */ jpid = pntid = -1; } if (pid != PLAYER_ID) { /* Apply an action if already selected. */ if (!pilot_isFlag(player.p, PILOT_DEAD) && (pid == player.p->target)) { p = pilot_get(pid); if (pilot_isDisabled(p) || pilot_isFlag(p, PILOT_BOARDABLE)) player_board(); else player_hail(); } else player_targetSet( pid ); } else if (pntid >= 0) { /* Planet is closest. */ if (pntid == player.p->nav_planet) { pnt = cur_system->planets[ pntid ]; if (planet_hasService(pnt, PLANET_SERVICE_LAND) && (!areEnemies( player.p->faction, pnt->faction ) || pnt->bribed )) player_land(); else player_hailPlanet(); } else player_targetPlanetSet( pntid ); } else if (jpid >= 0) { /* Jump point is closest. */ jp = &cur_system->jumps[ jpid ]; if (jpid == player.p->nav_hyperspace) { if (space_canHyperspace(player.p)) { if (!paused) player_autonavAbort(NULL); player_jump(); } else player_autonavStart(); } else player_targetHyperspaceSet( jpid ); } }
/** * @brief Recalculates the pilot's stats based on his outfits. * * @param pilot Pilot to recalculate his stats. */ void pilot_calcStats( Pilot* pilot ) { int i; Outfit* o; PilotOutfitSlot *slot; double ac, sc, ec, fc; /* temporary health coefficients to set */ ShipStats amount, *s, *default_s; /* * set up the basic stuff */ /* mass */ pilot->solid->mass = pilot->ship->mass; pilot->base_mass = pilot->solid->mass; /* cpu */ pilot->cpu = 0.; /* movement */ pilot->thrust_base = pilot->ship->thrust; pilot->turn_base = pilot->ship->turn; pilot->speed_base = pilot->ship->speed; /* crew */ pilot->crew = pilot->ship->crew; /* cargo */ pilot->cap_cargo = pilot->ship->cap_cargo; /* fuel_consumption. */ pilot->fuel_consumption = pilot->ship->fuel_consumption; /* health */ ac = (pilot->armour_max > 0.) ? pilot->armour / pilot->armour_max : 0.; sc = (pilot->shield_max > 0.) ? pilot->shield / pilot->shield_max : 0.; ec = (pilot->energy_max > 0.) ? pilot->energy / pilot->energy_max : 0.; fc = (pilot->fuel_max > 0.) ? pilot->fuel / pilot->fuel_max : 0.; pilot->armour_max = pilot->ship->armour; pilot->shield_max = pilot->ship->shield; pilot->fuel_max = pilot->ship->fuel; pilot->armour_regen = pilot->ship->armour_regen; pilot->shield_regen = pilot->ship->shield_regen; /* Absorption. */ pilot->dmg_absorb = pilot->ship->dmg_absorb; /* Energy. */ pilot->energy_max = pilot->ship->energy; pilot->energy_regen = pilot->ship->energy_regen; pilot->energy_loss = 0.; /* Initially no net loss. */ /* Stats. */ s = &pilot->stats; memcpy( s, &pilot->ship->stats_array, sizeof(ShipStats) ); memset( &amount, 0, sizeof(ShipStats) ); /* * Now add outfit changes */ pilot->mass_outfit = 0.; pilot->jamming = 0; for (i=0; i<pilot->noutfits; i++) { slot = pilot->outfits[i]; o = slot->outfit; /* Outfit must exist. */ if (o==NULL) continue; /* Modify CPU. */ pilot->cpu += outfit_cpu(o); /* Add mass. */ pilot->mass_outfit += o->mass; /* Keep a separate counter for required (core) outfits. */ if (sp_required( o->slot.spid )) pilot->base_mass += o->mass; /* Add ammo mass. */ if (outfit_ammo(o) != NULL) if (slot->u.ammo.outfit != NULL) pilot->mass_outfit += slot->u.ammo.quantity * slot->u.ammo.outfit->mass; if (outfit_isAfterburner(o)) /* Afterburner */ pilot->afterburner = pilot->outfits[i]; /* Set afterburner */ /* Active outfits must be on to affect stuff. */ if (slot->active && !(slot->state==PILOT_OUTFIT_ON)) continue; if (outfit_isMod(o)) { /* Modification */ /* Movement. */ pilot->thrust_base += o->u.mod.thrust; pilot->turn_base += o->u.mod.turn; pilot->speed_base += o->u.mod.speed; /* Health. */ pilot->dmg_absorb += o->u.mod.absorb; pilot->armour_max += o->u.mod.armour; pilot->armour_regen += o->u.mod.armour_regen; pilot->shield_max += o->u.mod.shield; pilot->shield_regen += o->u.mod.shield_regen; pilot->energy_max += o->u.mod.energy; pilot->energy_regen += o->u.mod.energy_regen; pilot->energy_loss += o->u.mod.energy_loss; /* Fuel. */ pilot->fuel_max += o->u.mod.fuel; /* Misc. */ pilot->cap_cargo += o->u.mod.cargo; pilot->mass_outfit += o->u.mod.mass_rel * pilot->ship->mass; pilot->crew += o->u.mod.crew_rel * pilot->ship->crew; /* * Stats. */ ss_statsModFromList( s, o->u.mod.stats, &amount ); } else if (outfit_isAfterburner(o)) { /* Afterburner */ pilot_setFlag( pilot, PILOT_AFTERBURNER ); /* We use old school flags for this still... */ pilot->energy_loss += pilot->afterburner->outfit->u.afb.energy; /* energy loss */ } else if (outfit_isJammer(o)) { /* Jammer */ pilot->jamming = 1; pilot->energy_loss += o->u.jam.energy; } } if (!pilot_isFlag( pilot, PILOT_AFTERBURNER )) pilot->solid->speed_max = pilot->speed; /* Slot voodoo. */ s = &pilot->stats; default_s = &pilot->ship->stats_array; /* Fire rate: * amount = p * exp( -0.15 * (n-1) ) * 1x 15% -> 15% * 2x 15% -> 25.82% * 3x 15% -> 33.33% * 6x 15% -> 42.51% */ if (amount.fwd_firerate > 0) { s->fwd_firerate = default_s->fwd_firerate + (s->fwd_firerate-default_s->fwd_firerate) * exp( -0.15 * (double)(MAX(amount.fwd_firerate-1.,0)) ); } /* Cruiser. */ if (amount.tur_firerate > 0) { s->tur_firerate = default_s->tur_firerate + (s->tur_firerate-default_s->tur_firerate) * exp( -0.15 * (double)(MAX(amount.tur_firerate-1.,0)) ); } /* * Electronic warfare setting base parameters. */ s->ew_hide = default_s->ew_hide + (s->ew_hide-default_s->ew_hide) * exp( -0.2 * (double)(MAX(amount.ew_hide-1.,0)) ); s->ew_detect = default_s->ew_detect + (s->ew_detect-default_s->ew_detect) * exp( -0.2 * (double)(MAX(amount.ew_detect-1.,0)) ); s->ew_jump_detect = default_s->ew_jump_detect + (s->ew_jump_detect-default_s->ew_jump_detect) * exp( -0.2 * (double)(MAX(amount.ew_jump_detect-1.,0)) ); /* Square the internal values to speed up comparisons. */ pilot->ew_base_hide = pow2( s->ew_hide ); pilot->ew_detect = pow2( s->ew_detect ); pilot->ew_jump_detect = pow2( s->ew_jump_detect ); /* * Relative increases. */ /* Movement. */ pilot->thrust_base *= s->thrust_mod; pilot->turn_base *= s->turn_mod; pilot->speed_base *= s->speed_mod; /* Health. */ pilot->armour_max *= s->armour_mod; pilot->armour_regen *= s->armour_regen_mod; pilot->shield_max *= s->shield_mod; pilot->shield_regen *= s->shield_regen_mod; pilot->energy_max *= s->energy_mod; pilot->energy_regen *= s->energy_regen_mod; /* cpu */ pilot->cpu_max = (int)floor((float)(pilot->ship->cpu + s->cpu_max)*s->cpu_mod); pilot->cpu += pilot->cpu_max; /* CPU is negative, this just sets it so it's based off of cpu_max. */ /* Misc. */ pilot->dmg_absorb = MAX( 0., pilot->dmg_absorb ); pilot->crew *= s->crew_mod; pilot->cap_cargo *= s->cargo_mod; s->engine_limit *= s->engine_limit_rel; /* * Flat increases. */ pilot->energy_max += s->energy_flat; pilot->energy += s->energy_flat; pilot->energy_regen -= s->energy_usage; /* Give the pilot his health proportion back */ pilot->armour = ac * pilot->armour_max; pilot->shield = sc * pilot->shield_max; pilot->energy = ec * pilot->energy_max; pilot->fuel = fc * pilot->fuel_max; /* Set final energy tau. */ pilot->energy_tau = pilot->energy_max / pilot->energy_regen; /* Cargo has to be reset. */ pilot_cargoCalc(pilot); /* Calculate mass. */ pilot->solid->mass = s->mass_mod*pilot->ship->mass + pilot->stats.cargo_inertia*pilot->mass_cargo + pilot->mass_outfit; /* Calculate the heat. */ pilot_heatCalc( pilot ); /* Modulate by mass. */ pilot_updateMass( pilot ); /* Update GUI as necessary. */ gui_setGeneric( pilot ); }
/** * @brief Opens the map window. */ void map_open (void) { unsigned int wid; StarSystem *cur; int w, h, x, y, rw; /* Not under manual control. */ if (pilot_isFlag( player.p, PILOT_MANUAL_CONTROL )) return; /* Destroy window if exists. */ wid = window_get(MAP_WDWNAME); if (wid > 0) { window_destroy( wid ); return; } /* set position to focus on current system */ map_xpos = cur_system->pos.x; map_ypos = cur_system->pos.y; /* mark systems as needed */ mission_sysMark(); /* Attempt to select current map if none is selected */ if (map_selected == -1) map_selectCur(); /* get the selected system. */ cur = system_getIndex( map_selected ); /* Set up window size. */ w = MAX(600, SCREEN_W - 100); h = MAX(540, SCREEN_H - 100); /* create the window. */ wid = window_create( MAP_WDWNAME, -1, -1, w, h ); window_setCancel( wid, window_close ); window_handleKeys( wid, map_keyHandler ); /* * SIDE TEXT * * $System * * Faction: * $Faction (or Multiple) * * Status: * $Status * * Planets: * $Planet1, $Planet2, ... * * Services: * $Services * * ... * [Autonav] * [ Find ] * [ Close ] */ x = -70; /* Right column X offset. */ y = -20; rw = ABS(x) + 60; /* Right column indented width maximum. */ /* System Name */ window_addText( wid, -90 + 80, y, 160, 20, 1, "txtSysname", &gl_defFont, &cDConsole, cur->name ); y -= 10; /* Faction image */ window_addImage( wid, -90 + 32, y - 32, 0, 0, "imgFaction", NULL, 0 ); y -= 64 + 10; /* Faction */ window_addText( wid, x, y, 90, 20, 0, "txtSFaction", &gl_smallFont, &cDConsole, "Faction:" ); window_addText( wid, x + 50, y-gl_smallFont.h-5, rw, 100, 0, "txtFaction", &gl_smallFont, &cBlack, NULL ); y -= 2 * gl_smallFont.h + 5 + 15; /* Standing */ window_addText( wid, x, y, 90, 20, 0, "txtSStanding", &gl_smallFont, &cDConsole, "Standing:" ); window_addText( wid, x + 50, y-gl_smallFont.h-5, rw, 100, 0, "txtStanding", &gl_smallFont, &cBlack, NULL ); y -= 2 * gl_smallFont.h + 5 + 15; /* Presence. */ window_addText( wid, x, y, 90, 20, 0, "txtSPresence", &gl_smallFont, &cDConsole, "Presence:" ); window_addText( wid, x + 50, y-gl_smallFont.h-5, rw, 100, 0, "txtPresence", &gl_smallFont, &cBlack, NULL ); y -= 2 * gl_smallFont.h + 5 + 15; /* Planets */ window_addText( wid, x, y, 90, 20, 0, "txtSPlanets", &gl_smallFont, &cDConsole, "Planets:" ); window_addText( wid, x + 50, y-gl_smallFont.h-5, rw, 150, 0, "txtPlanets", &gl_smallFont, &cBlack, NULL ); y -= 2 * gl_smallFont.h + 5 + 15; /* Services */ window_addText( wid, x, y, 90, 20, 0, "txtSServices", &gl_smallFont, &cDConsole, "Services:" ); window_addText( wid, x + 50, y-gl_smallFont.h-5, rw, 100, 0, "txtServices", &gl_smallFont, &cBlack, NULL ); y -= 2 * gl_smallFont.h + 5 + 15; /* Close button */ window_addButton( wid, -20, 20, BUTTON_WIDTH, BUTTON_HEIGHT, "btnClose", "Close", window_close ); /* Find button */ window_addButton( wid, -20 - (BUTTON_WIDTH+20), 20, BUTTON_WIDTH, BUTTON_HEIGHT, "btnFind", "Find", map_inputFind ); /* Autonav button */ window_addButton( wid, -20 - 2*(BUTTON_WIDTH+20), 20, BUTTON_WIDTH, BUTTON_HEIGHT, "btnAutonav", "Autonav", player_autonavStartWindow ); /* * Bottom stuff * * [+] [-] Nebula, Interference */ /* Zoom buttons */ window_addButton( wid, 40, 20, 30, 30, "btnZoomIn", "+", map_buttonZoom ); window_addButton( wid, 80, 20, 30, 30, "btnZoomOut", "-", map_buttonZoom ); /* Situation text */ window_addText( wid, 140, 10, w - 80 - 30 - 30, 30, 0, "txtSystemStatus", &gl_smallFont, &cBlack, NULL ); /* * The map itself. */ map_show( wid, 20, -40, w-200, h-100, 1. ); /* Reset zoom. */ map_update( wid ); /* * Disable Autonav button if player lacks fuel. */ if ((player.p->fuel < HYPERSPACE_FUEL) || pilot_isFlag( player.p, PILOT_NOJUMP)) window_disableButton( wid, "btnAutonav" ); }