/* v1.1 * * smisc.c: Miscellaneous code. * * This program is free software and may be freely redistributed as * specified in the GNU General Public License. Please see the file * 'COPYING' for details. */ #include #include #include #include "spaceconf.h" #include "pseint.h" #include "dbint.h" #include "space.h" #include "class.h" #include "object.h" #include "smisc.h" void MFNotify(SHIP *ship, dbref player, unsigned int oflags, unsigned int mflags, const char *message, ...) { char *buff; dbref eto[MAX_CONSOLES], user; int etnum, elist, eflag; int cnum; va_list list; buff = pse_malloc(LARGE_BUF_SIZE); va_start(list, message); vsprintf(buff, message, list); va_end(list); etnum = 0; if (player != PSE_NOTHING) { Notify(player, buff); eto[0] = player; etnum = 1; } if (ship == NULL) { pse_free(buff); return; } for (cnum = 0; cnum < ship->num_consoles; cnum ++) { if (((ship->consoles[cnum].flags & mflags) == mflags) && ((ship->consoles[cnum].flags & oflags) != 0)) { user = dbrefUser(ship->consoles[cnum].dbref); for (elist = eflag = 0; elist < etnum; elist ++) { if (eto[elist] == user) { eflag = 1; break; } } if (eflag == 0) { Notify(user, buff); eto[etnum] = user; etnum ++; } } } pse_free(buff); } int MetricScale(float value, unsigned int width, unsigned int options, char *output) { float sval; unsigned int s; char ms; const char div_string[] = {'K', 'M', 'G', 'T', '\0' }; const char mul_string[] = {'m', 'u', 'n', 'p', '\0' }; if ((width < 4) || (width > 32)) { strcpy(output, "Invalid width specified."); return 0; } ms = ' '; /* Do the scaling on a positive float to reduce bloat */ sval = (value >= 0.0) ? value : -value; /* If the value is under 1, do some multiplying up */ if ((sval < 1.0) && (sval != 0.0)) { for (s = 0; (sval < 1.0) && (mul_string[s] != '\0'); s++) { sval = sval * 1000.0; } /* If the value is smaller than we can show, set it to 0 */ if (sval < 1.0) { sval = 0.0; ms = ' '; } else ms = mul_string[s-1]; } /* If the value is over 1000, do some dividing */ if (sval >= 1000.0) { for (s = 0; (sval >= 1000.0) && (div_string[s] != '\0'); s++) sval = sval / 1000.0; if (sval >= 1000.0) ms = '\0'; else ms = div_string[s-1]; } /* If ms is '\0', the value couldn't be scaled enough, so return the right amount of --- */ if (ms == '\0') { strncpy(output, "---.---------------", width); output[width-1] = ' '; output[width] = '\0'; return -1; } s = 0; /* Pad with a leading - if number is negative and not suppressing */ if ((value < 0.0) && ((options & 1) == 0)) output[s++] = '-'; /* Pad with a leading + if number is positive and not suppressing */ if ((value >= 0.0) && ((options & 2) == 0)) output[s++] = '+'; /* Pad with a leading space if signs are suppressed and not suppressing the leading spaces */ if ((s == 0) && ((options & 4) == 0)) output[s++] = ' '; if (width-s < 5) { strcpy(output, "Invalid width specified."); return 0; } /* Output the floating point number */ snprintf(&output[s], width-s, "%-.32f", sval); /* Add the scale factor identifier, and terminator */ output[width-1] = ms; output[width] = '\0'; return width; } void FNotify(dbref target, const char *message, ...) { char *buff; va_list list; buff = pse_malloc(LARGE_BUF_SIZE); va_start(list, message); vsprintf(buff, message, list); va_end(list); Notify(target, buff); pse_free(buff); } void sph_to_xyz(SPH coord, XYZ *new_coord) { float r; #ifdef ENABLE_FLOATS r = (float) coord.range; #else r = (float) coord.range + 0.9; #endif new_coord->x = r * (cos(coord.bearing * PI / 180.0) * cos(coord.elevation * PI / 180.0)); new_coord->y = r * (sin(coord.bearing * PI / 180.0) * cos(coord.elevation * PI / 180.0)); new_coord->z = r * (sin(coord.elevation * PI / 180.0)); return; } void xyz_to_sph(XYZ coord, SPH *new_coord) { double x = (double) coord.x; double y = (double) coord.y; double z = (double) coord.z; if (y == 0) new_coord->bearing = (x >= 0) ? 0.0 : 180.0; else if (x == 0) new_coord->bearing = (y > 0.0) ? 90.0 : 270.0; else { new_coord->bearing = atan(y / x) * 180.0 / PI; if (x < 0.0) new_coord->bearing +=180.0; } if (z == 0) new_coord->elevation = 0.0; else if (x == 0 && y == 0) new_coord->elevation = (z > 0) ? 90.0 : -90.0; else new_coord->elevation = atan(z / sqrt((double) x*x + (double) y*y)) * 180.0 / PI; new_coord->range = (range_t) sqrt((double) x*x + (double) y*y + (double) z*z); /* bring bearing into range */ if (new_coord->bearing < 0.0) new_coord->bearing += 360.0; if (new_coord->bearing >= 360.0) new_coord->bearing -=360.0; return; } range_t distance(const XYZ *a, const XYZ *b) { double dx, dy, dz; double sum; dx = (double) b->x - (double) a->x; dy = (double) b->y - (double) a->y; dz = (double) b->z - (double) a->z; sum = dx*dx + dy*dy + dz*dz; return (range_t) sqrt(sum); } void log_space(const char *message, ...) { char *buff; va_list list; buff = pse_malloc(LARGE_BUF_SIZE); va_start(list, message); vsprintf(buff, message, list); va_end(list); fprintf(stderr, "SPACE- %s\n", buff); if (ValidObject(getDebugCharacter())) FNotify(getDebugCharacter(), "SPACE- %s", buff); pse_free(buff); } void fillXYZCoords(const char *str, XYZ *coords) { char buf[100]; const char *ptr; strncpy(buf, str, 100); buf[99] = '\0'; coords->x = 0; coords->y = 0; coords->z = 0; ptr=strtok(buf, " "); if (ptr) coords->x = RANGEI(ptr); ptr=strtok(NULL, " "); if (ptr) coords->y = RANGEI(ptr); ptr=strtok(NULL, " "); if (ptr) coords->z = RANGEI(ptr); return; } void fillSPHCoords(dbref obj, const char *attrname, SPH *coords) { const char *ptr; char *buff; buff = pse_malloc(MAX_ATTRIBUTE_LEN); getAttrByNameLen(buff, obj, attrname, MAX_ATTRIBUTE_LEN); if (buff[0] == '\0') { coords->bearing = 0; coords->elevation = 0; coords->range = 0; pse_free(buff); return; } ptr=strtok(buff, " "); if (ptr) coords->bearing = atof(ptr); ptr=strtok(NULL, " "); if (ptr) coords->elevation = atof(ptr); ptr=strtok(NULL, " "); if (ptr) coords->range = RANGEI(ptr); pse_free(buff); return; } int calcFacingShield(XYZ source, TAG *target) { float dx1, dy1, dz1, dx2, dy2, dz2, dx3, dy3, dz3, dyr, dzr; float theta, psi, omega; float angle_from_front; int zgy; struct timeval now; XYZ pos1; /* Protect against accessing non-ship objects. */ if (!Ship(target)) { log_space("Tried to calcFacingShield for a non-ship (#%d)", target->rec->db_obj); return 0; } /* Short-circuit the check for uni-shielded objects */ if (F_INT(target->shipdata->rec, shield_config) == SHIELD_CONFIG_ONE) return 0; gettimeofday(&now, NULL); object_position(target, &pos1, &now, 0); dx1 = (float) source.x - pos1.x; dy1 = (float) source.y - pos1.y; dz1 = (float) source.z - pos1.z; psi = (target->heading.bearing * PI / 180.0); theta = (target->heading.elevation * PI / 180.0); /* first transformation -- NOTICE Euler is using Euler angles. * If you don't believe it, look them up. How ironic. */ dx2 = dx1*cos(psi) + dy1*sin(psi); dy2 = dy1*cos(psi) - dx1*sin(psi); dz2 = dz1; /* second transformation */ dx3 = dx2*cos(theta) + dz2*sin(theta); dy3 = dy2; dz3 = dz2*cos(theta) - dx2*sin(theta); /* third transformation. Basically a rotation around the X axis now. */ omega = target->roll * PI / 180.0; dyr = dy3*cos(omega) + dz3*sin(omega); dzr = dz3*cos(omega) - dy3*sin(omega); dy3 = dyr; dz3 = dzr; if (dx3 == 0.0) angle_from_front = 90.0; else angle_from_front = acos(dx3 / sqrt((double) dx3*dx3 + (double) dy3*dy3 + (double) dz3*dz3)) * 180.0 / PI; /* * dx3, dy3 and dz3 are distances in the source object's normalised * coordinate system. * angle_from_front is incident angle. */ switch(F_INT(target->shipdata->rec, shield_config)) { case SHIELD_CONFIG_ORANGE1: zgy = (int) ((dz3 >= 0.0) ? dz3 : -dz3) > ((dy3 >= 0.0) ? dy3 : -dy3); /* If Z > Y, then it's the dorsal/ventral shield */ if (zgy) { if (dz3 >= 0.0) return DORSAL_SHIELD; else return VENTRAL_SHIELD; } if (dy3 >= 0.0) return STARBOARD_SHIELD; return PORT_SHIELD; case SHIELD_CONFIG_ORANGE2: if ((dz3 >= 0.0) && (dy3 >= 0.0)) return 1; if ((dz3 < 0.0) && (dy3 >= 0.0)) return 3; if ((dz3 >= 0.0) && (dy3 < 0.0)) return 0; return 2; case SHIELD_CONFIG_CUBE: if (angle_from_front <= 45.0) return FORE_SHIELD; if (angle_from_front >= 135.0) return AFT_SHIELD; zgy = (int) ((dz3 >= 0.0) ? dz3 : -dz3) > ((dy3 >= 0.0) ? dy3 : -dy3); /* If Z > Y, then it's the dorsal/ventral shield */ if (zgy) { if (dz3 >= 0.0) return DORSAL_SHIELD; else return VENTRAL_SHIELD; } if (dy3 >= 0.0) return STARBOARD_SHIELD; return PORT_SHIELD; case SHIELD_CONFIG_ORIG6: if (angle_from_front <= 60.0) return FORE_SHIELD; if (angle_from_front >= 120.0) return AFT_SHIELD; zgy = (int) ((dz3 >= 0.0) ? dz3 : -dz3) > ((dy3 >= 0.0) ? dy3 : -dy3); /* If Z > Y, then it's the dorsal/ventral shield */ if (zgy) { if (dz3 >= 0.0) return DORSAL_SHIELD; else return VENTRAL_SHIELD; } if (dy3 >= 0.0) return STARBOARD_SHIELD; return PORT_SHIELD; case SHIELD_CONFIG_ORIG4: default: if (angle_from_front <= 60.0) return FORE_SHIELD; if (angle_from_front >= 120.0) return AFT_SHIELD; if (dy3 < 0.0) return PORT_SHIELD; return STARBOARD_SHIELD; } } /* * capstr: CAUTION - not reentrant. */ char *capstr(const char *string) { static char str[100]; /* Just make sure we weren't passed an empty string */ if ((string != NULL) && (string[0] != '\0')) { snprintf(str, 100, "%c%s", toupper(string[0]), string+1); str[99] = '\0'; } else str[0] = '\0'; return str; } /* * next_dbref: Routine to handle iterating through a list of dbrefs in a * string. (i.e. #1 #2 #3). Note that this function does not modify the * original string is modified and is reentrant. */ /* Get the next dbref in the list. */ const char *next_dbref(const char *str, dbref* pnum) { long int num; char *nptr; /* Skip leading spaces */ for ( ; *str == ' '; str++) ; /* Check for a # */ if (*str++ != '#') { *pnum=PSE_NOTHING; return NULL; } /* Ensure that it's really a number, not a +/- sign */ if (!isdigit(*str) || *str == '\0') { *pnum=PSE_NOTHING; return NULL; } num = strtol(str, &nptr, 10); /* End of string, or gap to next reference */ if ((*nptr == '\0') || (*nptr == ' ')) { *pnum = (dbref) num; return nptr; } /* conversion stopped at a non-digit that wasn't a space */ *pnum = PSE_NOTHING; return NULL; } /* * Called to return, and optionally update the position of a ship * at some time. The position is based on time of last course change, * position of last course change, and the requested time. */ void object_position(TAG *obj, XYZ *pos, struct timeval *at, int upd) { if (obj->speed == 0) { pos->x = obj->dc_pos.x; pos->y = obj->dc_pos.y; pos->z = obj->dc_pos.z; } else { /* FIXME: Not exactly right. Need to account for speed and * direction changes. It'll do for now. deltat's usually * small enough that we're not greatly bothered about * tracing arcs / spirals to deal with finate time turns * and speed changes. As we're going to use this to update * the position of the object in future, maybe we will * eventually wish to fix it. (Or call it often enough we * claim it's correct enough not to worry about) */ double deltat; double dist; /* Calculate the time difference in seconds */ deltat = (at->tv_sec - obj->dc_time.tv_sec) + ((at->tv_usec - obj->dc_time.tv_usec) / 1000000.0); #ifndef ENABLE_FLOATS /* Add a 0.9 to account for rounding */ dist = object->speed * object->speed * object->speed + 0.9; #else /* Distance is speed ^ 3 at warp * Distance is a linear derating at impulse */ if (object->speed >= 1.0) dist = object->speed * object->speed * object->speed; else dist = FULL_IMPULSE_DISTANCE * object->speed; #endif #ifdef ENABLE_ODOMETER if (Ship(obj)) F_INT(obj->shipdata->rec, odometer) += dist; #endif /* The basic linear position calculation */ pos->x = obj->dc_pos.x + obj->v_move[0] * dist * deltat; pos->y = obj->dc_pos.y + obj->v_move[1] * dist * deltat; pos->z = obj->dc_pos.z + obj->v_move[2] * dist * deltat; } /* Update time and position if we were requested to */ if (upd == 1) { obj->dc_pos.x = pos->x; obj->dc_pos.y = pos->y; obj->dc_pos.z = pos->z; obj->dc_time.tv_sec = at->tv_sec; obj->dc_time.tv_usec = at->tv_usec; } } #ifdef WIN32 int gettimeofday(struct timeval *tv, void *tz) { MMTIME win_time; timeGetSystemTime(&win_time, sizeof(win_time)); tv->tv_sec = win_time.u.ms / 1000; tv->tv_usec = (win_time.u.ms % 1000) *1000; } #endif