void Ent_ScoresInfo();
void CSQC_Ent_Update(float bIsNewEntity)
{SELFPARAM();
- float t;
- float savetime;
- t = ReadByte();
+ int t = ReadByte();
if(autocvar_developer_csqcentities)
LOG_INFOF("CSQC_Ent_Update(%d) with self=%i self.entnum=%d self.enttype=%d t=%d\n", bIsNewEntity, self, self.entnum, self.enttype, t);
// set up the "time" global for received entities to be correct for interpolation purposes
- savetime = time;
+ float savetime = time;
if(servertime)
{
time = servertime;
{
if(t != self.enttype || bIsNewEntity)
{
- //print("A CSQC entity changed its type!\n");
LOG_INFOF("A CSQC entity changed its type! (edict: %d, server: %d, type: %d -> %d)\n", num_for_edict(self), self.entnum, self.enttype, t);
Ent_Remove();
clearentity(self);
}
#endif
self.enttype = t;
+ bool done = false;
+ FOREACH(Linked, it.m_id == t, LAMBDA(
+ it.m_read(self, bIsNewEntity);
+ done = true;
+ break;
+ ));
+ if (!done)
switch(t)
{
case ENT_CLIENT_MUTATOR: {
case ENT_CLIENT_SPAWNPOINT: Ent_ReadSpawnPoint(bIsNewEntity); break;
case ENT_CLIENT_SPAWNEVENT: Ent_ReadSpawnEvent(bIsNewEntity); break;
case ENT_CLIENT_NOTIFICATION: Read_Notification(bIsNewEntity); break;
- case ENT_CLIENT_HEALING_ORB: ent_healer(); break;
case ENT_CLIENT_MINIGAME: ent_read_minigame(); break;
case ENT_CLIENT_VIEWLOC: ent_viewloc(); break;
case ENT_CLIENT_VIEWLOC_TRIGGER: ent_viewloc_trigger(); break;
const int ENT_CLIENT_MINIGAME = 75;
const int ENT_CLIENT_VIEWLOC = 78;
const int ENT_CLIENT_VIEWLOC_TRIGGER = 79;
-const int ENT_CLIENT_HEALING_ORB = 80;
const int ENT_CLIENT_MUTATOR = TE_CSQC_MUTATOR; // 99
#endif
-#ifdef SVQC
-float healer_send(entity to, int sf)
-{SELFPARAM();
- WriteByte(MSG_ENTITY, ENT_CLIENT_HEALING_ORB);
- WriteByte(MSG_ENTITY, sf);
-
- if(sf & 1)
- {
- WriteCoord(MSG_ENTITY, self.origin.x);
- WriteCoord(MSG_ENTITY, self.origin.y);
- WriteCoord(MSG_ENTITY, self.origin.z);
-
- WriteByte(MSG_ENTITY, self.healer_lifetime);
- //WriteByte(MSG_ENTITY, self.ltime - time + 1);
- WriteShort(MSG_ENTITY, self.healer_radius);
- // round time delta to a 1/10th of a second
- WriteByte(MSG_ENTITY, (self.ltime - time)*10.0+0.5);
- }
-
- return true;
-}
-#endif // SVQC
-
#ifdef CSQC
.float ltime;
void healer_draw()
self.alpha = (self.ltime - time) / self.healer_lifetime;
self.scale = min((1 - self.alpha)*self.healer_lifetime*4,1)*self.healer_radius;
-
}
-void healer_setup()
-{SELFPARAM();
- setmodel(self, MDL_NADE_HEAL);
-
- setorigin(self, self.origin);
-
- float model_radius = self.maxs.x;
- vector size = '1 1 1' * self.healer_radius / 2;
- setsize(self,-size,size);
- self.healer_radius = self.healer_radius/model_radius*0.6;
-
- self.draw = healer_draw;
- self.health = 255;
- self.movetype = MOVETYPE_NONE;
- self.solid = SOLID_NOT;
- self.drawmask = MASK_NORMAL;
- self.scale = 0.01;
- self.avelocity = self.move_avelocity = '7 0 11';
- self.colormod = '1 0 0';
- self.renderflags |= RF_ADDITIVE;
+void healer_setup(entity e)
+{
+ setmodel(e, MDL_NADE_HEAL);
+
+ setorigin(e, e.origin);
+
+ float model_radius = e.maxs.x;
+ vector size = '1 1 1' * e.healer_radius / 2;
+ setsize(e,-size,size);
+ e.healer_radius = e.healer_radius/model_radius*0.6;
+
+ e.draw = healer_draw;
+ e.health = 255;
+ e.movetype = MOVETYPE_NONE;
+ e.solid = SOLID_NOT;
+ e.drawmask = MASK_NORMAL;
+ e.scale = 0.01;
+ e.avelocity = e.move_avelocity = '7 0 11';
+ e.colormod = '1 0 0';
+ e.renderflags |= RF_ADDITIVE;
}
+#endif // CSQC
-void ent_healer()
-{SELFPARAM();
+REGISTER_LINKED(Nade_Heal, bool isNew)
+#ifdef CSQC
+{
int sf = ReadByte();
+ if (sf & 1) {
+ this.origin_x = ReadCoord();
+ this.origin_y = ReadCoord();
+ this.origin_z = ReadCoord();
+ setorigin(this, this.origin);
+ this.healer_lifetime = ReadByte();
+ this.healer_radius = ReadShort();
+ this.ltime = time + ReadByte()/10.0;
+ // this.ltime = time + this.healer_lifetime;
+ healer_setup(this);
+ }
+}
+#endif
- if(sf & TNSF_SETUP)
- {
- self.origin_x = ReadCoord();
- self.origin_y = ReadCoord();
- self.origin_z = ReadCoord();
- setorigin(self, self.origin);
-
- self.healer_lifetime = ReadByte();
- self.healer_radius = ReadShort();
- self.ltime = time + ReadByte()/10.0;
- //self.ltime = time + self.healer_lifetime;
-
- healer_setup();
+#ifdef SVQC
+float healer_send(entity to, int sf)
+{
+ SELFPARAM();
+ WriteByte(MSG_ENTITY, Linked_Nade_Heal.m_id);
+ WriteByte(MSG_ENTITY, sf);
+ if (sf & 1) {
+ WriteCoord(MSG_ENTITY, this.origin.x);
+ WriteCoord(MSG_ENTITY, this.origin.y);
+ WriteCoord(MSG_ENTITY, this.origin.z);
+
+ WriteByte(MSG_ENTITY, this.healer_lifetime);
+ //WriteByte(MSG_ENTITY, this.ltime - time + 1);
+ WriteShort(MSG_ENTITY, this.healer_radius);
+ // round time delta to a 1/10th of a second
+ WriteByte(MSG_ENTITY, (this.ltime - time)*10.0+0.5);
}
+ return true;
}
-#endif // CSQC
+#endif // SVQC
float healer_send(entity to, int sf);
#endif
-#ifdef CSQC
-// misc functions
-void ent_healer();
-#endif // CSQC
#endif
return 1;
}
-void shuffle(float n, swapfunc_t swap, entity pass)
-{
- float i, j;
- for(i = 1; i < n; ++i)
- {
- // swap i-th item at a random position from 0 to i
- // proof for even distribution:
- // n = 1: obvious
- // n -> n+1:
- // item n+1 gets at any position with chance 1/(n+1)
- // all others will get their 1/n chance reduced by factor n/(n+1)
- // to be on place n+1, their chance will be 1/(n+1)
- // 1/n * n/(n+1) = 1/(n+1)
- // q.e.d.
- j = floor(random() * (i + 1));
- if(j != i)
- swap(j, i, pass);
- }
-}
-
string substring_range(string s, float b, float e)
{
return substring(s, b, e - b);
return v;
}
-void heapsort(float n, swapfunc_t swap, comparefunc_t cmp, entity pass)
-{
- float start, end, root, child;
-
- // heapify
- start = floor((n - 2) / 2);
- while(start >= 0)
- {
- // siftdown(start, count-1);
- root = start;
- while(root * 2 + 1 <= n-1)
- {
- child = root * 2 + 1;
- if(child < n-1)
- if(cmp(child, child+1, pass) < 0)
- ++child;
- if(cmp(root, child, pass) < 0)
- {
- swap(root, child, pass);
- root = child;
- }
- else
- break;
- }
- // end of siftdown
- --start;
- }
-
- // extract
- end = n - 1;
- while(end > 0)
- {
- swap(0, end, pass);
- --end;
- // siftdown(0, end);
- root = 0;
- while(root * 2 + 1 <= end)
- {
- child = root * 2 + 1;
- if(child < end && cmp(child, child+1, pass) < 0)
- ++child;
- if(cmp(root, child, pass) < 0)
- {
- swap(root, child, pass);
- root = child;
- }
- else
- break;
- }
- // end of siftdown
- }
-}
void RandomSelection_Init()
{
float isGametypeInFilter(float gt, float tp, float ts, string pattern);
-typedef void(float i1, float i2, entity pass) swapfunc_t; // is only ever called for i1 < i2
-typedef float(float i1, float i2, entity pass) comparefunc_t; // <0 for <, ==0 for ==, >0 for > (like strcmp)
-void shuffle(float n, swapfunc_t swap, entity pass);
-void heapsort(float n, swapfunc_t swap, comparefunc_t cmp, entity pass);
-
string swapwords(string str, float i, float j);
string shufflewords(string str);
#include "lazy.qh"
#include "log.qh"
#include "math.qh"
+#include "net.qh"
#include "nil.qh"
#include "noise.qc"
#include "oo.qh"
#include "progname.qh"
#include "registry.qh"
#include "replicate.qh"
+#include "sort.qh"
#include "sortlist.qc"
#include "spawnfunc.qh"
#include "static.qh"
--- /dev/null
+#ifndef NET_H
+#define NET_H
+
+#ifdef SVQC
+.int SendFlags;
+.bool(entity to, int sendflags) SendEntity;
+
+void Net_LinkEntity(entity e, bool docull, float dt, bool(entity to, int sendflags) sendfunc)
+{
+ if (!e.classname) e.classname = "net_linked";
+
+ if (!e.model || !self.modelindex) {
+ vector mi = e.mins;
+ vector ma = e.maxs;
+ _setmodel(e, "null");
+ setsize(e, mi, ma);
+ }
+
+ e.SendEntity = sendfunc;
+ e.SendFlags = 0xFFFFFF;
+
+ if (!docull) e.effects |= EF_NODEPTHTEST;
+
+ if (dt) {
+ e.nextthink = time + dt;
+ e.think = SUB_Remove;
+ }
+}
+
+.void() uncustomizeentityforclient;
+.float uncustomizeentityforclient_set;
+
+void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
+{
+ e.customizeentityforclient = customizer;
+ e.uncustomizeentityforclient = uncustomizer;
+ e.uncustomizeentityforclient_set = !!uncustomizer;
+}
+
+void UncustomizeEntitiesRun()
+{
+ for (entity e = NULL; (e = findfloat(e, uncustomizeentityforclient_set, 1)); ) {
+ WITH(entity, self, e, e.uncustomizeentityforclient());
+ }
+}
+
+#endif
+
+#include "registry.qh"
+#include "sort.qh"
+
+REGISTRY(Linked, 24)
+
+.string netname;
+.int m_id;
+.void(entity this, bool isNew) m_read;
+
+#ifdef CSQC
+ #define REGISTER_LINKED(id, param) \
+ void Ent_Read##id(entity this, param) { this = self; } \
+ REGISTER(RegisterLinked, Linked, Linked, Linked_COUNT, id, m_id, spawn()) { \
+ this.netname = #id; \
+ this.m_read = Ent_Read##id; \
+ } \
+ [[accumulate]] void Ent_Read##id(entity this, param)
+#else
+ #define REGISTER_LINKED(id, param) \
+ REGISTER(RegisterLinked, Linked, Linked, Linked_COUNT, id, m_id, spawn()) { \
+ this.netname = #id; \
+ }
+#endif
+
+REGISTER_REGISTRY(RegisterLinked)
+REGISTRY_SORT(Linked, netname, 0)
+STATIC_INIT(RegisterLinked_renumber) {
+ for (int i = 0; i < Linked_COUNT; ++i) {
+ Linked[i].m_id = 100 + i;
+ }
+}
+
+#endif
#define OO_H
#include "nil.qh"
-#include "registry.qh"
#ifdef MENUQC
#define NULL (null_entity)
#define REGISTER_INIT(ns, id) [[accumulate]] void Register_##ns##_##id##_init(entity this)
#define REGISTER_INIT_POST(ns, id) [[accumulate]] void Register_##ns##_##id##_init_post(entity this)
+#define REGISTRY(id, max) \
+ void Register##id() {} \
+ const int id##_MAX = max; \
+ noref entity id[id##_MAX], id##_first, id##_last; \
+ int id##_COUNT;
+
/**
* Register a new entity with a global constructor.
* Must be followed by a semicolon or a function body with a `this` parameter.
ACCUMULATE_FUNCTION(initfunc, Register_##ns##_##id) \
REGISTER_INIT(ns, id)
+#define REGISTRY_SORT(id, field, skip) \
+ void _REGISTRY_SWAP_##id(int i, int j, entity pass) { \
+ i += skip; j += skip; \
+ entity e = id[i]; \
+ id[i] = id[j]; \
+ id[j] = e; \
+ } \
+ float _REGISTRY_CMP_##id(int i, int j, entity pass) { \
+ i += skip; j += skip; \
+ string a = id[i].field; \
+ string b = id[j].field; \
+ return strcasecmp(a, b); \
+ } \
+ STATIC_INIT(Registry_sort_##id) { \
+ heapsort(id##_COUNT, _REGISTRY_SWAP_##id, _REGISTRY_CMP_##id, NULL); \
+ }
+
#endif
--- /dev/null
+#ifndef SORT_H
+#define SORT_H
+
+/** is only ever called for i1 < i2 */
+typedef void(float i1, float i2, entity pass) swapfunc_t;
+/** <0 for <, ==0 for ==, >0 for > (like strcmp) */
+typedef float(float i1, float i2, entity pass) comparefunc_t;
+
+void heapsort(float n, swapfunc_t swap, comparefunc_t cmp, entity pass)
+{
+ int root, child;
+
+ // heapify
+ int start = floor((n - 2) / 2);
+ while (start >= 0) {
+ // siftdown(start, n - 1);
+ root = start;
+ while (root * 2 + 1 <= n - 1) {
+ child = root * 2 + 1;
+ if (child < n - 1 && cmp(child, child + 1, pass) < 0) {
+ child += 1;
+ }
+ if (cmp(root, child, pass) < 0) {
+ swap(root, child, pass);
+ root = child;
+ } else {
+ break;
+ }
+ }
+ // end of siftdown
+ --start;
+ }
+
+ // extract
+ int end = n - 1;
+ while (end > 0) {
+ swap(0, end, pass);
+ end -= 1;
+ // siftdown(0, end);
+ root = 0;
+ while (root * 2 + 1 <= end) {
+ child = root * 2 + 1;
+ if (child < end && cmp(child, child+1, pass) < 0) {
+ child += 1;
+ }
+ if (cmp(root, child, pass) < 0) {
+ swap(root, child, pass);
+ root = child;
+ } else {
+ break;
+ }
+ }
+ // end of siftdown
+ }
+}
+
+void shuffle(float n, swapfunc_t swap, entity pass)
+{
+ for (int i = 1; i < n; ++i) {
+ // swap i-th item at a random position from 0 to i
+ // proof for even distribution:
+ // n = 1: obvious
+ // n -> n+1:
+ // item n+1 gets at any position with chance 1/(n+1)
+ // all others will get their 1/n chance reduced by factor n/(n+1)
+ // to be on place n+1, their chance will be 1/(n+1)
+ // 1/n * n/(n+1) = 1/(n+1)
+ // q.e.d.
+ int j = floor(random() * (i + 1));
+ if (j != i)
+ swap(j, i, pass);
+ }
+}
+
+#endif
float next_pingtime;
.float Version;
-.int SendFlags;
-.bool(entity to, int sendflags) SendEntity;
// player sounds, voice messages
// TODO implemented fall and falling
remove = remove_unsafely;
}
-void UncustomizeEntitiesRun()
-{SELFPARAM();
- for (entity e = NULL; (e = findfloat(e, uncustomizeentityforclient_set, 1)); )
- {
- WITH(entity, self, e, e.uncustomizeentityforclient());
- }
-}
-void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
-{
- e.customizeentityforclient = customizer;
- e.uncustomizeentityforclient = uncustomizer;
- e.uncustomizeentityforclient_set = !!uncustomizer;
-}
-
-void Net_LinkEntity(entity e, bool docull, float dt, bool(entity, int) sendfunc)
-{SELFPARAM();
- vector mi, ma;
-
- if (e.classname == "")
- e.classname = "net_linked";
-
- if (e.model == "" || self.modelindex == 0)
- {
- mi = e.mins;
- ma = e.maxs;
- setmodel(e, MDL_Null);
- setsize(e, mi, ma);
- }
-
- e.SendEntity = sendfunc;
- e.SendFlags = 0xFFFFFF;
-
- if (!docull)
- e.effects |= EF_NODEPTHTEST;
-
- if (dt)
- {
- e.nextthink = time + dt;
- e.think = SUB_Remove;
- }
-}
-
-
.float(entity) isEliminated;
float EliminatedPlayers_SendEntity(entity to, float sendflags)
{
#define cvar_set_normal builtin_cvar_set
.vector dropped_origin;
-.void(void) uncustomizeentityforclient;
-.float uncustomizeentityforclient_set;
.float nottargeted;
entity eliminatedPlayers;
void soundat(entity e, vector o, float chan, string samp, float vol, float _atten);
-void defer(float fdelay, void() func);
-
-void UncustomizeEntitiesRun();
void InitializeEntitiesRun();
void stopsoundto(float _dest, entity e, float chan);