--- /dev/null
+root = true
+
+[*.{qc,qh,inc}]
+end_of_line = lf
+insert_final_newline = true
+indent_style = tab
+charset = utf-8
#endif
+#include <ecs/_lib.inc>
+
#if BUILD_MOD
#include "../../mod/client/progs.inc"
#endif
--- /dev/null
+# Xonotic entity component system
+
+## guidelines
+
+* avoid #if and #ifdef
+* avoid string
+* avoid declaring entity fields outside of components
+* uncrustify relentlessly
+* shared code in $file, prog specific code uses prefix: { client: cl_, server: sv_, menu: ui_ }. $file must exist
+* component naming =~ com_$component_$name
+* system naming =~ sys_$system_$name
+* event naming =~ evt_$component_$name
+* global naming =~ g_$name
+* cvar naming =~ xon_$name
+
+## components
+
+ COMPONENT($component);
+ .int com_$component_$property;
+
+## entities
+
+ entity e = new(foo);
+ e.com_$component = true;
+ e.com_$component_$property = 42;
+
+## systems
+
+ SYSTEM($system, 30, 10);
+ sys_$system_update(entity this, float dt) {
+ code;
+ }
+
+## events
+
+### declaring
+
+ EVENT($component_$name, (entity this));
+
+### emitting
+
+ emit($event, it);
+
+### listening
+
+ entity listener = new_pure(someListener);
+ listener.evt_$event = void(entity this) { code; };
+ subscribe(listener, $event);
--- /dev/null
+/** Components always interpolate from the previous state */
+#define COMPONENT(com) \
+ void com_##com##_interpolate(entity it, float a); \
+ .bool com_##com
+
+#define FOREACH_COMPONENT(com, body) FOREACH_ENTITY_FLOAT(com_##com, true, body)
+
+
+#define EVENT(T, args) .bool evt_##T##_listener; .void args evt_##T
+
+#define emit(T, ...) \
+ MACRO_BEGIN \
+ FOREACH_ENTITY_FLOAT_ORDERED(evt_##T##_listener, true, it.evt_##T(__VA_ARGS__)); \
+ MACRO_END
+
+#define subscribe(listener, T) \
+ MACRO_BEGIN \
+ listener.evt_##T##_listener = true; \
+ MACRO_END
+
+
+/**
+ * framelimit 0 is no limit, interpolation does not apply
+ * framerate below minfps will result in less than 100% speed
+ */
+#define SYSTEM(sys, frameLimit, minfps) \
+ void sys_##sys##_update(entity this, float dt); \
+ float autocvar_xon_sys_##sys##_dt = ((frameLimit) ? (1 / (frameLimit)) : 0); \
+ float autocvar_xon_sys_##sys##_minfps = (1 / (1 / (minfps)))
+
+#define SYSTEM_UPDATE(sys) \
+ MACRO_BEGIN \
+ static float t = 0; \
+ float dt = autocvar_xon_sys_##sys##_dt; \
+ float minfps = autocvar_xon_sys_##sys##_minfps; \
+ static float accumulator = 0; \
+ float a = 0; \
+ if (dt) { \
+ accumulator += min(frametime, 1 / (minfps)); \
+ } else { \
+ accumulator += frametime; \
+ dt = accumulator; \
+ a = 1; \
+ } \
+ while (accumulator >= dt) \
+ { \
+ time = t; \
+ FOREACH_COMPONENT(sys, sys_##sys##_update(it, dt)); \
+ t += dt; \
+ accumulator -= dt; \
+ } \
+ if (!a) a = accumulator / dt; \
+ FOREACH_COMPONENT(sys, com_##sys##_interpolate(it, a)); \
+ MACRO_END
+
+
+#include "_mod.inc"
+#include "components/_mod.inc"
+#include "events/_mod.inc"
+#include "systems/_mod.inc"
--- /dev/null
+// generated file; do not modify
+#include <ecs/main.qc>
--- /dev/null
+// generated file; do not modify
+#include <ecs/main.qh>
--- /dev/null
+// generated file; do not modify
+#include <ecs/components/input.qc>
+#include <ecs/components/physics.qc>
--- /dev/null
+// generated file; do not modify
+#include <ecs/components/input.qh>
+#include <ecs/components/physics.qh>
--- /dev/null
+#include "input.qh"
+
+void com_in_interpolate(entity it, float a) { }
--- /dev/null
+#pragma once
+
+COMPONENT(in);
+.vector com_in_move;
+.vector com_in_angles;
+.bool com_in_jump;
--- /dev/null
+#include "physics.qh"
+
+bool autocvar_xon_com_phys_interpolate = true;
+
+void com_phys_interpolate(entity it, float a)
+{
+ if (!autocvar_xon_com_phys_interpolate) a = 1;
+ it.origin = it.com_phys_pos_prev * (1 - a) + it.com_phys_pos * a;
+ it.angles = it.com_phys_ang_prev * (1 - a) + it.com_phys_ang * a; // TODO: slerp, not lerp
+}
--- /dev/null
+#pragma once
+
+COMPONENT(phys);
+.vector com_phys_pos, com_phys_pos_prev;
+.vector com_phys_ang, com_phys_ang_prev;
+.vector com_phys_vel;
+.vector com_phys_acc;
--- /dev/null
+// generated file; do not modify
--- /dev/null
+// generated file; do not modify
--- /dev/null
+#include "main.qh"
+
+#include "components/_mod.qh"
+#include "events/_mod.qh"
+#include "systems/_mod.qh"
+
+void systems_update()
+{
+ float realtime = time;
+ SYSTEM_UPDATE(phys);
+ time = realtime;
+}
--- /dev/null
+#pragma once
+
+void systems_update();
--- /dev/null
+// generated file; do not modify
+#include <ecs/systems/physics.qc>
--- /dev/null
+// generated file; do not modify
+#include <ecs/systems/physics.qh>
--- /dev/null
+#include "physics.qh"
+
+void sys_phys_update(entity this, float dt)
+{
+ PM_Main(this);
+}
--- /dev/null
+#pragma once
+
+SYSTEM(phys, 30, 10);
#endif
+#include <ecs/_lib.inc>
+
#if BUILD_MOD
#include "../../mod/server/progs.inc"
#endif
-#!/bin/bash
+#!/usr/bin/env bash
set -eu
-cd "$(dirname "$0")"
+cd ${0%/*}
cd ..
ROOT=$PWD/
(cd lib; genmod)
(cd common; genmod)
+(cd ecs; genmod)
(cd client; genmod)
(cd server; genmod)
(cd menu; genmod)