|  | @@ -7,6 +7,16 @@
 | 
	
		
			
				|  |  |  struct Line final {
 | 
	
		
			
				|  |  |      Vector2 a;
 | 
	
		
			
				|  |  |      Vector2 b;
 | 
	
		
			
				|  |  | +    Color4 color;
 | 
	
		
			
				|  |  | +    float friction;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    Line(const Vector2& a, const Vector2& b, Color4 color, float friction)
 | 
	
		
			
				|  |  | +        : a(a), b(b), color(color), friction(friction) {
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    Line(const Vector2& a, const Vector2& b)
 | 
	
		
			
				|  |  | +        : a(a), b(b), color(Color4(0xFF, 0xFF, 0xFF, 0xFF)), friction(0.0f) {
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static List<Line> lines;
 | 
	
	
		
			
				|  | @@ -26,12 +36,18 @@ static float mass = 5.0f;
 | 
	
		
			
				|  |  |  static bool onGround = false;
 | 
	
		
			
				|  |  |  static float jumpPower = 0.0f;
 | 
	
		
			
				|  |  |  static float steepness = 0.0f;
 | 
	
		
			
				|  |  | +static float friction = 0.0f;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static bool physicsToggle = true;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static Vector2 waterPosition;
 | 
	
		
			
				|  |  |  static Vector2 waterSize;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +static Vector2 projectileLastPosition;
 | 
	
		
			
				|  |  | +static Vector2 projectilePosition;
 | 
	
		
			
				|  |  | +static Vector2 projectileVelocity;
 | 
	
		
			
				|  |  | +static Vector2 projectileSize{20.0f, 20.0f};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  static void addForce(const Vector2& force) {
 | 
	
		
			
				|  |  |      acceleration += force / mass;
 | 
	
		
			
				|  |  |  }
 | 
	
	
		
			
				|  | @@ -130,7 +146,7 @@ static bool doesPlayerCollide() {
 | 
	
		
			
				|  |  |      return false;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -static float slopeSteepness() {
 | 
	
		
			
				|  |  | +static void slopeSteepnessAndFriction() {
 | 
	
		
			
				|  |  |      Line left{position - Vector2(0.0f, 10.0f * COLLISION_STEP),
 | 
	
		
			
				|  |  |                position + Vector2(0.0f, size[1])};
 | 
	
		
			
				|  |  |      Line right{position + Vector2(size[0], -10.0f * COLLISION_STEP),
 | 
	
	
		
			
				|  | @@ -143,10 +159,11 @@ static float slopeSteepness() {
 | 
	
		
			
				|  |  |              if(std::abs(f) < min) {
 | 
	
		
			
				|  |  |                  min = std::abs(f);
 | 
	
		
			
				|  |  |                  value = f;
 | 
	
		
			
				|  |  | +                friction = line.friction;
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -    return value;
 | 
	
		
			
				|  |  | +    steepness = value;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static void applyDrag() {
 | 
	
	
		
			
				|  | @@ -175,6 +192,69 @@ static void handleJump() {
 | 
	
		
			
				|  |  |      jumpPower *= Controller::a.isDown() && (velocity[1] > 0.0f || justJumped);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +static void relaunchProjectile() {
 | 
	
		
			
				|  |  | +    if(projectilePosition[1] >= 0.0f &&
 | 
	
		
			
				|  |  | +       !isIn(position, size, projectilePosition, projectileSize)) {
 | 
	
		
			
				|  |  | +        return;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    projectilePosition = windowSize * Vector2(0.0f, 0.5f);
 | 
	
		
			
				|  |  | +    projectileLastPosition = projectilePosition;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    Vector2 start = projectilePosition;
 | 
	
		
			
				|  |  | +    Vector2 end = position + size * 0.5f;
 | 
	
		
			
				|  |  | +    constexpr float strengh = 25.0f;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    float best = 100000000.0f;
 | 
	
		
			
				|  |  | +    float bestRad = 0.0f;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    for(float angle = -30.0f; angle < 90.0f; angle += 0.25f) {
 | 
	
		
			
				|  |  | +        float rad = angle * 0.017453293f;
 | 
	
		
			
				|  |  | +        Vector2 v(strengh * cosf(rad), strengh * sinf(rad));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        float t = (end[0] - start[0]) / v[0];
 | 
	
		
			
				|  |  | +        float y = v[1] * t - GRAVITY * t * t * 0.5f + start[1];
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        float diff = std::abs(y - end[1]);
 | 
	
		
			
				|  |  | +        if(diff < best) {
 | 
	
		
			
				|  |  | +            bestRad = rad;
 | 
	
		
			
				|  |  | +            best = diff;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    projectileVelocity =
 | 
	
		
			
				|  |  | +        Vector2(strengh * cosf(bestRad), strengh * sinf(bestRad));
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static float sign(float f) {
 | 
	
		
			
				|  |  | +    return (f > 0.0f) - (f < 0.0f);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void applyFriction() {
 | 
	
		
			
				|  |  | +    if(!onGround) {
 | 
	
		
			
				|  |  | +        return;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    float rad = atanf(steepness);
 | 
	
		
			
				|  |  | +    float c = cosf(rad);
 | 
	
		
			
				|  |  | +    float fnLength = mass * GRAVITY * c;
 | 
	
		
			
				|  |  | +    Vector2 fn(fnLength * c, fnLength * sinf(rad)); // already swapt
 | 
	
		
			
				|  |  | +    Vector2 fullFriction = fn * friction;
 | 
	
		
			
				|  |  | +    fullFriction =
 | 
	
		
			
				|  |  | +        Vector2(std::abs(fullFriction[0]), std::abs(fullFriction[1]));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    float signX = sign(acceleration[0]);
 | 
	
		
			
				|  |  | +    float signY = sign(acceleration[1]);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    fullFriction *= -Vector2(signX, signY);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    addForce(fullFriction);
 | 
	
		
			
				|  |  | +    if(sign(acceleration[0]) != signX) {
 | 
	
		
			
				|  |  | +        acceleration[0] = 0.0f;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    if(sign(acceleration[1]) != signY) {
 | 
	
		
			
				|  |  | +        acceleration[1] = 0.0f;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  void Game::tick() {
 | 
	
		
			
				|  |  |      windowSize = Vector2(static_cast<float>(Engine::getSize().width),
 | 
	
		
			
				|  |  |                           static_cast<float>(Engine::getSize().height));
 | 
	
	
		
			
				|  | @@ -184,6 +264,7 @@ void Game::tick() {
 | 
	
		
			
				|  |  |      applyGravity();
 | 
	
		
			
				|  |  |      addMovement();
 | 
	
		
			
				|  |  |      handleJump();
 | 
	
		
			
				|  |  | +    applyFriction();
 | 
	
		
			
				|  |  |      applyDrag();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      if(steepness != 100.0f) {
 | 
	
	
		
			
				|  | @@ -205,7 +286,40 @@ void Game::tick() {
 | 
	
		
			
				|  |  |      lines.add({{0.0f, 90.0f}, {200.0f, 0.0f}});
 | 
	
		
			
				|  |  |      lines.add({{0.0f, 120.0f}, {100.0f, 0.0f}});
 | 
	
		
			
				|  |  |      lines.add({{100.0f, 180.0f}, {200.0f, 180.0f}});
 | 
	
		
			
				|  |  | -    lines.add({{200.0f, 180.0f}, {600.0f, 380.0f}});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    lines.add({{200.0f, 180.0f},
 | 
	
		
			
				|  |  | +               {300.0f, 230.0f},
 | 
	
		
			
				|  |  | +               Color4(0xF0, 0x00, 0x00, 0xFF),
 | 
	
		
			
				|  |  | +               0.2f});
 | 
	
		
			
				|  |  | +    lines.add({{300.0f, 230.0f},
 | 
	
		
			
				|  |  | +               {400.0f, 280.0f},
 | 
	
		
			
				|  |  | +               Color4(0xC0, 0x00, 0x00, 0xFF),
 | 
	
		
			
				|  |  | +               0.4f});
 | 
	
		
			
				|  |  | +    lines.add({{400.0f, 280.0f},
 | 
	
		
			
				|  |  | +               {500.0f, 330.0f},
 | 
	
		
			
				|  |  | +               Color4(0x90, 0x00, 0x00, 0xFF),
 | 
	
		
			
				|  |  | +               0.6f});
 | 
	
		
			
				|  |  | +    lines.add({{500.0f, 330.0f},
 | 
	
		
			
				|  |  | +               {600.0f, 380.0f},
 | 
	
		
			
				|  |  | +               Color4(0x60, 0x00, 0x00, 0xFF),
 | 
	
		
			
				|  |  | +               0.8f});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    lines.add({{300.0f, 140.0f},
 | 
	
		
			
				|  |  | +               {400.0f, 200.0f},
 | 
	
		
			
				|  |  | +               Color4(0xF0, 0x00, 0x00, 0xFF),
 | 
	
		
			
				|  |  | +               0.2f});
 | 
	
		
			
				|  |  | +    lines.add({{400.0f, 200.0f},
 | 
	
		
			
				|  |  | +               {500.0f, 220.0f},
 | 
	
		
			
				|  |  | +               Color4(0xC0, 0x00, 0x00, 0xFF),
 | 
	
		
			
				|  |  | +               0.4f});
 | 
	
		
			
				|  |  | +    lines.add({{500.0f, 220.0f},
 | 
	
		
			
				|  |  | +               {600.0f, 240.0f},
 | 
	
		
			
				|  |  | +               Color4(0x90, 0x00, 0x00, 0xFF),
 | 
	
		
			
				|  |  | +               0.6f});
 | 
	
		
			
				|  |  | +    lines.add({{600.0f, 240.0f},
 | 
	
		
			
				|  |  | +               {700.0f, 260.0f},
 | 
	
		
			
				|  |  | +               Color4(0x60, 0x00, 0x00, 0xFF),
 | 
	
		
			
				|  |  | +               0.8f});
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      velocity += acceleration;
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -241,11 +355,20 @@ void Game::tick() {
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -    steepness = slopeSteepness();
 | 
	
		
			
				|  |  | +    float oldSteepness = steepness;
 | 
	
		
			
				|  |  | +    slopeSteepnessAndFriction();
 | 
	
		
			
				|  |  |      onGround = std::abs(steepness) < 1.0f;
 | 
	
		
			
				|  |  |      if(onGround) {
 | 
	
		
			
				|  |  |          velocity[1] = 0.0f;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | +    if(steepness == 100.0f) {
 | 
	
		
			
				|  |  | +        steepness = oldSteepness;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    projectileLastPosition = projectilePosition;
 | 
	
		
			
				|  |  | +    projectileVelocity -= Vector2(0.0f, GRAVITY);
 | 
	
		
			
				|  |  | +    projectilePosition += projectileVelocity;
 | 
	
		
			
				|  |  | +    relaunchProjectile();
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  void Game::render(float lag, Renderer& r) {
 | 
	
	
		
			
				|  | @@ -255,8 +378,22 @@ void Game::render(float lag, Renderer& r) {
 | 
	
		
			
				|  |  |          .update();
 | 
	
		
			
				|  |  |      r.drawRectangle(waterPosition, waterSize, Color4(0x00, 0x00, 0xFF, 0xFF));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    Vector2 pos = Utils::interpolate(lastPosition, position, lag);
 | 
	
		
			
				|  |  | -    r.drawRectangle(pos, size, Color4(0xFF, 0x00, 0x00, 0xFF));
 | 
	
		
			
				|  |  | +    r.push();
 | 
	
		
			
				|  |  | +    r.translateTo(0.0f, 0.0f)
 | 
	
		
			
				|  |  | +        .translate(-size * 0.5f)
 | 
	
		
			
				|  |  | +        .rotate(atanf(steepness) * 180.0f / M_PI)
 | 
	
		
			
				|  |  | +        .translate(Utils::interpolate(lastPosition, position, lag))
 | 
	
		
			
				|  |  | +        .translate(size * 0.5f)
 | 
	
		
			
				|  |  | +        .scale(1.0f, -1.0f)
 | 
	
		
			
				|  |  | +        .translateY(Engine::getSize().height)
 | 
	
		
			
				|  |  | +        .update();
 | 
	
		
			
				|  |  | +    r.drawRectangle(Vector2(), size, Color4(0xFF, 0x00, 0x00, 0xFF));
 | 
	
		
			
				|  |  | +    r.pop();
 | 
	
		
			
				|  |  | +    r.update();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    r.drawRectangle(
 | 
	
		
			
				|  |  | +        Utils::interpolate(projectileLastPosition, projectilePosition, lag),
 | 
	
		
			
				|  |  | +        projectileSize, Color4(0xFF, 0xFF, 0x00, 0xFF));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      for(const Line& line : lines) {
 | 
	
		
			
				|  |  |          Vector2 dir = line.b - line.a;
 | 
	
	
		
			
				|  | @@ -267,8 +404,8 @@ void Game::render(float lag, Renderer& r) {
 | 
	
		
			
				|  |  |          Vector2 bp = line.b + normal;
 | 
	
		
			
				|  |  |          Vector2 an = line.a - normal;
 | 
	
		
			
				|  |  |          Vector2 bn = line.b - normal;
 | 
	
		
			
				|  |  | -        r.drawTriangle(ap, bp, an, Color4(0xFF, 0xFF, 0xFF, 0xFF));
 | 
	
		
			
				|  |  | -        r.drawTriangle(bp, an, bn, Color4(0xFF, 0xFF, 0xFF, 0xFF));
 | 
	
		
			
				|  |  | +        r.drawTriangle(ap, bp, an, line.color);
 | 
	
		
			
				|  |  | +        r.drawTriangle(bp, an, bn, line.color);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      r.translateTo(0.0f, 0.0f).update();
 | 
	
	
		
			
				|  | @@ -284,6 +421,10 @@ void Game::render(float lag, Renderer& r) {
 | 
	
		
			
				|  |  |      y = r.drawString(10.0f, y, s);
 | 
	
		
			
				|  |  |      s.clear().append("Ground = ").append(onGround);
 | 
	
		
			
				|  |  |      y = r.drawString(10.0f, y, s);
 | 
	
		
			
				|  |  | +    s.clear().append("Angle = ").append(atanf(steepness) * 57.295779513f);
 | 
	
		
			
				|  |  | +    y = r.drawString(10.0f, y, s);
 | 
	
		
			
				|  |  | +    s.clear().append("Friction = ").append(friction);
 | 
	
		
			
				|  |  | +    y = r.drawString(10.0f, y, s);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  bool Game::isRunning() {
 |