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