/* pr_cmds.c (description) Copyright (C) 1996-1997 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to: Free Software Foundation, Inc. 59 Temple Place - Suite 330 Boston, MA 02111-1307, USA */ static const char rcsid[] = "$Id$"; #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef HAVE_ERRNO_H # include #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #include #include "QF/clip_hull.h" #include "QF/cmd.h" #include "QF/cvar.h" #include "QF/mathlib.h" #include "QF/msg.h" #include "QF/progs.h" #include "QF/sys.h" #include "QF/va.h" #include "QF/zone.h" #include "compat.h" const char *pr_gametype = ""; /* BUILT-IN FUNCTIONS */ // FIXME: Hunk_TempAlloc, Sys_Printf, Cvar_*, PR_SetString, PR_RunError, ED_PrintEdicts, PF_traceon, PF_traceoff, ED_PrintNum, PR_FindBuiltin isn't threadsafe/reentrant const char * PF_VarString (progs_t *pr, int first) { char *out; int len, i; for (len = 0, i = first; i < pr->pr_argc; i++) len += strlen (G_STRING (pr, (OFS_PARM0 + i * 3))); out = Hunk_TempAlloc (len + 1); out[0] = 0; for (i = first; i < pr->pr_argc; i++) strcat (out, G_STRING (pr, (OFS_PARM0 + i * 3))); return out; } /* PF_normalize vector normalize(vector) */ void PF_normalize (progs_t *pr) { float new; float *value1; vec3_t newvalue; value1 = G_VECTOR (pr, OFS_PARM0); new = value1[0] * value1[0] + value1[1] * value1[1] + value1[2] * value1[2]; new = sqrt (new); if (new == 0) newvalue[0] = newvalue[1] = newvalue[2] = 0; else { new = 1 / new; newvalue[0] = value1[0] * new; newvalue[1] = value1[1] * new; newvalue[2] = value1[2] * new; } VectorCopy (newvalue, G_VECTOR (pr, OFS_RETURN)); } /* PF_vlen scalar vlen(vector) */ void PF_vlen (progs_t *pr) { float new; float *value1; value1 = G_VECTOR (pr, OFS_PARM0); new = value1[0] * value1[0] + value1[1] * value1[1] + value1[2] * value1[2]; new = sqrt (new); G_FLOAT (pr, OFS_RETURN) = new; } /* PF_vectoyaw float vectoyaw(vector) */ void PF_vectoyaw (progs_t *pr) { float yaw; float *value1; value1 = G_VECTOR (pr, OFS_PARM0); if (value1[1] == 0 && value1[0] == 0) yaw = 0; else { yaw = (int) (atan2 (value1[1], value1[0]) * 180 / M_PI); if (yaw < 0) yaw += 360; } G_FLOAT (pr, OFS_RETURN) = yaw; } /* PF_vectoangles vector vectoangles(vector) */ void PF_vectoangles (progs_t *pr) { float forward, pitch, yaw; float *value1; value1 = G_VECTOR (pr, OFS_PARM0); if (value1[1] == 0 && value1[0] == 0) { yaw = 0; if (value1[2] > 0) pitch = 90; else pitch = 270; } else { yaw = (int) (atan2 (value1[1], value1[0]) * 180 / M_PI); if (yaw < 0) yaw += 360; forward = sqrt (value1[0] * value1[0] + value1[1] * value1[1]); pitch = (int) (atan2 (value1[2], forward) * 180 / M_PI); if (pitch < 0) pitch += 360; } G_FLOAT (pr, OFS_RETURN + 0) = pitch; G_FLOAT (pr, OFS_RETURN + 1) = yaw; G_FLOAT (pr, OFS_RETURN + 2) = 0; } /* PF_Random Returns a number from 0<= num < 1 random() */ void PF_random (progs_t *pr) { float num; num = (rand () & 0x7fff) / ((float) 0x7fff); G_FLOAT (pr, OFS_RETURN) = num; } /* PF_break break() */ void PF_break (progs_t *pr) { Sys_Printf ("break statement\n"); *(int *) -4 = 0; // dump to debugger // PR_RunError (pr, "break statement"); } #if 0 /* PF_localcmd Sends text to the host's execution buffer localcmd (string) */ void PF_localcmd (progs_t *pr) { const char *str; str = G_STRING (pr, OFS_PARM0); Cbuf_AddText (str); } #endif /* PF_cvar float cvar (string) */ void PF_cvar (progs_t *pr) { const char *str; str = G_STRING (pr, OFS_PARM0); G_FLOAT (pr, OFS_RETURN) = Cvar_VariableValue (str); } /* PF_cvar_set float cvar (string) */ void PF_cvar_set (progs_t *pr) { const char *var_name, *val; cvar_t *var; var_name = G_STRING (pr, OFS_PARM0); val = G_STRING (pr, OFS_PARM1); var = Cvar_FindVar (var_name); if (!var) var = Cvar_FindAlias (var_name); if (!var) { Sys_Printf ("PF_cvar_set: variable %s not found\n", var_name); return; } Cvar_Set (var, val); } void PF_fabs (progs_t *pr) { float v; v = G_FLOAT (pr, OFS_PARM0); G_FLOAT (pr, OFS_RETURN) = fabs (v); } // entity (entity start, .string field, string match) find = #5; void PF_Find (progs_t *pr) { const char *s = 0, *t; // ev_string int i; // ev_vector int e, f; etype_t type; ddef_t *field_def; edict_t *ed; e = G_EDICTNUM (pr, OFS_PARM0); f = G_INT (pr, OFS_PARM1); field_def = ED_FieldAtOfs (pr, f); if (!field_def) PR_RunError (pr, "PF_Find: bad search field"); type = field_def->type & ~DEF_SAVEGLOBAL; if (type == ev_string) { s = G_STRING (pr, OFS_PARM2); if (!s) PR_RunError (pr, "PF_Find: bad search string"); } for (e++; e < *pr->num_edicts; e++) { ed = EDICT_NUM (pr, e); if (ed->free) continue; switch (type) { case ev_string: t = E_STRING (pr, ed, f); if (!t) continue; if (strcmp (t, s)) continue; RETURN_EDICT (pr, ed); return; case ev_float: if (G_FLOAT (pr, OFS_PARM2) != E_FLOAT (ed, f)) continue; RETURN_EDICT (pr, ed); return; case ev_vector: for (i = 0; i <= 2; i++) if (G_FLOAT (pr, OFS_PARM2 + i) != E_FLOAT (ed, f + i)) continue; RETURN_EDICT (pr, ed); return; case ev_integer: case ev_entity: if (G_INT (pr, OFS_PARM2) != E_INT (ed, f)) continue; RETURN_EDICT (pr, ed); return; default: PR_Error (pr, "PF_Find: unsupported search field"); } } RETURN_EDICT (pr, *pr->edicts); } void PF_coredump (progs_t *pr) { ED_PrintEdicts (pr, ""); } void PF_traceon (progs_t *pr) { pr->pr_trace = true; } void PF_traceoff (progs_t *pr) { pr->pr_trace = false; } void PF_eprint (progs_t *pr) { ED_PrintNum (pr, G_EDICTNUM (pr, OFS_PARM0)); } void PF_dprint (progs_t *pr) { Sys_Printf ("%s", PF_VarString (pr, 0)); } void PF_rint (progs_t *pr) { float f; f = G_FLOAT (pr, OFS_PARM0); if (f > 0) G_FLOAT (pr, OFS_RETURN) = (int) (f + 0.5); else G_FLOAT (pr, OFS_RETURN) = (int) (f - 0.5); } void PF_floor (progs_t *pr) { G_FLOAT (pr, OFS_RETURN) = floor (G_FLOAT (pr, OFS_PARM0)); } void PF_ceil (progs_t *pr) { G_FLOAT (pr, OFS_RETURN) = ceil (G_FLOAT (pr, OFS_PARM0)); } /* PF_nextent entity nextent(entity) */ void PF_nextent (progs_t *pr) { int i; edict_t *ent; i = G_EDICTNUM (pr, OFS_PARM0); while (1) { i++; if (i == *pr->num_edicts) { RETURN_EDICT (pr, *pr->edicts); return; } ent = EDICT_NUM (pr, i); if (!ent->free) { RETURN_EDICT (pr, ent); return; } } } // we assume that ints are smaller than floats #ifdef FLT_MAX_10_EXP # define STRING_BUF (FLT_MAX_10_EXP + 8) #else # define STRING_BUF 128 #endif /* PF_ftoi integer (float f) ftoi */ void PF_ftoi (progs_t *pr) { G_INT (pr, OFS_RETURN) = G_FLOAT (pr, OFS_PARM0); } /* PF_ftos string (float f) ftos */ void PF_ftos (progs_t *pr) { char string[STRING_BUF]; int i; // trimming 0s idea thanks to Maddes i = snprintf (string, sizeof (string), "%1.6f", G_FLOAT (pr, OFS_PARM0)) - 1; for (; i > 0; i--) { if (string[i] == '0') string[i] = '\0'; else if (string[i] == '.') { string[i] = '\0'; break; } else break; } RETURN_STRING (pr, string); } /* PF_itof float (integer i) itof */ void PF_itof (progs_t *pr) { G_FLOAT (pr, OFS_RETURN) = G_INT (pr, OFS_PARM0); } /* PF_itos string (integer i) itos */ void PF_itos (progs_t *pr) { char string[STRING_BUF]; snprintf (string, sizeof (string), "%d", G_INT (pr, OFS_PARM0)); RETURN_STRING (pr, string); } /* PF_stof float (string s) stof */ void PF_stof (progs_t *pr) { G_FLOAT (pr, OFS_RETURN) = atof (G_STRING (pr, OFS_PARM0)); } /* PF_stoi integer (string s) stoi */ void PF_stoi (progs_t *pr) { G_INT (pr, OFS_RETURN) = atoi (G_STRING (pr, OFS_PARM0)); } /* PF_stov vector (string s) stov */ void PF_stov (progs_t *pr) { float v[3] = {0, 0, 0}; sscanf (G_STRING (pr, OFS_PARM0), "'%f %f %f'", v, v + 1, v + 2); RETURN_VECTOR (pr, v); } /* PF_vtos string (vector v) vtos */ void PF_vtos (progs_t *pr) { char string[STRING_BUF * 3 + 5]; snprintf (string, sizeof (string), "'%5.1f %5.1f %5.1f'", G_VECTOR (pr, OFS_PARM0)[0], G_VECTOR (pr, OFS_PARM0)[1], G_VECTOR (pr, OFS_PARM0)[2]); RETURN_STRING (pr, string); } /* PF_strlen float(string s) strlen */ void PF_strlen (progs_t *pr) { const char *s; s = G_STRING (pr, OFS_PARM0); G_FLOAT (pr, OFS_RETURN) = strlen(s); } /* PF_charcount float(string char, string s) charcount */ void PF_charcount (progs_t *pr) { char goal; const char *s; int count; goal = (G_STRING (pr, OFS_PARM0))[0]; if (goal == '\0') { G_FLOAT (pr, OFS_RETURN) = 0; return; } count = 0; s = G_STRING (pr, OFS_PARM1); while (*s) { if (*s == goal) count++; s++; } G_FLOAT (pr, OFS_RETURN) = count; } #if (INT_MAX == 2147483647) && (INT_MIN == -2147483648) # define INT_WIDTH 11 #else /* I hope... */ # define INT_WIDTH 20 #endif #define MAX_ARG 23 void PF_sprintf (progs_t *pr) { char *format; char *c; // current char *out = 0; char new_format[INT_WIDTH * 2 + 9]; // "%0-+ #." and conversion int fmt_alternate, fmt_leadzero, fmt_leftjust, fmt_minwidth, fmt_precision, fmt_signed, fmt_space, fmt_type, looping, new_format_i, ret; int curarg = 3, out_max = 32, out_size = 0; format = G_STRING (pr, OFS_PARM0); c = format; out = malloc (out_max); if (!out) goto mallocerror; while (*c) { if (*c == '%' && c[1] != '%' && c[1] != 's') { c++; if (curarg > MAX_ARG) goto maxargs; // flags looping = 1; fmt_leadzero = 0; fmt_leftjust = 0; fmt_signed = 0; fmt_space = 0; fmt_alternate = 0; while (looping) { switch (*c) { case '0': fmt_leadzero = 1; break; case '-': fmt_leftjust = 1; break; case '+': fmt_signed = 1; break; case ' ': fmt_space = 1; break; case '#': fmt_alternate = 1; break; case '\0': goto endofstring; default: looping = 0; continue; } c++; } // minimum field width fmt_minwidth = 0; if (*c >= '1' && *c <= '9') while (*c >= '0' && *c <= '9') { fmt_minwidth *= 10; fmt_minwidth += *c - '0'; c++; } else if (*c == '*') { fmt_minwidth = G_INT (pr, OFS_PARM0 + curarg); curarg += 3; } // precision fmt_precision = -1; if (*c == '.') { c++; if (*c >= '0' && *c <= '9') { fmt_precision = 0; while (*c >= '0' && *c <= '9') { fmt_precision *= 10; fmt_precision += *c - '0'; c++; } } else if (*c == '*') { fmt_precision = G_INT (pr, OFS_PARM0 + curarg); curarg += 3; } } if (!*c) goto endofstring; // length? Nope, not in QC fmt_type = *c++; // some preperation if (fmt_precision < 0) switch (fmt_type) { case 'i': fmt_precision = 0; break; case 'f': fmt_precision = 6; break; case 'v': fmt_precision = 1; break; } // built the format string new_format_i = 0; new_format[new_format_i++] = '%'; if (fmt_leadzero) new_format[new_format_i++] = '0'; if (fmt_leftjust) new_format[new_format_i++] = '-'; if (fmt_signed) new_format[new_format_i++] = '+'; if (fmt_space) new_format[new_format_i++] = ' '; if (fmt_alternate) new_format[new_format_i++] = '#'; if (fmt_minwidth) if ((new_format_i += snprintf (new_format + new_format_i, sizeof (new_format) - new_format_i, "%d", fmt_minwidth)) >= sizeof (new_format)) PR_Error (pr, "PF_sprintf: new_format overflowed?!"); if (fmt_type != 'i') { new_format[new_format_i++] = '.'; if ((new_format_i += snprintf (new_format + new_format_i, sizeof (new_format) - new_format_i, "%d", fmt_precision)) >= sizeof (new_format)) PR_Error (pr, "PF_sprintf: new_format overflowed?!"); } switch (fmt_type) { case 'i': new_format[new_format_i++] = 'd'; break; case 'f': case 'v': new_format[new_format_i++] = 'f'; break; default: PR_Error (pr, "PF_sprintf: unknown type '%c'!", *c); } new_format[new_format_i++] = '\0'; switch (fmt_type) { case 'i': while ((ret = snprintf (&out[out_size], out_max - out_size, new_format, G_INT (pr, OFS_PARM0 + curarg))) >= out_max - out_size) { char *o; out_max *= 2; o = realloc (out, out_max); if (!o) goto mallocerror; out = o; } out_size += ret; curarg += 3; break; case 'f': while ((ret = snprintf (&out[out_size], out_max - out_size, new_format, G_FLOAT (pr, OFS_PARM0 + curarg))) >= out_max - out_size) { char *o; out_max *= 2; o = realloc (out, out_max); if (!o) goto mallocerror; out = o; } out_size += ret; curarg += 3; break; case 'v': { int i; for (i = 0; i <= 2; i++) { if (curarg > MAX_ARG) goto maxargs; while ((ret = snprintf (&out[out_size], out_max - out_size, new_format, G_FLOAT (pr, OFS_PARM0 + curarg))) >= out_max - out_size) { char *o; out_max *= 2; o = realloc (out, out_max); if (!o) goto mallocerror; out = o; } out_size += ret; curarg++; i++; } break; } } } else if (*c == '%' && *(c + 1) == 's') { char *s; if (curarg > MAX_ARG) goto maxargs; s = G_STRING (pr, OFS_PARM0 + curarg); while ((ret = snprintf (&out[out_size], out_max - out_size, "%s", s)) >= out_max - out_size) { char *o; out_max *= 2; o = realloc (out, out_max); if (!o) goto mallocerror; out = o; } out_size += ret; curarg += 3; c += 2; } else { if (*c == '%') c++; if (out_size == out_max) { char *o; out_max *= 2; o = realloc (out, out_max); if (!o) goto mallocerror; out = o; } out[out_size] = *c; out_size++; c++; } } if (out_size == out_max) { char *o; out_max *= 2; o = realloc (out, out_max); if (!o) goto mallocerror; out = o; } out[out_size] = '\0'; RETURN_STRING (pr, out); free (out); return; mallocerror: // if (errno == ENOMEM) // hopefully we can free up some mem so it can be used during shutdown // free (out); PR_Error (pr, "PF_sprintf: memory allocation error!\n"); endofstring: PR_Error (pr, "PF_sprintf: unexpected end of string!\n"); maxargs: PR_Error (pr, "PF_sprintf: argument limit exceeded\n"); } void PR_gametype (progs_t *pr) { RETURN_STRING (pr, pr_gametype); } void PR_Cmds_Init (progs_t *pr) { PR_AddBuiltin (pr, "break", PF_break, 6); // void () break PR_AddBuiltin (pr, "random", PF_random, 7); // float () random PR_AddBuiltin (pr, "normalize", PF_normalize, 9); // vector (vector v) normalize PR_AddBuiltin (pr, "vlen", PF_vlen, 12); // float (vector v) vlen PR_AddBuiltin (pr, "vectoyaw", PF_vectoyaw, 13); // float (vector v) vectoyaw PR_AddBuiltin (pr, "find", PF_Find, 18); // entity (entity start, .(...) fld, ... match) find PR_AddBuiltin (pr, "dprint", PF_dprint, 25); // void (string s) dprint PR_AddBuiltin (pr, "coredump", PF_coredump, 28); // void () coredump PR_AddBuiltin (pr, "traceon", PF_traceon, 29); // void () traceon PR_AddBuiltin (pr, "traceoff", PF_traceoff, 30); // void () traceoff PR_AddBuiltin (pr, "eprint", PF_eprint, 31); // void (entity e) eprint PR_AddBuiltin (pr, "rint", PF_rint, 36); // float (float v) rint PR_AddBuiltin (pr, "floor", PF_floor, 37); // float (float v) floor PR_AddBuiltin (pr, "ceil", PF_ceil, 38); // float (float v) ceil PR_AddBuiltin (pr, "fabs", PF_fabs, 43); // float (float f) fabs PR_AddBuiltin (pr, "cvar", PF_cvar, 45); // float (string s) cvar #if 0 PR_AddBuiltin (pr, "localcmd", PF_localcmd, 46); // void (string s) localcmd #endif PR_AddBuiltin (pr, "nextent", PF_nextent, 47); // entity (entity e) nextent PR_AddBuiltin (pr, "vectoangles", PF_vectoangles, 51); // vector (vector v) vectoangles PR_AddBuiltin (pr, "cvar_set", PF_cvar_set, 72); // void (string var, string val) cvar_set PR_AddBuiltin (pr, "strlen", PF_strlen, 100); // float (string s) strlen PR_AddBuiltin (pr, "charcount", PF_charcount, 101); // float (string goal, string s) charcount PR_AddBuiltin (pr, "sprintf", PF_sprintf, 109); // string (...) sprintf PR_AddBuiltin (pr, "ftos", PF_ftos, 26); // string (float f) ftos PR_AddBuiltin (pr, "ftoi", PF_ftoi, 110); // integer (float f) ftoi PR_AddBuiltin (pr, "itof", PF_itof, 111); // float (integer i) itof PR_AddBuiltin (pr, "itos", PF_itos, 112); // string (integer i) itos PR_AddBuiltin (pr, "stof", PF_stof, 81); // float (string s) stof PR_AddBuiltin (pr, "stoi", PF_stoi, 113); // integer (string s) stoi PR_AddBuiltin (pr, "stov", PF_stov, 114); // vector (string s) stov PR_AddBuiltin (pr, "vtos", PF_vtos, 27); // string (vector v) vtos PR_AddBuiltin (pr, "gametype", PR_gametype, 115); // string () gametype };