zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 1 | // (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 | |
Zesstra | 1d54d2a | 2019-06-24 22:25:12 +0200 | [diff] [blame] | 31 | // Indizes fuer walker |
| 32 | #define WALK_DELAY 0 |
| 33 | #define WALK_CLOSURE 1 |
| 34 | |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 35 | static int counter; // zur Orientierung im walker-array |
| 36 | static int num_walker; // anzahl der walker im array |
| 37 | //static mixed *walker; // ({ ..., ({ ..., ({ wert, closure }), ...}), ...}) |
| 38 | nosave < < <int|closure>* >* >* walker; |
Zesstra | d41e671 | 2019-07-19 18:35:37 +0200 | [diff] [blame^] | 39 | // Mapping mit allen registrierten MNPC (als Objekte) als Key und deren |
| 40 | // Zeitdaten (1. wert) und deren walk_closure (2. Wert). |
| 41 | nosave mapping clients = m_allocate(0,2); |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 42 | |
Zesstra | bc85487 | 2019-06-24 22:51:54 +0200 | [diff] [blame] | 43 | int Registration(); |
| 44 | |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 45 | protected 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. |
| 61 | varargs 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 | |
Zesstra | e7956f3 | 2019-06-24 22:32:04 +0200 | [diff] [blame] | 72 | if (Registration()) |
Zesstra | bc85487 | 2019-06-24 22:51:54 +0200 | [diff] [blame] | 73 | raise_error(sprintf("Mehrfachanmeldung nicht erlaubt. Objekt: %O\n", |
| 74 | previous_object())); |
Zesstra | e7956f3 | 2019-06-24 22:32:04 +0200 | [diff] [blame] | 75 | |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 76 | 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 }) }); |
Zesstra | d41e671 | 2019-07-19 18:35:37 +0200 | [diff] [blame^] | 94 | clients += ([ get_type_info(func, 2): wert; func ]); |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 95 | num_walker++; |
| 96 | } |
| 97 | |
| 98 | int 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. |
| 104 | void RemoveWalker() |
| 105 | { |
Zesstra | d41e671 | 2019-07-19 18:35:37 +0200 | [diff] [blame^] | 106 | 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--) |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 111 | { |
Zesstra | 1d54d2a | 2019-06-24 22:25:12 +0200 | [diff] [blame] | 112 | if (get_type_info(walker[i][j][WALK_CLOSURE], 2)==previous_object()) |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 113 | { |
| 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 | } |
Zesstra | d41e671 | 2019-07-19 18:35:37 +0200 | [diff] [blame^] | 124 | m_delete(clients, previous_object()); |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 125 | } |
| 126 | |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 127 | int Registration() |
| 128 | { |
Zesstra | d41e671 | 2019-07-19 18:35:37 +0200 | [diff] [blame^] | 129 | return member(clients, previous_object()); |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 130 | } |
| 131 | |
| 132 | void 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] && |
Zesstra | 1d54d2a | 2019-06-24 22:25:12 +0200 | [diff] [blame] | 147 | !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; |
zesstra | 42fd8bf | 2016-06-27 22:04:14 +0200 | [diff] [blame] | 152 | 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 | |
| 169 | void 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 | |
| 190 | mixed *WalkerList() // nur fuer Debugzwecke |
| 191 | { return ({ num_walker, walker, counter }); } |