At the moment I'm trying to make an efficient and flexible movement system that I could use later on in a game. One part that's very important is the ability for the player to walk up steps/stairs. I thought about how I might do this, and came up with this:

Black = The level
Blue = The player
Red = c_trace
Basically, a c_trace acts as the feet of the player. It stops gravity from pulling him down when something is detected, and when something is detected, it checks the hit.z of the c_trace. If it's too close to the player, it moves the player up, making it so that he can walk up steps. The player's collision box stops him from moving if the step is too big. I got this working rather well, but then walking on slopes, while it worked, was very jittery and unnatural. I then tried it with 2 c_traces (one for walking on the ground, one for falling/jumping), which solved the slope problem, but now the player would walk up steps instantly rather than gradually moving up (things got weird otherwise), and overall it's probably very easy to break on complex terrain. Heck, I broke it simply by having a step that touches both the collision box, and the c_trace.

So my question is:
How does a system like this (climbing steps, walking on slopes) work on games like Quake? How might I do this with Lite-C (not exact code, but in general)? I'm simply stuck, and I want to solve this so I can use it for my games, rather than trying to work around it all the time.

Thank you!