bool Wall::clippoint (vect2d npt, double rad, vect2d* clip, vect2d* hitpt, double *oprio, double surfaceepsilon) { double distm=-1; double prio=0; // Transform the point into the coordinate space of the wall. (so // that the wall becomes <0, 0> to <len, 0> vect2d tp(j * (npt-v1), ~j * (npt-v1)); vect2d ret; if (tp.x >= 0 && tp.x <= len && tp.y <= rad && tp.y >= -rad) { ret.x=tp.x; if (tp.y > 0) { ret.y = rad; distm = rad-tp.y; } else { ret.y = -rad; distm = rad+tp.y; } prio=rad*1000; } else { double ln2 = vlen2(tp); double r2=rad*rad; if (ln2 < r2) { double ln = sqrt (ln2); distm = rad-ln; ret = tp * (rad / ln); } else { tp.x-=len; ln2=vlen2(tp); if (ln2<r2) { double ln = sqrt (ln2); distm = rad-ln; ret = tp * (rad / ln); ret.x += len; } } } // If the circle intersected, and clipx is not null, transform the // point back to normal coordinate space. if (distm>EPSILON) { if (clip) *clip = v1 + j*ret.x + ~j*ret.y; if (ret.x < 0) ret.x=0; else if (ret.x > len) ret.x=len; if (hitpt) *hitpt = v1 + j*ret.x; if (oprio) *oprio=distm+prio; return true; } return false; }
double Plane::getzbound(Area* limit, vect2d pt, double rad, double height, bool clipsolid) { PROBEGIN(checkz_fast); PROBEGIN(checkz_slow); if (slope==0 && !clipsolid) { // Special case: slope is zero, so bound is z [+-] height. return cpt.z+(ceil?-height:height); } if (rad == 0) { return getz(pt)+(ceil?-height:height); } // Calculate the highest (or lowest) point where the cylinder // intersects with the plane. This is always <px, py> [+-] <ix, iy>*rad vect2d tpt = pt; if ((slope>0) == ceil) { tpt -= dir*rad; } else { tpt += dir*rad; } // If the limit area is not specified, or <tpx, tpy> is in the // area, simply return the value of the plane at that point [+-] height. if ((limit == NULL) || limit->pointin(tpt)) { PROEND(checkz_fast); double ret = getz(tpt); if (ceil) { ret -= height; } else { ret += height; } return ret; } // Ok, so the point is not in the area. So, we have to find some // other point to be the (max|min)imum. This point will either be // 1. a vertex of the area, or // 2. a point along one of the walls. // // We start out with the value at [+-]infinity, and then check all // possible points. We loop through the walls, checking the vertex // if the vertex is inside the circle, then check the points where // the circle intersects the wall. double cz = (ceil?INFINITY:-INFINITY); #define ckz(pt) { \ double tz=getz(pt) + (ceil?-height:height); \ if (ceil == (tz<cz)) cz = tz; \ } double rad2 = sqr(rad); FOREACHW(w, limit) { double a, ta; // Check the vertex. if (vlen2(w->v1-pt) < rad2) { ckz(w->v1); } // Transform the point to wall coordinate space. double doti = ~w->j*(pt-w->v1); double dotj = w->j*(pt-w->v1); // Circle does not intersect. if (fabs(doti)>rad+EPSILON) continue; // Check intersection points. a = sqrt(rad2-sqr(doti)); if (isnan(a)) a = 0; ta = dotj+a; if (ta >= 0 && ta <= w->len) { ckz(w->v1+w->j*ta); } ta = dotj-a; if (ta >= 0 && ta <= w->len) { ckz(w->v1+w->j*ta); } }
//----------------------------------------------------------------------------- void RunGame() { // Control main ship if (g_gs == GS_VICTORY || g_gs == GS_STARTING) { if (MAIN_SHIP.vel.y < SHIP_CRUISE_SPEED) { MAIN_SHIP.vel.y = SAFEADD(MAIN_SHIP.vel.y, SHIP_INC_SPEED, SHIP_CRUISE_SPEED); } MAIN_SHIP.fuel = SAFESUB(MAIN_SHIP.fuel, FRAME_FUEL_COST); } // Heal main ship if (g_gs != GS_DYING) { if (MAIN_SHIP.energy < MAX_ENERGY && MAIN_SHIP.fuel > MIN_FUEL_FOR_HEAL) { MAIN_SHIP.energy = SAFEADD(MAIN_SHIP.energy, ENERGY_HEAL_PER_FRAME, MAX_ENERGY); MAIN_SHIP.fuel = SAFESUB(MAIN_SHIP.fuel, FUEL_HEAL_PER_FRAME); LOG(("- energy: %f, fuel: %f\n", MAIN_SHIP.energy, MAIN_SHIP.fuel)); } } // Move entities for (int i = MAX_ENTITIES - 1; i >= 0; i--) { if (g_entities[i].type != E_NULL) { g_entities[i].pos = vadd(g_entities[i].pos, g_entities[i].vel); // Remove entities that fell off screen if (g_entities[i].pos.y < g_camera_offset - G_HEIGHT) g_entities[i].type = E_NULL; } } // Advance "stars" for (int i = 0; i < MAX_ENTITIES; i++) { if (g_entities[i].type == E_STAR) g_entities[i].gfxscale *= 1.008f; } // Dont let steering off the screen if (MAIN_SHIP.pos.x < MAINSHIP_RADIUS) MAIN_SHIP.pos.x = MAINSHIP_RADIUS; if (MAIN_SHIP.pos.x > G_WIDTH - MAINSHIP_RADIUS) MAIN_SHIP.pos.x = G_WIDTH - MAINSHIP_RADIUS; // Check collisions if (g_gs == GS_PLAYING) { // Check everything against ship for (int i = 1; i < MAX_ENTITIES; i++) { // Should check against ship? if (g_entities[i].type == E_ROCK || g_entities[i].type == E_JUICE || g_entities[i].type == E_MINE || g_entities[i].type == E_DRONE) { float distance = vlen2(vsub(g_entities[i].pos, MAIN_SHIP.pos)); // Distance from object to ship float crash_distance = CORE_FSquare(g_entities[i].radius + MAIN_SHIP.radius); // Minimum allowed distance before crash if (distance < crash_distance) { switch (g_entities[i].type) { case E_ROCK: if (g_entities[i].energy > 0) { MAIN_SHIP.energy = SAFESUB(MAIN_SHIP.energy, ROCK_CRASH_ENERGY_LOSS); MAIN_SHIP.vel.y = SHIP_START_SPEED; // Set rock velocity vec2 vel_direction = vsub(g_entities[i].pos, MAIN_SHIP.pos); // direction of rock velocity, away from ship vec2 normalized_vel_direction = vunit(vel_direction); // normalize vec2 vel = vscale(normalized_vel_direction, CRASH_VEL); // Scale, ie give the rock correct speed. g_entities[i].vel = vel; g_entities[i].energy = 0; } break; case E_JUICE: MAIN_SHIP.fuel = SAFEADD(MAIN_SHIP.fuel, JUICE_FUEL, MAX_FUEL); g_entities[i].type = E_NULL; break; case E_MINE: MAIN_SHIP.energy = SAFESUB(MAIN_SHIP.energy, MINE_CRASH_ENERGY_LOSS); MAIN_SHIP.vel.y = SHIP_START_SPEED; g_entities[i].type = E_NULL; break; case E_DRONE: MAIN_SHIP.energy = SAFESUB(MAIN_SHIP.energy, MINE_CRASH_ENERGY_LOSS); MAIN_SHIP.vel.y = SHIP_START_SPEED; g_entities[i].type = E_NULL; break; default: break; } } } else if (g_entities[i].type == E_ROCKET) { // Check all hit-able objects against this rocket for (int j = 1; i < MAX_ENTITIES; j++) { // Should check against rocket? if (g_entities[j].type == E_ROCK || g_entities[j].type == E_MINE || g_entities[j].type == E_DRONE) { float distance = vlen2(vsub(g_entities[i].pos, g_entities[j].pos)); float crash_distance = CORE_FSquare(g_entities[i].radius + g_entities[j].radius); if (distance < crash_distance) { // Impact! g_entities[i].type = E_NULL; g_entities[j].type = E_NULL; break; } } } } } } // Generate new level elements as we advance GenNextElements(); // Possibly insert new juice if (g_gs == GS_PLAYING) { float trench = MAIN_SHIP.pos.y - g_current_race_pos; // How much advanced from previous frame if (CORE_RandChance(trench * JUICE_CHANCE_PER_PIXEL)) { vec2 pos = vmake(CORE_FRand(0.f, G_WIDTH), g_camera_offset + G_HEIGHT + GEN_IN_ADVANCE); // Random x, insert 400y above window vec2 vel = vmake(CORE_FRand(-1.f, +1.f), CORE_FRand(-1.f, +1.f)); // Random small velocity to make rocks "float" InsertEntity(E_JUICE, pos, vel, JUICE_RADIUS, g_juice, false, true); } } // Set camera to follow the main ship g_camera_offset = MAIN_SHIP.pos.y - G_HEIGHT / 8.f; g_current_race_pos = MAIN_SHIP.pos.y; if (g_gs == GS_PLAYING) { if (g_current_race_pos >= RACE_END) // Check if victory { g_gs = GS_VICTORY; g_gs_timer = 0.f; MAIN_SHIP.gfxadditive = true; } } // Advance game mode g_gs_timer += FRAMETIME; switch (g_gs) { case GS_STARTING: if (g_gs_timer >= STARTING_TIME) // Start delay before starting to play { g_gs = GS_PLAYING; g_gs_timer = 0.f; } break; case GS_DYING: if (g_gs_timer >= DYING_TIME) { ResetNewGame(); } break; case GS_PLAYING: if (MAIN_SHIP.energy <= 0.f || MAIN_SHIP.fuel <= 0.f) // No energy or fuel --> die { g_gs = GS_DYING; g_gs_timer = 0.f; MAIN_SHIP.gfx = g_ship_RR; } break; case GS_VICTORY: if (CORE_RandChance(1.f / 10.f)) { InsertEntity(E_STAR, MAIN_SHIP.pos, vadd(MAIN_SHIP.vel, vmake(CORE_FRand(-5.f, 5.f), CORE_FRand(-5.f, 5.f))), 0, g_star, false, true); } if (g_gs_timer >= 8.f) // Should use VICTORY_TIME, but stupid VS dont want me to... { ResetNewGame(); } break; } g_time_from_last_rocket += FRAMETIME; }