Mover Control example code

From ActiveWiki
Jump to navigation Jump to search
/*-----------------------------------------------------------------------------
                            M o v e r   C o n t r o l
                      Copyright 2007 Activeworlds Inc
          Licensed Material -- Program Property of Activeworlds Inc.
-----------------------------------------------------------------------------*/


#define MAX_MODEL_NAME        64
#define WAIT_TIME             10  // time to wait for SDK packets and events before update
#define WAYPOINT_RESOLUTION   1   // accuracy, interpolates waypoint (1 to 10) - use as low as possible!
#define UPDATE_INTERVAL       (1000 / (aw_int (AW_WORLD_AVATAR_REFRESH_RATE) * WAYPOINT_RESOLUTION))
#define IDLE_INTERVAL         ((aw_int (AW_WORLD_MOVER_USED_RESET_TIMEOUT) * 1000) / 2)
#define STATE_INVALIDATE      -1  // invalidate mover's state
#define MAX_WAYPOINTS         16  // number of elements in "waypoints" array

// announced messages
#define START_MSG             "Bus started."
#define STOP_MSG              "Bus stopped. Please leave the bus."
#define READY_MSG             "Passengers are welcome! Bus will start in 15 seconds."
#define POSTPONE_MSG          "Tour postponed by 15 seconds. Passengers are welcome!"
#define RIDER_ADDED_WHISPER   "You joined the bus tour."
#define RIDER_DELETED_WHISPER "You left the bus tour."

#include "aw.h"
#include <windows.h>
#include <stdio.h>
#include <time.h>

struct {
  int x;
  int y;
  int z;
  int yaw;
  int pitch;
  int roll;
} waypoints[MAX_WAYPOINTS] = {
// X      Y     Z     YAW   PITCH ROLL
  {0,     0,    0,    0,    0,    0},
  {0,     100,  500,  0,    -16,  32},
  {500,   400,  1000, 160,  -32,  64},
  {1000,  300,  1500, 280,  -16,  128},
  {1500,  200,  2000, 360,  -16,  64},
  {2000,  0,    2000, 450,  0,    -128},
  {1000,  200,  2500, -450, -16,  -256},
  {500,   400,  3000, -300, -16,  -64},
  {0,     900,  3500, -215, -32,  -32},
  {0,     400,  4000, 0,    -32,  0},
  {0,     0,    4200, 0,    -16,  0},
  {0,     900,  4000, 900,  -180, 180},
  {500,   1500, 2000, 2400, -360, 360},
  {-1500, 900,  500,  1200, 180, -90},
  {-800,  400,  -900, 0,    32,   -32},
  {0,     0,    0,    0,    0,    0},
};

struct rider {
  int           session;
  struct rider* next;
};

struct mover {
  unsigned int  id;
  int           state;
  int           init_x;
  int           init_y;
  int           init_z;
  int           init_yaw;
  int           init_pitch;
  int           init_roll;
  int           cur_x;
  int           cur_y;
  int           cur_z;
  int           cur_yaw;
  int           cur_pitch;
  int           cur_roll;
  unsigned int  start;
  unsigned int  last_update;
  unsigned int  last_move;
  float         last_waypoint;
  int           rider_count;
  struct rider* riders;
  struct mover* next;
};

static  struct mover*   movers      = NULL;
static  int             mv_owner    = 0;
static  char            mv_model[MAX_MODEL_NAME];


void log_msg (char* format, ...)
{

  va_list  		args;
  struct tm*            now;
  time_t                secs;
  char                  buffer[1024];

  time (&secs);
  now = localtime (&secs);
  strftime (buffer, sizeof (buffer), "%y-%m-%d %X ", now);
  printf (buffer);

  va_start (args, format);
  __try {
    vsprintf (buffer, format, args);
  }
	__except( EXCEPTION_EXECUTE_HANDLER ) {
    return;
  }
  va_end (args);
  puts (buffer);

}

void announce (char* format, ...)
{

  va_list  		args;
  char                  buffer[1024];

  va_start (args, format);
  __try {
    vsprintf (buffer, format, args);
  }
	__except( EXCEPTION_EXECUTE_HANDLER ) {
    return;
  }
  va_end (args);
  aw_say (buffer);
  log_msg ("announce : \"%s\"", buffer);
}

BOOL rider_add (unsigned int id, int session)
{

  struct mover*   mv;
  struct rider*   rd;
  BOOL            exists;

  if (session == aw_session ())
    return TRUE;  // dont add own session

  for (mv = movers; mv; mv = mv->next)
    if (mv->id == id)
      break;
  if (!mv)
    return FALSE;

  for (exists = FALSE, rd = mv->riders; rd; rd = rd->next)
    if (rd->session == session) {
      exists = TRUE;
      break;  // exists already
    }

  if (!rd
  && !(rd = (struct rider*)calloc (1, sizeof (struct rider))))
    return FALSE;

  rd->session = session;

  if (!exists) {
    if (mv->riders)
      rd->next = mv->riders;
    mv->riders = rd;
    mv->rider_count++;
    aw_whisper (session, RIDER_ADDED_WHISPER);
  }
  return TRUE;

}

BOOL rider_del (unsigned int id, int session)
{

  struct mover*   mv;
  struct rider*   rd;
  struct rider*   prev;

  for (mv = movers; mv; mv = mv->next)
    if (mv->id == id)
      break;
  if (!mv)
    return FALSE; // mover does not exist

  for (prev = NULL, rd = mv->riders; rd; rd = rd->next) {
    if (rd->session == session)
      break;
    prev = rd;
  }
  if (!rd)
    return FALSE;
  if (prev)
    prev->next = rd->next;
  else
    movers->riders = rd->next;
  free (rd);
  mv->rider_count--;
  aw_whisper (session, RIDER_DELETED_WHISPER);
  return TRUE;

}

BOOL mover_add (unsigned int id, int state, int x, int y, int z,
                int yaw, int pitch, int roll)
{

  struct mover*   mv;
  BOOL            exists;

  if (movers)
    return FALSE; // this bot controls one mover only!

  for (exists = FALSE, mv = movers; mv; mv = mv->next)
    if (mv->id == id) {
      exists = TRUE;
      break;  // exists already
    }

  if (!mv
  && !(mv = (struct mover*)calloc (1, sizeof (struct mover))))
    return FALSE;

  mv->id            = id;
  mv->init_x        = x;
  mv->init_y        = y;
  mv->init_z        = z;
  mv->init_yaw      = yaw;
  mv->init_pitch    = pitch;
  mv->init_roll     = roll;
  mv->cur_x         = waypoints[0].x;
  mv->cur_y         = waypoints[0].y;
  mv->cur_z         = waypoints[0].z;
  mv->cur_yaw       = waypoints[0].yaw;
  mv->cur_pitch     = waypoints[0].pitch;
  mv->cur_roll      = waypoints[0].roll;
  mv->last_waypoint = 0.0f;
  mv->start         = aw_tick ();
  mv->state         = state;

  if (!exists) {
    if (movers)
      mv->next = movers;
    movers = mv;
  }
  return TRUE;

}

BOOL mover_change (unsigned int id, int state, int x, int y, int z,
                   int yaw, int pitch, int roll)
{

  struct mover*   mv;

  for (mv = movers; mv; mv = mv->next)
    if (mv->id == id)
      break;
  if (!mv)
    return FALSE;

  mv->state       = state;

  if (state == AW_MOVER_STATE_RESET) {
    mv->cur_x         = waypoints[0].x;
    mv->cur_y         = waypoints[0].y;
    mv->cur_z         = waypoints[0].z;
    mv->cur_yaw       = waypoints[0].yaw;
    mv->cur_pitch     = waypoints[0].pitch;
    mv->cur_roll      = waypoints[0].roll;
    mv->last_waypoint = 0.0f;
    mv->start = aw_tick ();
  }

  return TRUE;

}

BOOL mover_del (unsigned int id)
{

  struct mover*   mv;
  struct mover*   prev;
  struct rider*   rd;
  struct rider*   rd_tmp;

  for (prev = NULL, mv = movers; mv; mv = mv->next) {
    if (mv->id == id)
      break;
    prev = mv;
  }
  if (!mv)
    return FALSE;
  for (rd = mv->riders; rd; ) {
    rd_tmp  = rd;
    rd      = rd->next;
    free (rd_tmp);
  }
  mv->riders = NULL;
  if (prev)
    prev->next = mv->next;
  else
    movers = mv->next;
  free (mv);
  return TRUE;

}

void reset ()
{

  struct mover*   mv;
  struct mover*   mv_tmp;
  struct rider*   rd;
  struct rider*   rd_tmp;

  for (mv = movers; mv; ) {
    for (rd = mv->riders; rd; ) {
      rd_tmp  = rd;
      rd      = rd->next;
      free (rd_tmp);
    }
    mv_tmp = mv;
    mv = mv->next;
    free (mv_tmp);
  }
  movers = NULL;

}

void handle_entity_add (void)
{

  if (aw_int (AW_ENTITY_TYPE) == AW_ENTITY_MOVER
  && aw_int (AW_ENTITY_ID)
  && aw_int (AW_ENTITY_OWNER_CITIZEN) == mv_owner
  && !stricmp (aw_string (AW_OBJECT_MODEL), mv_model)) {
    if (mover_add (aw_int (AW_ENTITY_ID), aw_int (AW_ENTITY_STATE),
      aw_int (AW_OBJECT_X), aw_int (AW_OBJECT_Y), aw_int (AW_OBJECT_Z),
      aw_int (AW_OBJECT_YAW), aw_int (AW_OBJECT_TILT), aw_int (AW_OBJECT_ROLL))) {
      // Reset the mover in case it's used (causes mover to blick 10 seconds)
      if (aw_int (AW_ENTITY_STATE) != AW_MOVER_STATE_IDLE)
        aw_mover_set_state (aw_int (AW_ENTITY_ID), AW_MOVER_STATE_RESET, 0);

      log_msg ("Mover ADD: %u %s", aw_int (AW_ENTITY_ID), aw_string (AW_OBJECT_MODEL));
    }

  }
  
}

void handle_entity_change (void)
{

  if (mover_change (aw_int (AW_ENTITY_ID), aw_int (AW_ENTITY_STATE),
    aw_int (AW_ENTITY_X), aw_int (AW_ENTITY_Y), aw_int (AW_ENTITY_Z),
    aw_int (AW_ENTITY_YAW), aw_int (AW_ENTITY_PITCH), aw_int (AW_ENTITY_ROLL))) {
    // Reset the mover when bot isn't owner! (somewhere else is using it)
    if (aw_int (AW_ENTITY_STATE) != AW_MOVER_STATE_RESET
    && aw_int (AW_ENTITY_STATE) != AW_MOVER_STATE_IDLE
    && aw_int (AW_ENTITY_OWNER_SESSION) != aw_session ())
      aw_mover_set_state (aw_int (AW_ENTITY_ID), AW_MOVER_STATE_RESET, 0);
    log_msg ("Mover CHG: %u state: %d", aw_int (AW_ENTITY_ID), aw_int (AW_ENTITY_STATE));
  }

}

void handle_entity_delete (void)
{

  if (mover_del (aw_int (AW_ENTITY_ID)))
    log_msg ("Mover DEL: %u", aw_int (AW_ENTITY_ID));

}

void handle_entity_rider_add (void)
{

  if (rider_add (aw_int (AW_ENTITY_ID), aw_int (AW_AVATAR_SESSION)))
    log_msg ("Rider ADD: %u session# %d", aw_int (AW_ENTITY_ID), aw_int (AW_AVATAR_SESSION));

}

void handle_entity_rider_change (void)
{

  if (rider_add (aw_int (AW_ENTITY_ID), aw_int (AW_AVATAR_SESSION)))
    log_msg ("Rider CHG: %u session# %d", aw_int (AW_ENTITY_ID), aw_int (AW_AVATAR_SESSION));

}

void handle_entity_rider_delete (void)
{

  if (rider_del (aw_int (AW_ENTITY_ID), aw_int (AW_AVATAR_SESSION)))
    log_msg ("Rider DEL: %u session# %d", aw_int (AW_ENTITY_ID), aw_int (AW_AVATAR_SESSION));

}

void handle_enter (int rc)
{

  reset ();
  if (rc) {
    log_msg ("world enter failed [%d]", rc);
    return;
  }

  // announce our position in the world
  aw_int_set (AW_MY_X, 0);        // 0W
  aw_int_set (AW_MY_Y, 0);        // 0a
  aw_int_set (AW_MY_Z, 0);        // 0N
  aw_int_set (AW_MY_YAW, 0);      // facing south
  aw_int_set (AW_MY_PITCH, 0);
  if (rc = aw_state_change ()) {
    log_msg ("Unable to change state (reason %d)", rc);
    exit (1);
  }

  log_msg ("%s entered world %s",
    aw_string (AW_LOGIN_NAME), aw_string (AW_WORLD_NAME));

}

void handle_world_disconnect (void)
{

  reset ();
  log_msg ("world disconnected [%d]", aw_int (AW_DISCONNECT_REASON));

}

void pause (int id)
{

  aw_mover_set_state (id, AW_MOVER_STATE_PAUSE, 0);

}

void start (int id)
{

  struct mover*   mv;
  struct rider*   rd;
  int             num_riders;
  int             angle;

  aw_mover_set_state (id, AW_MOVER_STATE_CONTINUE, 0);

// optionally position all riders to their seats,
// starting with world build 86 and browser build 969
  for (mv = movers; mv; mv = mv->next)
    if (mv->id == (unsigned int)id)
      break;
  if (!mv)
    return;
  for (num_riders = 0, rd = mv->riders; rd; rd = rd->next)
    num_riders++;
  for (angle = 0, rd = mv->riders; rd; rd = rd->next) {
    aw_mover_rider_change (aw_int (AW_ENTITY_ID), rd->session, 100, angle, -35, 1800, 0);
    angle += 3600 / num_riders;
    log_msg ("Rider %d changed to riding position", rd->session);
  }

}

void move_to (struct mover* mv)
{

  int interval;
  int wp;
  int dx;
  int dy;
  int dz;
  int dyaw;
  int dpitch;
  int droll;

  interval  = aw_int (AW_WORLD_AVATAR_REFRESH_RATE) * WAYPOINT_RESOLUTION;
  wp        = (int)mv->last_waypoint;
  if (wp < 0 || wp >= MAX_WAYPOINTS)
    wp = 0;

  if (wp) {
    dx      = mv->cur_x + ((waypoints[wp].x - waypoints[wp - 1].x) / interval);
    dy      = mv->cur_y + ((waypoints[wp].y - waypoints[wp - 1].y) / interval);
    dz      = mv->cur_z + ((waypoints[wp].z - waypoints[wp - 1].z) / interval);
    dyaw    = mv->cur_yaw + ((waypoints[wp].yaw - waypoints[wp - 1].yaw) / interval);
    dpitch  = mv->cur_pitch + ((waypoints[wp].pitch - waypoints[wp - 1].pitch) / interval);
    droll   = mv->cur_roll + ((waypoints[wp].roll - waypoints[wp - 1].roll) / interval);
  } else {
    dx      = waypoints[wp].x;
    dy      = waypoints[wp].y;
    dz      = waypoints[wp].z;
    dyaw    = waypoints[wp].yaw;
    dpitch  = waypoints[wp].pitch;
    droll   = waypoints[wp].roll;
  }
  mv->cur_x       = dx;
  mv->cur_y       = dy;
  mv->cur_z       = dz;
  mv->cur_yaw     = dyaw;
  mv->cur_pitch   = dpitch;
  mv->cur_roll    = droll;

  aw_mover_set_position (mv->id,
    mv->init_x + dx, mv->init_y + dy, mv->init_z + dz,
    mv->init_yaw + dyaw, mv->init_pitch + dpitch, mv->init_roll + droll);

  mv->last_move = aw_tick ();

  log_msg ("Mover MOV: %u delta: %d %d %d %d %d %d", mv->id,
    dx, dy, dz, dyaw, dpitch, droll);

}

void idle (struct mover* mv)
{

  if (mv->last_move + IDLE_INTERVAL < aw_tick ())
    move_to (mv);

}

int update ()
{

  struct mover*   mv;
  struct rider*   rd;
  int             count;
  unsigned int    frame_time;

  for (count = 0, mv = movers; mv; mv = mv->next) {
              
    if (!mv->start
    || mv->last_update + UPDATE_INTERVAL > aw_tick ())
      continue;

    frame_time = aw_tick () - mv->start;
//    log_msg ("UPDATE   : %u state %d dt %d", mv->id, mv->state, frame_time);

    if (mv->state != AW_MOVER_STATE_PAUSE
    && mv->state != AW_MOVER_STATE_MOVE) {
      if (mv->state != AW_MOVER_STATE_IDLE)
        continue;
      // Set state to Pause, so other users can join in
      pause (mv->id);
      mv->state = STATE_INVALIDATE; // invalidate state
      // Move to first waypoint
      mv->last_waypoint = 0.0f;
      move_to (mv);
      // Add bot as driver to first mover
      if (mv == movers)
        aw_mover_rider_add (mv->id, aw_session (), 450, 1800, -30, 1800, 0);
      mv->start = aw_tick ();
      announce (READY_MSG);
      continue;
    }
    idle (mv); // see if we have to send pos to prevent timeout
    if (frame_time < 15000) {
      // wait to get ready or something else?
      continue;
    } else if (mv->last_waypoint >= 0.0f) {
      if (mv->state != AW_MOVER_STATE_MOVE
      && mv->state != STATE_INVALIDATE) {
        if (mv->rider_count <= 0) {
          mv->start = aw_tick ();
          announce (POSTPONE_MSG);
          continue;
        }
        start (mv->id);
        mv->state = STATE_INVALIDATE; // invalidate state
        announce (START_MSG);
      } else if (mv->state == AW_MOVER_STATE_MOVE) {
        mv->last_waypoint += 1.0f / (float)(aw_int (AW_WORLD_AVATAR_REFRESH_RATE) * WAYPOINT_RESOLUTION);
        if (mv->last_waypoint >= MAX_WAYPOINTS)
          mv->last_waypoint = 0.0f;
        move_to (mv);
        count++;
        if (mv->last_waypoint == 0.0f)
          mv->last_waypoint = -1.0f;
      }
    } else if (mv->last_waypoint < 0.0f && mv->state == AW_MOVER_STATE_MOVE) {
      // Don't pause before mover has reached the last waypoint,
      if (mv->last_update + (2000 / aw_int (AW_WORLD_AVATAR_REFRESH_RATE)) < aw_tick ()) {
        pause (mv->id);
        mv->state = STATE_INVALIDATE; // invalidate state
        announce (STOP_MSG);
      } else
        continue;
    } else if (mv->last_waypoint < 0.0f && mv->state == AW_MOVER_STATE_PAUSE) {
      for (rd = mv->riders; rd; rd = rd->next)
        if (rd->session != aw_session ())
          aw_mover_rider_delete (mv->id, rd->session);
      mv->start = aw_tick ();
      mv->last_waypoint = 0.0f;
      announce (READY_MSG);
      count++;
    }
    mv->last_update = aw_tick ();
  }

  return count;

}

main (int argc, char *argv[])
{

  int   rc;
  int   owner;
	char* pw;
  char* world;

  // check command line
  if (argc < 4) {
    log_msg ("Usage: %s citnumber password world", argv[0]);
    exit (1);
  }

  // initialize Active Worlds API
  if (rc = aw_init (AW_BUILD)) {
    log_msg ("Unable to initialize API (reason %d)", rc);
    exit (1);
  }

  // install handler for callbacks and events
  aw_callback_set (AW_CALLBACK_ENTER, handle_enter);
  aw_event_set (AW_EVENT_WORLD_DISCONNECT, handle_world_disconnect);

  aw_event_set (AW_EVENT_ENTITY_ADD, handle_entity_add);
  aw_event_set (AW_EVENT_ENTITY_CHANGE, handle_entity_change);
  aw_event_set (AW_EVENT_ENTITY_DELETE, handle_entity_delete);
  aw_event_set (AW_EVENT_ENTITY_RIDER_ADD, handle_entity_rider_add);
  aw_event_set (AW_EVENT_ENTITY_RIDER_DELETE, handle_entity_rider_delete);
  aw_event_set (AW_EVENT_ENTITY_RIDER_CHANGE, handle_entity_rider_change);

  // create bot instance
  if (rc = aw_create ("", 0, 0)) {
    log_msg ("Unable to create bot instance (reason %d)", rc);
    exit (1);
  }

  owner = atoi (argv[1]);
  pw    = argv[2];
  world = argv[3];

  // log bot into the universe
  aw_int_set (AW_LOGIN_OWNER, owner);
  aw_string_set (AW_LOGIN_PRIVILEGE_PASSWORD, pw);
  aw_string_set (AW_LOGIN_APPLICATION, "MoverControlSample");
  aw_string_set (AW_LOGIN_NAME, "MoverControl");
  if (rc = aw_login ()) {
    log_msg ("Unable to login (reason %d)", rc);
    exit (1);
  }

  log_msg ("Successfully logged into Universe");

  strcpy (mv_model, "trolly.rwx");
  mv_owner = 101;

  // log bot into the world
  if (rc = aw_enter (world)) {
    log_msg ("Unable to enter world (reason %d)", rc);
    exit (1);
  }

  log_msg ("Successfully entered World %s", world);

  // main event loop
  while (!aw_wait (WAIT_TIME))
    update ();

  // close everything
  aw_destroy ();
  aw_term ();
  return 0;

}

/*-----------------------------------------------------------------------------
                                  E O F
-----------------------------------------------------------------------------*/