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