src/Player.cc
author terom
Sat, 06 Dec 2008 16:17:05 +0000
changeset 221 fbc5db6fce45
parent 220 1c92222af6d3
child 222 293ddf4c067d
permissions -rw-r--r--
reorganize the weapons code and input handling code

#include "Engine.hh"
#include "Player.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), arsenal(), selectedWeapon(0), changing(false), animation_step(0) {
    // TODO: arsenal's size should be affected by some value
    // and weapons should be loaded from somewhere, not generated here
    for (int i = 0; i < 5; i++) {
        arsenal.push_back(Weapon(state, 10000, (5 - i) * 40 + 30, i * 6 + 5, i * 100 + 50, "asdf"));
    }
    
    // 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;

    // add to player-object list
    world.addPlayerObject(this);
}
 
void Player::handleDig (Vector position, float radius) {
    Vector unitVectorAim = facingRight ? Vector(std::cos(aim), -std::sin(aim)) : 
            Vector(-std::cos(aim), -std::sin(aim));
    float shotspeed = 0*PHYSICS_TICK_MS;
    Vector shotVelocity = unitVectorAim*shotspeed;
    new Projectile(this->state, this->position, shotVelocity, false, radius, 1);

}

void Player::handleShoot (Weapon &weapon) {
    Vector unitVectorAim = (facingRight ? 
        Vector(std::cos(aim), -std::sin(aim)) : 
        Vector(-std::cos(aim), -std::sin(aim))
    );
    
    // XXX: what does the PHYSICS_TICK_MS stuff mean?
    float shotspeed = weapon.getVelocity() * PHYSICS_TICK_MS / 2;

    Vector shotRelativeVelocity = unitVectorAim * shotspeed;
    Vector shotVelocity = this->velocity + shotRelativeVelocity;
    Vector shotPosition = this->position + unitVectorAim * 10;
    
    weapon.shoot(shotPosition, shotVelocity);
}

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
    getWeapon().tickReload(dt);
}

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

        // Should create Projectile which destroys ground, but also should be destroyed then,
        // but it doesn't.
        // But this now just segfaults
//        world.addObject(new Projectile(state, position, true));
    }
    
    // XXX: currently not network safe
    if (input & INPUT_CHANGE) {
        if (changing) {

        } else {
            changing = true;
            selectedWeapon = (selectedWeapon + 1) % arsenal.size();
            Engine::log(DEBUG, "Player.cc:input ") << "changed weapon " << selectedWeapon;
        }
    } else {
        changing = false;
    }
    
    // validate shoot events, and then outsource to handleShoot so Network can intercept it
    if (input & INPUT_SHOOT && getWeapon().canShoot()) {
        this->handleShoot(getWeapon());
    }
    
    // XXX: how should this be written?
    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::getWeapon() {
    return arsenal[selectedWeapon % arsenal.size()];
}

void Player::draw (CL_GraphicContext *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);
    }
}