Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AGS 4: implement MotionPath script API #2687

Draft
wants to merge 4 commits into
base: ags4
Choose a base branch
from

Conversation

ivan-mogilko
Copy link
Contributor

@ivan-mogilko ivan-mogilko commented Feb 21, 2025

Resolves #2685

This exposes MoveList functionality into the script.
Initially thought to call this MovePath, but MovePath name is taken by a function name in Character and Object, so I came with MotionPath. This object replicates internal MoveList struct, which defines a state of moving along the path following certain rules, such as direction and repeat style.

Here's the MotionPath declaration, as you may notice it's pretty expansive:

builtin managed struct MotionPath {
  /// Creates a new MotionPath from the array of Points and x/y speed values.
  import static MotionPath* Create(Point* path[], float speedx, float speedy=0.0, RepeatStyle=eOnce, Direction=eForwards); // $AUTOCOMPLETESTATICONLY$
  /// Creates a new MotionPath from the array of Points and arrays of x/y speed values per each stage.
  import static MotionPath* Create2(Point* path[], float speedx[], float speedy[], RepeatStyle=eOnce, Direction=eForwards); // $AUTOCOMPLETESTATICONLY$
  /// Gets the copy of an underlying path as an array of Points.
  import Point*[] GetPath();
  /// Moves the current path progress one step back.
  import void StepBack();
  /// Moves the current path progress one step forward.
  import void StepForward();
  /// Reset the current path to the certain stage and progress position.
  import void Reset(int stage, float progress);
  /// Gets this motion path's current moving direction.
  import readonly attribute Direction Direction;
  /// Gets this motion path's repeat style.
  import readonly attribute RepeatStyle RepeatStyle;
  /// Gets if this motion path's was generated by pathfinder respecting walkable areas.
  import readonly attribute WalkWhere WalkWhere;
  /// Gets whether this motion path has reached the end and an object should stop.
  import readonly attribute bool IsCompleted;
  /// Gets number of path stages. Last stage defines the path's end-point, and there's always at least 2 stages.
  import readonly attribute int StageCount;
  /// Accesses the X coordinate of the each stage's starting position.
  import readonly attribute int StageX[];
  /// Accesses the Y coordinate of the each stage's starting position.
  import readonly attribute int StageY[];
  /// Accesses the horizontal speed magnitude of the each stage.
  import readonly attribute float SpeedX[];
  /// Accesses the vertical speed magnitude of the each stage.
  import readonly attribute float SpeedY[];
  /// Gets the current stage index
  import readonly attribute int Stage;
  /// Gets the progress of the current stage
  import readonly attribute float Progress;
  /// Gets the current would-be X coordinate of an object
  import readonly attribute int PositionX;
  /// Gets the current would-be Y coordinate of an object
  import readonly attribute int PositionY;
};

MotionPath is constructed from a path of points, movement speeds and few optional parameters.
For each pair of points there's a speed vector built.
Stage means a state of traversion from point A to point B. Here in this API StageX/Y mean the stage's start position.
Progress is a fraction of current stage passed, a float in the range of [0, 1). When the progress reaches 1.0, the Stage counter updates.
When the last Stage is reached, MotionPath either marks itself as "Completed" or, if told to Repeat, resets to the beginning of the path.

Here in this API I added 3 functions that control the state of movement: StepForward increments progress by +1.0, StepBack decrements it by -1.0, Reset allows to set any stage and in-stage progress (but these values are clamped to existing limits).
I.e. calling Reset(2, 0.5) would reset it to the middle of the third stage.

Following is an example of using MotionPath in order to move Overlay:
// room script file
MotionPath mpath;
Overlay *movingover;
bool go_forward;

Point CreatePoint(int x, int y)
{
  Point pt = new Point;
  pt.x = x;
  pt.y = y;
  return pt;
}

function room_AfterFadeIn()
{
  Point path[] = new Point[5];
  path[0] = CreatePoint(40, 40);
  path[1] = CreatePoint(80, 80);
  path[2] = CreatePoint(180, 40);
  path[3] = CreatePoint(240, 80);
  path[4] = CreatePoint(240, 120);
  mpath = MotionPath.Create(path, 4.0, 4.0, eRepeat, eForwards);
  movingover = Overlay.CreateGraphical(0, 0, 2100);
  go_forward = true;
}

function room_RepExec()
{
  movingover.X = mpath.PositionX;
  movingover.Y = mpath.PositionY;
  if (go_forward)
    mpath.StepForward();
  else
    mpath.StepBack();
}

function on_key_press(eKeyCode k)
{
  if (k == eKeyF)
  {
    go_forward = !go_forward;
  }
}

TODO:
This is a draft for now, because following planned parts have not been implemented yet:

  1. Character and Object give access to their MotionPath as property, this is to allow to track (and possibly hack) their movement.
  2. [DONE] Second Create factory method (with arrays of speeds) is not implemented yet.
  3. MotionPath is not added to saves.

@ivan-mogilko ivan-mogilko added ags 4 related to the ags4 development context: script api labels Feb 21, 2025
@ivan-mogilko
Copy link
Contributor Author

While testing this, I've been thinking that this struct could be used for more complicated movement, paths made with the use of the spline interpolation and such. But in that case "speed-per-stage" definition will be inapplicable, as movement will be based rather on "time between points". In fact, I'm not sure if the concept of "stage" will be applicable or not, as with interpolation it may not be convenient to view the path split between the points (or is it feasible?).

Perhaps this struct would require to be split into two: a generic parent with path of points and most basic setup, and a struct meant for moving objects when pathfinding and navigation walkable areas?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ags 4 related to the ags4 development context: script api
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant