blob: 3e003c85c939444c75bfc7a281e85286646ac081 [file] [log] [blame]
zesstra42fd8bf2016-06-27 22:04:14 +02001// (c) by Padreic (Padreic@mg.mud.de)
2
3/* 12 Juli 1998, Padreic
4 *
5 * Dieser Master dient zum einsparen von call_out Ketten bei Laufnpcs.
6 * Anmelden bei diesem Master geschieht ueber die Funktion RegisterWalker().
7 * varargs void RegisterWalker(int time, int random, closure walk_closure)
8 * time - in welchen Abstaenden soll das Objekt bewegt werden
9 * rand - dieser Wert wird als random immer beim bewegen zu time addiert
10 * walk_closure - die Closure die immer aufgerufen werden muss, wenn dieser
11 * Parameter weggelassen wird, wird statdessen die Funktion
12 * Walk() im NPC aufgerufen.
13 *
14 * Abgemeldet wird der NPC sobald die Closure bzw. die Walk-Funktion 0
15 * returned. Bei allen Werten !=0 bleibt der NPC aktiv.
16 *
17 * Hinweis: Der NPC muss sich mind. alle 180 sek. bewegen (time+random),
18 * ansonsten kann dieser Master nicht verwendet werden.
19 */
20
21#define MAX_DELAYTIME 90 /* max. delay ist 2*MAX_DELAYTIME */
22#define DEFAULT_WALK_DELAY 180 /* ist der billigste Wert :) */
23#define MAX_JOB_COST 200000 /* Wieviel Zeit darf ein NPC max. nutzen */
24
25// Funktionen zum vereinfachten Zugriff auf die Komprimierten Daten
26// im Walkerarray
27#define TIME(t) (t & 0x00ff) /* 8 Bit = 256 */
28#define RANDOM(r) ((r & 0xff00) >> 8) /* 8 Bit = 256 */
29#define WERT(t, r) ((t & 0x00ff)+((r << 8) & 0xff00)) /* 16 Bit */
30
31static int counter; // zur Orientierung im walker-array
32static int num_walker; // anzahl der walker im array
33//static mixed *walker; // ({ ..., ({ ..., ({ wert, closure }), ...}), ...})
34nosave < < <int|closure>* >* >* walker;
35
36protected void create()
37{
38 if (clonep(this_object())) {
39 destruct(this_object());
40 raise_error("walk_master can't be clonend.\n");
41 }
42 walker=({});
43 num_walker=0;
44 enable_commands(); // ohne das, kein heart_beat()
45}
46
47#define ERROR(x) raise_error(sprintf(x, previous_object()));
48
49// Man muss selbst darauf aufpassen, das sich ein NPC immer nur einmal
50// anmeldet, da sonst auch mehrere Paralelle Walk-Ketten laufen!!!
51// Am besten nie direkt sondern nur ueber einen Standardnpc benutzen.
52varargs void RegisterWalker(int time, int rand, closure walk_closure)
53{
54 int wert, next;
55 closure func;
56
57 // pruefen ob die Paramter zulaessig sind...
58 if (time<0) ERROR("negative time to RegisterWalker() from %O.\n");
59 if (rand<0) ERROR("negative random to RegisterWalker() from %O.\n");
60 if ((time+rand) > (2*MAX_DELAYTIME))
61 ERROR("Too long delaytime from %s to RegisterWalker().\n");
62
63 wert=WERT(time, rand);
64 if (!wert && !rand) wert=DEFAULT_WALK_DELAY;
65 if (walk_closure)
66 func=walk_closure;
67 else {
68 func=symbol_function("Walk", previous_object());
69 if (!func)
70 raise_error("RegisterWalker() call from Object without Walk() function.\n");
71 }
72 if (!num_walker) {
73 set_heart_beat(1);
74 if (!pointerp(walker) || !sizeof(walker))
75 walker=map(allocate(MAX_DELAYTIME+1), #'allocate);
76 }
77 next=counter;
78 next+=(time+random(rand))/2;
79 if (next>MAX_DELAYTIME) next-=MAX_DELAYTIME;
80 walker[next]+=({ ({ wert, func }) });
81 num_walker++;
82}
83
84int dummy_walk() // liefert immer 0 fuer abbrechen...
85{ return 0; }
86
87// Aufruf nach Moeglichkeit bitte vermeiden, da recht aufwendig. Meist ist
88// es leicht im NPC "sauber Buch zu fuehren" und dann ggf. aus Walk()
89// 0 zu returnen.
90void RemoveWalker()
91{
92 int i, j;
93 if (!num_walker) return;
94 for (i=MAX_DELAYTIME; i>=0; i--) {
95 for (j=sizeof(walker[i])-1; j>=0; j--)
96 {
97 if (get_type_info(walker[i][j][1], 2)==previous_object())
98 {
99 if (i==counter) // koennte gerade im heart_beat stecken...
100 walker[i][j]=({ 0, #'dummy_walk });
101 else
102 walker[i][j]=0;
103 num_walker--;
104 }
105 }
106 if (i!=counter) // koennte gerade im heart_beat stecken...
107 walker[i]-=({ 0 });
108 }
109}
110
111// Aufruf nach Moeglichkeit bitte vermeiden, da recht aufwendig. Meist ist
112// es leichter im NPC "sauber Buch zu fuehren" und sich zu merken, ob er
113// bereits angemeldet ist. Liefert zurueck, wie oft ein NPC als Walker
114// angemeldet ist (normalfall nie mehr als 1).
115int Registration()
116{
117 int i, j, reg;
118 if (!num_walker) return 0;
119 reg=0;
120 for (i=MAX_DELAYTIME; i>=0; i--)
121 {
122 for (j=sizeof(walker[i])-1; j>=0; j--)
123 if (get_type_info(walker[i][j][1], 2)==previous_object())
124 reg++;
125 }
126 return reg;
127}
128
129void heart_beat()
130{
131 int i;
132 if (num_walker && i=sizeof(walker[counter])) {
133 int tmp;
134 num_walker-=i;
135 for (i--;i>=0;i--) {
136 if (get_eval_cost() < MAX_JOB_COST) {
137 // nicht abgefertigte NPCs im naechsten heart_beat ausfuehren
138 walker[counter]=walker[counter][0..i];
139 num_walker+=i+1;
140 return;
141 }
142 else {
143 if (walker[counter][i][1] &&
144 !catch(tmp=(int)funcall(walker[counter][i][1])) && tmp) {
145 tmp=counter+(TIME(walker[counter][i][0])
146 +random(RANDOM(walker[counter][i][0])))/2;
147 if (tmp>MAX_DELAYTIME) tmp-=MAX_DELAYTIME;
148 walker[tmp]+=({ walker[counter][i] });
149 num_walker++;
150 }
151 }
152 }
153 walker[counter]=({}); // komplett leeren
154 }
155 if (counter == MAX_DELAYTIME)
156 counter=0;
157 else counter++;
158 if (!num_walker) {
159 set_heart_beat(0);
160 walker=({}); // Speicher freigeben...
161 }
162}
163
164void reset()
165// kostet maximal einen unnoetigen heart_beat() pro reset -> vertretbar
166// dient zu einem wieder anwerfen im Falle eines Fehlers im heart_beat()
167{
168 if (set_heart_beat(0)<=0) {
169 int i;
170 num_walker=0; // neu berechnen...
171 if (!sizeof(walker)) return;
172 for (i=MAX_DELAYTIME; i>=0; i--)
173 num_walker+=sizeof(walker[i]);
174 if (num_walker>0) {
175 write_file(object_name()+".err", sprintf(
176 "%s: Fehler im heart_beat(). %d aktive Prozesse.\n",
177 dtime(time()), num_walker));
178 enable_commands();
179 set_heart_beat(1);
180 }
181 }
182 else set_heart_beat(1);
183}
184
185mixed *WalkerList() // nur fuer Debugzwecke
186{ return ({ num_walker, walker, counter }); }