/************************************************** * * $Log: quagent_funcs.c,v $ * Revision 1.16 2004/11/09 19:52:43 costello * added runby to protocol and associated run_by functions to monster bot code * * Revision 1.15 2004/02/24 23:54:37 bh * Clamped the turn angle to [0,360), unconditionally. * * Revision 1.14 2004/02/24 23:31:59 bh * Clamped turn angle within 0,360. * * Revision 1.13 2004/02/24 02:46:44 bh * Corrected a typo that gives wrong roll angles. * * Revision 1.12 2004/02/21 04:45:30 bh * Removed one diagnostic statement. * * Revision 1.11 2004/02/20 20:44:02 bh * Reset connected_socket to -1 for dead quagents. * * Revision 1.10 2004/02/09 03:12:02 bh * Added quagent_die(). Put out a message and close the socket. * * Revision 1.9 2004/01/29 04:25:58 bh * Added mutant to the quagent lineup. * * Revision 1.8 2004/01/28 19:10:17 bh * Added monster name mappings. * * Revision 1.7 2004/01/27 16:12:22 bh * Added desired_distance in quagentinfo. * * Revision 1.6 2004/01/25 18:34:45 bh * Added name mapping between quake and our quagent world. * * Revision 1.5 2004/01/22 19:54:09 bh * Not using vtos() anymore. Making the parsing of numbers on the other side a little easier.\n * * Revision 1.4 2004/01/15 17:49:21 bh * * Changed tell_rays and tell_radius. The number of rays and radius are limited * by wisdom, now. * * Revision 1.3 2004/01/12 04:56:01 bh * Added tell_radius and tell_rays. * * Revision 1.2 2004/01/11 21:32:28 bh * * Add set/get functions. * All the functions returns positive values for success. And they are not * responsible for sending feedback to the socket, which has been moved to * quagent.c. * * Revision 1.1 2004/01/09 20:32:00 bh * * Most quagent functions, as specified by the protocol, are in this file. * These functions rely on what's in quagent.c (orignally davebot.c). At * the time of this check-in, some function are just shells. * * **************************************************/ /* * @file quagent_funcs.c * @breif */ #include #include "quagent.h" struct { char *quake_name; char *quagent_name; } name_dict[] = { { "health", "tofu" }, { "ammo pack", "battery" }, { "grenades", "gold"}, { "data cd", "data"}, { "power cube", "kryptonite"}, { "ancient head", "head"}, { "bot_soldier", "mitsu"}, { "bot_berserk", "randal"}, { "bot_chick", "sandhya"}, { "bot_gladiator", "lane"}, { "bot_gunner", "dana"}, { "bot_infantry", "michael"}, { "bot_parasite", "chris"}, { "bot_mutant", "george"} }; #define DICT_SIZE (sizeof(name_dict)/sizeof(name_dict[0])) char *translate_quake2quagent(char *quake_name) { int i; for(i=0; iquagentinfo.connected_socket, "TELL DYING\n"); close(self->quagentinfo.connected_socket); self->quagentinfo.connected_socket = -1; } /** * * @fn void quagent_turn_by(edict_t *self, float yaw_angle) * @brief Turn by specified angle * @param yaw_angle * * @return 1 if succeed, 0 otherwise * */ int quagent_turn_by(edict_t *self, float yaw_angle) { if(self->quagentinfo.energy > 0.1) self->s.angles[YAW] += yaw_angle; // if s.angle[YAW] original is with [0,360), and the user won't give // values outside that range, the if's should keep // it clamped down. Or else, we'd use two while's // what the heck, give them while's while (self->s.angles[YAW] > 360.f) self->s.angles[YAW] -= 360.f; while (self->s.angles[YAW] < 0.f) self->s.angles[YAW] += 360.f; return 1; } /** * * @fn void quagent_walk_by(edict_t *self, float distance) * @brief Walk the robot by distance. * @param distance Distance to walk. * * @return 1 if walking is possible (even if distance is not achieved). * * This function only sets the distance and check if energy is too low to do * the walk, the real walking (animation) is done in individule walkby functions */ int quagent_walk_by(edict_t *self, float distance) { // @see quagent.c: check_quagent_states // It's no "<= 0" here because there could be some leftover, absolute // value of which is less than 0.1, when the energy is decayed. if(self->quagentinfo.energy <= 0.1){ gi.dprintf("quagent_walk_by: energy low, can't move.\n"); self->quagentinfo.walk_distance = 0; return 1; } gi.dprintf("set walk_distance: %s %f\n", self->classname, distance); self->quagentinfo.desired_distance = distance; self->quagentinfo.walk_distance = distance; //self->monsterinfo.currentmove = &quagent_move_walk2; return 1; } /* * this is the same as walk_by without the energy check */ int quagent_run_by(edict_t *self, float distance) { gi.dprintf("set run_distance: %s %f\n", self->classname, distance); self->quagentinfo.desired_distance = distance; self->quagentinfo.walk_distance = distance; return 1; } /** * * @fn int quagent_tell_radius(edict_t *self, float radius, unsigned long mask) * @brief Find all (interested) items within a sphere of radius * @param radius The size of interested region * @param mask Mask of interested items. * * @return 1 * * One choice would be put mask into quagentinfo, with its get/set functions, * so user doesn't have to give the mask every time to query radius. But mask * is an operational thing, rather than a state (comparing with, e.g., alarm_ * mask). It's more naturally to make it a function argument. * * The format of returned message is (in this->message) * ( )* * asterisk instead of + because there may not be anything at all. We don't * return 0 for the no-object case, though. Because the operation itself * still succeeded, even no objects are found. * * If a large radius is given, there are a LOT of items, and we have to write * some ugly code to guarantee message[]'s not out of bound. XXX Maybe we * should put the socket-access code in quagent_funcs after all. * */ int quagent_tell_radius(edict_t *self, float radius, unsigned long mask) { edict_t *ent = NULL; char buffer[QUAGENT_MESSAGE_LEN+1]; vec3_t pos; int num_items = 0; float radius_limit; char *item_name; if(radius < 0) return 0; radius_limit = QUAGENT_INIT_MAX_RADIUS+(QUAGENT_ULT_MAX_RADIUS-QUAGENT_INIT_MAX_RADIUS)*MIN(1,(float)self->quagentinfo.wisdom/QUAGENT_MAX_WISDOM); if(radius> radius_limit) radius = radius_limit; self->quagentinfo.message[0]='\0'; buffer[0]='\0'; while ((ent = findradius(ent, self->s.origin, radius)) != NULL) { if (ent == self){ continue; } item_name = translate_quake2quagent(ent->classname); if(item_name == ent->classname) // not anything we care continue; Com_Printf("Entity seen in %f radius: %s \n",radius,item_name); // XXX: masking //if ( ent->item ){ //sees everything, nost just items ++num_items; VectorSubtract(ent->s.origin, self->s.origin, pos); snprintf(buffer, QUAGENT_MESSAGE_LEN, " %s %g %g %g", item_name,pos[0],pos[1],pos[2]); if(strlen(self->quagentinfo.message)+strlen(buffer)>QUAGENT_MESSAGE_LEN) { gi.dprintf("tell_radius: too many items, message[] out of space.\n"); break; // snprintf will take care prepending num_items } strcat(self->quagentinfo.message, buffer); //} } if(num_items == 0) { self->quagentinfo.message[0]=' '; self->quagentinfo.message[1]='0'; self->quagentinfo.message[2]='\0'; }else { //prepend num_items to message snprintf(buffer, QUAGENT_MESSAGE_LEN, " %d %s", num_items, self->quagentinfo.message); strcpy(self->quagentinfo.message, buffer); } return 1; } /** * * @fn int quagent_tell_ray(edict_t *self, int num_rays) * @brief Tell what n rays evenly distributed around a circle hit * @param num_rays Number of rays * * @return 0 if num_rays < 0, 1 otherwise * * The format of the response is * ( )+ * * XXX potential overflow in quagentinfo.message */ #include #define D2R(a) ((a)*M_PI/180.) int quagent_tell_ray(edict_t *self, int num_rays) { #define LONG_DIST 100000 int i; float theta; vec3_t dir; vec3_t far_point; trace_t tr; char buffer[QUAGENT_MESSAGE_LEN]; int rays_limit; if(num_rays <= 0) return -1; // num_rays is limited by wisdom rays_limit = QUAGENT_INIT_MAX_RAYS+(QUAGENT_ULT_MAX_RAYS-QUAGENT_INIT_MAX_RAYS)*MIN(1,(float)self->quagentinfo.wisdom/QUAGENT_MAX_WISDOM); if(num_rays > rays_limit) num_rays = rays_limit; dir[2] = 0; self->quagentinfo.message[0] = '\0'; // generate num_rays rays for(i=0; is.angles[YAW]); dir[0] = cos(theta); dir[1] = sin(theta); // compute the far end VectorMA(self->s.origin, LONG_DIST, dir, far_point); // trace along the ray //CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER); tr = gi.trace (self->s.origin, NULL, NULL, far_point, self, MASK_ALL); // position of the hit point relative self /* far_point[0] = LONG_DIST*tr.fraction*dir[0]; far_point[1] = LONG_DIST*tr.fraction*dir[1]; far_point[2] = 0.; */ VectorSubtract(tr.endpos, self->s.origin, far_point); if(tr.ent) { char *item_name = translate_quake2quagent(tr.ent->classname); snprintf(buffer, QUAGENT_MESSAGE_LEN, " %d %s %g %g %g ", i+1, item_name, far_point[0],far_point[1],far_point[2]); }else { int type = gi.pointcontents(tr.endpos); switch (type) { case CONTENTS_SOLID: snprintf(buffer, QUAGENT_MESSAGE_LEN, "%d SOLIDWALL %g %g %g ", i+1, far_point[0],far_point[1],far_point[2]); break; case CONTENTS_WINDOW: snprintf(buffer, QUAGENT_MESSAGE_LEN, "%d WINDOW %g %g %g ", i+1, far_point[0],far_point[1],far_point[2]); break; case CONTENTS_LAVA: snprintf(buffer, QUAGENT_MESSAGE_LEN, "%d LAVA %g %g %g ", i+1, far_point[0],far_point[1],far_point[2]); break; case CONTENTS_SLIME: snprintf(buffer, QUAGENT_MESSAGE_LEN, "%d SLIME %g %g %g ", i+1, far_point[0],far_point[1],far_point[2]); break; case CONTENTS_WATER: snprintf(buffer, QUAGENT_MESSAGE_LEN, "%d WATER %g %g %g ", i+1, far_point[0],far_point[1],far_point[2]); break; case CONTENTS_MONSTER: snprintf(buffer, QUAGENT_MESSAGE_LEN, "%d MONSTER %g %g %g ", i+1, far_point[0],far_point[1],far_point[2]); break; case CONTENTS_DEADMONSTER: snprintf(buffer, QUAGENT_MESSAGE_LEN, "%d DEADMONSTER %g %g %g ", i+1, far_point[0],far_point[1],far_point[2]); break; case CONTENTS_LADDER: snprintf(buffer, QUAGENT_MESSAGE_LEN, "%d LADDER %g %g %g ", i+1, far_point[0],far_point[1],far_point[2]); break; default: snprintf(buffer, QUAGENT_MESSAGE_LEN, "%d OTHER %g %g %g ", i+1, far_point[0],far_point[1],far_point[2]); break; } } if(strlen(self->quagentinfo.message)+strlen(buffer) >QUAGENT_MESSAGE_LEN) { gi.dprintf("tell_rays: too many items, message[] out of space.\n"); break; } strcat(self->quagentinfo.message, buffer); } return 1; #undef LONG_DIST } /** * * @fn int quagent_camera_off(edict_t *self) * @brief Return the camera to client * * @return 1 * */ int quagent_camera_off(edict_t *self) { // reshow the gun (sdl: commented-out because appears broken: shows gun if it wasn't // visible before) // self->client->ps.gunindex = gi.modelindex(self->client->pers.weapon->view_model); self->client->camera_on = 0; self->client->camera = 0; return 1; } /** * * @fn int quagent_camera_on(edict_t *self) * @brief Displace the camera to self * * @return 1 * */ int quagent_camera_on(edict_t *self) { self->client->camera_on = 1; // hide the gun self->client->ps.gunindex = 0; self->client->camera = self; return 1; } /** * * @fn void quagent_image(edict_t *self, int *w, int *h, int *bytesperrow, char **image) * @brief Take a picture with current camera. * @param w Width of the (returned) image * @param h Height of the (returned) image * @param bytesperrow Bytes per row * @param image Pointer to the image. * * Note: it's not necessary to copy the framebuffer to private store. * The whole game runs in a single process and there is no race condition * (partly updated framebuffer) * */ int quagent_image(edict_t *self, int *size, char **image) { int w, h, bytesperrow; *image = (char*)gi.GetFramebuffer(&w, &h, &bytesperrow); *size = h * bytesperrow; snprintf(self->quagentinfo.message, QUAGENT_MESSAGE_LEN, "%d %d %d\n", w, h, bytesperrow); return 1; } int quagent_trigger_alarm(edict_t *self, unsigned char alarm, ...) { char buffer[512]; va_list arglist; //check if this alarm is masked if(!(alarm & self->quagentinfo.alarm_mask)) return; va_start(arglist, alarm); switch (alarm) { case ALR_HITOBJECTS: //name of the object strncpy(buffer, va_arg(arglist, char*), 511); break; case ALR_ENERGYLOW: break; } return 1; } // set and get functions int quagent_set_alarm(edict_t *self, unsigned long mask) { self->quagentinfo.alarm_mask = mask; return 1; } int quagent_set_age_threshold(edict_t *self, int threshold) { self->quagentinfo.tell_old_threshold = threshold; return 1; } int quagent_set_energy_threshold(edict_t *self, float threshold) { self->quagentinfo.tell_weak_threshold = threshold; return 1; } int quagent_get_alarm(edict_t *self) { snprintf(self->quagentinfo.message, QUAGENT_MESSAGE_LEN, " %lu ", self->quagentinfo.alarm_mask); return 1; } int quagent_get_wealth(edict_t *self) { snprintf(self->quagentinfo.message, QUAGENT_MESSAGE_LEN, " %d ", self->quagentinfo.wealth); return 1; } int quagent_get_energy(edict_t *self) { snprintf(self->quagentinfo.message, QUAGENT_MESSAGE_LEN, " %g ", self->quagentinfo.energy); return 1; } int quagent_get_health(edict_t *self) { snprintf(self->quagentinfo.message, QUAGENT_MESSAGE_LEN, " %d ", self->health); return 1; } int quagent_get_age(edict_t *self) { snprintf(self->quagentinfo.message, QUAGENT_MESSAGE_LEN, " %d ", self->quagentinfo.age); return 1; } // XXX: we don't do much with velocity yet, return a constant int quagent_get_where(edict_t *self) { snprintf(self->quagentinfo.message, QUAGENT_MESSAGE_LEN, " %g %g %g %g %g %g 1 ", self->s.origin[0],self->s.origin[1],self->s.origin[2], self->s.angles[0],self->s.angles[1], self->s.angles[2]); return 1; } int quagent_set_walk_speed(edict_t *self, float speed) { self->quagentinfo.speed = speed; return 1; } int quagent_get_walk_speed(edict_t *self) { snprintf(self->quagentinfo.message, QUAGENT_MESSAGE_LEN, " %f ", self->quagentinfo.speed); return 1; } int quagent_get_wellbeing(edict_t *self) { snprintf(self->quagentinfo.message, QUAGENT_MESSAGE_LEN, " %d %d %d %d %f ", self->quagentinfo.age, self->health, self->quagentinfo.wealth, self->quagentinfo.wisdom, self->quagentinfo.energy); return 1; } int quagent_get_inventory(edict_t *self) { int i; char *item_name; self->quagentinfo.message[0]=' '; self->quagentinfo.message[1]='\0'; for (i=0; iquagentinfo.invcount; ++i) { item_name = translate_quake2quagent(self->quagentinfo.inventory[i]->pickup_name); if(strlen(self->quagentinfo.message)+strlen(item_name) >= QUAGENT_MESSAGE_LEN-1) break; strcat(self->quagentinfo.message, " "); strcat(self->quagentinfo.message, item_name); } return 1; }