#define AW_NO_FUNCTION_MAPPING // for UTF-8 or older single-byte and multi-byte character sets
#include <windows.h>
#include "reasons.h"
#include "aw.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define MAX_AVATARS_IN_SCENE 35
#define MAX_HUD_LINES 10
#define MAX_LINE_LENGTH 50
#define MAX_HUD_LINE_LINES 5
#define HEARTBEAT_INTERVAL 30
struct AVATAR
{
int session;
char name[18];
};
struct HUD_LINE
{
int id;
int num_lines;
char lines[MAX_HUD_LINE_LINES][MAX_LINE_LENGTH + 1];
};
struct AVATAR avatars[MAX_AVATARS_IN_SCENE];
struct HUD_LINE hud_lines[MAX_HUD_LINES];
void handle_avatar_add(void);
void handle_avatar_delete(void);
void handle_address(int rc);
void handle_chat(void);
void update_hud_messages(void);
void wrap_line(const char* line, struct HUD_LINE* hud_line, int max_line_length);
void send_heartbeat(void); // Prototype heartbeat function, keeps bot from timing out
int main(int argc, char* argv[])
{
int rc;
// Check command line
if (argc < 4)
{
printf("To begin type: DWbot1.exe cit-number password world\n", argv[0]);
return 1;
}
// Initialize Delta Worlds API
rc = aw_init(AW_BUILD);
if (rc != RC_SUCCESS)
{
printf("Unable to initialize API (reason %d)\n", rc);
return 1;
}
// Variable to track last heartbeat time
time_t last_heartbeat = time(NULL);
// Install handler for avatar_add, avatar_delete, and chat events
aw_event_set(AW_EVENT_AVATAR_ADD, handle_avatar_add);
aw_event_set(AW_EVENT_AVATAR_DELETE, handle_avatar_delete);
aw_event_set(AW_EVENT_CHAT, handle_chat);
/* Install callback for aw_address */
aw_callback_set(AW_CALLBACK_ADDRESS, handle_address);
/* create bot instance */
if ((rc = aw_create("auth.deltaworlds.com", 6671, 0)) != 0) {
printf("Unable to create bot instance (reason %d)\n", rc);
exit(1);
}
// Log bot into the universe
aw_int_set(AW_LOGIN_OWNER, atoi(argv[1]));
aw_string_set(AW_LOGIN_PRIVILEGE_PASSWORD, argv[2]);
aw_string_set(AW_LOGIN_APPLICATION, "OCMBOTv1");
aw_string_set(AW_LOGIN_NAME, "HUDchat");
rc = aw_login();
if (rc != RC_SUCCESS)
{
printf("Unable to login (reason %d)\n", rc);
return 1;
}
// Enter bot into the world
rc = aw_enter(argv[3]);
if (rc != RC_SUCCESS)
{
printf("Unable to enter world (reason %d)\n", rc);
return 1;
}
// Announce position in the world
aw_int_set(AW_MY_X, 1000); // 1W
aw_int_set(AW_MY_Z, -7000); // 7S
aw_int_set(AW_MY_YAW, 2250); // Face towards GZ
rc = aw_state_change();
if (rc != RC_SUCCESS)
{
printf("Unable to change state (reason %d)\n", rc);
return 1;
}
// Main event loop
while (aw_wait(-1) == RC_SUCCESS) {
if (difftime(time(NULL), last_heartbeat) >= HEARTBEAT_INTERVAL) {
send_heartbeat();
last_heartbeat = time(NULL); // Update the last heartbeat time
}
// Close everything down
aw_destroy();
aw_term();
return 0;
}
}
void send_heartbeat() {
// Heartbeat function - sends clear command to chat to keep from timing out.
aw_say("/clear");
}
void handle_avatar_add(void)
{
int i;
int rc;
// Add avatar to the scene
for (i = 0; i < MAX_AVATARS_IN_SCENE; i++)
{
if (avatars[i].session == 0)
{
avatars[i].session = aw_int(AW_AVATAR_SESSION);
strcpy(avatars[i].name, aw_string(AW_AVATAR_NAME));
break;
}
}
aw_address(aw_int(AW_AVATAR_SESSION));
// Say Hello (remove slashes from the following lines to enable greeting message) //
// char message[100];
// static char avatar_name[AW_MAX_ATTRIBUTE];
// strcpy(avatar_name, aw_string(AW_AVATAR_NAME));
// sprintf(message, "Ahoy there %s!", avatar_name);
// aw_say(message);
}
void handle_avatar_delete(void)
{
int i;
// Remove avatar from the scene
for (i = 0; i < MAX_AVATARS_IN_SCENE; i++)
{
if (avatars[i].session == aw_int(AW_CHAT_SESSION))
{
avatars[i].session = 0; // Mark struct as unused
strcpy(avatars[i].name, "");
break;
}
}
// Say Bye (remove slashes from the following lines to enable farewell message) //
// char message[100];
// static char avatar_name[AW_MAX_ATTRIBUTE];
// strcpy(avatar_name, aw_string(AW_AVATAR_NAME));
// sprintf(message, "Bye, %s!", avatar_name);
// aw_say(message);
}
void handle_address(int rc)
{
/* Keep in mind that AW_AVATAR_NAME is not defined within the context of this callback */
int i;
char hud_message[100];
char name[256];
char msg[256];
strcpy(name, "<UNKNOWN>");
/* Find name of the avatar */
for (i = 0; i < MAX_AVATARS_IN_SCENE; i++)
{
if (avatars[i].session == aw_int(AW_AVATAR_SESSION))
{
strcpy(name, avatars[i].name);
break;
}
}
if (rc != RC_SUCCESS)
{
sprintf(msg, "%s, I cannot determine your IP address (reason %d)", name, rc);
}
else
{
int address;
unsigned char* p = (unsigned char*)&address;
address = aw_int(AW_AVATAR_ADDRESS);
/* The address is in network byte order which means that the most significant byte comes first in memory */
sprintf(hud_message, "%s [%u.%u.%u.%u] enters", name, p[0], p[1], p[2], p[3]);
/* log the event to the console */
printf("[%u.%u.%u.%u] %s entered chat.\n", p[0], p[1], p[2], p[3], name);
// Shift up old messages etc
for (i = 0; i < MAX_HUD_LINES - 1; i++)
{
hud_lines[i].id = hud_lines[i + 1].id;
hud_lines[i].num_lines = hud_lines[i + 1].num_lines;
for (int j = 0; j < hud_lines[i].num_lines; j++)
{
strcpy(hud_lines[i].lines[j], hud_lines[i + 1].lines[j]);
}
}
hud_lines[MAX_HUD_LINES - 1].id = 0; // We don't need the session ID for this message
wrap_line(hud_message, &hud_lines[MAX_HUD_LINES - 1], MAX_LINE_LENGTH);
// Update HUD messages
update_hud_messages();
}
}
void handle_chat(void)
{
char message[301];
static char avatar_name[AW_MAX_ATTRIBUTE];
strcpy(avatar_name, aw_string(AW_AVATAR_NAME));
// Check for the "/clear" command
if (strcmp(aw_string(AW_CHAT_MESSAGE), "/clear") == 0)
{
// Clear HUD
for (int i = 0; i < MAX_HUD_LINES; i++)
{
hud_lines[i].id = 0;
hud_lines[i].num_lines = 0;
}
// Display "Chat cleared" message
wrap_line("Chat cleared", &hud_lines[MAX_HUD_LINES - 1], MAX_LINE_LENGTH);
}
else
{
// Handle normal chat messages
snprintf(message, 300, "%s: %s", avatar_name, aw_string(AW_CHAT_MESSAGE));
// Shift up old messages
for (int i = 0; i < MAX_HUD_LINES - 1; i++)
{
hud_lines[i].id = hud_lines[i + 1].id;
hud_lines[i].num_lines = hud_lines[i + 1].num_lines;
for (int j = 0; j < hud_lines[i].num_lines; j++)
{
strcpy(hud_lines[i].lines[j], hud_lines[i + 1].lines[j]);
}
}
// Check if the chat message starts with "/me" and remove for action
if (strncmp(aw_string(AW_CHAT_MESSAGE), "/me", 3) == 0) {
snprintf(message, 300, "%s %s", avatar_name, aw_string(AW_CHAT_MESSAGE) + 4); // +4 to remove the "/me " from the message
}
hud_lines[MAX_HUD_LINES - 1].id = aw_int(AW_CHAT_SESSION);
wrap_line(message, &hud_lines[MAX_HUD_LINES - 1], MAX_LINE_LENGTH);
}
// Update HUD messages
update_hud_messages();
}
void wrap_line(const char* line, struct HUD_LINE* hud_line, int max_line_length)
{
const char* line_end = line + strlen(line);
const char* line_start = line;
hud_line->num_lines = 0;
while (line_start < line_end && hud_line->num_lines < MAX_HUD_LINE_LINES)
{
const char* line_break = line_start + max_line_length;
if (line_break > line_end)
{
line_break = line_end;
}
else
{
while (line_break > line_start && *line_break != ' ')
{
line_break--;
}
if (line_break == line_start)
{
line_break = line_start + max_line_length;
}
}
strncpy(hud_line->lines[hud_line->num_lines], line_start, line_break - line_start);
hud_line->lines[hud_line->num_lines][line_break - line_start] = '\0';
hud_line->num_lines++;
line_start = line_break;
while (*line_start == ' ')
{
line_start++;
}
}
}
void update_hud_messages(void)
{
int rc;
// Destroy old HUD elements
for (int i = 0; i < MAX_HUD_LINES; i++)
{
for (int j = 0; j < MAX_HUD_LINE_LINES; j++)
{
aw_hud_destroy(0, i * MAX_HUD_LINE_LINES + j + 1);
}
}
// Create new HUD elements
int y_offset = 0;
for (int i = 0; i < MAX_HUD_LINES; i++)
{
for (int j = 0; j < hud_lines[i].num_lines; j++)
{
// create the HUD element
aw_int_set(AW_HUD_ELEMENT_TYPE, AW_HUD_TYPE_TEXT);
aw_string_set(AW_HUD_ELEMENT_TEXT, hud_lines[i].lines[j]);
aw_int_set(AW_HUD_ELEMENT_ID, y_offset + 1);
aw_int_set(AW_HUD_ELEMENT_SESSION, 0);
aw_int_set(AW_HUD_ELEMENT_ORIGIN, AW_HUD_ORIGIN_TOP_LEFT);
aw_float_set(AW_HUD_ELEMENT_OPACITY, 1.0f);
aw_int_set(AW_HUD_ELEMENT_X, -64);
aw_int_set(AW_HUD_ELEMENT_Y, 50 + y_offset * 28); // Adjust the spacing between lines
aw_int_set(AW_HUD_ELEMENT_Z, 1);
aw_int_set(AW_HUD_ELEMENT_FLAGS, AW_HUD_ELEMENT_FLAG_CLICKS);
aw_int_set(AW_HUD_ELEMENT_SIZE_X, 800);
aw_int_set(AW_HUD_ELEMENT_SIZE_Y, 30); // Increase the text size
aw_int_set(AW_HUD_ELEMENT_FLAGS, AW_HUD_ELEMENT_FLAG_TRANSITION | true);
// Set the color to green for the IP address and name announcement message
if (strstr(hud_lines[i].lines[j], "enters") != NULL && strstr(hud_lines[i].lines[j], "[") != NULL && strstr(hud_lines[i].lines[j], "]") != NULL) {
aw_int_set(AW_HUD_ELEMENT_COLOR, 0x00FF00); // Green color
}
else {
aw_int_set(AW_HUD_ELEMENT_COLOR, 0xFFFFFF); // White color
}
rc = aw_hud_create();
if (rc != RC_SUCCESS)
{
printf("Unable to create HUD element (reason %d)\n", rc);
}
y_offset++;
}
}
}