terom@218: terom@236: #include "Player.hh" terom@236: #include "Weapons.hh" saiam@199: #include "Engine.hh" terom@233: #include "Graphics.hh" ekku@198: terom@209: #include nireco@215: #include nireco@216: #include terom@218: #include nireco@215: terom@220: // player static state terom@220: bool Player::skin_loaded = false; terom@220: CL_Surface Player::skin_surface; nireco@215: terom@220: // XXX: give these better names and move elsewhere nireco@248: const int img_num_aim = 2; nireco@219: const int img_num_step = 4; nireco@248: const int img_height = 20; nireco@248: const int img_width = 17; nireco@216: ekku@198: Player::Player(GameState &state, Vector position, bool visible) : terom@236: PhysicsObject(state.world, PLAYER_MASS, position, Vector(0, 0)), state(state), visible(visible), terom@239: weapons(buildWeaponsList()), selectedWeapon(0), animation_step(0), rope(*this) ekku@222: { terom@236: // XXX: populate weapons from somewhere else terom@236: terom@220: // build the player's shape terom@220: // XXX: these dimensions are incorrect... ekku@198: std::vector shape(4); ekku@198: shape[0] = Vector(0,-9); ekku@198: shape[1] = Vector(6,0); ekku@198: shape[2] = Vector(0,9); ekku@198: shape[3] = Vector(-6,0); terom@220: ekku@198: // Initialize the shape of the player (salmiakki shape) ekku@198: setShape(shape); terom@220: terom@220: // XXX: this should be a PhysicsObject constructor arg ekku@198: collision_elasticity = PLAYER_COLLISION_ELASTICITY; ekku@198: } terom@236: terom@209: void Player::handleDig (Vector position, float radius) { terom@235: // XXX: clean this bit up nireco@250: // calculate new position and velocity nireco@250: Vector shotPosition = position + getDirection() * PROJECTILE_START_DISTANCE; nireco@250: // execute nireco@250: new Projectile(state, shotPosition, Vector(0, 0), false, radius, 1); terom@209: } terom@209: terom@236: void Player::handleCreateProjectile (Weapon *weapon, Vector position, Vector velocity) { terom@263: new Projectile(state, position, velocity, true, weapon->getExplosionRadius(), weapon->getRadius(), weapon->getExpireTicks()); saiam@199: } terom@237: terom@237: void Player::handleChangeWeapon (unsigned int weaponIndex) { terom@237: assert(weaponIndex < weapons.size()); terom@237: terom@237: selectedWeapon = weaponIndex; terom@237: } saiam@199: terom@221: void Player::printDebugInfo (void) { terom@221: Engine::log(DEBUG, "layer.debug") << "In air: " << this->inAir; terom@221: } terom@221: terom@221: void Player::tick (TimeMS dt) { terom@221: // let PhysicsObject execute terom@221: PhysicsObject::tick(dt); terom@221: terom@221: // tick current weapon reload terom@236: if (getCurrentWeapon()) terom@236: getCurrentWeapon()->tickReload(dt); terom@221: } terom@241: terom@241: void Player::handleRopeState (RopeState state) { terom@241: terom@241: } terom@241: terom@241: void Player::handleRopeLength (float length) { terom@241: terom@241: } saiam@199: terom@236: void LocalPlayer::fireWeapon (Weapon *weapon) { terom@223: // update reload timer terom@236: weapon->reload(); terom@223: terom@235: // calculate new position and velocity terom@235: Vector shotPosition = position + getDirection() * PROJECTILE_START_DISTANCE; terom@236: Vector shotVelocity = velocity + getDirection() * weapon->getSpeed(); saiam@258: saiam@258: // Recoil saiam@258: Vector recoilForce = (getDirection() * weapon->getRecoil())*(-1); saiam@258: applyForce(recoilForce); terom@223: terom@223: // execute terom@223: handleCreateProjectile(weapon, shotPosition, shotVelocity); terom@223: } terom@237: terom@237: void LocalPlayer::changeWeapon (int delta) { terom@237: // need to handle negative deltas terom@237: handleChangeWeapon((weapons.size() + selectedWeapon + delta) % weapons.size()); terom@237: } terom@223: terom@221: void LocalPlayer::handleInput (PlayerInput input) { terom@221: // Movement force, vertical is always zero terom@221: Vector move_force = Vector(0, 0); saiam@199: terom@221: // Crosshair angle change terom@221: float aim_delta = 0; saiam@199: terom@221: // handle movement left/right by applying a horizontal force, but limit the player's speed terom@235: // also update facing if needed ekku@262: if (!((input & INPUT_MOVE_LEFT) && (input & INPUT_MOVE_RIGHT))) { ekku@262: if (input & INPUT_MOVE_LEFT) { ekku@262: if (velocity.x > -PLAYER_MAX_SPEED) ekku@262: move_force.x -= PLAYER_MOVE_FORCE; ekku@262: nireco@260: setFacing(false); ekku@262: } terom@221: ekku@262: if (input & INPUT_MOVE_RIGHT) { ekku@262: if (velocity.x < PLAYER_MAX_SPEED) ekku@262: move_force.x += PLAYER_MOVE_FORCE; ekku@262: ekku@262: setFacing(true); ekku@262: } terom@221: } terom@221: terom@221: // handle aim by creating a aim angle delta terom@221: if (input & INPUT_AIM_UP) terom@221: aim_delta += CROSSHAIR_ANGLE_SPEED; terom@221: terom@221: if (input & INPUT_AIM_DOWN) terom@221: aim_delta -= CROSSHAIR_ANGLE_SPEED; terom@221: terom@221: // handle jumping by invoking the jump method terom@221: // XXX: the direction should ideally be given using some other method terom@221: if (input & INPUT_JUMP) { terom@221: if (input & INPUT_MOVE_LEFT) saiam@199: jump(-1); terom@221: terom@221: else if (input & INPUT_MOVE_RIGHT) saiam@199: jump(1); terom@221: saiam@199: else saiam@199: jump(0); saiam@199: } terom@221: terom@221: // outsource digging to Player::handleDig, since this modifies the Terrain and Network needs to know terom@235: if (input & INPUT_DIG) terom@221: handleDig(position, 15); terom@221: terom@237: // change weapon back/forth terom@237: if (input & INPUT_CHANGE_PREV) terom@237: changeWeapon(-1); terom@237: terom@237: if (input & INPUT_CHANGE_NEXT) terom@237: changeWeapon(+1); nireco@213: terom@221: // validate shoot events, and then outsource to handleShoot so Network can intercept it terom@236: if ((input & INPUT_SHOOT) && getCurrentWeapon()->canShoot()) terom@236: fireWeapon(getCurrentWeapon()); terom@221: terom@241: // rope throw+release+changeLength, but supress spurious events ekku@225: if (input & INPUT_ROPE) terom@235: rope.throwRope(); ekku@225: terom@241: if (input & INPUT_UNROPE && rope.getState() != ROPE_FOLDED) ekku@229: rope.release(); ekku@229: terom@241: if (input & INPUT_ROPE_UP && rope.getState() == ROPE_FIXED) terom@235: rope.changeLength(-ROPE_GROWTH_RATE); ekku@231: terom@241: if (input & INPUT_ROPE_DOWN && rope.getState() == ROPE_FIXED) terom@235: rope.changeLength(ROPE_GROWTH_RATE); ekku@231: terom@235: // XXX: how should this be written? What does this do? Probably broken under network play terom@221: if (move_force.x != 0) terom@221: animation_step = (animation_step + 1) % img_num_step; saiam@199: terom@221: // apply aim delta terom@221: if (aim_delta) terom@221: changeAim(aim_delta); nireco@219: terom@221: // apply force terom@221: if (!move_force.zero()) terom@221: applyForce(move_force); saiam@199: } saiam@199: terom@236: Weapon* Player::getCurrentWeapon() { terom@235: return weapons[selectedWeapon % weapons.size()]; nireco@212: } nireco@215: terom@255: void Player::draw (Graphics *g, PixelCoordinate camera) { terom@233: CL_GraphicContext *gc = g->get_gc(); terom@255: terom@255: // animation indexes terom@255: int aim_img_idx = (int)((1 - (getAim() + KG_PI / 2) / KG_PI) * img_num_aim); terom@255: int step_img_idx = animation_step % img_num_step; nireco@217: terom@220: // load skin image if not yet loaded terom@220: if (!skin_loaded) { terom@220: skin_surface = CL_Surface(PLAYER_SKIN_PATH); terom@220: skin_loaded = true; terom@220: } terom@221: terom@255: // calulate where to draw the worm terom@255: CL_Rectf destination( terom@255: position.x + (getFacing() ? -1 : 1) * img_width / 2 - camera.x, terom@255: position.y - img_height / 2 - camera.y, terom@255: position.x + (getFacing() ? 1 : -1) * img_width / 2 - camera.x, terom@255: position.y + img_height / 2 - camera.y terom@221: ); nireco@217: terom@255: // draw the correct animation frame from the skin terom@255: skin_surface.draw_subpixel( terom@255: CL_Rectf( terom@255: step_img_idx * img_width, terom@255: aim_img_idx * img_height, terom@255: (1 + step_img_idx) * img_width, terom@255: (aim_img_idx + 1) * img_height terom@255: ), destination, gc terom@255: ); terom@218: terom@261: // draw a proper crosshair-box terom@261: PixelCoordinate crosshair = getCoordinate() + world.getPixelCoordinate(getDirection() * PLAYER_CROSSHAIR_DISTANCE) - camera; terom@255: terom@261: gc->draw_rect( terom@261: CL_Rectf( terom@261: crosshair.x - PLAYER_CROSSHAIR_WIDTH / 2, terom@261: crosshair.y - PLAYER_CROSSHAIR_WIDTH / 2, terom@261: crosshair.x + PLAYER_CROSSHAIR_WIDTH / 2, terom@261: crosshair.y + PLAYER_CROSSHAIR_WIDTH / 2 terom@261: ), CL_Color::red terom@261: ); terom@255: terom@255: // draw rope nireco@248: rope.draw(g, camera); nireco@215: } nireco@215: terom@255: void LocalPlayer::draw (Graphics *g, bool displayWeapon, PixelCoordinate camera) { terom@233: // superclass draw nireco@248: Player::draw(g, camera); nireco@219: terom@233: // display weapon name? terom@236: if (displayWeapon && getCurrentWeapon()) { terom@236: const std::string weaponName = getCurrentWeapon()->getName(); terom@236: terom@257: PixelCoordinate pc = getCoordinate() - camera; terom@255: terom@255: // XXX: fix magic constants once we know how big the worm is terom@233: g->getSimpleFont().draw( terom@255: pc.x - g->getSimpleFont().get_width(weaponName) / 2, terom@255: pc.y - 20, terom@236: weaponName, terom@233: g->get_gc() terom@233: ); terom@233: } terom@233: } terom@235: