Mover Control example code
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 -----------------------------------------------------------------------------*/