Nuclide
Software Development Kit for id Technology (BETA)
defs.h
1/*
2 * Copyright (c) 2016-2024 Vera Visions LLC.
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
13 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
14 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15*/
16
17#include "../common/defs.h"
18
19.float w_reload_next;
20.float w_attack_next;
21
22/* networking helpers */
23#define NETWORKED_INT(x) int x; int x ##_net;
24#define NETWORKED_FLOAT(x) float x; float x ##_net;
25#define NETWORKED_VECTOR(x) vector x; vector x ##_net;
26#define NETWORKED_ENT(x) entity x; entity x ##_net;
27#define NETWORKED_STRING(x) string x; string x ##_net;
28#define NETWORKED_BOOL(x) bool x; bool x ##_net;
29#define NETWORKED_MODELINDEX(x) float x; float x ##_net;
30
31#define NETWORKED_INT_N(x) int x ##_net;
32#define NETWORKED_FLOAT_N(x) float x ##_net;
33#define NETWORKED_VECTOR_N(x) vector x ##_net;
34#define NETWORKED_STRING_N(x) string x ##_net;
35
36#define PREDICTED_INT(x) int x; int x ##_net;
37#define PREDICTED_FLOAT(x) float x; float x ##_net;
38#define PREDICTED_VECTOR(x) vector x; vector x ##_net;
39#define PREDICTED_ENT(x) entity x; entity x ##_net;
40#define PREDICTED_STRING(x) string x; string x ##_net;
41#define PREDICTED_BOOL(x) bool x; bool x ##_net;
42
43#define PREDICTED_INT_N(x) int x ##_net;
44#define PREDICTED_FLOAT_N(x) float x ##_net;
45#define PREDICTED_VECTOR_N(x) vector x ##_net;
46#define PREDICTED_STRING_N(x) string x ##_net;
47
48#ifdef CLIENT
49#define NSENTITY_READENTITY(x, y) \
50 { \
51 local x x ##_e = ( x )self;\
52 local float x ##receivedFlags;\
53 if (y == true) { \
54 self.classname = strcat("spawnfunc_", #x); \
55 callfunction(self.classname); \
56 } \
57 x ##receivedFlags = readfloat();\
58 x ##_e.ReceiveEntity( y, x ##receivedFlags );\
59 x ##_e.Relink();\
60 x ##_e._ReceiveComplete( y, x ##receivedFlags );\
61 }
62#else
63
64#endif
65
66#define NETWORKED_DEFAULT(x, y) x ##_net = x = y;
67
68#define ROLL_BACK(x) x = x ##_net;
69#define SAVE_STATE(x) x ##_net = x;
70#define SAVE_STATE_FIELD(x, y) x ##_net[y] = x[y];
71#define ATTR_CHANGED(x) (x ##_net != x)
72#define VEC_CHANGED(x,y) (x ##_net[y] != x[y])
73
74#ifndef MAX_AMMO_TYPES
75#define MAX_AMMO_TYPES 16i
76#endif
77
78noref const float SVC_TEMPENTITY = 23;
79
80#ifdef CLIENT
81string __fullspawndata;
82#endif
83
84#include "cloader.h"
85#include "sound.h"
86#include "effects.h"
87
88#ifdef CLIENT
89#include "../gs-entbase/client/defs.h"
90#else
91#include "../gs-entbase/server/defs.h"
92#endif
93
94#include "Decal.h"
95
96#include "../botlib/botinfo.h"
97#include "sentences.h"
98
99#include "IO.h"
100#include "Dict.h"
101#include "Trigger.h"
102#include "Entity.h"
103#include "Timer.h"
104#include "RenderableEntity.h"
105#include "SurfacePropEntity.h"
106#include "Ragdoll.h"
107#include "Mover.h"
108#include "PhysicsConstraint.h"
109#include "PhysicsEntity.h"
110#include "BrushTrigger.h"
111#include "PointTrigger.h"
112#include "Item.h"
113#include "Weapon.h"
114#include "Actor.h"
115#include "Monster.h"
116#include "SquadMonster.h"
117#include "TalkMonster.h"
118#include "SpawnPoint.h"
119#include "SoundScape.h"
120#include "Attack.h"
121#include "Projectile.h"
122#include "Spraylogo.h"
123#include "Portal.h"
124#include "Sound.h"
125#include "Debris.h"
126
127#include "xr.h"
128#include "../botlib/Bot.h"
129#include "Client.h"
130#include "Spectator.h"
131#include "pmove.h"
132#include "Player.h"
133
134#include "Vehicle.h"
135
136#include "materials.h"
137#include "damage.h"
138#include "entities.h"
139#include "hitmesh.h"
140#include "propdata.h"
141#include "surfaceproperties.h"
142#include "decalgroups.h"
143#include "bodyque.h"
144#include "motd.h"
145#include "util.h"
146#include "ammo.h"
147#include "activities.h"
148
149#define BSPVER_PREREL 28
150#define BSPVER_Q1 29
151#define BSPVER_HL 30
152#define BSPVER_Q2 38
153#define BSPVER_Q2W 69
154#define BSPVER_Q3 46
155#define BSPVER_RTCW 47
156#define BSPVER_RBSP 1
157
158#define CLASSEXPORT(a,b) void a(void) { if (!isfunction(#b)) { self.classname = strcat("spawnfunc_", #b); } else { self.classname = #b; } callfunction(self.classname); }
159
160const vector VEC_HULL_MIN = [-16,-16,-36];
161const vector VEC_HULL_MAX = [16,16,36];
162const vector VEC_CHULL_MIN = [-16,-16,-18];
163const vector VEC_CHULL_MAX = [16,16,18];
164
165#include "input.h"
166
167/* sendflags */
168#define UPDATE_ALL 16777215
169
170#define clamp(d,min,max) bound(min,d,max)
171
173{
174 SEARCH_INSENSITIVE,
175 SEARCH_FULLPACKAGE,
176 SEARCH_ALLOWDUPES,
177 SEARCH_FORCESEARCH,
178 SEARCH_MULTISEARCH,
179 SEARCH_NAMESORT
180};
181
182.float jumptime;
183.float teleport_time;
184.vector basevelocity;
185.float gflags;
186.float identity;
187.bool _isWeapon;
188.bool _isItem;
189
190void
191Empty(void)
192{
193
194}
195
196void Util_Destroy(void);
197string Util_TimeToString(float fTime);
198bool Util_IsTeamplay(void);
199bool Util_IsPaused(void);
200
201void
202crossprint(string m)
203{
204#ifdef CLIENT
205 print(strcat("CLIENT: ", m));
206#else
207 print(strcat("SERVER: ", m));
208#endif
209}
210
211#if 0
212__wrap string
213precache_model(string m)
214{
215 if not (m) {
216 breakpoint();
217 return "";
218 }
219
220 if (m == "") {
221 breakpoint();
222 return "";
223 }
224
225 return prior(m);
226}
227#endif
228
229__wrap string
230precache_sound(string sample)
231{
232 if (sample != "") /* not empty */
233 if not(whichpack(strcat("sound/", sample))) { /* not present on disk */
234 NSError("SFX sample %S does not exist.", sample);
235 return "misc/missing.wav";
236 }
237
238 return prior(sample);
239}
240
241/* this could probably be a lot better, use this from now on so that it can be improved later */
242noref float input_sequence;
243float
244pseudorandom()
245{
246 float seed = (float)input_sequence % 5.0f;
247 seed += (float)input_sequence % 8.0f;
248 seed += (float)input_sequence % 4.0f;
249 seed += (float)input_sequence % 13.0f;
250 seed += (float)input_sequence % 70.0f;
251
252 /* like the engine its random(), never return 0, never return 1 */
253 return bound(0.01, (seed) / 100.0f, 0.99f);
254}
255
256#if 0
257__wrap void
258WriteByte(float to, float val)
259{
260 breakpoint();
261 prior(to, val);
262}
263
264__wrap void
265WriteChar(float to, float val)
266{
267 breakpoint();
268 prior(to, val);
269}
270
271__wrap void
272WriteShort(float to, float val)
273{
274 breakpoint();
275 prior(to, val);
276}
277
278__wrap void
279WriteLong(float to, float val)
280{
281 breakpoint();
282 prior(to, val);
283}
284
285__wrap void
286WriteCoord(float to, float val)
287{
288 breakpoint();
289 prior(to, val);
290}
291
292__wrap void
293WriteAngle(float to, float val)
294{
295 breakpoint();
296 prior(to, val);
297}
298
299__wrap void
300WriteString(float to, string val)
301{
302 breakpoint();
303 prior(to, val);
304}
305
306__wrap void
307WriteEntity(float to, entity val)
308{
309 breakpoint();
310 prior(to, val);
311}
312#endif
313
314__wrap void
315setmodel(entity ent, string mname)
316{
317 if (mname != "") /* not empty */
318 if (substring(mname, 0, 1) != "*") /* not a brush */
319 if not(whichpack(mname)) /* not present on disk */
320 return prior(ent, "models/error.vvm");
321
322 return prior(ent, mname);
323}
324
325__wrap __variant*
326memalloc(int size)
327{
328#if 0
329 if (size > 55000)
330 breakpoint();
331
332 print(sprintf("memalloc: %i\n", size));
333#endif
334 return prior(size);
335}
336
337.float removed;
338__wrap void
339remove(entity target)
340{
341 /* it's an ncEntity sub-class */
342 if (target.identity) {
343 ncEntity ent = (ncEntity)target;
344
345 /* if we're calling remove() on it and not .Destroy()... it's being uncleanly removed! */
346 if (ent.removed == 0) {
347 ent.OnRemoveEntity();
348 breakpoint();
349 print(sprintf("^1WARNING: Entity %d of class %s uncleanly removed!\n", num_for_edict(ent), ent.classname));
350 ent.removed = 1;
351 }
352 }
353
354 target.removed = 0;
355 prior(target);
356}
357
358__wrap void
359traceline(vector v1, vector v2, float flags, entity ent)
360{
361#ifdef SERVER
362 if (autocvar(com_showTracers, 0) == 1) {
363 WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
364 WriteByte(MSG_MULTICAST, EV_TRACEDEBUG);
365 WriteCoord(MSG_MULTICAST, v1[0]);
366 WriteCoord(MSG_MULTICAST, v1[1]);
367 WriteCoord(MSG_MULTICAST, v1[2]);
368 WriteCoord(MSG_MULTICAST, v2[0]);
369 WriteCoord(MSG_MULTICAST, v2[1]);
370 WriteCoord(MSG_MULTICAST, v2[2]);
371 msg_entity = world;
372 multicast(v1, MULTICAST_PVS_R);
373 }
374#endif
375
376#ifdef CLIENT
377 // TODO
378#endif
379 prior(v1, v2, flags, ent);
380}
381
382#ifdef SERVER
383string Skill_GetStringValue(string, string);
384#endif
385
386string
387unpackStringCommand(string commandString)
388{
389#ifdef SERVER
390 /* is this supposed to be read from a skill cvar? */
391 if (substring(commandString, 0, 6) == "skill:") {
392 return Skill_GetStringValue(substring(commandString, 6, -1), "");
393 }
394 /* is this supposed to be read from a skill cvar? */
395 if (substring(commandString, 0, 5) == "cvar:") {
396 return cvar_string(substring(commandString, 5, -1));
397 }
398#endif
399
400 return Constants_LookUp(commandString, commandString);
401}
402
403#ifdef NO_LEGACY
404void
405readcmd(string foo)
406{
407 localcmd(foo);
408}
409#else
410void(string cmd) readcmd = #0:readcmd;
411#endif
412
413/* This is required because people who use Hammer do awful things
414 to get their models to update. We get a multitude of juicy
415 hacks and symbols that Half-Life's engine strips and now we have to
416 replicate this behaviour. Be thankful this is not done in-engine for
417 every game/mod ever.
418*/
419string Util_FixModel(string mdl)
420{
421 if (!STRING_SET(mdl)) {
422 return "";
423 }
424 mdl = strreplace("\\", "/", mdl);
425
426 /* don't allow empty directories, some Source maps have them. */
427 mdl = strreplace("//", "/", mdl);
428
429 int c = tokenizebyseparator(mdl, "/", "\\ ", "!");
430 string newpath = "";
431
432 for (int i = 0; i < c; i++) {
433 newpath = sprintf("%s/%s", newpath, argv(i));
434 }
435
436 /* Kill the first / */
437 newpath = substring(newpath, 1, strlen(newpath)-1);
438
439 /* Now we need to fix \/ because I hate people */
440 c = tokenizebyseparator(newpath, "\\/");
441 mdl = "";
442 for (int i = 0; i < c; i++) {
443 mdl = sprintf("%s/%s", mdl, argv(i));
444 }
445 /* Kill the first / again */
446 mdl = substring(mdl, 1, strlen(mdl)-1);
447
448 if (substring(mdl, 0, 1) == "/")
449 mdl = substring(mdl, 1, -1);
450
451 return mdl;
452}
453
457string
458Util_ChangeExtension(string baseString, string newExtension)
459{
460 float stringOffset = 0;
461 string tempString = "";
462 float foundOffset = 0;
463
464 while ((tempString = substring(baseString, stringOffset, 1))) {
465 if (tempString == ".")
466 foundOffset = stringOffset;
467 if (tempString == "")
468 break;
469 if not (tempString)
470 break;
471
472 stringOffset++;
473 }
474
475 /* no extension found? append to the end then. */
476 if (foundOffset == 0) {
477 return strcat(baseString, ".", newExtension);
478 }
479
480 return strcat(substring(baseString, 0, foundOffset), ".", newExtension);
481}
482
483bool
484Util_IsSingleplayer(void)
485{
486#ifdef SERVER
487 /* playerslots 1 is always singleplayer */
488 if (cvar("sv_playerslots") == 1)
489 return true;
490
491 /* only when coop is 1, only in multiplayer */
492 if (cvar("coop") == 1 && cvar("sv_playerslots") > 1)
493 return true;
494#endif
495
496 /* else we're multiplayer */
497 return false;
498}
499
500float
501crandom(void)
502{
503 return ((random() - 0.5f) * 2.0f);
504}
505
506string
507dirname(string input)
508{
509 if (!input) {
510 return "";
511 }
512
513 int c = tokenizebyseparator(input, "/", "\\ ", "!");
514 string newpath = "";
515
516 for (int i = 0; i < (c-1); i++) {
517 newpath = sprintf("%s/%s", newpath, argv(i));
518 }
519
520 /* Kill the first / */
521 newpath = substring(newpath, 1, strlen(newpath)-1);
522
523 return newpath;
524}
525
526string
527textfile_to_string(string filename)
528{
529 string fileContents = __NULL__;
530
531 filestream fileHandle = fopen(filename, FILE_READ);
532 string temp;
533
534 if (fileHandle != -1) {
535 while ((temp = fgets(fileHandle))) {
536 fileContents = strcat(fileContents, temp, "\n");
537 }
538 } else {
539 fileContents = __NULL__;
540 }
541
542 return fileContents;
543}
544
545/* moved from src/botlib/route.qc */
546/* Get's a velocity vector with which we can successfully jump from one place to another */
547vector
548Route_GetJumpVelocity(vector vecFrom, vector vecTo, float flGravMod)
549{
550#if 1
551 float flHeight, flGravity, flTime, flDistance, flDir;
552 vector vecJump = [0,0,0];
553
554 if (flGravMod <= 0.0)
555 flGravMod = 1.0f;
556
557 flGravity = serverkeyfloat("phy_gravity") * flGravMod;
558 flHeight = vecTo[2] - vecFrom[2];
559
560 /* this may not be a much verticality to this jump, use distance instead */
561 if (flHeight <= 0) {
562 flHeight = vlen(vecTo - vecFrom);
563 flTime = flHeight / flGravity;
564 } else {
565 flTime = sqrt(flHeight / (flGravity * 0.5f));
566 if (flTime <= 0) {
567 return [0,0,0];
568 }
569 }
570
571 vecJump = vecTo - vecFrom;
572 vecJump[2] = 0;
573 flDistance = vlen(normalize(vecJump));
574
575 flDir = flDistance / flTime;
576 vecJump *= flDir;
577 vecJump[2] = flTime * flGravity;
578#else
579 vector vecJump = [0,0,0];
580 float flDist = vlen(vecTo - vecFrom);
581 makevectors(vectoangles(vecTo - vecFrom));
582 vecJump = v_forward * flDist;
583 vecJump[2] = 280;
584#endif
585 return vecJump;
586}
587
588void
589DebugBox(vector absPos, vector minSize, vector maxSize, vector boxColor, float boxAlpha)
590{
591 vector a, b, c, d;
592 vector w, x, y, z;
593
594 a[0] = absPos[0] + minSize[0];
595 a[1] = absPos[1] + maxSize[1];
596
597 b[0] = absPos[0] + maxSize[0];
598 b[1] = absPos[1] + maxSize[1];
599
600 c[0] = absPos[0] + maxSize[0];
601 c[1] = absPos[1] + minSize[1];
602
603 d[0] = absPos[0] + minSize[0];
604 d[1] = absPos[1] + minSize[1];
605
606 a[2] = absPos[2] + maxSize[2];
607 c[2] = absPos[2] + maxSize[2];
608 d[2] = absPos[2] + maxSize[2];
609 b[2] = absPos[2] + maxSize[2];
610
611 w = a;
612 x = b;
613 y = c;
614 z = d;
615
616 w[2] = absPos[2] + minSize[2];
617 x[2] = absPos[2] + minSize[2];
618 y[2] = absPos[2] + minSize[2];
619 z[2] = absPos[2] + minSize[2];
620
621 /* top */
622 R_BeginPolygon("", 0, 0);
623 R_PolygonVertex(a, [1,1], boxColor, boxAlpha);
624 R_PolygonVertex(b, [0,1], boxColor, boxAlpha);
625 R_PolygonVertex(c, [0,0], boxColor, boxAlpha);
626 R_PolygonVertex(d, [1,0], boxColor, boxAlpha);
627 R_EndPolygon();
628
629 /* front */
630 R_BeginPolygon("", 0, 0);
631 R_PolygonVertex(d, [1,1], boxColor * 0.9f, boxAlpha);
632 R_PolygonVertex(c, [0,1], boxColor * 0.9f, boxAlpha);
633 R_PolygonVertex(y, [0,0], boxColor * 0.9f, boxAlpha);
634 R_PolygonVertex(z, [1,0], boxColor * 0.9f, boxAlpha);
635 R_EndPolygon();
636
637 /* back */
638 R_BeginPolygon("", 0, 0);
639 R_PolygonVertex(w, [1,1], boxColor * 0.9f, boxAlpha);
640 R_PolygonVertex(x, [0,1], boxColor * 0.9f, boxAlpha);
641 R_PolygonVertex(b, [0,0], boxColor * 0.9f, boxAlpha);
642 R_PolygonVertex(a, [1,0], boxColor * 0.9f, boxAlpha);
643 R_EndPolygon();
644
645 /* left */
646 R_BeginPolygon("", 0, 0);
647 R_PolygonVertex(a, [1,1], boxColor * 0.8f, boxAlpha);
648 R_PolygonVertex(d, [0,1], boxColor * 0.8f, boxAlpha);
649 R_PolygonVertex(z, [0,0], boxColor * 0.8f, boxAlpha);
650 R_PolygonVertex(w, [1,0], boxColor * 0.8f, boxAlpha);
651 R_EndPolygon();
652
653 /* right */
654 R_BeginPolygon("", 0, 0);
655 R_PolygonVertex(c, [1,1], boxColor * 0.8f, boxAlpha);
656 R_PolygonVertex(b, [0,1], boxColor * 0.8f, boxAlpha);
657 R_PolygonVertex(x, [0,0], boxColor * 0.8f, boxAlpha);
658 R_PolygonVertex(y, [1,0], boxColor * 0.8f, boxAlpha);
659 R_EndPolygon();
660
661 /* bottom */
662 R_BeginPolygon("", 0, 0);
663 R_PolygonVertex(z, [1,1], boxColor, boxAlpha);
664 R_PolygonVertex(y, [0,1], boxColor, boxAlpha);
665 R_PolygonVertex(x, [0,0], boxColor, boxAlpha);
666 R_PolygonVertex(w, [1,0], boxColor, boxAlpha);
667 R_EndPolygon();
668}
669
670/* doxygen definitions */
671
ncEntity is the lowest of the user-accessible entity class.
Definition: Entity.h:75
string Constants_LookUp(string constName, string returnValue)
Look up a name and retrieve its value.
Definition: cloader.qc:84
typedef enumflags
Defines the valid alignment flags for text fields.
Definition: font.h:37
vector Route_GetJumpVelocity(vector, vector, float)
Definition: defs.h:548
float flags
Definition: sound.h:114