/** * @brief Updates the camera zoom. */ static void cam_updatePilotZoom( Pilot *follow, Pilot *target, double dt ) { double d, x,y, z,tz, dx, dy; double zfar, znear; double c; /* Must have auto zoom enabled. */ if (conf.zoom_manual) return; /* Minimum depends on velocity normally. * * w*h = A, cte area constant * w/h = K, cte proportion constant * d^2 = A, cte geometric longitud * * A_v = A*(1+v/d) area of view is based on speed * A_v / A = (1 + v/d) * * z = A / A_v = 1. / (1 + v/d) */ d = sqrt(SCREEN_W*SCREEN_H); znear = MIN( conf.zoom_near, 1. / (0.8 + VMOD(follow->solid->vel)/d) ); /* Maximum is limited by nebulae. */ if (cur_system->nebu_density > 0.) { c = MIN( SCREEN_W, SCREEN_H ) / 2; zfar = CLAMP( conf.zoom_far, conf.zoom_near, c / nebu_getSightRadius() ); } else zfar = conf.zoom_far; znear = MAX( znear, zfar ); /* * Set Zoom to pilot target. */ z = cam_getZoom(); if (target != NULL) { /* Get current relative target position. */ gui_getOffset( &x, &y ); x += target->solid->pos.x - follow->solid->pos.x; y += target->solid->pos.y - follow->solid->pos.y; /* Get distance ratio. */ dx = (SCREEN_W/2.) / (FABS(x) + 2*target->ship->gfx_space->sw); dy = (SCREEN_H/2.) / (FABS(y) + 2*target->ship->gfx_space->sh); /* Get zoom. */ tz = MIN( dx, dy ); } else tz = znear; /* Aim at in. */ /* Gradually zoom in/out. */ d = CLAMP(-conf.zoom_speed, conf.zoom_speed, tz - z); d *= dt / dt_mod; /* Remove dt dependence. */ if (d < 0) /** Speed up if needed. */ d *= 2.; camera_Z = CLAMP( zfar, znear, z + d); }
/** * @brief Blits a sprite interpolating, position is relative to the player. * * Since position is in "game coordinates" it is subject to all * sorts of position transformations. * * Interpolation is: sa*inter + sb*1.-inter) * * @param sa Sprite A to blit. * @param sb Sprite B to blit. * @param inter Amount to interpolate. * @param bx X position of the texture relative to the player. * @param by Y position of the texture relative to the player. * @param sx X position of the sprite to use. * @param sy Y position of the sprite to use. * @param c Colour to use (modifies texture colour). */ void gl_blitSpriteInterpolate( const glTexture* sa, const glTexture *sb, double inter, const double bx, const double by, const int sx, const int sy, const glColour *c ) { double x,y, w,h, tx,ty, cx,cy, gx,gy; /* Get parameters. */ gl_cameraGet( &cx, &cy ); gui_getOffset( &gx, &gy ); /* calculate position - we'll use relative coords to player */ x = (bx - cx - sa->sw/2. + gx) * gl_cameraZ; y = (by - cy - sa->sh/2. + gy) * gl_cameraZ; /* Scaled sprite dimensions. */ w = sa->sw*gl_cameraZ; h = sa->sh*gl_cameraZ; /* check if inbounds */ if ((fabs(x) > SCREEN_W/2 + w) || (fabs(y) > SCREEN_H/2 + h) ) return; /* texture coords */ tx = sa->sw*(double)(sx)/sa->rw; ty = sa->sh*(sa->sy-(double)sy-1)/sa->rh; gl_blitTextureInterpolate( sa, sb, inter, x, y, w, h, tx, ty, sa->srw, sa->srh, c ); }
/** * @brief Converts screen coordinates to ingame coordinates. * * @param[out] nx New ingame X coord. * @param[out] ny New ingame Y coord. * @param bx Screen X coord to translate. * @param by Screen Y coord to translate. */ void gl_screenToGameCoords( double *nx, double *ny, int bx, int by ) { double cx,cy, gx,gy, z; /* Get parameters. */ cam_getPos( &cx, &cy ); z = cam_getZoom(); gui_getOffset( &gx, &gy ); /* calculate position - we'll use relative coords to player */ *nx = (bx - SCREEN_W/2. - gx) / z + cx; *ny = (by - SCREEN_H/2. - gy) / z + cy; }
/** * @brief Converts ingame coordinates to screen coordinates. * * @param[out] nx New screen X coord. * @param[out] ny New screen Y coord. * @param bx Game X coord to translate. * @param by Game Y coord to translate. */ void gl_gameToScreenCoords( double *nx, double *ny, double bx, double by ) { double cx,cy, gx,gy, z; /* Get parameters. */ cam_getPos( &cx, &cy ); z = cam_getZoom(); gui_getOffset( &gx, &gy ); /* calculate position - we'll use relative coords to player */ *nx = (bx - cx) * z + gx + SCREEN_W/2.; *ny = (by - cy) * z + gy + SCREEN_H/2.; }
/** * @brief Renders the nebula overlay (hides what player can't see). * * @param dt Current delta tick. */ void nebu_renderOverlay( const double dt ) { (void) dt; double gx, gy; double ox, oy; double z; double sx, sy; /* Get GUI offsets. */ gui_getOffset( &gx, &gy ); /* Get zoom. */ z = cam_getZoom(); /* * Renders the puffs */ nebu_renderPuffs( 0 ); /* Prepare the matrix */ ox = gx; oy = gy; spfx_getShake( &sx, &sy ); ox += sx; oy += sy; gl_matrixPush(); gl_matrixTranslate( SCREEN_W/2.+ox, SCREEN_H/2.+oy ); gl_matrixScale( z, z ); /* * Mask for area player can still see (partially) */ glShadeModel(GL_SMOOTH); gl_vboActivateOffset( nebu_vboOverlay, GL_VERTEX_ARRAY, 0, 2, GL_FLOAT, 0 ); gl_vboActivateOffset( nebu_vboOverlay, GL_COLOR_ARRAY, sizeof(GLfloat)*2*18, 4, GL_FLOAT, 0 ); glDrawArrays( GL_TRIANGLE_FAN, 0, 18 ); /* * Solid nebula for areas the player can't see */ glShadeModel(GL_FLAT); /* Colour is shared. */ gl_vboActivateOffset( nebu_vboOverlay, GL_COLOR_ARRAY, sizeof(GLfloat)*((2+4)*18 + 2*28), 4, GL_FLOAT, 0 ); /* Top left. */ gl_vboActivateOffset( nebu_vboOverlay, GL_VERTEX_ARRAY, sizeof(GLfloat)*((2+4)*18 + 0*2*7), 2, GL_FLOAT, 0 ); glDrawArrays( GL_TRIANGLE_FAN, 0, 7 ); /* Top right. */ gl_vboActivateOffset( nebu_vboOverlay, GL_VERTEX_ARRAY, sizeof(GLfloat)*((2+4)*18 + 1*2*7), 2, GL_FLOAT, 0 ); glDrawArrays( GL_TRIANGLE_FAN, 0, 7 ); /* Bottom right. */ gl_vboActivateOffset( nebu_vboOverlay, GL_VERTEX_ARRAY, sizeof(GLfloat)*((2+4)*18 + 2*2*7), 2, GL_FLOAT, 0 ); glDrawArrays( GL_TRIANGLE_FAN, 0, 7 ); /* Bottom left. */ gl_vboActivateOffset( nebu_vboOverlay, GL_VERTEX_ARRAY, sizeof(GLfloat)*((2+4)*18 + 3*2*7), 2, GL_FLOAT, 0 ); glDrawArrays( GL_TRIANGLE_FAN, 0, 7 ); gl_vboDeactivate(); gl_matrixPop(); /* Reset puff movement. */ puff_x = 0.; puff_y = 0.; gl_checkErr(); }
/** * @brief Regenerates the overlay. */ void nebu_genOverlay (void) { int i; GLfloat *data; double a; double gx, gy; double z; /* Get GUI offsets. */ gui_getOffset( &gx, &gy ); /* Here we calculate outer corners. It should actually be /2. because we * are centered around 0,0. However, we treat this extra space as a buffer * for when it's shaking. */ z = 1./conf.zoom_far; /* See if need to generate overlay. */ if (nebu_vboOverlay == NULL) { nebu_vboOverlay = gl_vboCreateStatic( sizeof(GLfloat) * ((2+4)*18 + 2*28 + 4*7), NULL ); /* Set colors, those will be pure static. */ data = gl_vboMap( nebu_vboOverlay ); /* Alpha overlay. */ for (i=0; i<18; i++) { data[2*18 + 4*i + 0] = cDarkBlue.r; data[2*18 + 4*i + 1] = cDarkBlue.g; data[2*18 + 4*i + 2] = cDarkBlue.b; data[2*18 + 4*i + 3] = cDarkBlue.a; } data[2*18 + 3] = 0.; /* Origin is transparent. */ /* Solid overlay. */ for (i=0; i<7; i++) { data[(2+4)*18 + 2*28 + 4*i + 0] = cDarkBlue.r; data[(2+4)*18 + 2*28 + 4*i + 1] = cDarkBlue.g; data[(2+4)*18 + 2*28 + 4*i + 2] = cDarkBlue.b; data[(2+4)*18 + 2*28 + 4*i + 3] = cDarkBlue.a; } gl_vboUnmap( nebu_vboOverlay ); } /* Generate the main chunk. */ data = gl_vboMap( nebu_vboOverlay ); /* Main chunk. */ data[0] = 0.; data[1] = 0.; for (i=0; i<17; i++) { a = M_PI*2./16. * (double)i; data[2*(i+1) + 0] = nebu_view * cos(a); data[2*(i+1) + 1] = nebu_view * sin(a); } /* Top Left */ data[(2+4)*18+0] = -SCREEN_W*z-gx; data[(2+4)*18+1] = SCREEN_H*z-gy; data[(2+4)*18+2] = -nebu_view; data[(2+4)*18+3] = 0.; data[(2+4)*18+4] = -nebu_view*COS225; data[(2+4)*18+5] = nebu_view*SIN225; data[(2+4)*18+6] = -nebu_view*ANG45; data[(2+4)*18+7] = nebu_view*ANG45; data[(2+4)*18+8] = -nebu_view*SIN225; data[(2+4)*18+9] = nebu_view*COS225; data[(2+4)*18+10] = 0.; data[(2+4)*18+11] = nebu_view; data[(2+4)*18+12] = SCREEN_W*z-gx; data[(2+4)*18+13] = SCREEN_H*z-gy; /* Top Right */ data[(2+4)*18+14] = SCREEN_W*z-gx; data[(2+4)*18+15] = SCREEN_H*z-gy; data[(2+4)*18+16] = 0.; data[(2+4)*18+17] = nebu_view; data[(2+4)*18+18] = nebu_view*SIN225; data[(2+4)*18+19] = nebu_view*COS225; data[(2+4)*18+20] = nebu_view*ANG45; data[(2+4)*18+21] = nebu_view*ANG45; data[(2+4)*18+22] = nebu_view*COS225; data[(2+4)*18+23] = nebu_view*SIN225; data[(2+4)*18+24] = nebu_view; data[(2+4)*18+25] = 0.; data[(2+4)*18+26] = SCREEN_W*z-gx; data[(2+4)*18+27] = -SCREEN_H*z-gy; /* Bottom Right */ data[(2+4)*18+28] = SCREEN_W*z-gx; data[(2+4)*18+29] = -SCREEN_H*z-gy; data[(2+4)*18+30] = nebu_view; data[(2+4)*18+31] = 0.; data[(2+4)*18+32] = nebu_view*COS225; data[(2+4)*18+33] = -nebu_view*SIN225; data[(2+4)*18+34] = nebu_view*ANG45; data[(2+4)*18+35] = -nebu_view*ANG45; data[(2+4)*18+36] = nebu_view*SIN225; data[(2+4)*18+37] = -nebu_view*COS225; data[(2+4)*18+38] = 0.; data[(2+4)*18+39] = -nebu_view; data[(2+4)*18+40] = -SCREEN_W*z-gx; data[(2+4)*18+41] = -SCREEN_H*z-gy; /* Bottom left */ data[(2+4)*18+42] = -SCREEN_W*z-gx; data[(2+4)*18+43] = -SCREEN_H*z-gy; data[(2+4)*18+44] = 0.; data[(2+4)*18+45] = -nebu_view; data[(2+4)*18+46] = -nebu_view*SIN225; data[(2+4)*18+47] = -nebu_view*COS225; data[(2+4)*18+48] = -nebu_view*ANG45; data[(2+4)*18+49] = -nebu_view*ANG45; data[(2+4)*18+50] = -nebu_view*COS225; data[(2+4)*18+51] = -nebu_view*SIN225; data[(2+4)*18+52] = -nebu_view; data[(2+4)*18+53] = 0.; data[(2+4)*18+54] = -SCREEN_W*z-gx; data[(2+4)*18+55] = SCREEN_H*z-gy; gl_vboUnmap( nebu_vboOverlay ); }
/** * @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; }