src/Rope.cc
author Tero Marttila <terom@fixme.fi>
Tue, 27 Jan 2009 00:25:58 +0200
changeset 439 9823e6cd1086
parent 428 712b943195a6
permissions -rw-r--r--
some README text

// XXX: must include Player first, as it contains an instance of Rope
#include "Player.hh"
#include "Rope.hh"      
#include "Engine.hh"
#include "Error.hh"

#include <math.h>
#include <stdexcept>

Rope::Rope(Player &player) : 
    PhysicsObject(player.state.world, ROPE_MASS, Vector(0,0), Vector(0,0), ROPE, 0.00, false), 
    player(player), 
    state(ROPE_FOLDED) 
{
    // XXX: better shape
    std::vector<Vector> shape(4);
    shape[0] = Vector(-1, -1);
    shape[1] = Vector(-1, 1);
    shape[2] = Vector(1, 1);
    shape[3] = Vector(1, -1);
    setShape(shape);
}

void Rope::throwRope (void) {
    if (state == ROPE_FIXED) {
        // unset pivot if we re-throw rope
        player.setPivot(NULL);
    }

    // update state
    state = ROPE_FLYING;

    // XXX: this should probably be more dynamic?
    length = ROPE_LENGTH;
    
    // copy position + velocity from player
    setPosition(player.getPosition());
    setVelocity(player.getVelocity() + player.getDirection() * ROPE_VELOCITY);
    
    // we are FLYING
    inAir = true;
    
    // enable the physics object
    enable();

    // inform network
    player.handleRopeState(state);
}

void Rope::onCollision (Vector collisionPoint, PhysicsObject *other) {
    // Fix the rope to another player if collided with it
    if (other != NULL) {
        // we collided with another object
        if (other->getType() == PLAYER) {
            // set our player's pivot to the object that we collided with
            Player *target = dynamic_cast<Player*>(other);
            
            // ignore if the rope hits ourself
            if (target == &this->player)
                return;
            
            // set player's pivot to the other player
            player.setPivot(target);

            // disable ourselves as we're no longer relevant, don't keep colliding with the player
            disable();

        } else {
            // ignore other objects
            return;
        }

    } else { 
        // Collided with terrain, set player's pivot to ourselves
        player.setPivot(this);
    }

    // attached to something!
    state = ROPE_FIXED;

    // XXX: reset, except we override tick() in ugly ways
    setVelocity(Vector(0,0));
        
    // Ropes location will be used as the pivot point, so move the location to the collisionPoint.
    // Currently the position is something like one pixel away from the collisionPoint where there isn't ground.
    setPosition(collisionPoint);
    
    // inform network
    player.handleRopeState(state);
}

void Rope::release (void) {
    // Remove the rope from the PhysicsWorld
    disable();

    state = ROPE_FOLDED;
    
    // player doesn't have a pivot anymore
    player.setPivot(NULL);
    
    // inform network
    player.handleRopeState(state);
}

void Rope::changeLength (float delta) {
    // change length
    length += delta;
    
    // minimum length
    if (length < 0)
        length = 0;

    // inform network
    player.handleRopeLength(length);
}

RopeState Rope::getState (void) {
    return state;
}
        
float Rope::getLength (void) {
    return length;
}

Player *Rope::getPivotPlayer (void) {
    if (player.getPivot() == this)
        return NULL;
    else
        return dynamic_cast<Player*>(player.getPivot());
}

void Rope::updateState (RopeState new_state, Vector position, Vector velocity, float new_length, Player *pivot_player) {
    // update physics enabled/disabled state
    if (new_state == ROPE_FOLDED || new_state == ROPE_FIXED)
        disable();

    else // new_state == ROPE_FLYING
        enable();
    
    // update player.pivot to either the given pivot_player, or this rope
    if (new_state == ROPE_FIXED) {
        if (pivot_player)
            player.setPivot(pivot_player);
        else
            player.setPivot(this);

    } else if (this->state == ROPE_FIXED)
        player.setPivot(NULL);

    // update position stuff
    updatePhysics(position, velocity, true, FACING_RIGHT, 0);

    // update vars
    this->state = new_state;
    this->length = new_length;
}

void Rope::updateLength (float length) {
    // update length
    this->length = length;
}

#if GRAPHICS_ENABLED
void Rope::draw (graphics::Display &display, PixelCoordinate camera) {
    PixelCoordinate player_pos = player.getCoordinate() - camera;
    PixelCoordinate target_pos;
    
    // figure out what target is
    if (state == ROPE_FOLDED) {
        return;

    } else if (state == ROPE_FLYING) {
        // target is us
        target_pos = getCoordinate();

    } else {    // state == ROPE_FIXED
        // sanity-check
        if (player.getPivot() == NULL)
            throw Error("Rope::draw in state ROPE_FIXED, yet player.getPivot() is NULL");
        
        // target is our pivot
        target_pos = player.getPivot()->getCoordinate();
    }
 
    // align with camera
    target_pos -= camera;
    
    // draw a line from the player to the target chosen above
    display.get_gc()->draw_line(
        player_pos.x, player_pos.y,
        target_pos.x, target_pos.y,
        ROPE_COLOR_DARK
    );
}
#endif

void Rope::tick (TimeMS dt) {
    if (state == ROPE_FLYING) {
        // let PhysicsObject handle the flying stage
        PhysicsObject::tick(dt); 

    } else if (state == ROPE_FIXED) {
        // if player's pivot is some other player, then don't re-check the terrain
        if (player.getPivot() != this)
            return;

        // If there's no ground on the pivot point anymore, release the rope
        if (!world.terrain.collides(getPosition())) { 
            // XXX: move to some new method
            state = ROPE_FLYING;
            length = ROPE_LENGTH;
            inAir = true;
            player.setPivot(NULL);
            player.handleRopeState(state);
        }
    } else { // state == ROPE_FOLDED
        // ignore ticks when folded
    }
}