You must basicly create your own pathing algorythm for that.
There are natives that allow getting the terrain tile type.
So what you need to do is write an algorythm that detects all tile types in an area around the target point and the ordered unit, then approach the problem from a maths perspective.
Consider the linear movement. If it's a complete path of the 100% movement type (roads), that's it. If there's 75% or 50% movement tiles in it, you need to check the tiles around the tiles in the linear way.
You should google pathing algorythms before you start with that. There are some pretty effective ways to do that, as the deviation from the linear path is very limited depending on the type of pathing on the linear way.
The lower the total "sum" of movement points on the linear way, the less adjacent tiles actually need to be checked, as the possible "longer" routes need to be faster to be considered.
Basicly, the algorythm could look like this:
You consider the direct linear movement tiles, sum all movement factors together, to keep as a "reference":
example: 1+1+1.5+1.25+1.25+1 = 7
Then, you consider your starting point.
At the first tile, you have 4 possible choices.
Each choice comes with 3 more possible choices (the fourth choice is going back, which would make no sense).
Each of those comes with another 3 more possible choices.
So the number of possible paths equals down to:
4*3^(n-1) with n being the maximum number of tiles a unit can possibly move.
If a unit can move up to 10 tiles of "road" pathing, then this equals to: 78732 possible paths (but only a very small fraction of those paths actually reach the target tile!).
As you can see, this would be way too much to calculate in a loop. What we can do to reduce the number of possible paths by comparing it to the movement time of the current "shortest" path.
As we have no "shortest" path saved yet, we simply consider the linear movement path as the shortest, so that makes an "action point cost" of 7.
So what do we do now?
Basicly, we use a recursive function.
This recursive function has a starting tile passed to and a target tile passed to. The target tile is passed over every time and does not change. The starting tile changes with every recursion.
Whenever the function is called, we create 3 "moved" counters. This is because the paths "branch out". With every tile moved, the number of possible paths multiplies by 3.
We then call this function again for each of the 3 adjacent tiles (excluding the backwards movement). By this, we also increment the counter of the considered path by the movement value of this tile.
If we exceed the current "shortest" path movement value, we do not need another recursion on this tile again, as it will never be shorter.
If one of the adjacent tiles is the target tile, we can also stop recursion of the "parent" tile and consider the new value of the counter as the new "shortest" value (this path will always be the shortest, as all recursion steps stop as soon as the current "shortest" value is reached).
This way, the algorythm will clean all recursions that exceed the current "shortest" value one after one, until there is only the shortest possible path left.
This process is very CPU heavy, though and might reach the operation limit. Make sure each recursion runs on a seperate thread for longer paths!