From: Christoph Sommer Date: Wed, 28 Jun 2006 22:31:07 +0000 (+0000) Subject: Added backflip action (no gfx yet) / X-Git-Url: https://git.verplant.org/?a=commitdiff_plain;h=7d3004fc6167b7fcd7495dd1ac278f2ac527cbec;p=supertux.git Added backflip action (no gfx yet) / Major rework of Player class' input system and actions / Some actions can now be triggered by scripts / Modified squirrel addkey() to not end level / Changed levels/world2/key1.stl to play new cheer action SVN-Revision: 3800 --- diff --git a/data/credits.txt b/data/credits.txt index df31c428b..33a9ad55e 100644 --- a/data/credits.txt +++ b/data/credits.txt @@ -28,7 +28,7 @@ Ryan \"sik0fewl\" Flegel - Christoph \"delta\" Sommer + Christoph Sommer Bastiaan \"basti_\" Zapf diff --git a/data/images/creatures/tux_big/big-tux-arms.sprite b/data/images/creatures/tux_big/big-tux-arms.sprite index d31242552..a8cb066d4 100644 --- a/data/images/creatures/tux_big/big-tux-arms.sprite +++ b/data/images/creatures/tux_big/big-tux-arms.sprite @@ -72,6 +72,16 @@ (mirror-action "kick-right")) (action + (name "backflip-right") + (hitbox 5 62 0 0) + (images "arms-duck-0.png")) + + (action + (name "backflip-left") + (hitbox 27 62 0 0) + (mirror-action "backflip-right")) + + (action (name "buttjump-right") (hitbox 5 32 0 0) (images "arms-stand-0.png")) diff --git a/data/images/creatures/tux_big/big-tux-body.sprite b/data/images/creatures/tux_big/big-tux-body.sprite index 8d8cd09c6..9146a1791 100644 --- a/data/images/creatures/tux_big/big-tux-body.sprite +++ b/data/images/creatures/tux_big/big-tux-body.sprite @@ -70,6 +70,18 @@ (mirror-action "kick-right")) (action + (name "backflip-right") + (fps 15.0) + (hitbox 5 62 0 0) + (images "body-duck-0.png")) + + (action + (name "backflip-left") + (fps 15.0) + (hitbox 27 62 0 0) + (mirror-action "backflip-right")) + + (action (name "buttjump-right") (hitbox 5 32 0 0) (images "body-stand-0.png")) diff --git a/data/images/creatures/tux_big/big-tux-feet.sprite b/data/images/creatures/tux_big/big-tux-feet.sprite index 6c2e94ff6..1018f547b 100644 --- a/data/images/creatures/tux_big/big-tux-feet.sprite +++ b/data/images/creatures/tux_big/big-tux-feet.sprite @@ -73,6 +73,18 @@ (mirror-action "kick-right")) (action + (name "backflip-right") + (fps 15.0) + (hitbox 5 62 0 0) + (images "feet-duck-0.png")) + + (action + (name "backflip-left") + (fps 15.0) + (hitbox 27 62 0 0) + (mirror-action "backflip-right")) + + (action (name "buttjump-right") (hitbox 5 32 0 0) (images "feet-buttjump-0.png")) diff --git a/data/images/creatures/tux_big/big-tux-head.sprite b/data/images/creatures/tux_big/big-tux-head.sprite index cbb0eb385..ecec7456b 100644 --- a/data/images/creatures/tux_big/big-tux-head.sprite +++ b/data/images/creatures/tux_big/big-tux-head.sprite @@ -80,13 +80,24 @@ (mirror-action "buttjump-right")) (action + (name "backflip-right") + (fps 15.0) + (hitbox 5 62 0 0) + (images "head-duck-0.png")) + + (action + (name "backflip-left") + (fps 15.0) + (hitbox 27 62 0 0) + (mirror-action "backflip-right")) + + (action (name "idle-right") (fps 1.0) (hitbox 6 31 0 0) (images "head-idle-blink-0.png" "head-idle-blink-1.png")) - (action (name "idle-left") (fps 1.0) diff --git a/data/levels/world2/default.nut b/data/levels/world2/default.nut index e3ec741df..de5ef1b0a 100644 --- a/data/levels/world2/default.nut +++ b/data/levels/world2/default.nut @@ -3,7 +3,6 @@ function add_key(key) local keys = state.world2_keys; keys[key] = true; update_keys(); - end_level(); } function level2_init() diff --git a/data/levels/world2/key1.stl b/data/levels/world2/key1.stl index f6c6910cd..66b791492 100644 --- a/data/levels/world2/key1.stl +++ b/data/levels/world2/key1.stl @@ -13,7 +13,13 @@ (y 96) ) (powerup - (script "add_key(\"water\");") + (script " + add_key(\"water\"); + Tux.deactivate(); + wait(0.5); + Tux.do_cheer(); + end_level(); + ") (disable-physics #t) (x 200) (y 602) diff --git a/src/object/player.cpp b/src/object/player.cpp index 66f4291ab..5104012ed 100644 --- a/src/object/player.cpp +++ b/src/object/player.cpp @@ -63,6 +63,7 @@ static const float MAX_RUN_XM = 320; static const float WALK_SPEED = 100; static const float KICK_TIME = .3; +static const float CHEER_TIME = 1; static const float UNDUCK_HURT_TIME = 0.25; /**< if Tux cannot unduck for this long, he will get hurt */ @@ -202,6 +203,98 @@ Player::update(float elapsed_time) if(!dying && !deactivated) handle_input(); + // handle_input() calls apply_friction() when Tux is not walking, so we'll have to do this ourselves + if (deactivated) apply_friction(); + + // extend/shrink tux collision rectangle so that we fall through/walk over 1 + // tile holes + if(fabsf(physic.get_velocity_x()) > MAX_WALK_XM) { + set_width(34); + } else { + set_width(31.8); + } + + // on downward slopes, adjust vertical velocity to match slope angle + if (on_ground()) { + if (floor_normal.y != 0) { + if ((floor_normal.x * physic.get_velocity_x()) > 0) { + // we overdo it a little, just to be on the safe side + physic.set_velocity_y(-physic.get_velocity_x() * (floor_normal.x / floor_normal.y) * 2); + } + } + } + + // handle backflipping + if (backflipping) { + //prevent player from changing direction when backflipping + dir = (backflip_direction == 1) ? LEFT : RIGHT; + if (backflip_timer.check()) physic.set_velocity_x(100 * backflip_direction); + } + + // set fall mode... + if(on_ground()) { + fall_mode = ON_GROUND; + last_ground_y = get_pos().y; + } else { + if(get_pos().y > last_ground_y) + fall_mode = FALLING; + else if(fall_mode == ON_GROUND) + fall_mode = JUMPING; + } + + // check if we landed + if(on_ground()) { + jumping = false; + if (backflipping && (!backflip_timer.started())) { + backflipping = false; + backflip_direction = 0; + + // if controls are currently deactivated, we take care of standing up ourselves + if (deactivated) do_standup(); + } + } + +#if 0 + // Do butt jump + if (butt_jump && on_ground() && is_big()) { + // Add a smoke cloud + if (duck) + Sector::current()->add_smoke_cloud(Vector(get_pos().x - 32, get_pos().y)); + else + Sector::current()->add_smoke_cloud( + Vector(get_pos().x - 32, get_pos().y + 32)); + + butt_jump = false; + + // Break bricks beneath Tux + if(Sector::current()->trybreakbrick( + Vector(base.x + 1, base.y + base.height), false) + || Sector::current()->trybreakbrick( + Vector(base.x + base.width - 1, base.y + base.height), false)) { + physic.set_velocity_y(-2); + butt_jump = true; + } + + // Kill nearby badguys + std::vector gameobjects = Sector::current()->gameobjects; + for (std::vector::iterator i = gameobjects.begin(); + i != gameobjects.end(); + i++) { + BadGuy* badguy = dynamic_cast (*i); + if(badguy) { + // don't kill when badguys are already dying or in a certain mode + if(badguy->dying == DYING_NOT && badguy->mode != BadGuy::BOMB_TICKING && + badguy->mode != BadGuy::BOMB_EXPLODE) { + if (fabsf(base.x - badguy->base.x) < 96 && + fabsf(base.y - badguy->base.y) < 64) + badguy->kill_me(25); + } + } + } + } +#endif + + // calculate movement for this frame movement = physic.get_movement(elapsed_time); if(grabbed_object != NULL && !dying) { @@ -234,6 +327,35 @@ Player::is_big() } void +Player::apply_friction() +{ + if ((on_ground()) && (fabs(physic.get_velocity_x()) < WALK_SPEED)) { + physic.set_velocity_x(0); + physic.set_acceleration_x(0); + } else if(physic.get_velocity_x() < 0) { + physic.set_acceleration_x(WALK_ACCELERATION_X * 1.5); + } else { + physic.set_acceleration_x(WALK_ACCELERATION_X * -1.5); + } + +#if 0 + // if we're on ice slow down acceleration or deceleration + if (isice(base.x, base.y + base.height)) + { + /* the acceleration/deceleration rate on ice is inversely proportional to + * the current velocity. + */ + + // increasing 1 will increase acceleration/deceleration rate + // decreasing 1 will decrease acceleration/deceleration rate + // must stay above zero, though + if (ax != 0) ax *= 1 / fabs(vx); + } +#endif + +} + +void Player::handle_horizontal_input() { float vx = physic.get_velocity_x(); @@ -302,101 +424,106 @@ Player::handle_horizontal_input() } } + physic.set_velocity(vx, vy); + physic.set_acceleration(ax, ay); + // we get slower when not pressing any keys if(dirsign == 0) { - if ((on_ground()) && (fabs(vx) < WALK_SPEED)) { - vx = 0; - ax = 0; - } else if(vx < 0) { - ax = WALK_ACCELERATION_X * 1.5; - } else { - ax = WALK_ACCELERATION_X * -1.5; - } + apply_friction(); } -#if 0 - // if we're on ice slow down acceleration or deceleration - if (isice(base.x, base.y + base.height)) - { - /* the acceleration/deceleration rate on ice is inversely proportional to - * the current velocity. - */ +} - // increasing 1 will increase acceleration/deceleration rate - // decreasing 1 will decrease acceleration/deceleration rate - // must stay above zero, though - if (ax != 0) ax *= 1 / fabs(vx); - } -#endif +void +Player::do_cheer() +{ + do_duck(); + do_backflip(); + do_standup(); +} - // extend/shrink tux collision rectangle so that we fall through/walk over 1 - // tile holes - if(fabsf(vx) > MAX_WALK_XM) { - set_width(34); +void +Player::do_duck() { + if (duck) return; + if (!is_big()) return; + + if (physic.get_velocity_y() != 0) return; + if (!on_ground()) return; + + if (adjust_height(31.8)) { + duck = true; + unduck_hurt_timer.stop(); } else { - set_width(31.8); + // FIXME: what now? } +} - // on downward slopes, adjust vertical velocity to match slope angle - if (on_ground()) { - if (floor_normal.y != 0) { - if ((floor_normal.x * vx) > 0) { - // we overdo it a little, just to be on the safe side - vy = -vx * (floor_normal.x / floor_normal.y) * 2; - } +void +Player::do_standup() { + if (!duck) return; + if (!is_big()) return; + if (backflipping) return; + + if (adjust_height(63.8)) { + duck = false; + unduck_hurt_timer.stop(); + } else { + // if timer is not already running, start it. + if (unduck_hurt_timer.get_period() == 0) { + unduck_hurt_timer.start(UNDUCK_HURT_TIME); + } + else if (unduck_hurt_timer.check()) { + kill(false); } } - physic.set_velocity(vx, vy); - physic.set_acceleration(ax, ay); } void -Player::handle_vertical_input() -{ - // set fall mode... - if(on_ground()) { - fall_mode = ON_GROUND; - last_ground_y = get_pos().y; +Player::do_backflip() { + if (!duck) return; + if (!on_ground()) return; + + backflip_direction = (dir == LEFT)?(+1):(-1); + backflipping = true; + do_jump(-580); + backflip_timer.start(0.15); +} + +void +Player::do_jump(float yspeed) { + if (!on_ground()) return; + + physic.set_velocity_y(yspeed); + //bbox.move(Vector(0, -1)); + jumping = true; + on_ground_flag = false; + can_jump = false; + + // play sound + if (is_big()) { + sound_manager->play("sounds/bigjump.wav"); } else { - if(get_pos().y > last_ground_y) - fall_mode = FALLING; - else if(fall_mode == ON_GROUND) - fall_mode = JUMPING; + sound_manager->play("sounds/jump.wav"); } +} - if(on_ground()) { /* Make sure jumping is off. */ - jumping = false; - if (backflipping) { - backflipping = false; - backflip_direction = 0; - } - } +void +Player::handle_vertical_input() +{ // Press jump key - if(controller->pressed(Controller::JUMP) && can_jump && on_ground()) { + if(controller->pressed(Controller::JUMP) && (can_jump)) { if (duck) { - if (physic.get_velocity_x() != 0) // only jump a little bit when running ducked - physic.set_velocity_y(-300); - else { //do a backflip - backflipping = true; - physic.set_velocity_y(-580); - backflip_timer.start(0.15); - } + // when running, only jump a little bit; else do a backflip + if (physic.get_velocity_x() != 0) do_jump(-300); else do_backflip(); + } else { + // jump a bit higher if we are running; else do a normal jump + if (fabs(physic.get_velocity_x()) > MAX_WALK_XM) do_jump(-580); else do_jump(-520); } - else if (fabs(physic.get_velocity_x()) > MAX_WALK_XM) // jump higher if we are running - physic.set_velocity_y(-580); - else - physic.set_velocity_y(-520); - - //bbox.move(Vector(0, -1)); - jumping = true; - can_jump = false; - if (is_big()) - sound_manager->play("sounds/bigjump.wav"); - else - sound_manager->play("sounds/jump.wav"); - } else if(!controller->hold(Controller::JUMP)) { // Let go of jump key + } + // Let go of jump key + else if(!controller->hold(Controller::JUMP)) { if (!backflipping && jumping && physic.get_velocity_y() < 0) { jumping = false; physic.set_velocity_y(0); @@ -405,54 +532,13 @@ Player::handle_vertical_input() /* In case the player has pressed Down while in a certain range of air, enable butt jump action */ - if (controller->hold(Controller::DOWN) && !butt_jump && !duck) - //if(tiles_on_air(TILES_FOR_BUTTJUMP) && jumping) + if (controller->hold(Controller::DOWN) && !butt_jump && !duck && is_big() && jumping) { butt_jump = true; + } /* When Down is not held anymore, disable butt jump */ - if(butt_jump && !controller->hold(Controller::DOWN)) - butt_jump = false; - -#if 0 - // Do butt jump - if (butt_jump && on_ground() && is_big()) { - // Add a smoke cloud - if (duck) - Sector::current()->add_smoke_cloud(Vector(get_pos().x - 32, get_pos().y)); - else - Sector::current()->add_smoke_cloud( - Vector(get_pos().x - 32, get_pos().y + 32)); - - butt_jump = false; - - // Break bricks beneath Tux - if(Sector::current()->trybreakbrick( - Vector(base.x + 1, base.y + base.height), false) - || Sector::current()->trybreakbrick( - Vector(base.x + base.width - 1, base.y + base.height), false)) { - physic.set_velocity_y(-2); - butt_jump = true; - } - - // Kill nearby badguys - std::vector gameobjects = Sector::current()->gameobjects; - for (std::vector::iterator i = gameobjects.begin(); - i != gameobjects.end(); - i++) { - BadGuy* badguy = dynamic_cast (*i); - if(badguy) { - // don't kill when badguys are already dying or in a certain mode - if(badguy->dying == DYING_NOT && badguy->mode != BadGuy::BOMB_TICKING && - badguy->mode != BadGuy::BOMB_EXPLODE) { - if (fabsf(base.x - badguy->base.x) < 96 && - fabsf(base.y - badguy->base.y) < 64) - badguy->kill_me(25); - } - } - } - } -#endif - + if(butt_jump && !controller->hold(Controller::DOWN)) butt_jump = false; + /** jumping is only allowed if we're about to touch ground soon and if the * button has been up in between the last jump */ @@ -500,18 +586,12 @@ Player::handle_input() /* Handle horizontal movement: */ if (!backflipping) handle_horizontal_input(); - else { - if (backflip_direction == 0) { - dir == LEFT ? backflip_direction = 1 : backflip_direction = -1; - } - else backflip_direction == 1 ? dir = LEFT : dir = RIGHT; //prevent player from changing direction when backflipping - if (backflip_timer.check()) physic.set_velocity_x(100 * backflip_direction); - } - - + /* Jump/jumping? */ if (on_ground() && !controller->hold(Controller::JUMP)) can_jump = true; + + /* Handle vertical movement: */ handle_vertical_input(); /* Shoot! */ @@ -523,31 +603,9 @@ Player::handle_input() shooting_timer.start(SHOOTING_TIME); } - /* Duck! */ - if (controller->hold(Controller::DOWN) && is_big() && !duck - && physic.get_velocity_y() == 0 && on_ground()) { - if (adjust_height(31.8)) { - duck = true; - unduck_hurt_timer.stop(); - } else { - // FIXME: what now? - } - } - /* Unduck! */ - else if(!controller->hold(Controller::DOWN) && is_big() && duck) { - if (adjust_height(63.8)) { - duck = false; - unduck_hurt_timer.stop(); - } else { - // if timer is not already running, start it. - if (unduck_hurt_timer.get_period() == 0) { - unduck_hurt_timer.start(UNDUCK_HURT_TIME); - } - else if (unduck_hurt_timer.check()) { - kill(false); - } - } - } + /* Duck or Standup! */ + if (controller->hold(Controller::DOWN)) do_duck(); else do_standup(); + } void @@ -675,7 +733,14 @@ Player::draw(DrawingContext& context) int layer = LAYER_OBJECTS + 1; /* Set Tux sprite action */ - if (duck && is_big()) + if (backflipping) + { + if(dir == LEFT) + tux_body->set_action("backflip-left"); + else // dir == RIGHT + tux_body->set_action("backflip-right"); + } + else if (duck && is_big()) { if(dir == LEFT) tux_body->set_action("duck-left"); @@ -1052,6 +1117,7 @@ Player::bounce(BadGuy& ) void Player::deactivate() { + if (deactivated) return; deactivated = true; physic.set_velocity_x(0); physic.set_velocity_y(0); @@ -1062,6 +1128,7 @@ Player::deactivate() void Player::activate() { + if (!deactivated) return; deactivated = false; } diff --git a/src/object/player.hpp b/src/object/player.hpp index d4645a1b2..1f69640cf 100644 --- a/src/object/player.hpp +++ b/src/object/player.hpp @@ -162,6 +162,34 @@ public: // set kick animation void kick(); + /** + * play cheer animation. + * This might need some space and behave in an unpredictable way. Best to use this at level end. + */ + void do_cheer(); + + /** + * duck down if possible. + * this won't last long as long as input is enabled. + */ + void do_duck(); + + /** + * stand back up if possible. + */ + void do_standup(); + + /** + * do a backflip if possible. + */ + void do_backflip(); + + /** + * jump in the air if possible + * sensible values for yspeed are negative - unless we want to jump into the ground of course + */ + void do_jump(float yspeed); + /** * Adds velocity to the player (be carefull when using this) */ @@ -219,6 +247,11 @@ private: void deactivate(); void walk(float speed); + /** + * slows Tux down a little, based on where he's standing + */ + void apply_friction(); + bool visible; Portable* grabbed_object; diff --git a/src/scripting/player.hpp b/src/scripting/player.hpp index 34a6d1f7e..45f3d849d 100644 --- a/src/scripting/player.hpp +++ b/src/scripting/player.hpp @@ -83,6 +83,35 @@ public: */ virtual bool get_ghost_mode() = 0; + /** + * play cheer animation. + * This might need some space and behave in an unpredictable way. Best to use this at level end. + */ + virtual void do_cheer() = 0; + + /** + * duck down if possible. + * this won't last long as long as input is enabled. + */ + virtual void do_duck() = 0; + + /** + * stand back up if possible. + */ + virtual void do_standup() = 0; + + /** + * do a backflip if possible. + */ + virtual void do_backflip() = 0; + + /** + * jump in the air if possible + * sensible values for yspeed are negative - unless we want to jump into the ground of course + */ + virtual void do_jump(float yspeed) = 0; + + }; } diff --git a/src/scripting/wrapper.cpp b/src/scripting/wrapper.cpp index 6465363aa..be281dea8 100644 --- a/src/scripting/wrapper.cpp +++ b/src/scripting/wrapper.cpp @@ -1341,6 +1341,131 @@ static SQInteger Player_get_ghost_mode_wrapper(HSQUIRRELVM vm) } +static SQInteger Player_do_cheer_wrapper(HSQUIRRELVM vm) +{ + SQUserPointer data; + if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) { + sq_throwerror(vm, _SC("'do_cheer' called without instance")); + return SQ_ERROR; + } + Scripting::Player* _this = reinterpret_cast (data); + + try { + _this->do_cheer(); + + return 0; + + } catch(std::exception& e) { + sq_throwerror(vm, e.what()); + return SQ_ERROR; + } catch(...) { + sq_throwerror(vm, _SC("Unexpected exception while executing function 'do_cheer'")); + return SQ_ERROR; + } + +} + +static SQInteger Player_do_duck_wrapper(HSQUIRRELVM vm) +{ + SQUserPointer data; + if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) { + sq_throwerror(vm, _SC("'do_duck' called without instance")); + return SQ_ERROR; + } + Scripting::Player* _this = reinterpret_cast (data); + + try { + _this->do_duck(); + + return 0; + + } catch(std::exception& e) { + sq_throwerror(vm, e.what()); + return SQ_ERROR; + } catch(...) { + sq_throwerror(vm, _SC("Unexpected exception while executing function 'do_duck'")); + return SQ_ERROR; + } + +} + +static SQInteger Player_do_standup_wrapper(HSQUIRRELVM vm) +{ + SQUserPointer data; + if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) { + sq_throwerror(vm, _SC("'do_standup' called without instance")); + return SQ_ERROR; + } + Scripting::Player* _this = reinterpret_cast (data); + + try { + _this->do_standup(); + + return 0; + + } catch(std::exception& e) { + sq_throwerror(vm, e.what()); + return SQ_ERROR; + } catch(...) { + sq_throwerror(vm, _SC("Unexpected exception while executing function 'do_standup'")); + return SQ_ERROR; + } + +} + +static SQInteger Player_do_backflip_wrapper(HSQUIRRELVM vm) +{ + SQUserPointer data; + if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) { + sq_throwerror(vm, _SC("'do_backflip' called without instance")); + return SQ_ERROR; + } + Scripting::Player* _this = reinterpret_cast (data); + + try { + _this->do_backflip(); + + return 0; + + } catch(std::exception& e) { + sq_throwerror(vm, e.what()); + return SQ_ERROR; + } catch(...) { + sq_throwerror(vm, _SC("Unexpected exception while executing function 'do_backflip'")); + return SQ_ERROR; + } + +} + +static SQInteger Player_do_jump_wrapper(HSQUIRRELVM vm) +{ + SQUserPointer data; + if(SQ_FAILED(sq_getinstanceup(vm, 1, &data, 0))) { + sq_throwerror(vm, _SC("'do_jump' called without instance")); + return SQ_ERROR; + } + Scripting::Player* _this = reinterpret_cast (data); + SQFloat arg0; + if(SQ_FAILED(sq_getfloat(vm, 2, &arg0))) { + sq_throwerror(vm, _SC("Argument 1 not a float")); + return SQ_ERROR; + } + + try { + _this->do_jump(static_cast (arg0)); + + return 0; + + } catch(std::exception& e) { + sq_throwerror(vm, e.what()); + return SQ_ERROR; + } catch(...) { + sq_throwerror(vm, _SC("Unexpected exception while executing function 'do_jump'")); + return SQ_ERROR; + } + +} + static SQInteger FloatingImage_release_hook(SQUserPointer ptr, SQInteger ) { Scripting::FloatingImage* _this = reinterpret_cast (ptr); @@ -3290,6 +3415,36 @@ void register_supertux_wrapper(HSQUIRRELVM v) throw SquirrelError(v, "Couldn't register function 'get_ghost_mode'"); } + sq_pushstring(v, "do_cheer", -1); + sq_newclosure(v, &Player_do_cheer_wrapper, 0); + if(SQ_FAILED(sq_createslot(v, -3))) { + throw SquirrelError(v, "Couldn't register function 'do_cheer'"); + } + + sq_pushstring(v, "do_duck", -1); + sq_newclosure(v, &Player_do_duck_wrapper, 0); + if(SQ_FAILED(sq_createslot(v, -3))) { + throw SquirrelError(v, "Couldn't register function 'do_duck'"); + } + + sq_pushstring(v, "do_standup", -1); + sq_newclosure(v, &Player_do_standup_wrapper, 0); + if(SQ_FAILED(sq_createslot(v, -3))) { + throw SquirrelError(v, "Couldn't register function 'do_standup'"); + } + + sq_pushstring(v, "do_backflip", -1); + sq_newclosure(v, &Player_do_backflip_wrapper, 0); + if(SQ_FAILED(sq_createslot(v, -3))) { + throw SquirrelError(v, "Couldn't register function 'do_backflip'"); + } + + sq_pushstring(v, "do_jump", -1); + sq_newclosure(v, &Player_do_jump_wrapper, 0); + if(SQ_FAILED(sq_createslot(v, -3))) { + throw SquirrelError(v, "Couldn't register function 'do_jump'"); + } + if(SQ_FAILED(sq_createslot(v, -3))) { throw SquirrelError(v, "Couldn't register class 'Player'"); }