src/Player.cc
author terom
Sat, 06 Dec 2008 23:47:13 +0000
changeset 236 0048ba274152
parent 235 0a0c729365ee
child 237 3d5465bcb67d
permissions -rw-r--r--
move weapons definition out to Weapons.cc

#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 = 9;
const int img_num_step = 4;
const int img_height = 9;
const int img_width = 10;

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), changing(false), 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
    float shotspeed = 1; //0*PHYSICS_TICK_MS;

    Vector shotVelocity = getDirection() * shotspeed;

    new Projectile(state, position, shotVelocity, false, radius, 1);
}

void Player::handleCreateProjectile (Weapon *weapon, Vector position, Vector velocity) {
    new Projectile(state, position, velocity, true, weapon->getExplosionRadius());
}

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 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();

    // execute
    handleCreateProjectile(weapon, shotPosition, shotVelocity);
}

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;
    }

    // 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);
    
    // XXX: currently not network safe
    if (input & INPUT_CHANGE) {
        selectedWeapon = (selectedWeapon + 1) % weapons.size();
        Engine::log(DEBUG, "player.input ") << "changed weapon to " << selectedWeapon;
    }

    // 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
    if (input & INPUT_ROPE)
        rope.throwRope();

    if (input & INPUT_UNROPE)
        rope.release();

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

    if (input & INPUT_ROPE_DOWN)
        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) {
    CL_GraphicContext *gc = g->get_gc();

    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;
    }
    
    // XXX: this logic looks weird
    CL_Rectf destination(position.x - 4, position.y - 4, position.x + 5, position.y + 4);

    if (!getFacing()) {
        destination = CL_Rect(position.x + 5, position.y - 4, position.x - 4, position.y + 4);
    }

    skin_surface.draw_subpixel(
            CL_Rectf(
                1 + step_img_idx * img_width, 
                aim_img_idx * img_height + 1, 
                1 + (1 + step_img_idx) * img_width, 
                (aim_img_idx + 1) * img_height + 1
            ), 
            destination, gc
    );
    
    const uint16_t chlen = 10;
    uint16_t x = position.x;
    uint16_t y = position.y;
    
    // draw "crosshair"
    if (facingRight) {
        gc->draw_line(x + std::cos(aim)*chlen/2, 
                      y - std::sin(aim)*chlen/2,
                      x + std::cos(aim)*chlen,
                      y - std::sin(aim)*chlen,
                      CL_Color::black);
    } else {
        gc->draw_line(x - std::cos(aim)*chlen/2, 
                      y - std::sin(aim)*chlen/2,
                      x - std::cos(aim)*chlen,
                      y - std::sin(aim)*chlen,
                      CL_Color::black);
    }

    rope.draw(g);
}

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

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

        g->getSimpleFont().draw(
                position.x - g->getSimpleFont().get_width(weaponName) / 2,
                position.y + 10,
                weaponName,
                g->get_gc()
        );
    }
}