+
+#define APPROXPASTTIME_ACCURACY_REQUIREMENT 0.05
+#define APPROXPASTTIME_MAX (16384 * APPROXPASTTIME_ACCURACY_REQUIREMENT)
+#define APPROXPASTTIME_RANGE (64 * APPROXPASTTIME_ACCURACY_REQUIREMENT)
+// this will use the value:
+// 128
+// accuracy near zero is APPROXPASTTIME_MAX/(256*255)
+// accuracy at x is 1/derivative, i.e.
+// APPROXPASTTIME_MAX * (1 + 256 * (dt / APPROXPASTTIME_MAX))^2 / 65536
+#ifdef SVQC
+void WriteApproxPastTime(float dst, float t)
+{
+ float dt = time - t;
+
+ // warning: this is approximate; do not resend when you don't have to!
+ // be careful with sendflags here!
+ // we want: 0 -> 0.05, 1 -> 0.1, ..., 255 -> 12.75
+
+ // map to range...
+ dt = 256 * (dt / ((APPROXPASTTIME_MAX / 256) + dt));
+
+ // round...
+ dt = rint(bound(0, dt, 255));
+
+ WriteByte(dst, dt);
+}
+#endif
+#ifdef CSQC
+float ReadApproxPastTime()
+{
+ float dt = ReadByte();
+
+ // map from range...PPROXPASTTIME_MAX / 256
+ dt = (APPROXPASTTIME_MAX / 256) * (dt / (256 - dt));
+
+ return servertime - dt;
+}
+#endif
+
+#ifndef MENUQC
+.float skeleton_bones_index;
+void Skeleton_SetBones(entity e)
+{
+ // set skeleton_bones to the total number of bones on the model
+ if(e.skeleton_bones_index == e.modelindex)
+ return; // same model, nothing to update
+
+ float skelindex;
+ skelindex = skel_create(e.modelindex);
+ e.skeleton_bones = skel_get_numbones(skelindex);
+ skel_delete(skelindex);
+ e.skeleton_bones_index = e.modelindex;
+}
+#endif
+
+string to_execute_next_frame;
+void execute_next_frame()
+{
+ if(to_execute_next_frame)
+ {
+ localcmd("\n", to_execute_next_frame, "\n");
+ strunzone(to_execute_next_frame);
+ to_execute_next_frame = string_null;
+ }
+}
+void queue_to_execute_next_frame(string s)
+{
+ if(to_execute_next_frame)
+ {
+ s = strcat(s, "\n", to_execute_next_frame);
+ strunzone(to_execute_next_frame);
+ }
+ to_execute_next_frame = strzone(s);
+}
+
+float cubic_speedfunc(float startspeedfactor, float endspeedfactor, float x)
+{
+ return
+ ((( startspeedfactor + endspeedfactor - 2
+ ) * x - 2 * startspeedfactor - endspeedfactor + 3
+ ) * x + startspeedfactor
+ ) * x;
+}
+
+float cubic_speedfunc_is_sane(float startspeedfactor, float endspeedfactor)
+{
+ if(startspeedfactor < 0 || endspeedfactor < 0)
+ return FALSE;
+
+ /*
+ // if this is the case, the possible zeros of the first derivative are outside
+ // 0..1
+ We can calculate this condition as condition
+ if(se <= 3)
+ return TRUE;
+ */
+
+ // better, see below:
+ if(startspeedfactor <= 3 && endspeedfactor <= 3)
+ return TRUE;
+
+ // if this is the case, the first derivative has no zeros at all
+ float se = startspeedfactor + endspeedfactor;
+ float s_e = startspeedfactor - endspeedfactor;
+ if(3 * (se - 4) * (se - 4) + s_e * s_e <= 12) // an ellipse
+ return TRUE;
+
+ // Now let s <= 3, s <= 3, s+e >= 3 (triangle) then we get se <= 6 (top right corner).
+ // we also get s_e <= 6 - se
+ // 3 * (se - 4)^2 + (6 - se)^2
+ // is quadratic, has value 12 at 3 and 6, and value < 12 in between.
+ // Therefore, above "better" check works!
+
+ return FALSE;
+
+ // known good cases:
+ // (0, [0..3])
+ // (0.5, [0..3.8])
+ // (1, [0..4])
+ // (1.5, [0..3.9])
+ // (2, [0..3.7])
+ // (2.5, [0..3.4])
+ // (3, [0..3])
+ // (3.5, [0.2..2.3])
+ // (4, 1)
+}
+
+.float FindConnectedComponent_processing;
+void FindConnectedComponent(entity e, .entity fld, findNextEntityNearFunction_t nxt, isConnectedFunction_t iscon, entity pass)
+{
+ entity queue_start, queue_end;
+
+ // we build a queue of to-be-processed entities.
+ // queue_start is the next entity to be checked for neighbors
+ // queue_end is the last entity added
+
+ if(e.FindConnectedComponent_processing)
+ error("recursion or broken cleanup");
+
+ // start with a 1-element queue
+ queue_start = queue_end = e;
+ queue_end.fld = world;
+ queue_end.FindConnectedComponent_processing = 1;
+
+ // for each queued item:
+ for(; queue_start; queue_start = queue_start.fld)
+ {
+ // find all neighbors of queue_start
+ entity t;
+ for(t = world; (t = nxt(t, queue_start, pass)); )
+ {
+ if(t.FindConnectedComponent_processing)
+ continue;
+ if(iscon(t, queue_start, pass))
+ {
+ // it is connected? ADD IT. It will look for neighbors soon too.
+ queue_end.fld = t;
+ queue_end = t;
+ queue_end.fld = world;
+ queue_end.FindConnectedComponent_processing = 1;
+ }
+ }
+ }
+
+ // unmark
+ for(queue_start = e; queue_start; queue_start = queue_start.fld)
+ queue_start.FindConnectedComponent_processing = 0;
+}