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