blob: 370fcd06843916cb0e8ac94c122b6929cd896fdb [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
Zesstra90026882019-07-19 18:46:28 +020021#pragma strong_types,rtt_checks
22#pragma no_clone,no_inherit
23
zesstra42fd8bf2016-06-27 22:04:14 +020024#define MAX_DELAYTIME 90 /* max. delay ist 2*MAX_DELAYTIME */
25#define DEFAULT_WALK_DELAY 180 /* ist der billigste Wert :) */
26#define MAX_JOB_COST 200000 /* Wieviel Zeit darf ein NPC max. nutzen */
27
28// Funktionen zum vereinfachten Zugriff auf die Komprimierten Daten
29// im Walkerarray
30#define TIME(t) (t & 0x00ff) /* 8 Bit = 256 */
31#define RANDOM(r) ((r & 0xff00) >> 8) /* 8 Bit = 256 */
32#define WERT(t, r) ((t & 0x00ff)+((r << 8) & 0xff00)) /* 16 Bit */
33
Zesstra90026882019-07-19 18:46:28 +020034// Indizes fuer walker und clients
Zesstra1d54d2a2019-06-24 22:25:12 +020035#define WALK_DELAY 0
36#define WALK_CLOSURE 1
37
zesstra42fd8bf2016-06-27 22:04:14 +020038static int num_walker; // anzahl der walker im array
Zesstra90026882019-07-19 18:46:28 +020039nosave int counter; // Markiert aktuellen Zeitslot im Array walker
zesstra42fd8bf2016-06-27 22:04:14 +020040nosave < < <int|closure>* >* >* walker;
Zesstrad41e6712019-07-19 18:35:37 +020041// Mapping mit allen registrierten MNPC (als Objekte) als Key und deren
42// Zeitdaten (1. wert) und deren walk_closure (2. Wert).
43nosave mapping clients = m_allocate(0,2);
zesstra42fd8bf2016-06-27 22:04:14 +020044
Zesstra90026882019-07-19 18:46:28 +020045public int Registration();
Zesstrabc854872019-06-24 22:51:54 +020046
zesstra42fd8bf2016-06-27 22:04:14 +020047protected void create()
48{
Zesstraeeddeeb2019-07-19 18:44:46 +020049 walker=map(allocate(MAX_DELAYTIME+1), #'allocate);
Zesstra90026882019-07-19 18:46:28 +020050 num_walker=0;
zesstra42fd8bf2016-06-27 22:04:14 +020051 enable_commands(); // ohne das, kein heart_beat()
52}
53
54#define ERROR(x) raise_error(sprintf(x, previous_object()));
55
56// Man muss selbst darauf aufpassen, das sich ein NPC immer nur einmal
57// anmeldet, da sonst auch mehrere Paralelle Walk-Ketten laufen!!!
58// Am besten nie direkt sondern nur ueber einen Standardnpc benutzen.
Zesstra90026882019-07-19 18:46:28 +020059// Bemerkung: man kann hiermit andere Objekt registrieren. Aber nur das Objekt
60// selber kann spaeter seine Registrierung pruefen oder sich abmelden...
61// (Fraglich, ob das so gewollt ist.)
62public varargs void RegisterWalker(int time, int rand, closure walk_closure)
zesstra42fd8bf2016-06-27 22:04:14 +020063{
zesstra42fd8bf2016-06-27 22:04:14 +020064 // pruefen ob die Paramter zulaessig sind...
65 if (time<0) ERROR("negative time to RegisterWalker() from %O.\n");
66 if (rand<0) ERROR("negative random to RegisterWalker() from %O.\n");
67 if ((time+rand) > (2*MAX_DELAYTIME))
68 ERROR("Too long delaytime from %s to RegisterWalker().\n");
69
Zesstrae7956f32019-06-24 22:32:04 +020070 if (Registration())
Zesstrabc854872019-06-24 22:51:54 +020071 raise_error(sprintf("Mehrfachanmeldung nicht erlaubt. Objekt: %O\n",
72 previous_object()));
Zesstrae7956f32019-06-24 22:32:04 +020073
Zesstra90026882019-07-19 18:46:28 +020074 int wert=WERT(time, rand);
zesstra42fd8bf2016-06-27 22:04:14 +020075 if (!wert && !rand) wert=DEFAULT_WALK_DELAY;
Zesstra90026882019-07-19 18:46:28 +020076
77 closure func = walk_closure;
78 if (!closurep(func))
79 {
zesstra42fd8bf2016-06-27 22:04:14 +020080 func=symbol_function("Walk", previous_object());
81 if (!func)
82 raise_error("RegisterWalker() call from Object without Walk() function.\n");
83 }
84 if (!num_walker) {
85 set_heart_beat(1);
zesstra42fd8bf2016-06-27 22:04:14 +020086 }
Zesstra90026882019-07-19 18:46:28 +020087 int next=counter;
zesstra42fd8bf2016-06-27 22:04:14 +020088 next+=(time+random(rand))/2;
89 if (next>MAX_DELAYTIME) next-=MAX_DELAYTIME;
90 walker[next]+=({ ({ wert, func }) });
Zesstrad41e6712019-07-19 18:35:37 +020091 clients += ([ get_type_info(func, 2): wert; func ]);
zesstra42fd8bf2016-06-27 22:04:14 +020092 num_walker++;
93}
94
95int dummy_walk() // liefert immer 0 fuer abbrechen...
96{ return 0; }
97
98// Aufruf nach Moeglichkeit bitte vermeiden, da recht aufwendig. Meist ist
99// es leicht im NPC "sauber Buch zu fuehren" und dann ggf. aus Walk()
100// 0 zu returnen.
Zesstra90026882019-07-19 18:46:28 +0200101public void RemoveWalker()
zesstra42fd8bf2016-06-27 22:04:14 +0200102{
Zesstrad41e6712019-07-19 18:35:37 +0200103 if (!member(clients, previous_object()))
104 return;
105
106 for (int i=MAX_DELAYTIME; i>=0; i--) {
107 for (int j=sizeof(walker[i])-1; j>=0; j--)
zesstra42fd8bf2016-06-27 22:04:14 +0200108 {
Zesstra1d54d2a2019-06-24 22:25:12 +0200109 if (get_type_info(walker[i][j][WALK_CLOSURE], 2)==previous_object())
zesstra42fd8bf2016-06-27 22:04:14 +0200110 {
111 if (i==counter) // koennte gerade im heart_beat stecken...
112 walker[i][j]=({ 0, #'dummy_walk });
113 else
114 walker[i][j]=0;
115 num_walker--;
116 }
117 }
118 if (i!=counter) // koennte gerade im heart_beat stecken...
119 walker[i]-=({ 0 });
120 }
Zesstrad41e6712019-07-19 18:35:37 +0200121 m_delete(clients, previous_object());
zesstra42fd8bf2016-06-27 22:04:14 +0200122}
123
Zesstra90026882019-07-19 18:46:28 +0200124public int Registration()
zesstra42fd8bf2016-06-27 22:04:14 +0200125{
Zesstrad41e6712019-07-19 18:35:37 +0200126 return member(clients, previous_object());
zesstra42fd8bf2016-06-27 22:04:14 +0200127}
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] &&
Zesstra1d54d2a2019-06-24 22:25:12 +0200144 !catch(tmp=(int)funcall(walker[counter][i][WALK_CLOSURE]))
145 && tmp)
146 {
147 tmp=counter+(TIME(walker[counter][i][WALK_DELAY])
148 +random(RANDOM(walker[counter][i][WALK_DELAY])))/2;
zesstra42fd8bf2016-06-27 22:04:14 +0200149 if (tmp>MAX_DELAYTIME) tmp-=MAX_DELAYTIME;
150 walker[tmp]+=({ walker[counter][i] });
151 num_walker++;
152 }
153 }
154 }
155 walker[counter]=({}); // komplett leeren
156 }
157 if (counter == MAX_DELAYTIME)
158 counter=0;
159 else counter++;
160 if (!num_walker) {
161 set_heart_beat(0);
zesstra42fd8bf2016-06-27 22:04:14 +0200162 }
163}
164
165void reset()
166// kostet maximal einen unnoetigen heart_beat() pro reset -> vertretbar
167// dient zu einem wieder anwerfen im Falle eines Fehlers im heart_beat()
168{
169 if (set_heart_beat(0)<=0) {
170 int i;
171 num_walker=0; // neu berechnen...
172 if (!sizeof(walker)) return;
173 for (i=MAX_DELAYTIME; i>=0; i--)
174 num_walker+=sizeof(walker[i]);
175 if (num_walker>0) {
176 write_file(object_name()+".err", sprintf(
177 "%s: Fehler im heart_beat(). %d aktive Prozesse.\n",
178 dtime(time()), num_walker));
179 enable_commands();
180 set_heart_beat(1);
181 }
182 }
183 else set_heart_beat(1);
184}
185
Zesstra90026882019-07-19 18:46:28 +0200186// Bemerkung: damit kann jeder die Closures ermitteln und dann selber rufen.
zesstra42fd8bf2016-06-27 22:04:14 +0200187mixed *WalkerList() // nur fuer Debugzwecke
188{ return ({ num_walker, walker, counter }); }