src/Player.cc
author nireco
Sun, 07 Dec 2008 20:37:21 +0000
changeset 259 4964438840f5
parent 258 833ad8d7db8b
child 260 8377571871f0
permissions -rw-r--r--
digging works when walking worm has speed

#include "Player.hh"
#include "Weapons.hh"
#include "Engine.hh"
#include "Graphics.hh"

#include <cstdlib>
#include <ClanLib/display.h>
#include <string>
#include <cassert>

// player static state
bool Player::skin_loaded = false;
CL_Surface Player::skin_surface;

// XXX: give these better names and move elsewhere
const int img_num_aim = 2;
const int img_num_step = 4;
const int img_height = 20;
const int img_width = 17;

Player::Player(GameState &state, Vector position, bool visible) : 
    PhysicsObject(state.world, PLAYER_MASS, position, Vector(0, 0)), state(state), visible(visible),
    weapons(buildWeaponsList()), selectedWeapon(0), animation_step(0), rope(*this) 
{
    // XXX: populate weapons from somewhere else

    // build the player's shape
    // XXX: these dimensions are incorrect...
    std::vector<Vector> shape(4);
    shape[0] = Vector(0,-9);
    shape[1] = Vector(6,0);
    shape[2] = Vector(0,9); 
    shape[3] = Vector(-6,0);

    // Initialize the shape of the player (salmiakki shape)
    setShape(shape);

    // XXX: this should be a PhysicsObject constructor arg
    collision_elasticity = PLAYER_COLLISION_ELASTICITY;
}

void Player::handleDig (Vector position, float radius) {
    // XXX: clean this bit up
    // calculate new position and velocity
    Vector shotPosition = position + getDirection() * PROJECTILE_START_DISTANCE;
    // execute
    new Projectile(state, shotPosition, Vector(0, 0), false, radius, 1);
}

void Player::handleCreateProjectile (Weapon *weapon, Vector position, Vector velocity) {
    new Projectile(state, position, velocity, true, weapon->getExplosionRadius());
}
        
void Player::handleChangeWeapon (unsigned int weaponIndex) {
    assert(weaponIndex < weapons.size());

    selectedWeapon = weaponIndex;
}

void Player::printDebugInfo (void) {
    Engine::log(DEBUG, "layer.debug") << "In air: " << this->inAir;
}
        
void Player::tick (TimeMS dt) {
    // let PhysicsObject execute
    PhysicsObject::tick(dt);
    
    // tick current weapon reload
    if (getCurrentWeapon())
        getCurrentWeapon()->tickReload(dt);
}
        
void Player::handleRopeState (RopeState state) {
    
}

void Player::handleRopeLength (float length) {

}

void LocalPlayer::fireWeapon (Weapon *weapon) {
    // update reload timer
    weapon->reload();
    
    // calculate new position and velocity
    Vector shotPosition = position + getDirection() * PROJECTILE_START_DISTANCE;
    Vector shotVelocity = velocity + getDirection() * weapon->getSpeed();
    
    // Recoil
    Vector recoilForce =  (getDirection() * weapon->getRecoil())*(-1);
    applyForce(recoilForce);

    // execute
    handleCreateProjectile(weapon, shotPosition, shotVelocity);
}
        
void LocalPlayer::changeWeapon (int delta) {
    // need to handle negative deltas
    handleChangeWeapon((weapons.size() + selectedWeapon + delta) % weapons.size());
}

void LocalPlayer::handleInput (PlayerInput input) {
    // Movement force, vertical is always zero
    Vector move_force = Vector(0, 0); 

    // Crosshair angle change
    float aim_delta = 0; 

    // handle movement left/right by applying a horizontal force, but limit the player's speed
    // also update facing if needed
    if ((input & INPUT_MOVE_LEFT) && (velocity.x > -PLAYER_MAX_SPEED)) {
        setFacing(false);
        move_force.x -= PLAYER_MOVE_FORCE;

    }

    if ((input & INPUT_MOVE_RIGHT) && (velocity.x < PLAYER_MAX_SPEED)) {
        setFacing(true);
        move_force.x += PLAYER_MOVE_FORCE;
    }

    if ((input & INPUT_MOVE_LEFT) && (input & INPUT_MOVE_RIGHT)) {
        if(velocity.x != 0)
            setFacing(velocity.x > 0);
    }

    // handle aim by creating a aim angle delta
    if (input & INPUT_AIM_UP)
        aim_delta += CROSSHAIR_ANGLE_SPEED;

    if (input & INPUT_AIM_DOWN)
        aim_delta -= CROSSHAIR_ANGLE_SPEED;
    
    // handle jumping by invoking the jump method
    // XXX: the direction should ideally be given using some other method
    if (input & INPUT_JUMP) {
        if (input & INPUT_MOVE_LEFT)
            jump(-1);

        else if (input & INPUT_MOVE_RIGHT)
            jump(1);

        else
            jump(0);
    }
    
    // outsource digging to Player::handleDig, since this modifies the Terrain and Network needs to know
    if (input & INPUT_DIG)
        handleDig(position, 15);
    
    // change weapon back/forth
    if (input & INPUT_CHANGE_PREV)
        changeWeapon(-1);

    if (input & INPUT_CHANGE_NEXT)
        changeWeapon(+1);

    // validate shoot events, and then outsource to handleShoot so Network can intercept it
    if ((input & INPUT_SHOOT) && getCurrentWeapon()->canShoot())
        fireWeapon(getCurrentWeapon());
    
    // rope throw+release+changeLength, but supress spurious events
    if (input & INPUT_ROPE)
        rope.throwRope();

    if (input & INPUT_UNROPE && rope.getState() != ROPE_FOLDED)
        rope.release();

    if (input & INPUT_ROPE_UP && rope.getState() == ROPE_FIXED)
        rope.changeLength(-ROPE_GROWTH_RATE);

    if (input & INPUT_ROPE_DOWN && rope.getState() == ROPE_FIXED)
        rope.changeLength(ROPE_GROWTH_RATE);

    // XXX: how should this be written? What does this do? Probably broken under network play
    if (move_force.x != 0) 
        animation_step = (animation_step + 1) % img_num_step;

    // apply aim delta
    if (aim_delta)
        changeAim(aim_delta);

    // apply force
    if (!move_force.zero())
        applyForce(move_force);
}

Weapon* Player::getCurrentWeapon() {
    return weapons[selectedWeapon % weapons.size()];
}

void Player::draw (Graphics *g, PixelCoordinate camera) {
    CL_GraphicContext *gc = g->get_gc();
    
    // animation indexes
    int aim_img_idx = (int)((1 - (getAim() + KG_PI / 2) / KG_PI) * img_num_aim);
    int step_img_idx = animation_step % img_num_step;

    // load skin image if not yet loaded
    if (!skin_loaded) {
        skin_surface = CL_Surface(PLAYER_SKIN_PATH);
        skin_loaded = true;
    }
    
    // calulate where to draw the worm
    CL_Rectf destination(
        position.x + (getFacing() ? -1 : 1) * img_width / 2 - camera.x, 
        position.y - img_height / 2                         - camera.y, 
        position.x + (getFacing() ? 1 : -1) * img_width / 2 - camera.x, 
        position.y + img_height / 2                         - camera.y
    );
    
    // draw the correct animation frame from the skin
    skin_surface.draw_subpixel(
        CL_Rectf(
            step_img_idx * img_width, 
            aim_img_idx * img_height, 
            (1 + step_img_idx) * img_width, 
            (aim_img_idx + 1) * img_height
        ), destination, gc
    );
    
    // draw "crosshair", a half-line from our position to 10px away
    Vector crosshair = getDirection() * PLAYER_CROSSHAIR_LENGTH;

    PixelCoordinate aim_start = getCoordinate() + world.getPixelCoordinate(crosshair / 2) - camera;
    PixelCoordinate aim_end = getCoordinate() + world.getPixelCoordinate(crosshair) - camera;
    
    gc->draw_line(aim_start.x, aim_start.y, aim_end.x, aim_end.y, CL_Color::black);
    
    // draw rope
    rope.draw(g, camera);
}

void LocalPlayer::draw (Graphics *g, bool displayWeapon, PixelCoordinate camera) {
    // superclass draw
    Player::draw(g, camera);

    // display weapon name?
    if (displayWeapon && getCurrentWeapon()) {
        const std::string weaponName = getCurrentWeapon()->getName();

        PixelCoordinate pc = getCoordinate() - camera;
        
        // XXX: fix magic constants once we know how big the worm is
        g->getSimpleFont().draw(
                pc.x - g->getSimpleFont().get_width(weaponName) / 2,
                pc.y - 20,
                weaponName,
                g->get_gc()
        );
    }
}