Multiprogs in FTEQW

From Quake Wiki

General Info

FTE_MULTIPROGS

void() init;
  • called before ents are available (spawn() will fail, but you can use addprogs() here)
void() initents;
  • called just before worldspawn, after ents are spawnable spawn() works but addprogs() may fail)
float thisprogs;
  • set by the engine. main progs will always be 0, addons will be non-zero and unique.
float(string modname) addprogs = #202;
  • load another progs as well - may fail if too many fields are added.
__variant(float progsno, string defname) externvalue = #203;
  • retrieve a value from another mod
void(float progsno, __variant value, string defname) externset = #204;
  • set a value in another mod (don't use entire vectors, it'll only set the x part)

Additional cvars

addon0..15:

  • explicitly name an addon progs to be loaded. roughly equivelent to calling addprogs at the end of the main prog's init() function.

Additional fteqcc keywords

shared:

  • when prefixed in a type on a global def, the engine will propagate that global between progs. Caution: make sure its always used at the same global offset, so only use it in the shared defs.qc, before any differences. self, other, time will forcibly have this set, but it won't hurt to put it there (yet is the classic example of this keyword's use).

extern:

  • when prefixed on a function def, the function will be compiled successfully without a body. if you use FTE's progs7 format, it'll be auto-imported and be directly callable, otherwise you can define it with 'var' and use externvalue to initialise it before its callable (in one of the init funcs).

__variant:

  • type that stands for any, removes related type check compiler warnings/errors, limits operations to assignments only. May have issues with vectors.

Main progs hijack

To hijack/hook a function in the main progs:

#pragma PROGS_DAT myaddon.dat
#include "defs.qc"
var void() real_myfunc;
void() replacement_myfunc =
{
	dostuff();
	real_myfunc();
	dostuff();
};
void() dohooks =
{
	real_myfunc = externvalue(0, "myfunc");
	externset(0, replacement_myfunc, "myfunc");
};

Quirks

  • spawn funcs will be initialised from progs 0 upwards.
    • So the first progs with a function matching the ents classname spawns the ent.
  • Monsters need CheckAttack and ClientObituary hooked.
  • Can't mix crcs. NQ does not mix with QW.
  • Weapons are messy as heck - ut uses a different object for each item in a player's inventory. qc does not (normally). changing the fire function is simple enough, but adding much more than that becomes messy.
  • Fields are shared between all mods. They'll be remapped as required, but this can also mean addprogs can fail if fields are already mapped - ie: if initents has been called, you can't add more fields.
  • Globals are NOT shared. The shared keyword on a global will mark that global's index as one to be copied each time control passes from one progs to another. Avoid passing variables via globals. This also limits you to 8 arguments per cross-progs call.
  • Some optimisations can stop you from being able to use a progs as an addon. there is otherwise no format difference between main progs and addon progs. system defs must match.
  • externvalue/externset may find locals.
  • externset on constants may be unreliable as the constant may be stripped/merged, but on functions (which are default const) its reliable.
  • You can use frikqcc instead, but you can't use the keywords above (and no __variant means you need alternative copies of externvalue - externset can use an elipsis).
  • FTE will already attempt to load addons based upon map format. See source for specifics.
  • FTE will call a notification function each time an addon is loaded due to cvars or user request.
  • You cannot use spawn() or world or any entity fields inside the 'init' function. save it for initents. You should use this function only for hooking important functions.
  • initents permits the use of spawn(). spawned ents will be *after* world, even though worldspawn was not yet invoked.
  • Using addprogs later than the main progs' init function can fail if too many(any) new fields are defined.

Typical use-case

  • A great use of this extension is to add frikbots to any mod without modding it.
  • externvalue and externset do work for the current progs too. It can be used to implement arrays or some kind of scripts (fteqcc's builtin arrays are more efficient).