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
201__wrap void
202dprint(string m)
203{
204 if (cvar("developer") == 1)
205 return prior(m);
206}
207
208void
209crossprint(string m)
210{
211#ifdef CLIENT
212 print(strcat("CLIENT: ", m));
213#else
214 print(strcat("SERVER: ", m));
215#endif
216}
217
218#if 0
219__wrap string
220precache_model(string m)
221{
222 if not (m) {
223 breakpoint();
224 return "";
225 }
226
227 if (m == "") {
228 breakpoint();
229 return "";
230 }
231
232 return prior(m);
233}
234#endif
235
236__wrap string
237precache_sound(string sample)
238{
239 if (sample != "") /* not empty */
240 if not(whichpack(strcat("sound/", sample))) { /* not present on disk */
241 NSError("SFX sample %S does not exist.", sample);
242 return "misc/missing.wav";
243 }
244
245 return prior(sample);
246}
247
248/* this could probably be a lot better, use this from now on so that it can be improved later */
249noref float input_sequence;
250float
251pseudorandom()
252{
253 float seed = (float)input_sequence % 5.0f;
254 seed += (float)input_sequence % 8.0f;
255 seed += (float)input_sequence % 4.0f;
256 seed += (float)input_sequence % 13.0f;
257 seed += (float)input_sequence % 70.0f;
258
259 /* like the engine its random(), never return 0, never return 1 */
260 return bound(0.01, (seed) / 100.0f, 0.99f);
261}
262
263#if 0
264__wrap void
265WriteByte(float to, float val)
266{
267 breakpoint();
268 prior(to, val);
269}
270
271__wrap void
272WriteChar(float to, float val)
273{
274 breakpoint();
275 prior(to, val);
276}
277
278__wrap void
279WriteShort(float to, float val)
280{
281 breakpoint();
282 prior(to, val);
283}
284
285__wrap void
286WriteLong(float to, float val)
287{
288 breakpoint();
289 prior(to, val);
290}
291
292__wrap void
293WriteCoord(float to, float val)
294{
295 breakpoint();
296 prior(to, val);
297}
298
299__wrap void
300WriteAngle(float to, float val)
301{
302 breakpoint();
303 prior(to, val);
304}
305
306__wrap void
307WriteString(float to, string val)
308{
309 breakpoint();
310 prior(to, val);
311}
312
313__wrap void
314WriteEntity(float to, entity val)
315{
316 breakpoint();
317 prior(to, val);
318}
319#endif
320
321__wrap void
322setmodel(entity ent, string mname)
323{
324 if (mname != "") /* not empty */
325 if (substring(mname, 0, 1) != "*") /* not a brush */
326 if not(whichpack(mname)) /* not present on disk */
327 return prior(ent, "models/error.vvm");
328
329 return prior(ent, mname);
330}
331
332__wrap __variant*
333memalloc(int size)
334{
335#if 0
336 if (size > 55000)
337 breakpoint();
338
339 print(sprintf("memalloc: %i\n", size));
340#endif
341 return prior(size);
342}
343
344.float removed;
345__wrap void
346remove(entity target)
347{
348 /* it's an ncEntity sub-class */
349 if (target.identity) {
350 ncEntity ent = (ncEntity)target;
351
352 /* if we're calling remove() on it and not .Destroy()... it's being uncleanly removed! */
353 if (ent.removed == 0) {
354 ent.OnRemoveEntity();
355 breakpoint();
356 print(sprintf("^1WARNING: Entity %d of class %s uncleanly removed!\n", num_for_edict(ent), ent.classname));
357 ent.removed = 1;
358 }
359 }
360
361 target.removed = 0;
362 prior(target);
363}
364
365__wrap void
366traceline(vector v1, vector v2, float flags, entity ent)
367{
368#ifdef SERVER
369 if (autocvar(com_showTracers, 0) == 1) {
370 WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
371 WriteByte(MSG_MULTICAST, EV_TRACEDEBUG);
372 WriteCoord(MSG_MULTICAST, v1[0]);
373 WriteCoord(MSG_MULTICAST, v1[1]);
374 WriteCoord(MSG_MULTICAST, v1[2]);
375 WriteCoord(MSG_MULTICAST, v2[0]);
376 WriteCoord(MSG_MULTICAST, v2[1]);
377 WriteCoord(MSG_MULTICAST, v2[2]);
378 msg_entity = world;
379 multicast(v1, MULTICAST_PVS_R);
380 }
381#endif
382
383#ifdef CLIENT
384 // TODO
385#endif
386 prior(v1, v2, flags, ent);
387}
388
389#ifdef SERVER
390string Skill_GetStringValue(string, string);
391#endif
392
393string
394unpackStringCommand(string commandString)
395{
396#ifdef SERVER
397 /* is this supposed to be read from a skill cvar? */
398 if (substring(commandString, 0, 6) == "skill:") {
399 return Skill_GetStringValue(substring(commandString, 6, -1), "");
400 }
401 /* is this supposed to be read from a skill cvar? */
402 if (substring(commandString, 0, 5) == "cvar:") {
403 return cvar_string(substring(commandString, 5, -1));
404 }
405#endif
406
407 return Constants_LookUp(commandString, commandString);
408}
409
410#ifdef NO_LEGACY
411void
412readcmd(string foo)
413{
414 localcmd(foo);
415}
416#else
417void(string cmd) readcmd = #0:readcmd;
418#endif
419
420/* This is required because people who use Hammer do awful things
421 to get their models to update. We get a multitude of juicy
422 hacks and symbols that Half-Life's engine strips and now we have to
423 replicate this behaviour. Be thankful this is not done in-engine for
424 every game/mod ever.
425*/
426string Util_FixModel(string mdl)
427{
428 if (!STRING_SET(mdl)) {
429 return "";
430 }
431 mdl = strreplace("\\", "/", mdl);
432
433 int c = tokenizebyseparator(mdl, "/", "\\ ", "!");
434 string newpath = "";
435
436 for (int i = 0; i < c; i++) {
437 newpath = sprintf("%s/%s", newpath, argv(i));
438 }
439
440 /* Kill the first / */
441 newpath = substring(newpath, 1, strlen(newpath)-1);
442
443 /* Now we need to fix \/ because I hate people */
444 c = tokenizebyseparator(newpath, "\\/");
445 mdl = "";
446 for (int i = 0; i < c; i++) {
447 mdl = sprintf("%s/%s", mdl, argv(i));
448 }
449 /* Kill the first / again */
450 mdl = substring(mdl, 1, strlen(mdl)-1);
451
452 if (substring(mdl, 0, 1) == "/")
453 mdl = substring(mdl, 1, -1);
454
455 return mdl;
456}
457
461string
462Util_ChangeExtension(string baseString, string newExtension)
463{
464 float stringOffset = 0;
465 string tempString = "";
466 float foundOffset = 0;
467
468 while ((tempString = substring(baseString, stringOffset, 1))) {
469 if (tempString == ".")
470 foundOffset = stringOffset;
471 if (tempString == "")
472 break;
473 if not (tempString)
474 break;
475
476 stringOffset++;
477 }
478
479 /* no extension found? append to the end then. */
480 if (foundOffset == 0) {
481 return strcat(baseString, ".", newExtension);
482 }
483
484 return strcat(substring(baseString, 0, foundOffset), ".", newExtension);
485}
486
487bool
488Util_IsSingleplayer(void)
489{
490#ifdef SERVER
491 /* playerslots 1 is always singleplayer */
492 if (cvar("sv_playerslots") == 1)
493 return true;
494
495 /* only when coop is 1, only in multiplayer */
496 if (cvar("coop") == 1 && cvar("sv_playerslots") > 1)
497 return true;
498#endif
499
500 /* else we're multiplayer */
501 return false;
502}
503
504float
505crandom(void)
506{
507 return ((random() - 0.5f) * 2.0f);
508}
509
510string
511dirname(string input)
512{
513 if (!input) {
514 return "";
515 }
516
517 int c = tokenizebyseparator(input, "/", "\\ ", "!");
518 string newpath = "";
519
520 for (int i = 0; i < (c-1); i++) {
521 newpath = sprintf("%s/%s", newpath, argv(i));
522 }
523
524 /* Kill the first / */
525 newpath = substring(newpath, 1, strlen(newpath)-1);
526
527 return newpath;
528}
529
530string
531textfile_to_string(string filename)
532{
533 string fileContents = __NULL__;
534
535 filestream fileHandle = fopen(filename, FILE_READ);
536 string temp;
537
538 if (fileHandle != -1) {
539 while ((temp = fgets(fileHandle))) {
540 fileContents = strcat(fileContents, temp, "\n");
541 }
542 } else {
543 fileContents = __NULL__;
544 }
545
546 return fileContents;
547}
548
549/* moved from src/botlib/route.qc */
550/* Get's a velocity vector with which we can successfully jump from one place to another */
551vector
552Route_GetJumpVelocity(vector vecFrom, vector vecTo, float flGravMod)
553{
554#if 1
555 float flHeight, flGravity, flTime, flDistance, flDir;
556 vector vecJump = [0,0,0];
557
558 if (flGravMod <= 0.0)
559 flGravMod = 1.0f;
560
561 flGravity = serverkeyfloat("phy_gravity") * flGravMod;
562 flHeight = vecTo[2] - vecFrom[2];
563
564 /* this may not be a much verticality to this jump, use distance instead */
565 if (flHeight <= 0) {
566 flHeight = vlen(vecTo - vecFrom);
567 flTime = flHeight / flGravity;
568 } else {
569 flTime = sqrt(flHeight / (flGravity * 0.5f));
570 if (flTime <= 0) {
571 return [0,0,0];
572 }
573 }
574
575 vecJump = vecTo - vecFrom;
576 vecJump[2] = 0;
577 flDistance = vlen(normalize(vecJump));
578
579 flDir = flDistance / flTime;
580 vecJump *= flDir;
581 vecJump[2] = flTime * flGravity;
582#else
583 vector vecJump = [0,0,0];
584 float flDist = vlen(vecTo - vecFrom);
585 makevectors(vectoangles(vecTo - vecFrom));
586 vecJump = v_forward * flDist;
587 vecJump[2] = 280;
588#endif
589 return vecJump;
590}
591
592void
593DebugBox(vector absPos, vector minSize, vector maxSize, vector boxColor, float boxAlpha)
594{
595 vector a, b, c, d;
596 vector w, x, y, z;
597
598 a[0] = absPos[0] + minSize[0];
599 a[1] = absPos[1] + maxSize[1];
600
601 b[0] = absPos[0] + maxSize[0];
602 b[1] = absPos[1] + maxSize[1];
603
604 c[0] = absPos[0] + maxSize[0];
605 c[1] = absPos[1] + minSize[1];
606
607 d[0] = absPos[0] + minSize[0];
608 d[1] = absPos[1] + minSize[1];
609
610 a[2] = absPos[2] + maxSize[2];
611 c[2] = absPos[2] + maxSize[2];
612 d[2] = absPos[2] + maxSize[2];
613 b[2] = absPos[2] + maxSize[2];
614
615 w = a;
616 x = b;
617 y = c;
618 z = d;
619
620 w[2] = absPos[2] + minSize[2];
621 x[2] = absPos[2] + minSize[2];
622 y[2] = absPos[2] + minSize[2];
623 z[2] = absPos[2] + minSize[2];
624
625 /* top */
626 R_BeginPolygon("", 0, 0);
627 R_PolygonVertex(a, [1,1], boxColor, boxAlpha);
628 R_PolygonVertex(b, [0,1], boxColor, boxAlpha);
629 R_PolygonVertex(c, [0,0], boxColor, boxAlpha);
630 R_PolygonVertex(d, [1,0], boxColor, boxAlpha);
631 R_EndPolygon();
632
633 /* front */
634 R_BeginPolygon("", 0, 0);
635 R_PolygonVertex(d, [1,1], boxColor * 0.9f, boxAlpha);
636 R_PolygonVertex(c, [0,1], boxColor * 0.9f, boxAlpha);
637 R_PolygonVertex(y, [0,0], boxColor * 0.9f, boxAlpha);
638 R_PolygonVertex(z, [1,0], boxColor * 0.9f, boxAlpha);
639 R_EndPolygon();
640
641 /* back */
642 R_BeginPolygon("", 0, 0);
643 R_PolygonVertex(w, [1,1], boxColor * 0.9f, boxAlpha);
644 R_PolygonVertex(x, [0,1], boxColor * 0.9f, boxAlpha);
645 R_PolygonVertex(b, [0,0], boxColor * 0.9f, boxAlpha);
646 R_PolygonVertex(a, [1,0], boxColor * 0.9f, boxAlpha);
647 R_EndPolygon();
648
649 /* left */
650 R_BeginPolygon("", 0, 0);
651 R_PolygonVertex(a, [1,1], boxColor * 0.8f, boxAlpha);
652 R_PolygonVertex(d, [0,1], boxColor * 0.8f, boxAlpha);
653 R_PolygonVertex(z, [0,0], boxColor * 0.8f, boxAlpha);
654 R_PolygonVertex(w, [1,0], boxColor * 0.8f, boxAlpha);
655 R_EndPolygon();
656
657 /* right */
658 R_BeginPolygon("", 0, 0);
659 R_PolygonVertex(c, [1,1], boxColor * 0.8f, boxAlpha);
660 R_PolygonVertex(b, [0,1], boxColor * 0.8f, boxAlpha);
661 R_PolygonVertex(x, [0,0], boxColor * 0.8f, boxAlpha);
662 R_PolygonVertex(y, [1,0], boxColor * 0.8f, boxAlpha);
663 R_EndPolygon();
664
665 /* bottom */
666 R_BeginPolygon("", 0, 0);
667 R_PolygonVertex(z, [1,1], boxColor, boxAlpha);
668 R_PolygonVertex(y, [0,1], boxColor, boxAlpha);
669 R_PolygonVertex(x, [0,0], boxColor, boxAlpha);
670 R_PolygonVertex(w, [1,0], boxColor, boxAlpha);
671 R_EndPolygon();
672}
673
674/* doxygen definitions */
675
ncEntity is the lowest of the user-accessible entity class.
Definition: Entity.h:54
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:552
float flags
Definition: sound.h:114