GML Slopes and Platformer Movement for Beginners

Discussion in 'Tutorials' started by Bentley, Feb 2, 2018.

Tags:
  1. Bentley

    Bentley Member

    Joined:
    Jun 18, 2017
    Posts:
    605
    GM Version: GMS 1.4 / GM2
    Target Platform: ALL
    Download: N/A
    Links: BrandonSacState@yahoo.com

    Summary

    This tutorial is for beginners and it is about climbing slopes. Specifically, it's about slopes in games with destroyable terrain. The idea is, when we get to a slope, to measure the slope and compare the slope's height to how high the player can climb. There is also platformer basics like acceleration, jumping, setting sprites, and so forth. An example of what I'm talking about is in the GIF below (Yoshi's Island sprite).

    tutorial gif 4.gif

    Tutorial

    I drew a randomly shaped sprite, checked precise collision mask, and then assigned it to obj_level. All the terrain is obj_level and it is what I collision check against.

    The approach script is used a lot.
    Code:
    /// @function approach(a, b, amount);
    /// @description Moves value A to value B by amount.
    /// @arg a
    /// @arg b
    /// @arg amount
    
    var a, b, amount;
    
    a      = argument0;
    b      = argument1;
    amount = argument2;
    
    if (a < b)
    {
        return min(a + amount, b);
    }
    
    return max(a - amount, b);
    obj_player

    [Create Event]
    Code:
    hspd      = 0;
    vspd      = 0;
    hspd_max  = 3;     // Max horizontal speed. We will flip this when moving left to -hspd_max
    vspd_max  = 6;     // Max vertical speed. We will flip this when jumping to -vspd_max
    accel     = 0.2;
    frict     = 0.2;
    grav      = 0.4;
    climb_max = 8;     // The max distance we can climb for ascending and for descending
    
    [Step Event]
    Code:
    var key_right, key_left, key_jump, input_x, on_ground;
    
    key_right = keyboard_check(vk_right);
    key_left  = keyboard_check(vk_left);
    key_jump  = keyboard_check_pressed(vk_up);
    input_x   = keyboard_check(vk_right) - keyboard_check(vk_left);
    on_ground = place_meeting(x, y + 1, obj_level);
    
    if (input_x != 0)
    {
        // Set sprite if you're on the ground
        if (on_ground)
        {
            sprite_index = spr_player_run;
        }
     
        // Face right or left
        image_xscale = input_x;
     
        // Speed up
        hspd = approach(hspd, hspd_max * input_x, accel);
    }
    else
    {
        // Set sprite if you're on the ground
        if (on_ground)
        {
            sprite_index = spr_player_idle;
        }
     
        // Slow down
        hspd = approach(hspd, 0, fric);
    }
    
    // Jump
    if (key_jump && on_ground)
    {
        sprite_index = spr_player_jump;
     
        vspd = -vspd_max;
    }
    
    // Gravity
    if (!on_ground)
    {
        // Set your sprite if you're falling
        if (vspd > 0)
        {
            sprite_index = spr_player_fall;
        }
     
        vspd = approach(vspd, vspd_max, grav);
    }
    
    var climb_remaining, slope_height;
    climb_remaining = climb_max;
    
    // Horizontal movement
    repeat (abs(hspd))
    {
       // Check for slopes if we are on the ground
       if (on_ground)
       {
            // Up slope
            if (place_meeting(x + sign(hspd), y, obj_level))
            {
                // Measure the slope
                slope_height = up_slope(climb_remaining);
     
                // Can climb
                if (slope_height <= climb_remaining)
                {
                    x += sign(hspd);
                    y -= slope_height;
                    climb_remaining -= slope_height;
                    continue;
                }
     
                // Can not climb
                hspd = 0;
                break;
            }
     
            // Down slope (just like we did up slopes)
            if (!place_meeting(x + sign(hspd), y + 1, obj_level))
            {
                slope_height = down_slope(climb_remaining);                    
     
                if (slope_height <= climb_remaining)
                {
                    x += sign(hspd);
                    y += slope_height;
                    climb_remaining -= slope_height;
                    continue;
                }
         
                // Continue the loop rather than break the loop to keep momentum
                x += sign(hspd);
                continue;
            }
     
            // Flat
            x += sign(hspd);
        }
        else
        {
            // Mid-air so we don't check for slopes
            if (!place_meeting(x + sign(hspd), y, obj_level))
            {
                x += sign(hspd);
            }
            else
            {
                hspd = 0;
                break;
            }
        }
    }
    
    // Vertical movement
    repeat (abs(vspd))
    {
        if (!place_meeting(x, y + sign(vspd), obj_level))
        {
            y += sign(vspd);
        }
        else
        {
            vspd = 0;
            break;
        }
    }
    
    [Slope scripts]

    up_slope
    Code:
    /// @function up_slope(climb_remaining)
    /// @arg climb_remaining
    /// @desc This script will tell us how high the slope is.
    
    var climb_remaining, dy;
    climb_remaining = argument0;
    dy = 0;
    
    // While we are not at the top of the slope
    while (place_meeting(x + sign(hspd), y - dy, obj_level))
    {
        dy++;
        if (dy > climb_remaining)
        {
            // Break if the slope is greater than we can climb
            break;
        }
    }
    
    // dy will be either the exact distance to climb or 1 greater than climb_remaining.
    // That number is fed into: if (slope_height <= climb_remaining), which determines whether we can climb.
    return dy;
    
    down_slope
    Code:
    /// @function down_slope(climb_remaining)
    /// @arg climb_remaining
    /// @desc This script will tell us how low the slope is.
    
    var climb_remaining, dy;
    climb_remaining = argument0;
    dy = 0;
    
    // While we are not at the bottom of the slope
    while (!place_meeting(x + sign(hspd), y + dy + 1, obj_level))
    {
        dy++;
        if (dy > climb_remaining)
        {
            // Break if the slope is greater than we can climb
            break;
        }
    }
    
    // dy will be either the exact distance to climb or 1 greater than climb_remaining.
    // That number is fed into: if (slope_height <= climb_remaining), which determines whether we can climb.
    return dy;
    
    Thanks for checking it out. Post any questions.
     
    Last edited: Jan 8, 2019
  2. Ghost in the IDE

    Ghost in the IDE Member

    Joined:
    Jan 8, 2018
    Posts:
    576
    I'm not seeing anything here about the 'destroyable' element.

    All I am seeing is inefficient terrain collision.
     
  3. Bentley

    Bentley Member

    Joined:
    Jun 18, 2017
    Posts:
    605
    Well, I left making the destroyable terrain out, as I just wanted to focus on crossing slopes in that type of game. But, yeah, I could add that in.

    If you don't mind, how can I make the terrain collision more efficient? Thanks for the reply.
     
  4. Ghost in the IDE

    Ghost in the IDE Member

    Joined:
    Jan 8, 2018
    Posts:
    576
    Makes the title a bit misleading then. People will come along and think 'awesome!' And then will be disappointed when they find this is all only about pinning a character to varied height ground.
     
  5. Bentley

    Bentley Member

    Joined:
    Jun 18, 2017
    Posts:
    605
    Hmm, fair enough. I'll add it in.
     
  6. the_dude_abides

    the_dude_abides Member

    Joined:
    Jun 23, 2016
    Posts:
    408
    Just edit the title to "traversing slopes of destroyed terrain. NOTE: To anyone who can't be bothered to read the summary, and gets their overly entitled hopes up from just reading the title alone - techniques for destroyed terrain not included." (Millenials, eh? apparently they've never been told that the devil is in the details, and are very fragile mentally)
    :)
     
    Bentley likes this.
  7. Bentley

    Bentley Member

    Joined:
    Jun 18, 2017
    Posts:
    605
    Haha : )
     
  8. Ghost in the IDE

    Ghost in the IDE Member

    Joined:
    Jan 8, 2018
    Posts:
    576
    ROFL.

    I don't follow tutorials. I already know everything :D
     
  9. GilM

    GilM Member

    Joined:
    Jul 2, 2016
    Posts:
    17
    Great tutorial man! I learned a lot from it. I hope to see many more from you for sure. One thing you should correct though is where you put "climb_room_height". Remember that you defined this in the create event as climb_height. Still a very useful tutorial for those who never knew how to do slopes in their games.
     
    Bentley likes this.
  10. Bentley

    Bentley Member

    Joined:
    Jun 18, 2017
    Posts:
    605
    Hey GilM, thanks for the feedback, and thanks for pointing out that typo as well! I edited the tutorial recently and auto-correct must have changed climb_height to "climb_room_height". That would be an odd variable name lol. I'll change that now.
     
    Last edited: Mar 15, 2018
  11. MadPropz101

    MadPropz101 Member

    Joined:
    Jan 25, 2018
    Posts:
    122
    Hey, the only thing i have to apply precise collision checking to is the objects that you collide with right? Not the player object itself?
    I really hope this is the case because i'm using Spine and i assign all my bounding boxes there, so currently my character has a somewhat rectangular bbox assigned in Spine.
     
  12. Bentley

    Bentley Member

    Joined:
    Jun 18, 2017
    Posts:
    605
    Yes, you only need to check precise for the level sprite. I can't speak for Spine b/c I've never used it, but you probably won't have any problems.
     
  13. Christopher Rosa

    Christopher Rosa Member

    Joined:
    Dec 8, 2016
    Posts:
    21
    Ughhhh thank you so ****ing much I've been looking for good slopes for weeks I bought things from the market place that weren't as useful as this thanks. One thing when going up steep slopes the player goes up faster is there a way to make him go the same speed on any steepness of slope
     
    Bentley likes this.
  14. Christopher Rosa

    Christopher Rosa Member

    Joined:
    Dec 8, 2016
    Posts:
    21
    Also can I make this work with hspeed instand of hspd? I like hspeed because it handles friction smoother
     
  15. Bentley

    Bentley Member

    Joined:
    Jun 18, 2017
    Posts:
    605
    Edit: I posted some ideas below.
    I avoided the built in speed variables b/c I feel like I can control pixel by pixel movement better this way.
     
    Last edited: Oct 8, 2018
  16. Christopher Rosa

    Christopher Rosa Member

    Joined:
    Dec 8, 2016
    Posts:
    21
    I'm trying to slow down the players hspd when he goes up slopes. I tried doing if climb_height <= 4 hspd = hspd
    And
    If climb_height <= 8
    hspd = hspd/2

    I figured that since climb_height determines how steep of a slope you can move across this would work. It didn't. So then I tried

    If dy <= 4

    If dy <= 8

    But that didn't work either.

    So what tells the player to move up a steep slope when it's really steep? What tells the player when it has reached it's climb_height limit
     
  17. Misty

    Misty Member

    Joined:
    Jun 22, 2016
    Posts:
    865
    Does this have sliding down slopes? Like with momentum? And can it collide also with blocks, ceilings, and walls?
     
  18. Christopher Rosa

    Christopher Rosa Member

    Joined:
    Dec 8, 2016
    Posts:
    21
    This is the code that slows down my player. What happens is that the hspd -= 0.01; is being ignored because of the slope collision code

    Maybe because the slopes are moving the player a pixel it doesn't calculate the decimal idk.


    Code:
    if key_left && !key_right
    hspd -= 0.5
    else if !key_left && key_right
    hspd += 0.5
    else if !key_left && !key_right
    {
        if hspd > 0
        {
          
            hspd -= 0.01
        }
        if hspd < 0
        {
            
            hspd += 0.01
        }
    }
    
    
    if hspd > 3
       hspd = 3
        
    if hspd < -3
       hspd = -3
     
  19. Bentley

    Bentley Member

    Joined:
    Jun 18, 2017
    Posts:
    605
    "dy" is what tells you how big a slope is.

    I think there's some confusion, so let me explain how the code works, then I'll offer some ideas for slowing down movement when climbing.

    The repeat loop asks the following question "hspd" times:
    Is climb_height greater than or equal to the column of pixels 1 pixel ahead of you (x + sign(hspd))?
    sprite1.png
    You're that blue dot. Let's say you have an hspd of 3 and a climb_height of 3. In 1 frame, you'll end at that red dot.
    The "repeat (hspd)" loop runs 3 times (b/c hspd is 3).
    The first slope is 3 pixels high. climb_height is 3, so you'll climb it.
    The second slope is 3 pixels high from your new position. So you'll climb that.
    The same for the third slope.
    Now the repeat loop is over and you're at the red dot.
    (This all happens in 1 frame.)

    If you zoom in on a slope it's just a bunch of pixel columns next to each other. When you get to one of those columns, there's only two possibilities: climb or don't. So that's why you can't climb individual columns slower. Here are some ways to slow you down based on slope height.

    1. Think of climb_height as "climb_power". Instead of climbing "climb_height" each iteration of the loop, you use a temporary variable called climb_power.

    Set var climb_power equal to climb_height before the loop. Every time you climb a slope, decrease climb_power by "dy" (how much you just climbed). So the more you climb, the more you decrease climb_power, making it more difficult to climb the next slope in the next iteration.

    2. You could measure all the columns hspd away. You'd have some sort of formula where you'd add up their height total and the greater that number, the less your hspd. Increase hspd for down slopes.
     
    Last edited: Oct 23, 2018
  20. Bentley

    Bentley Member

    Joined:
    Jun 18, 2017
    Posts:
    605
    The repeat loop doesn't allow you to move at sub pixels.
     
  21. Christopher Rosa

    Christopher Rosa Member

    Joined:
    Dec 8, 2016
    Posts:
    21
    Is there a way to make the code work with the built in functions hspeed and vspeed because I need friction and it's the only way I can move on slopes smoothly. I have another script that handles slopes with hspeed and vspeed but it only handles up slopes but not down slopes. Can you make this code work with down slopes and steep slopes as well.
    Code:
    //Roll back movement
    //This is necessary as GM may or may not have carried out "Solid" behaviour
    x=xprevious
    y=yprevious
    //Check if we're on the ground
    if not (jumping or place_free(x,y+1))
    {
      //We're on the ground so check we haven't just walked off a cliff
        OnGround = true;
      if not place_free(x+hspeed,y+abs(hspeed)+1)
      {
     
          //Search for the ground level
          x+=hspeed;
          y+=abs(hspeed)   
       move_outside_solid(90,100+200*abs(hspeed))  //200 not 1000  //200 not 1000
          //Roughly set vspeed for the benefit of any AIs that may use it
          vspeed=(y-yprevious)
          //Check if we hit a wall.
          if not place_free(x,y)
          {
              x=xprevious
              y=yprevious
              if hspeed<>0
              {
                move_contact_solid(180*(hspeed<0),abs(hspeed))
              } 
              hspeed=0
              vspeed=0
            
          }
      }
      else
      {
        //Lookout!! Cliff!!
        vspeed=0
        //Just move out horizontally
       move_contact_solid(180*(hspeed<0),abs(hspeed))
        //jump=true
        
      } 
    }
    else
    {
        //Freefall code
        //Move horizontally
        if hspeed<>0
        {
          move_contact_solid(180*(hspeed<0),abs(hspeed))
     //here
          if not (place_free(x+sign(hspeed),y) or place_free(x+sign(hspeed),y-2))
          {
            hspeed=0
          }
        }
        //Move vertically
       if vspeed<>0
        {
          OnGround = false;
          move_contact_solid(90+180*(vspeed>0),abs(vspeed))
          if not place_free(x,y+sign(vspeed))
          {
            if vspeed>0
            {
              //jump= 0
            }
            vspeed=0
          }
        }
    }
     
  22. Christopher Rosa

    Christopher Rosa Member

    Joined:
    Dec 8, 2016
    Posts:
    21
    How would I do this exactly when I tried my player just went through the slopes
     
  23. Bentley

    Bentley Member

    Joined:
    Jun 18, 2017
    Posts:
    605
    I might add the things we're talking about into this tutorial. I'll message you if I do. But for now, let me post some code that may help:
    Code:
    var climb_power = climb_height; // Create a temporary variable called "climb_power" and set it equal to climb_height
    
    repeat (abs(hspd))
    {
        // Slope ahead
        if (place_meeting(x + sign(hspd), y, obj_level))
        {
            // Measure the slope ("dy" is its height)
            var dy = up_slope();
     
            // Check climb_power insead of climb_height.
            if (dy <= climb_power)
            {
                // climb_power decreases every slope.
                // The result being it's harder and harder to climb each iteration you climb a slope, based on the slopes' heights
                climb_power -= dy;
            }
        }
     
        // Rest of code here...
    }
    
    This should slow you down when climbing a bunch of high slopes, hspd away. Tinker with the variables to get feel you want.
     
    Last edited: Oct 12, 2018
  24. Amon

    Amon Member

    Joined:
    Sep 13, 2016
    Posts:
    156
    This is pretty cool. Thanks for sharing. Would I be right in assuming that the first post has all the code updated?

    [edit] I got it all working. This is, again, very cool stuff. Thank you again for sharing.
     
    Last edited: Oct 13, 2018
    Bentley likes this.
  25. Joe Ellis

    Joe Ellis Member

    Joined:
    Aug 30, 2016
    Posts:
    648
    This looks good, and the code isn't inefficient, it looks like alot at first but most player code ends up being quite big, and it only runs once per step so its not gonna lag the game
     
    Bentley likes this.
  26. Bentley

    Bentley Member

    Joined:
    Jun 18, 2017
    Posts:
    605
    Yeah, I think I'm going to update the tutorial. I'm going to slow the player down when ascending and speed the player up when descending. How fast/slow hspd becomes will depend on the both the number and the height of the slopes within hspd range. I'll tag you when it's posted.
     
    Amon likes this.
  27. Bentley

    Bentley Member

    Joined:
    Jun 18, 2017
    Posts:
    605
    Haha. Why the googly eyes? I'll tell you the truth, it's there just so you'd email me.
     
    Last edited: Oct 17, 2018
  28. Bentley

    Bentley Member

    Joined:
    Jun 18, 2017
    Posts:
    605
    lol, harsh
     
    Joe Ellis likes this.
  29. Bentley

    Bentley Member

    Joined:
    Jun 18, 2017
    Posts:
    605
    @Amon @Christopher Rosa At the end of the original post I added some code that will slope you down on up slopes.
     
    Amon likes this.
  30. Christopher Rosa

    Christopher Rosa Member

    Joined:
    Dec 8, 2016
    Posts:
    21
    The slowdown while climbing slopes isn't working for me I don't know exactly where to add the code you have a comment that says rest of code here I'm not sure what youean by it
     
  31. Bentley

    Bentley Member

    Joined:
    Jun 18, 2017
    Posts:
    605
    I actually left out an important line, I just put it back in. It's at the very bottom of my post. There's only a few changes.
     
  32. Christopher Rosa

    Christopher Rosa Member

    Joined:
    Dec 8, 2016
    Posts:
    21
    Can you add a slow down for downslopes too?
     
  33. Bentley

    Bentley Member

    Joined:
    Jun 18, 2017
    Posts:
    605
    Do the exact same thing for down slopes as you did for up slopes:
    Code:
    // Down slope
    if (!place_meeting(x + sign(hspd), y, obj_level))
    {
        // Check climb_remaining against the height of the slope
        if (climb_remaining >= slope_height)
        {
            // Climable...
            
            x += sign(hspd);   // 1 pixel forward
            y -= slope_height; // slope_height pixel's up
        
            // Decrease climb_remaining so it's harder to climb next iteration
            climb_remaining -= slope_height;
        }
    }
    
     
    Last edited: Oct 26, 2018
  34. Christopher Rosa

    Christopher Rosa Member

    Joined:
    Dec 8, 2016
    Posts:
    21
    I tweaked the slopes around and figured out how to add friction by accounting for subpixel movement. I have a problem when the player jumps into the side of a slopes he stops moving for a second. I believe it's because the player is no longer grounded when it touched the side of a wall and when it touches the side of a pixel it thinks it's a wall and stops him. How would I allow the player to. Be grounded while hitting the side of walls. And what determines how steep a slopes is. I think you said dy. So how would I say if a slopes dy is 10 pixels high switch state to state.playerslide
     
  35. Bentley

    Bentley Member

    Joined:
    Jun 18, 2017
    Posts:
    605
    if (dy >= 10) state = slide;
    else state = normal;

    I can't speak for subpixel movement. The repeat loop doesn't use fractions.
     
  36. Christopher Rosa

    Christopher Rosa Member

    Joined:
    Dec 8, 2016
    Posts:
    21
    How did you make the player in the gif gif flip left and right because when I use image_xscale = 1 and image_xscale = -1 to flip my Sprite it also flips the mask and it messes with the collisions even tho I have everything centered and it doesn't effect where the player is it still messes with it
     
  37. Christopher Rosa

    Christopher Rosa Member

    Joined:
    Dec 8, 2016
    Posts:
    21
    And how do I make the player grounded even when he touches the side of a wall because he's only grounded when the floor is beneathe is feet
     
  38. Bentley

    Bentley Member

    Joined:
    Jun 18, 2017
    Posts:
    605
    @Christopher Rosa
    grounded script
    Code:
    return (place_meeting(x, y + 1, obj_level) || place_meeting(x + image_xscale, y, obj_level))
    
    I'm using "image_xscale" as you said that's what your using and you centered your origin. So this should tell you if you're facing obj_level.

    You're probably going to have problems considering yourself on ground this way. For example, if your falling but there's a solid 1 pixel in front of you, you'll be considered standing. If you use this script to see whether you can jump, you'll be able to jump from mid-air. But maybe you're using the script in a different way. Just something to keep in mind.
     
    Last edited: Oct 29, 2018
  39. Christopher Rosa

    Christopher Rosa Member

    Joined:
    Dec 8, 2016
    Posts:
    21
  40. Christopher Rosa

    Christopher Rosa Member

    Joined:
    Dec 8, 2016
    Posts:
    21
    I worked for making the player grounded when touching the side of a wall but the glitch I'm having is when I change the image xscale of my player it makes his fall off the slope when going down slope. When I go up a slope then hit right I fall right off
     
  41. Christopher Rosa

    Christopher Rosa Member

    Joined:
    Dec 8, 2016
    Posts:
    21
    I ended up drawing the sprites over the mask so they're separated so the problem doesn't occur.
     
  42. Christopher Rosa

    Christopher Rosa Member

    Joined:
    Dec 8, 2016
    Posts:
    21
    So this is like climbing up stairs rather than going up and down slopes. So if dy = 9 that means you climb up 9 pixels at once which is bad. It teleports up 9 pixels or down 9 pixels instead of going 1 pixel at a time like how it does horizontally. I found a 360 slope engine that uses dcos and dsin and it goes around slopes smoothly but I don't understand how it works.
     
  43. Bentley

    Bentley Member

    Joined:
    Jun 18, 2017
    Posts:
    605
    Climbing stairs is a good way to put it. Climbing stairs that get harder and harder with ever stair you climb.

    What do you want to happen when there's a wall x + 1 away.
     
    Last edited: Nov 26, 2018
  44. Christopher Rosa

    Christopher Rosa Member

    Joined:
    Dec 8, 2016
    Posts:
    21


    Ive been trying to make going up slopes act the same as walking on a flat ground. Soo when dy = 9 the player still travels the 9 pixels 1 pixel at a time or the speed of the player so it doesn't just teleport 9 pixels up. If you know what I mean? When you're on flat ground if the floor is 30 pixels in width the player doesn't teleport 30 pixels to the right or left it goes one pixel at a time or what the speed is. That's how I want it when going up pixels going up the slope the same as flat ground.
     
  45. Bentley

    Bentley Member

    Joined:
    Jun 18, 2017
    Posts:
    605
    Where we are getting confused is the way we are thinking about slopes. That is my bad. Do not even think about slopes for a second. Let's call them columns.

    A column has a width of just one pixel.
    The code looks ahead at the next column and measures how many pixels are on top of that column.

    Now imagine climbing that column halfway. It does not really make sense. That is what I meant when I said, "You either climb it or you don't".

    I think moving is synonymous with what you are calling teleporting. x + 5 moves you instantly to x + 5. It does not move you pixel by pixel.

    Maybe a climb_angle is better, and you measure the angle and determine whether you can climb based on that.

    There are undoubtedly better ways to climb slopes. This is just a simple way that works for me.
     
    Last edited: Jan 6, 2019
  46. Christopher Rosa

    Christopher Rosa Member

    Joined:
    Dec 8, 2016
    Posts:
    21
     
  47. VerdeBoss

    VerdeBoss Member

    Joined:
    Nov 18, 2017
    Posts:
    17
    Any wall support? When you draw a straight vertical line on the terrain, the player ends up getting stuck.
     
  48. Bentley

    Bentley Member

    Joined:
    Jun 18, 2017
    Posts:
    605
    If by "wall support" you mean that you want to place other objects that you can collide with, the simplest solution would be to make them a child of obj_terrain. That way, a collision check for obj_terrain includes a collision check for obj_wall (or w/e you name the new object).

    If you mean that when you are drawing the sprite for obj_terrain, you draw a vertical line, the player should be stopped if the vertical line is higher than his climb_max variable. If you want your player to be able climb higher, just increase climb_max.
     
    Last edited: Jan 16, 2019 at 4:46 PM

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice