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