blob: 3e003c85c939444c75bfc7a281e85286646ac081 [file] [log] [blame]
// (c) by Padreic (Padreic@mg.mud.de)
/* 12 Juli 1998, Padreic
*
* Dieser Master dient zum einsparen von call_out Ketten bei Laufnpcs.
* Anmelden bei diesem Master geschieht ueber die Funktion RegisterWalker().
* varargs void RegisterWalker(int time, int random, closure walk_closure)
* time - in welchen Abstaenden soll das Objekt bewegt werden
* rand - dieser Wert wird als random immer beim bewegen zu time addiert
* walk_closure - die Closure die immer aufgerufen werden muss, wenn dieser
* Parameter weggelassen wird, wird statdessen die Funktion
* Walk() im NPC aufgerufen.
*
* Abgemeldet wird der NPC sobald die Closure bzw. die Walk-Funktion 0
* returned. Bei allen Werten !=0 bleibt der NPC aktiv.
*
* Hinweis: Der NPC muss sich mind. alle 180 sek. bewegen (time+random),
* ansonsten kann dieser Master nicht verwendet werden.
*/
#define MAX_DELAYTIME 90 /* max. delay ist 2*MAX_DELAYTIME */
#define DEFAULT_WALK_DELAY 180 /* ist der billigste Wert :) */
#define MAX_JOB_COST 200000 /* Wieviel Zeit darf ein NPC max. nutzen */
// Funktionen zum vereinfachten Zugriff auf die Komprimierten Daten
// im Walkerarray
#define TIME(t) (t & 0x00ff) /* 8 Bit = 256 */
#define RANDOM(r) ((r & 0xff00) >> 8) /* 8 Bit = 256 */
#define WERT(t, r) ((t & 0x00ff)+((r << 8) & 0xff00)) /* 16 Bit */
static int counter; // zur Orientierung im walker-array
static int num_walker; // anzahl der walker im array
//static mixed *walker; // ({ ..., ({ ..., ({ wert, closure }), ...}), ...})
nosave < < <int|closure>* >* >* walker;
protected void create()
{
if (clonep(this_object())) {
destruct(this_object());
raise_error("walk_master can't be clonend.\n");
}
walker=({});
num_walker=0;
enable_commands(); // ohne das, kein heart_beat()
}
#define ERROR(x) raise_error(sprintf(x, previous_object()));
// Man muss selbst darauf aufpassen, das sich ein NPC immer nur einmal
// anmeldet, da sonst auch mehrere Paralelle Walk-Ketten laufen!!!
// Am besten nie direkt sondern nur ueber einen Standardnpc benutzen.
varargs void RegisterWalker(int time, int rand, closure walk_closure)
{
int wert, next;
closure func;
// pruefen ob die Paramter zulaessig sind...
if (time<0) ERROR("negative time to RegisterWalker() from %O.\n");
if (rand<0) ERROR("negative random to RegisterWalker() from %O.\n");
if ((time+rand) > (2*MAX_DELAYTIME))
ERROR("Too long delaytime from %s to RegisterWalker().\n");
wert=WERT(time, rand);
if (!wert && !rand) wert=DEFAULT_WALK_DELAY;
if (walk_closure)
func=walk_closure;
else {
func=symbol_function("Walk", previous_object());
if (!func)
raise_error("RegisterWalker() call from Object without Walk() function.\n");
}
if (!num_walker) {
set_heart_beat(1);
if (!pointerp(walker) || !sizeof(walker))
walker=map(allocate(MAX_DELAYTIME+1), #'allocate);
}
next=counter;
next+=(time+random(rand))/2;
if (next>MAX_DELAYTIME) next-=MAX_DELAYTIME;
walker[next]+=({ ({ wert, func }) });
num_walker++;
}
int dummy_walk() // liefert immer 0 fuer abbrechen...
{ return 0; }
// Aufruf nach Moeglichkeit bitte vermeiden, da recht aufwendig. Meist ist
// es leicht im NPC "sauber Buch zu fuehren" und dann ggf. aus Walk()
// 0 zu returnen.
void RemoveWalker()
{
int i, j;
if (!num_walker) return;
for (i=MAX_DELAYTIME; i>=0; i--) {
for (j=sizeof(walker[i])-1; j>=0; j--)
{
if (get_type_info(walker[i][j][1], 2)==previous_object())
{
if (i==counter) // koennte gerade im heart_beat stecken...
walker[i][j]=({ 0, #'dummy_walk });
else
walker[i][j]=0;
num_walker--;
}
}
if (i!=counter) // koennte gerade im heart_beat stecken...
walker[i]-=({ 0 });
}
}
// Aufruf nach Moeglichkeit bitte vermeiden, da recht aufwendig. Meist ist
// es leichter im NPC "sauber Buch zu fuehren" und sich zu merken, ob er
// bereits angemeldet ist. Liefert zurueck, wie oft ein NPC als Walker
// angemeldet ist (normalfall nie mehr als 1).
int Registration()
{
int i, j, reg;
if (!num_walker) return 0;
reg=0;
for (i=MAX_DELAYTIME; i>=0; i--)
{
for (j=sizeof(walker[i])-1; j>=0; j--)
if (get_type_info(walker[i][j][1], 2)==previous_object())
reg++;
}
return reg;
}
void heart_beat()
{
int i;
if (num_walker && i=sizeof(walker[counter])) {
int tmp;
num_walker-=i;
for (i--;i>=0;i--) {
if (get_eval_cost() < MAX_JOB_COST) {
// nicht abgefertigte NPCs im naechsten heart_beat ausfuehren
walker[counter]=walker[counter][0..i];
num_walker+=i+1;
return;
}
else {
if (walker[counter][i][1] &&
!catch(tmp=(int)funcall(walker[counter][i][1])) && tmp) {
tmp=counter+(TIME(walker[counter][i][0])
+random(RANDOM(walker[counter][i][0])))/2;
if (tmp>MAX_DELAYTIME) tmp-=MAX_DELAYTIME;
walker[tmp]+=({ walker[counter][i] });
num_walker++;
}
}
}
walker[counter]=({}); // komplett leeren
}
if (counter == MAX_DELAYTIME)
counter=0;
else counter++;
if (!num_walker) {
set_heart_beat(0);
walker=({}); // Speicher freigeben...
}
}
void reset()
// kostet maximal einen unnoetigen heart_beat() pro reset -> vertretbar
// dient zu einem wieder anwerfen im Falle eines Fehlers im heart_beat()
{
if (set_heart_beat(0)<=0) {
int i;
num_walker=0; // neu berechnen...
if (!sizeof(walker)) return;
for (i=MAX_DELAYTIME; i>=0; i--)
num_walker+=sizeof(walker[i]);
if (num_walker>0) {
write_file(object_name()+".err", sprintf(
"%s: Fehler im heart_beat(). %d aktive Prozesse.\n",
dtime(time()), num_walker));
enable_commands();
set_heart_beat(1);
}
}
else set_heart_beat(1);
}
mixed *WalkerList() // nur fuer Debugzwecke
{ return ({ num_walker, walker, counter }); }