blob: abd61bc34027370ef21fcfb748dbe6c3c3c0ce26 [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
Zesstra1d54d2a2019-06-24 22:25:12 +020031// Indizes fuer walker
32#define WALK_DELAY 0
33#define WALK_CLOSURE 1
34
zesstra42fd8bf2016-06-27 22:04:14 +020035static int counter; // zur Orientierung im walker-array
36static int num_walker; // anzahl der walker im array
37//static mixed *walker; // ({ ..., ({ ..., ({ wert, closure }), ...}), ...})
38nosave < < <int|closure>* >* >* walker;
Zesstrad41e6712019-07-19 18:35:37 +020039// Mapping mit allen registrierten MNPC (als Objekte) als Key und deren
40// Zeitdaten (1. wert) und deren walk_closure (2. Wert).
41nosave mapping clients = m_allocate(0,2);
zesstra42fd8bf2016-06-27 22:04:14 +020042
Zesstrabc854872019-06-24 22:51:54 +020043int Registration();
44
zesstra42fd8bf2016-06-27 22:04:14 +020045protected void create()
46{
47 if (clonep(this_object())) {
48 destruct(this_object());
49 raise_error("walk_master can't be clonend.\n");
50 }
51 walker=({});
52 num_walker=0;
53 enable_commands(); // ohne das, kein heart_beat()
54}
55
56#define ERROR(x) raise_error(sprintf(x, previous_object()));
57
58// Man muss selbst darauf aufpassen, das sich ein NPC immer nur einmal
59// anmeldet, da sonst auch mehrere Paralelle Walk-Ketten laufen!!!
60// Am besten nie direkt sondern nur ueber einen Standardnpc benutzen.
61varargs void RegisterWalker(int time, int rand, closure walk_closure)
62{
63 int wert, next;
64 closure func;
65
66 // pruefen ob die Paramter zulaessig sind...
67 if (time<0) ERROR("negative time to RegisterWalker() from %O.\n");
68 if (rand<0) ERROR("negative random to RegisterWalker() from %O.\n");
69 if ((time+rand) > (2*MAX_DELAYTIME))
70 ERROR("Too long delaytime from %s to RegisterWalker().\n");
71
Zesstrae7956f32019-06-24 22:32:04 +020072 if (Registration())
Zesstrabc854872019-06-24 22:51:54 +020073 raise_error(sprintf("Mehrfachanmeldung nicht erlaubt. Objekt: %O\n",
74 previous_object()));
Zesstrae7956f32019-06-24 22:32:04 +020075
zesstra42fd8bf2016-06-27 22:04:14 +020076 wert=WERT(time, rand);
77 if (!wert && !rand) wert=DEFAULT_WALK_DELAY;
78 if (walk_closure)
79 func=walk_closure;
80 else {
81 func=symbol_function("Walk", previous_object());
82 if (!func)
83 raise_error("RegisterWalker() call from Object without Walk() function.\n");
84 }
85 if (!num_walker) {
86 set_heart_beat(1);
87 if (!pointerp(walker) || !sizeof(walker))
88 walker=map(allocate(MAX_DELAYTIME+1), #'allocate);
89 }
90 next=counter;
91 next+=(time+random(rand))/2;
92 if (next>MAX_DELAYTIME) next-=MAX_DELAYTIME;
93 walker[next]+=({ ({ wert, func }) });
Zesstrad41e6712019-07-19 18:35:37 +020094 clients += ([ get_type_info(func, 2): wert; func ]);
zesstra42fd8bf2016-06-27 22:04:14 +020095 num_walker++;
96}
97
98int dummy_walk() // liefert immer 0 fuer abbrechen...
99{ return 0; }
100
101// Aufruf nach Moeglichkeit bitte vermeiden, da recht aufwendig. Meist ist
102// es leicht im NPC "sauber Buch zu fuehren" und dann ggf. aus Walk()
103// 0 zu returnen.
104void RemoveWalker()
105{
Zesstrad41e6712019-07-19 18:35:37 +0200106 if (!member(clients, previous_object()))
107 return;
108
109 for (int i=MAX_DELAYTIME; i>=0; i--) {
110 for (int j=sizeof(walker[i])-1; j>=0; j--)
zesstra42fd8bf2016-06-27 22:04:14 +0200111 {
Zesstra1d54d2a2019-06-24 22:25:12 +0200112 if (get_type_info(walker[i][j][WALK_CLOSURE], 2)==previous_object())
zesstra42fd8bf2016-06-27 22:04:14 +0200113 {
114 if (i==counter) // koennte gerade im heart_beat stecken...
115 walker[i][j]=({ 0, #'dummy_walk });
116 else
117 walker[i][j]=0;
118 num_walker--;
119 }
120 }
121 if (i!=counter) // koennte gerade im heart_beat stecken...
122 walker[i]-=({ 0 });
123 }
Zesstrad41e6712019-07-19 18:35:37 +0200124 m_delete(clients, previous_object());
zesstra42fd8bf2016-06-27 22:04:14 +0200125}
126
zesstra42fd8bf2016-06-27 22:04:14 +0200127int Registration()
128{
Zesstrad41e6712019-07-19 18:35:37 +0200129 return member(clients, previous_object());
zesstra42fd8bf2016-06-27 22:04:14 +0200130}
131
132void heart_beat()
133{
134 int i;
135 if (num_walker && i=sizeof(walker[counter])) {
136 int tmp;
137 num_walker-=i;
138 for (i--;i>=0;i--) {
139 if (get_eval_cost() < MAX_JOB_COST) {
140 // nicht abgefertigte NPCs im naechsten heart_beat ausfuehren
141 walker[counter]=walker[counter][0..i];
142 num_walker+=i+1;
143 return;
144 }
145 else {
146 if (walker[counter][i][1] &&
Zesstra1d54d2a2019-06-24 22:25:12 +0200147 !catch(tmp=(int)funcall(walker[counter][i][WALK_CLOSURE]))
148 && tmp)
149 {
150 tmp=counter+(TIME(walker[counter][i][WALK_DELAY])
151 +random(RANDOM(walker[counter][i][WALK_DELAY])))/2;
zesstra42fd8bf2016-06-27 22:04:14 +0200152 if (tmp>MAX_DELAYTIME) tmp-=MAX_DELAYTIME;
153 walker[tmp]+=({ walker[counter][i] });
154 num_walker++;
155 }
156 }
157 }
158 walker[counter]=({}); // komplett leeren
159 }
160 if (counter == MAX_DELAYTIME)
161 counter=0;
162 else counter++;
163 if (!num_walker) {
164 set_heart_beat(0);
165 walker=({}); // Speicher freigeben...
166 }
167}
168
169void reset()
170// kostet maximal einen unnoetigen heart_beat() pro reset -> vertretbar
171// dient zu einem wieder anwerfen im Falle eines Fehlers im heart_beat()
172{
173 if (set_heart_beat(0)<=0) {
174 int i;
175 num_walker=0; // neu berechnen...
176 if (!sizeof(walker)) return;
177 for (i=MAX_DELAYTIME; i>=0; i--)
178 num_walker+=sizeof(walker[i]);
179 if (num_walker>0) {
180 write_file(object_name()+".err", sprintf(
181 "%s: Fehler im heart_beat(). %d aktive Prozesse.\n",
182 dtime(time()), num_walker));
183 enable_commands();
184 set_heart_beat(1);
185 }
186 }
187 else set_heart_beat(1);
188}
189
190mixed *WalkerList() // nur fuer Debugzwecke
191{ return ({ num_walker, walker, counter }); }