ekku@197: #include "Player.hh" ekku@197: #include "PhysicsObject.hh" ekku@197: #include "Engine.hh" ekku@197: ekku@197: #include ekku@197: ekku@197: PhysicsObject::PhysicsObject (PhysicsWorld &world, float mass, ekku@197: Vector position, Vector velocity) ekku@197: : world(world), position(position), velocity(velocity), ekku@197: mass(mass), inAir(true), aim(0), facingRight(true), reloadTimer(0) { ekku@197: // TODO: Is thir the right way to do this? ekku@197: //world.addPlayerObject(this); ekku@197: } ekku@197: ekku@197: /** ekku@197: * Player walks on floor. ekku@197: */ ekku@197: Vector PhysicsObject::walk_one_step (float partial, bool right) { ekku@197: // which way we are walking ekku@197: float deltaX = right ? partial : -partial; ekku@197: Vector reached = this->position; ekku@197: if(reached.roundToInt() == (reached+Vector(deltaX, 0)).roundToInt()) { ekku@197: return reached+Vector(deltaX, 0); ekku@197: } ekku@197: // Is there upward ramp ekku@197: if(!possibleLocation(position+Vector(deltaX, 0))) { ekku@197: // Yes. Then we check n pixels up ekku@197: for(int i = 1; i < 3; i++) { ekku@197: if(possibleLocation(position+Vector(deltaX, -i))) { ekku@197: // and when there is finally EMPTY, we can walk ekku@197: reached = position+Vector(deltaX, -i); ekku@197: break; ekku@197: } ekku@197: } ekku@197: } else { ekku@197: // Or downward ramp or flat ekku@197: for(int i = 0; 1; i++) { ekku@197: ekku@197: // And when there is finally ground we can step on ekku@197: // it. If there is no gound we still step there, ekku@197: // but will fall one pixel down ekku@197: if(possibleLocation(position+Vector(deltaX, i))) { ekku@197: reached = position+Vector(deltaX, i); ekku@197: } else { ekku@197: break; ekku@197: } ekku@197: ekku@197: // If the fall is big enough, set the worm in the air ekku@197: if (i >= 2) { ekku@197: // Vector back = walk(dt, !right); ekku@197: this->inAir = true; ekku@197: // this->velocity.x = right ? velocity : -velocity; ekku@197: // Avoid stepping two pixels down when it starts to free fall ekku@197: reached.y -= 2; ekku@197: // this->velocity = (reached-back)*1000/dt; ekku@197: break; ekku@197: } ekku@197: } ekku@197: } ekku@197: // And we return where we got ekku@197: return reached; ekku@197: } ekku@197: void PhysicsObject::walk (TimeMS dt, bool right) { ekku@197: float velocity = PLAYER_WALK_SPEED; ekku@197: float walkAmount = (velocity*dt)/1000; ekku@197: Vector reached = this->position; ekku@197: while(walkAmount > 0 && !this->inAir) { ekku@197: this->position = walk_one_step((1 < walkAmount ? 1 : walkAmount), right); ekku@197: walkAmount--; ekku@197: } ekku@197: // TODO: Should the remaining walkAmount be handled somehow? ekku@197: } ekku@197: ekku@197: /** ekku@197: * Makes the player jump in the air. ekku@197: * @param direction -1: jump left, 0: jump up, 1: jump right ekku@197: */ ekku@197: void PhysicsObject::jump (int direction) { ekku@197: // Jump only if player is "on the ground" ekku@197: if (!this->inAir) { ekku@197: velocity.y = -100; ekku@197: switch (direction) { ekku@197: case 1: ekku@197: velocity.x += 20; ekku@197: break; ekku@197: case -1: ekku@197: velocity.x -= 20; ekku@197: break; ekku@197: case 0: ekku@197: break; ekku@197: default: ekku@197: throw std::logic_error("Invalid jump direction"); ekku@197: } ekku@197: inAir = true; ekku@197: } ekku@197: } ekku@197: ekku@197: bool PhysicsObject::possibleLocation (Vector loc) { ekku@197: for(unsigned int i = 0; i < this->shape.size(); i++) { ekku@197: if(world.collides(loc+shape[i])) ekku@197: return false; ekku@197: } ekku@197: return true; ekku@197: } ekku@197: ekku@197: void func1() { ekku@197: ekku@197: } ekku@197: ekku@197: /** ekku@197: * Updates object speed and position. This function organises force ekku@197: * integration and collision detection. ekku@197: */ terom@205: void PhysicsObject::updatePosition (TimeMS dt) { ekku@197: ekku@197: // Reloads weapon if not reloaded terom@205: reloadTimer -= dt; ekku@197: if(reloadTimer < 0) ekku@197: reloadTimer = 0; ekku@197: ekku@197: // Add gravity to the force queue ekku@197: forceq.push(world.gravity); ekku@197: ekku@197: // Go trough every force in the queue ekku@197: Force total; ekku@197: while (!forceq.empty()) { ekku@197: total += forceq.front(); ekku@197: forceq.pop(); ekku@197: } ekku@197: ekku@197: // If the player has stopped and there's some ground under some of the 3 some of the 3t ekku@197: // set inAir false ekku@197: if (this->velocity == Vector(0,0)) { ekku@197: this->inAir = !world.collides(this->position+shape[1]+Vector(0, 1)) ekku@197: && !world.collides(this->position+shape[2]+Vector(0, 1)) ekku@197: && !world.collides(this->position+shape[3]+Vector(0, 1)); ekku@197: // If, however, there's a force caused by a bomb, e.g., set it in air. ekku@197: // Still, we have to be able to separate forces caused by walking attempts ekku@197: // and bombs etc (+0.1 because float comparison can be dangerous) ekku@197: if (total.y < 0 || abs(total.x) > PLAYER_MOVE_FORCE + 0.1) ekku@197: this->inAir = true; ekku@197: } ekku@197: ekku@197: if(!possibleLocation(position)) { ekku@197: //if we are trapped in ground form dirtball or something ekku@197: //we might want to just return and set velocity to some value ekku@197: //return; ekku@197: } ekku@197: ekku@197: // If the worm is not in the air make it walk, ekku@197: // otherwise integrate the new position and velocity ekku@197: if (!this->inAir) { ekku@197: //std::cout << "Tryin to walk" << std::endl; ekku@197: // It walks only if there's some vertical force ekku@197: if (total.x != 0) { ekku@197: std::cout << "Succeeding to walk" << std::endl; terom@205: walk(dt, total.x > 0); ekku@197: this->velocity = Vector(0,0); ekku@197: } ekku@197: } ekku@197: ekku@197: if(!possibleLocation(position)) { ekku@197: Engine::log(DEBUG, "great failure") << "great failure"; ekku@197: func1(); ekku@197: } ekku@197: Vector newPosition; ekku@197: Vector velAfterTick; ekku@197: // Calculate new position and velocity to the given references terom@205: integrate(total, dt, newPosition, velAfterTick); ekku@197: this->velocity = velAfterTick; ekku@197: ekku@197: ekku@197: // Collision detection ekku@197: bool collided = false; ekku@197: ekku@197: const Vector diffVec = newPosition-position; ekku@197: const Vector unitVector = diffVec / diffVec.length(); ekku@197: Vector reached = position; ekku@197: ekku@197: while ((position-reached).sqrLength() < diffVec.sqrLength()) { ekku@197: reached += unitVector; ekku@197: // Check if any of the shapes points collide ekku@197: for (uint64_t i = 0; i < shape.size(); i++) { ekku@197: if (world.collides(reached+shape[i])) { // Collision ekku@197: if (inAir) { ekku@197: // Engine::log(DEBUG, "Here"); ekku@197: this->bounce(world.getNormal(reached+shape[i], ekku@197: reached-unitVector+shape[i])); ekku@197: //this->velocity *= COLLISION_ELASTICITY; ekku@197: } ekku@197: reached = reached - unitVector; // Return to last point ekku@197: collided = true; ekku@197: if (this->velocity.sqrLength() < PLAYER_MIN_SPEED * PLAYER_MIN_SPEED) { ekku@197: this->velocity = Vector(0,0); ekku@197: } ekku@197: break; ekku@197: } ekku@197: } ekku@197: if (collided) ekku@197: break; ekku@197: // reached += unitVector; ekku@197: } ekku@197: ekku@197: ekku@197: if(!possibleLocation(reached)) { nireco@211: Engine::log(DEBUG, "PhysicsObject.updatePosition") << "inside ground. diffVec: " << diffVec; ekku@197: func1(); ekku@197: } ekku@197: ekku@197: // In case of some float error check the final coordinate ekku@197: if(!collided) { ekku@197: if(!possibleLocation(newPosition)) { ekku@197: newPosition = reached; ekku@197: } else { ekku@197: // This means everything was ok, so no need to do anything ekku@197: } ekku@197: } else { ekku@197: newPosition = reached; nireco@211: this->position = newPosition; ekku@197: onCollision(); nireco@210: //TODO: It should be moved before onCollision, for Shots ekku@197: } ekku@197: this->position = newPosition; ekku@197: // Engine::log(DEBUG, "PhysicsObject.updatePosition") << "Pos: " << this->position; ekku@197: } ekku@197: ekku@197: /** ekku@197: * Bounces from straight wall in any direction. ekku@197: * Direction given as normal of that wall ekku@197: */ ekku@197: void PhysicsObject::bounce (Vector normal) { ekku@197: // normal.sqrLength can't be 0 when got from getNormal() ekku@197: if (normal.sqrLength() != 0) { ekku@197: Vector nvel = velocity; ekku@197: // We project the velocity on normal and remove twice that much from velocity ekku@197: nvel = nvel - ((2)*((nvel*normal)/(normal*normal))*normal); ekku@197: velocity = nvel; ekku@197: // We lose some of our speed on collision ekku@197: this->velocity *= this->collision_elasticity; ekku@197: } ekku@197: } ekku@197: ekku@197: /** ekku@197: * Integrates given force over time and stores new position to ekku@197: * posAfterTick and new velocity to velAfterTick. ekku@197: * @param force Force vector. ekku@197: * @param dt The time the force is applied (<=PHYSICS_TICK_MS) ekku@197: */ ekku@197: void PhysicsObject::integrate(Force force, TimeMS dt, Vector &posAfterTick, Vector &velAfterTick) { ekku@197: posAfterTick = position; ekku@197: velAfterTick = velocity; ekku@197: Derivative tmpd; ekku@197: Derivative k1 = evaluate(force, 0, tmpd, posAfterTick, velAfterTick); ekku@197: Derivative k2 = evaluate(force, 0.5f*dt, k1, posAfterTick, velAfterTick); ekku@197: Derivative k3 = evaluate(force, 0.5f*dt, k2, posAfterTick, velAfterTick); ekku@197: Derivative k4 = evaluate(force, dt, k3, posAfterTick, velAfterTick); ekku@197: ekku@197: ekku@197: const Vector dxdt = (k1.dx + (k2.dx + k3.dx) * 2.0f + k4.dx) * 1.0f/6.0f; ekku@197: const Vector dvdt = (k1.dv + (k2.dv + k3.dv) * 2.0f + k4.dv) * 1.0f/6.0f; ekku@197: ekku@197: // Engine::log(DEBUG, "PhysicsObject.integrate") << "Changes: "<< dxdt << " " << dvdt << " Time: " <aim += da; ekku@197: ekku@197: if (this->aim > PLAYER_AIM_MAX) this->aim = PLAYER_AIM_MAX; ekku@197: if (this->aim < PLAYER_AIM_MIN) this->aim = PLAYER_AIM_MIN; ekku@197: //Engine::log(DEBUG, "PhysicsObject.changeAim") << "Player aim: " << this->aim; ekku@197: } ekku@197: ekku@197: void PhysicsObject::setFacing(bool facingRight) { ekku@197: //Engine::log(DEBUG, "PhysicsObject.setFacing") << "Facing: " << right; ekku@197: this->facingRight = facingRight; ekku@197: } ekku@197: terom@200: void PhysicsObject::updatePhysics (Vector position, Vector velocity, bool inAir, bool facingRight, float aim) { ekku@197: this->position = position; ekku@197: this->velocity = velocity; ekku@197: this->inAir = inAir; terom@200: this->facingRight = facingRight; terom@200: this->aim = aim; ekku@197: } terom@200: ekku@197: Vector PhysicsObject::getPosition () { ekku@197: return this->position; ekku@197: } ekku@197: ekku@197: bool PhysicsObject::getFacing() { ekku@197: return this->facingRight; ekku@197: } ekku@197: ekku@197: float PhysicsObject::getAim() { ekku@197: return this->aim; ekku@197: } ekku@197: ekku@197: std::vector& PhysicsObject::getShape () { ekku@197: return this->shape; ekku@197: } ekku@197: ekku@197: void PhysicsObject::setShape (std::vector shape) { ekku@197: this->shape = shape; ekku@197: } ekku@197: terom@205: void PhysicsObject::tick (TimeMS tick_length) { terom@205: this->updatePosition(tick_length); ekku@197: } ekku@197: ekku@197: bool PhysicsObject::canShoot() { ekku@197: return this->reloadTimer <= 0; ekku@197: } ekku@197: ekku@197: void PhysicsObject::draw(CL_GraphicContext *gc) { ekku@197: CL_Quad player( ekku@197: (position+shape[0]).x, (position+shape[0]).y, ekku@197: (position+shape[1]).x, (position+shape[1]).y, ekku@197: (position+shape[2]).x, (position+shape[2]).y, ekku@197: (position+shape[3]).x, (position+shape[3]).y ekku@197: ); ekku@197: ekku@197: gc->fill_quad(player, CL_Color::green); ekku@197: ekku@197: const uint16_t chlen = 10; ekku@197: uint16_t x = player.center().x; ekku@197: uint16_t y = player.center().y; ekku@197: if (facingRight) { ekku@197: gc->draw_line(x, y, ekku@197: x + std::cos(aim)*chlen, ekku@197: y - std::sin(aim)*chlen, ekku@197: CL_Color::black); ekku@197: } else { ekku@197: gc->draw_line(x, y, ekku@197: x - std::cos(aim)*chlen, ekku@197: y - std::sin(aim)*chlen, ekku@197: CL_Color::black); ekku@197: } ekku@197: } ekku@197: