MG Mud User | 88f1247 | 2016-06-24 23:31:02 +0200 | [diff] [blame^] | 1 | // MorgenGrauen MUDlib |
| 2 | // |
| 3 | // unit.c -- Basisklasse fuer Mengenobjekte |
| 4 | // (neue Version von Padreic) |
| 5 | // |
| 6 | // $Id: unit.c 9077 2015-01-16 23:26:00Z Zesstra $ |
| 7 | #pragma strong_types,save_types,rtt_checks |
| 8 | #pragma range_check |
| 9 | #pragma no_clone |
| 10 | #pragma pedantic |
| 11 | |
| 12 | inherit "/std/thing"; |
| 13 | |
| 14 | #define NEED_PROTOTYPES |
| 15 | #include <unit.h> |
| 16 | #undef NEED_PROTOTYPES |
| 17 | |
| 18 | #include <properties.h> |
| 19 | #include <thing/properties.h> |
| 20 | #include <defines.h> |
| 21 | #include <language.h> |
| 22 | #include <moving.h> |
| 23 | #include <wizlevels.h> |
| 24 | #include <debug_info.h> |
| 25 | |
| 26 | // zum debuggen und extra public |
| 27 | public string debugmode; |
| 28 | public string __set_debug(string recv) {return debugmode=recv;} |
| 29 | #include <living/comm.h> |
| 30 | #define ZDEBUG(x) if (stringp(debugmode) && find_player(debugmode)) \ |
| 31 | find_player(debugmode)->ReceiveMsg(x,MT_DEBUG,0,object_name()+": ",ME) |
| 32 | //#define ZDEBUG(x) |
| 33 | |
| 34 | private void _call_DoDecay(object *klone); |
| 35 | |
| 36 | private nosave string lastverb; |
| 37 | private nosave int lastevalnumber; |
| 38 | |
| 39 | protected void create() |
| 40 | { |
| 41 | ::create(); |
| 42 | SetProp(U_IDS,({({}),({})})); |
| 43 | SetProp(P_GENDER, 1); |
| 44 | SetProp(P_AMOUNT, 1); |
| 45 | // Props sollten nicht direkt manipuliert werden, nur ueber Set-Methoden |
| 46 | Set(P_UNIT_DECAY_INTERVAL, PROTECTED, F_MODE_AS); |
| 47 | Set(P_UNIT_DECAY_QUOTA, PROTECTED, F_MODE_AS); |
| 48 | } |
| 49 | |
| 50 | protected void create_super() { |
| 51 | set_next_reset(-1); |
| 52 | } |
| 53 | |
| 54 | //// Query und Set Funktionen fuer die Propertys |
| 55 | |
| 56 | static void check_leave() |
| 57 | { if (Query(P_AMOUNT,F_VALUE)<1) remove(1); } |
| 58 | |
| 59 | static int _query_invis() |
| 60 | { |
| 61 | if (QueryProp(P_AMOUNT) < 1) |
| 62 | return 1; |
| 63 | return Query(P_INVIS, F_VALUE); |
| 64 | } |
| 65 | |
| 66 | static int _set_amount(int am) |
| 67 | { |
| 68 | if (am<1) |
| 69 | call_out("check_leave",1); |
| 70 | SetProp(U_REQ, am); |
| 71 | return Set(P_AMOUNT,am, F_VALUE); |
| 72 | } |
| 73 | |
| 74 | static int _set_u_req(int ureq) |
| 75 | { |
| 76 | lastverb = query_verb(); |
| 77 | lastevalnumber = debug_info(DINFO_EVAL_NUMBER); |
| 78 | return Set(U_REQ, ureq, F_VALUE); |
| 79 | } |
| 80 | |
| 81 | static int _query_u_req() |
| 82 | { |
| 83 | // Zwei Dinge benutzten, um zu entscheiden, ob U_REQ noch gueltig ist oder |
| 84 | // besser auf P_AMOUNT zurueckgesetzt werden sollte: die DINFO_EVAL_NUMBER |
| 85 | // und das Kommandoverb. Wenn eines von beiden sich geaendert hat, wird |
| 86 | // U_REQ als ungueltig betrachtet. (Und dies bleibt ein uebler Hack.) |
| 87 | if (lastevalnumber != debug_info(DINFO_EVAL_NUMBER) |
| 88 | || lastverb != query_verb()) |
| 89 | { |
| 90 | return SetProp(U_REQ, Query(P_AMOUNT, F_VALUE)); |
| 91 | } |
| 92 | return Query(U_REQ, F_VALUE); |
| 93 | } |
| 94 | |
| 95 | // Gesamtwert der gerade mit U_REQ ausgewaehlten unitobjekte |
| 96 | static int _query_value() |
| 97 | { |
| 98 | mixed cpu; |
| 99 | cpu=Query(U_CPU); |
| 100 | if (intp(cpu)) return QueryProp(U_REQ) * cpu; |
| 101 | if (!pointerp(cpu)) return 0; |
| 102 | return (QueryProp(U_REQ) * cpu[0])/cpu[1]; |
| 103 | } |
| 104 | |
| 105 | static int _set_value(int num) { |
| 106 | // Setzt den Wert fuer die derzeitige Anzahl |
| 107 | SetCoinsPerUnits(num, QueryProp(U_REQ)); |
| 108 | return QueryProp(P_VALUE); |
| 109 | } |
| 110 | |
| 111 | static int _query_weight() |
| 112 | { |
| 113 | mixed gpu, req; |
| 114 | if ((req=QueryProp(U_REQ))<1) return 0; |
| 115 | gpu=Query(U_GPU); |
| 116 | if (intp(gpu)) return req * gpu; |
| 117 | if (!pointerp(gpu)) return 0; |
| 118 | return MAX(1,(req*gpu[0])/gpu[1]); |
| 119 | } |
| 120 | |
| 121 | static int _set_weight(int num) { |
| 122 | // Setzt das Gewicht fuer die derzeitige Anzahl |
| 123 | SetGramsPerUnits(num, QueryProp(U_REQ)); |
| 124 | return QueryProp(P_WEIGHT); |
| 125 | } |
| 126 | |
| 127 | static int _query_total_weight() |
| 128 | { |
| 129 | mixed gpu, amount; |
| 130 | if ((amount=Query(P_AMOUNT))<1) return 0; |
| 131 | gpu=Query(U_GPU); |
| 132 | if (intp(gpu)) |
| 133 | return amount*(int)gpu; |
| 134 | if (!pointerp(gpu)) return 0; |
| 135 | return MAX(1,(amount*gpu[0])/gpu[1]); |
| 136 | } |
| 137 | |
| 138 | static int _query_plural() |
| 139 | { |
| 140 | int i; |
| 141 | i=QueryProp(U_REQ); // wichtig _nicht_ QueryProp |
| 142 | return (i<=1 ? 0 : i); |
| 143 | } |
| 144 | |
| 145 | // gibt string | string* zurueck. |
| 146 | static string|string* _query_name() |
| 147 | { |
| 148 | if (QueryProp(U_REQ)==1) |
| 149 | return "%s%s"+((string *)Query(P_NAME))[0]; |
| 150 | return "%d %s"+((string *)Query(P_NAME))[1]; |
| 151 | } |
| 152 | |
| 153 | // gibt string | string* zurueck. |
| 154 | static string|string* _set_name(mixed names) |
| 155 | { |
| 156 | if(!names) |
| 157 | return Set(P_NAME,({"",""})); |
| 158 | if(stringp(names)) |
| 159 | return Set(P_NAME,({names,names})); |
| 160 | if(pointerp(names)) |
| 161 | switch(sizeof(names)) { |
| 162 | case 0: return Set(P_NAME,({"",""})); |
| 163 | case 1: if(!stringp(names[0])) |
| 164 | names[0]=""; |
| 165 | return Set(P_NAME,({names[0],names[0]})); |
| 166 | default: if(!stringp(names[0])) |
| 167 | names[0]=""; |
| 168 | if(!stringp(names[1])) |
| 169 | names[1]=""; |
| 170 | return Set(P_NAME,names[0..1]); |
| 171 | } |
| 172 | return QueryProp(P_NAME); |
| 173 | } |
| 174 | |
| 175 | // die Funktionen |
| 176 | |
| 177 | void AddSingularId(mixed str) |
| 178 | { |
| 179 | string *ids; |
| 180 | if (!pointerp(str)) |
| 181 | str=({str}); |
| 182 | ids=Query(U_IDS); |
| 183 | Set(U_IDS,({ids[0]+str,ids[1]})); |
| 184 | } |
| 185 | |
| 186 | void RemoveSingularId(mixed str) |
| 187 | { |
| 188 | if(stringp(str)) |
| 189 | str=({str}); |
| 190 | if(pointerp(str)) |
| 191 | Set(U_IDS,({Query(U_IDS)[0]-str,Query(U_IDS)[1]})); |
| 192 | } |
| 193 | |
| 194 | void AddPluralId(mixed str) |
| 195 | { |
| 196 | string *ids; |
| 197 | |
| 198 | if (!pointerp(str)) |
| 199 | str=({str}); |
| 200 | ids=Query(U_IDS); |
| 201 | Set(U_IDS,({ids[0],ids[1]+str})); |
| 202 | } |
| 203 | |
| 204 | void RemovePluralId(mixed str) |
| 205 | { |
| 206 | if(stringp(str)) |
| 207 | str=({str}); |
| 208 | if(pointerp(str)) |
| 209 | Set(U_IDS,({Query(U_IDS)[0],Query(U_IDS)[1]-str})); |
| 210 | } |
| 211 | |
| 212 | void SetCoinsPerUnits(int coins,int units) |
| 213 | { |
| 214 | if (coins<0||units<=0) return; |
| 215 | if (units==1) |
| 216 | Set(U_CPU, coins); |
| 217 | else Set(U_CPU,({coins,units})); |
| 218 | } |
| 219 | |
| 220 | void SetGramsPerUnits(int grams, int units) |
| 221 | { |
| 222 | if (grams<0||units<=0) return; |
| 223 | if (units==1) |
| 224 | Set(U_GPU, grams); |
| 225 | else Set(U_GPU,({grams,units})); |
| 226 | } |
| 227 | |
| 228 | int *QueryCoinsPerUnits() |
| 229 | { |
| 230 | mixed cpu; |
| 231 | cpu=Query(U_CPU); |
| 232 | if (intp(cpu)) return ({ cpu, 1 }); |
| 233 | return cpu; |
| 234 | } |
| 235 | |
| 236 | int *QueryGramsPerUnits() |
| 237 | { |
| 238 | mixed gpu; |
| 239 | gpu=Query(U_GPU); |
| 240 | if (intp(gpu)) return ({ gpu, 1 }); |
| 241 | return gpu; |
| 242 | } |
| 243 | |
| 244 | void AddAmount(int am) { |
| 245 | SetProp(P_AMOUNT, am+Query(P_AMOUNT)); |
| 246 | } |
| 247 | |
| 248 | int IsUnit() { |
| 249 | return 1; |
| 250 | } |
| 251 | |
| 252 | int IsEqual(object ob) |
| 253 | // haben ob und dieses Objekte die gleiche Blueprint? |
| 254 | { |
| 255 | if (ob==this_object()) return 0; // das ist ZU gleich :) |
| 256 | return (BLUE_NAME(ME)==BLUE_NAME(ob)); |
| 257 | } |
| 258 | |
| 259 | // ueberschreiben von Standardmethoden |
| 260 | |
| 261 | varargs string name(int fall, int demo) |
| 262 | { |
| 263 | int req; |
| 264 | mixed n_adj; |
| 265 | string adj; |
| 266 | |
| 267 | if ((req=QueryProp(U_REQ))<1) return 0; |
| 268 | if (fall!=RAW && |
| 269 | pointerp(n_adj=QueryProp(P_NAME_ADJ)) && sizeof(n_adj) ) |
| 270 | adj = implode(map(n_adj, #'DeclAdj, fall, demo && req==1), ""); |
| 271 | else |
| 272 | adj = ""; |
| 273 | |
| 274 | if (req==1) |
| 275 | return sprintf(QueryProp(P_NAME), |
| 276 | (fall==RAW || !QueryProp(P_ARTICLE) |
| 277 | ? "" : QueryArticle(fall,demo,1)), adj) /* +korrektur */; |
| 278 | |
| 279 | if (fall!=WEM) |
| 280 | return sprintf(QueryProp(P_NAME), req, adj); |
| 281 | else { |
| 282 | int last; |
| 283 | last=Query(P_NAME)[1][<1]; |
| 284 | return sprintf(QueryProp(P_NAME), req, adj) |
| 285 | +(last!='s' && last!='n' ? "n" : ""); |
| 286 | } |
| 287 | } |
| 288 | |
| 289 | varargs string QueryPronoun(int casus) |
| 290 | { |
| 291 | if (QueryProp(U_REQ)==1) |
| 292 | return ::QueryPronoun(casus); |
| 293 | switch(casus) { |
| 294 | case WESSEN: return "ihrer"; |
| 295 | case WEM: return "ihnen"; |
| 296 | //default: return "sie"; |
| 297 | } |
| 298 | return("sie"); //fall-through |
| 299 | } |
| 300 | |
| 301 | string short() |
| 302 | { |
| 303 | if (QueryProp(U_REQ)<1 || QueryProp(P_INVIS)) return 0; |
| 304 | return capitalize(name(WER,0))+".\n"; |
| 305 | /* |
| 306 | if (req>1) return sprintf(QueryProp(P_NAME), req)+".\n"; |
| 307 | return capitalize(sprintf(QueryProp(P_NAME), QueryArticle(WER,0,1)))+".\n"; |
| 308 | */ |
| 309 | } |
| 310 | |
| 311 | varargs string long() |
| 312 | { |
| 313 | return (QueryProp(P_LONG) ? process_string(QueryProp(P_LONG)) : "")+ |
| 314 | sprintf("Du siehst %s.\n",name(WEN)); |
| 315 | } |
| 316 | |
| 317 | varargs int id(string str,int lvl) |
| 318 | { |
| 319 | |
| 320 | string s1,s2,*ids; |
| 321 | int i; |
| 322 | |
| 323 | if (!str) return 0; |
| 324 | |
| 325 | // Wenn kein pos. Amount, hat dieses Objekt keine IDs mehr, damit es nicht |
| 326 | // mehr ansprechbar ist. |
| 327 | int amount=QueryProp(P_AMOUNT); |
| 328 | if (amount < 1) |
| 329 | return 0; |
| 330 | |
| 331 | if (::id(str)) { |
| 332 | SetProp(U_REQ, Query(P_AMOUNT,F_VALUE)); |
| 333 | return 1; |
| 334 | } |
| 335 | |
| 336 | ids=Query(U_IDS); |
| 337 | if (!ids) |
| 338 | return 0; |
| 339 | |
| 340 | //die casts auf 'mixed' sind absicht. Sonst geht der Driver offenbar davon |
| 341 | //aus, dass ids[1] ein String ist. Es ist aber aber ein Array von Strings |
| 342 | //und genau das will auch match_item() haben. ;-) Zesstra |
| 343 | if (match_item(str,(mixed)ids[1] )) { |
| 344 | SetProp(U_REQ, amount); |
| 345 | return 1; |
| 346 | } |
| 347 | if (match_item(str,(mixed)ids[0] )) { |
| 348 | SetProp(U_REQ,1); |
| 349 | return 1; |
| 350 | } |
| 351 | if (sscanf(str,"%s %s",s1,s2) && s1[0..3]=="alle" && |
| 352 | match_item(s2,(mixed)ids[1] )) { |
| 353 | SetProp(U_REQ, amount); |
| 354 | return 1; |
| 355 | } |
| 356 | if (sscanf(str,"%d %s",i,s1)==2 && i==1 && match_item(s1,(mixed)ids[0] )) { |
| 357 | SetProp(U_REQ,1); |
| 358 | return 1; |
| 359 | } |
| 360 | if (sscanf(str,"%d %s",i,s1)==2 && match_item(s1,(mixed)ids[1] ) && |
| 361 | i<=amount && i>0) { |
| 362 | SetProp(U_REQ,i); |
| 363 | return 1; |
| 364 | } |
| 365 | return 0; |
| 366 | } |
| 367 | |
| 368 | // Status fuer move... *seufz* |
| 369 | int _orig_amount, _move_requested; |
| 370 | |
| 371 | varargs int move(object|string dest,int method) |
| 372 | { |
| 373 | _orig_amount = QueryProp(P_AMOUNT); |
| 374 | // Wenn M_MOVE_ALL gesetzt ist, wird IMMER ALLES bewegt. |
| 375 | if (method & M_MOVE_ALL) |
| 376 | _move_requested = _orig_amount; |
| 377 | else |
| 378 | _move_requested = QueryProp(U_REQ); |
| 379 | |
| 380 | ZDEBUG(sprintf("move from %O to %O: amount %d, req %d\n",environment(),dest,_orig_amount,_move_requested)); |
| 381 | // Wenn nicht alles bewegt werden soll, wird die Menge dieses Objektes |
| 382 | // erstmal reduziert und bewegt. Erst nach der Bewegung wird der Rest dann |
| 383 | // im alten Environment neu erzeugt. |
| 384 | if ( _orig_amount > _move_requested ) |
| 385 | { |
| 386 | // wenn in einem alten Paket das Gewicht noch nicht neu berechnet |
| 387 | // wurde muss dies geschehn solange die Muenzen im Paket noch das |
| 388 | // volle Gewicht haben... |
| 389 | if ( environment() ) |
| 390 | environment()->query_weight_contents(); |
| 391 | // ab jetzt nur auf <_move_requested> Einheiten arbeiten |
| 392 | Set( P_AMOUNT, _move_requested, F_VALUE); |
| 393 | ZDEBUG(sprintf("move: amount set to %d\n",_move_requested)); |
| 394 | } |
| 395 | |
| 396 | int res = ::move( dest, method ); |
| 397 | |
| 398 | if ( res != MOVE_OK ) |
| 399 | { |
| 400 | // Fehlgeschlagene Bewegung ist doof. |
| 401 | // ggf. verfruehte Aenderung (P_AMOUNT oben auf <U_REQ> Einheiten |
| 402 | // reduziert) rueckgaengig machen |
| 403 | Set( P_AMOUNT, _orig_amount, F_VALUE ); |
| 404 | ZDEBUG(sprintf("move: not OK, amount restored to %d\n",_orig_amount)); |
| 405 | return res; |
| 406 | } |
| 407 | |
| 408 | // TODO: eigentlich sollte es nicht passieren, dass die Menge jetzt negagtiv |
| 409 | // ist. Aber bei Geld kann es durch vereinigen mit Geldboerse/Geldkarten |
| 410 | // passieren und das ist kein Fehler. Koennte man evtl. mal differenziert |
| 411 | // pruefen. |
| 412 | int finalamount= QueryProp(P_AMOUNT); |
| 413 | /*if ( finalamount < 1 ) |
| 414 | { |
| 415 | ZDEBUG(sprintf("move: zerstoerung, amount %d\n",finalamount)); |
| 416 | remove(1); |
| 417 | return ME_WAS_DESTRUCTED; |
| 418 | } |
| 419 | */ |
| 420 | ZDEBUG(sprintf("move: OK, amount %d\n",finalamount)); |
| 421 | return res; |
| 422 | } |
| 423 | |
| 424 | |
| 425 | protected void NotifyMove(object dest, object oldenv, int method) |
| 426 | { |
| 427 | // Erst einen evtl. nicht zu bewegenden Rest im alten Environment wieder erzeugen. |
| 428 | if (oldenv && _orig_amount > _move_requested) |
| 429 | { |
| 430 | if ( oldenv == dest ) |
| 431 | { |
| 432 | // Objekt wurde nur an den Anfang des inventory bewegt, sonst nichts. |
| 433 | // ggf. verfruehte Aenderung (oben auf <req> Einheiten reduziert) |
| 434 | // rueckgaengig machen |
| 435 | ZDEBUG(sprintf("move: same env, amount restored to %d\n",_orig_amount)); |
| 436 | Set( P_AMOUNT, _orig_amount, F_VALUE ); |
| 437 | } |
| 438 | else |
| 439 | { |
| 440 | // verbleibende Einheiten in einem neuen Objekte wieder zurueck ins |
| 441 | // alte Environment zurueck bewgen |
| 442 | object temp; |
| 443 | temp = clone_object(BLUE_NAME(ME)); |
| 444 | temp->SetProp( P_AMOUNT, _orig_amount-_move_requested ); |
| 445 | temp->move( oldenv, M_NOCHECK ); |
| 446 | ZDEBUG(sprintf("move: Restmenge im alten Env erzeugt, amount %d\n",_orig_amount-_move_requested)); |
| 447 | } |
| 448 | } |
| 449 | |
| 450 | // Und jetzt ggf. mit den anderen Units gleichen Typs im neuen Environment |
| 451 | // vereinigen. |
| 452 | foreach(object item: all_inventory(environment())) |
| 453 | { |
| 454 | if ( IS_EQUAL(item) ) |
| 455 | { |
| 456 | // Es ist ein Feature, dass auch mit Objekten mit negativen Amount |
| 457 | // vereinigt wird. |
| 458 | ZDEBUG(sprintf("move: vereinige mit %O (%d)\n", |
| 459 | item,item->QueryProp(P_AMOUNT))); |
| 460 | int mergedamount = QueryProp(P_AMOUNT) + item->QueryProp(P_AMOUNT); |
| 461 | item->Set( P_AMOUNT, 0, F_VALUE); |
| 462 | item->remove(1); |
| 463 | |
| 464 | SetProp( P_AMOUNT, mergedamount); |
| 465 | // U_REQ ist jetzt auch zurueckgesetzt. |
| 466 | |
| 467 | ZDEBUG(sprintf("move: nach vereinigung, amount %d\n",mergedamount)); |
| 468 | } |
| 469 | } |
| 470 | |
| 471 | // wenn man in frisch geclonten Units die noch kein environment haben |
| 472 | // per Hand U_REQ auf einen Wert != P_AMOUNT setzt, so gehen die |
| 473 | // restlichen Einheiten verloren (es gibt kein Environment, in das man die |
| 474 | // Resteinheiten zurueck bewegen koennte). |
| 475 | |
| 476 | // Und jetzt mal Decaykrams machen... |
| 477 | |
| 478 | // wenn NO_DECAY_UNTIL_MOVE an ist und dieses ein Move ist, was von einem |
| 479 | // Env in ein anderes Env geht, loeschen. Nicht geloescht wird |
| 480 | // hingegen, wenn das move() in das allererste Env erfolgt (nach dem Clonen) |
| 481 | if ( (QueryProp(P_UNIT_DECAY_FLAGS) & NO_DECAY_UNTIL_MOVE) |
| 482 | && objectp(dest) && objectp(oldenv) && dest != oldenv) |
| 483 | SetProp(P_UNIT_DECAY_FLAGS, |
| 484 | QueryProp(P_UNIT_DECAY_FLAGS) & ~NO_DECAY_UNTIL_MOVE ); |
| 485 | ::NotifyMove(dest, oldenv, method); |
| 486 | } |
| 487 | |
| 488 | |
| 489 | void reset() { |
| 490 | if (!clonep(ME)) { |
| 491 | // Blueprint ist Master fuer zerfallende Unitobjekte |
| 492 | // das Decay sollte besser nicht durch manuellen Aufruf in der BP |
| 493 | // ausgeloest werden, daher Pruefung hier (PO == ME, falls der Reset vom |
| 494 | // Driver kommt). |
| 495 | if (previous_object() && previous_object() != ME) |
| 496 | return; |
| 497 | int zeit = QueryProp(P_UNIT_DECAY_INTERVAL); |
| 498 | if (zeit > 0) |
| 499 | { |
| 500 | set_next_reset(zeit); |
| 501 | // Das Callout muss auf jeden Fall gemacht werden, wenn ein Decay |
| 502 | // Intervall gesetzt ist, damit die Blueprint auch den naechsten Reset |
| 503 | // kriegt, auch wenn es jetzt keine Clone gibt. Wenn es kein Quota gibt, |
| 504 | // kann der Callout wegfallen, beim Setzen eines Quota wird ja eine |
| 505 | // Funktion an diesem Objekt gerufen. |
| 506 | if (QueryProp(P_UNIT_DECAY_QUOTA) > 0) |
| 507 | call_out(#'_call_DoDecay, 1, 0); |
| 508 | } |
| 509 | } |
| 510 | else { |
| 511 | // Clones |
| 512 | if (Query(P_AMOUNT,F_VALUE)<1) remove(1); |
| 513 | else ::reset(); |
| 514 | } |
| 515 | } |
| 516 | |
| 517 | varargs int remove(int silent) |
| 518 | { |
| 519 | int amount = QueryProp(P_AMOUNT); |
| 520 | int req = QueryProp(U_REQ); |
| 521 | if (amount > req) |
| 522 | { |
| 523 | ZDEBUG(sprintf("remove: reduziere amount %d um %d -> %d\n", |
| 524 | amount, req, amount-req )); |
| 525 | SetProp(P_AMOUNT, amount - req); |
| 526 | return 1; |
| 527 | } |
| 528 | ZDEBUG(sprintf("remove: restlos weg.\n")); |
| 529 | return ::remove(silent); |
| 530 | } |
| 531 | |
| 532 | // sollte nicht von aussen gerufen werden. |
| 533 | private void _call_DoDecay(object *klone) { |
| 534 | ZDEBUG(sprintf("call_DoDecay() in %O\n",ME)); |
| 535 | // Clone oder kein Decay konfiguriert? |
| 536 | if (clonep() |
| 537 | || QueryProp(P_UNIT_DECAY_FLAGS) & NO_DECAY) |
| 538 | return; |
| 539 | |
| 540 | if (!pointerp(klone)) |
| 541 | klone = clones(2); |
| 542 | // Klone da? |
| 543 | int i=sizeof(klone); |
| 544 | if (!i) |
| 545 | return; |
| 546 | |
| 547 | // in allen Klonen DoDecay rufen |
| 548 | while(get_eval_cost() > __MAX_EVAL_COST__/2 && i--) { |
| 549 | if (objectp(klone[i])) |
| 550 | catch(call_other(klone[i],"DoDecay", 0);publish); |
| 551 | } |
| 552 | // das i ist schon abgearbeitet. |
| 553 | i--; |
| 554 | // noch was uebrig? |
| 555 | if (i >= 0) |
| 556 | call_out(#'_call_DoDecay, 4, klone[0..i]); |
| 557 | } |
| 558 | |
| 559 | // zerstoert quota Prozent der vorhandenen Units, liefert die Restmenge |
| 560 | // zurueck. Ruft ggf. DoDecayMessage() |
| 561 | public int DoDecay(int silent) { |
| 562 | // nur fuer Clones |
| 563 | if (!clonep()) |
| 564 | return 0; |
| 565 | ZDEBUG(sprintf("DoDecay() in %O\n",ME)); |
| 566 | |
| 567 | // wenn das Objekt NO_DECAY oder NO_DECAY_UNTIL_MOVE in den Flags |
| 568 | // zerfaellt nix. |
| 569 | int flags = QueryProp(P_UNIT_DECAY_FLAGS); |
| 570 | if ((flags & NO_DECAY) || (flags & NO_DECAY_UNTIL_MOVE) ) |
| 571 | return QueryProp(P_AMOUNT); |
| 572 | |
| 573 | int quota = QueryProp(P_UNIT_DECAY_QUOTA); |
| 574 | |
| 575 | // Zugewinn ist nicht. Ausserdem kann nicht mehr zerfallen, als da ist... |
| 576 | // Also ohne gueltiges Quota raus. Nur bei abs. Zerfall sind Zahlen > 10000 |
| 577 | // erlaubt, bei relativen Zerfall waere das sinnlos. |
| 578 | if (quota <= 0 || |
| 579 | (quota > 10000 && !(flags & ABSOLUTE_DECAY) ) ) |
| 580 | return QueryProp(P_AMOUNT); |
| 581 | |
| 582 | int amount = QueryProp(P_AMOUNT); |
| 583 | int minimum = QueryProp(P_UNIT_DECAY_MIN); |
| 584 | // Zerfall nur, wenn man mehr als das Minimum hat. |
| 585 | if (amount > minimum) { |
| 586 | int zerfall; |
| 587 | if (flags & ABSOLUTE_DECAY) { |
| 588 | // Decay Quota soll einfach als abs. Zahl aufgefasst werden: |
| 589 | zerfall = quota; |
| 590 | } |
| 591 | else if (flags & INACCURATE_DECAY) { |
| 592 | // Rundungsfehler ignorieren und dafuer immer min. eine Einheit |
| 593 | // zerfallen lassein |
| 594 | zerfall = (amount * quota) / 10000; |
| 595 | if (zerfall <= 0) zerfall = 1; |
| 596 | } |
| 597 | else { |
| 598 | // Standardfall: |
| 599 | // nicht-ganzzahligen Rest als Wahrscheinlichkeit fuer den Zerfall einer |
| 600 | // Einheit auffassen |
| 601 | float tmp = amount * quota / 10000.0; |
| 602 | zerfall = to_int(tmp); |
| 603 | tmp -= zerfall; // Rest in tmp |
| 604 | // tmp*n ist eine Zahl zwischen 0 und n, wenn tmp*n > random(n)+1, |
| 605 | // dann zerfaellt eine Einheit extra. Da das Random fuer grosse Zahlen |
| 606 | // besser verteilt ist, nehm ich n = __INT_MAX__ |
| 607 | if (ceil(tmp * __INT_MAX__) > random(__INT_MAX__) + 1) |
| 608 | zerfall++; |
| 609 | } |
| 610 | |
| 611 | // nicht unter Minimum zerfallen |
| 612 | if (amount - zerfall < minimum) |
| 613 | zerfall = amount - minimum; |
| 614 | |
| 615 | // erst ggf. Meldung ausgeben. |
| 616 | if (!silent && zerfall > 0) |
| 617 | DoDecayMessage(amount, zerfall); |
| 618 | // dann runtersetzen. |
| 619 | SetProp(P_AMOUNT, amount - zerfall); |
| 620 | ZDEBUG(sprintf("%O decayed by %d\n",ME, zerfall)); |
| 621 | return amount-zerfall; |
| 622 | } |
| 623 | else if (amount < 0) |
| 624 | return 0; // neg. Anzahl gilt als "0 uebrig". |
| 625 | |
| 626 | // sonst ists wohl zwischen 0 und minimum, nix aendert sich. |
| 627 | return amount; |
| 628 | } |
| 629 | |
| 630 | // gibt die Zerfallsmeldung aus. Wenn man mit der Standardmeldung nicht zufrieden |
| 631 | // ist (duerfte der Normalfall sein), sollte man diese Funktion |
| 632 | // ueberschreiben. Bekommt bisherige und jetzt gerade zerfallene Menge |
| 633 | // uebergeben. Wichtig: zu diesem Zeitpunkt hat sich an der Unit noch nix (!) |
| 634 | // geaendert! |
| 635 | protected void DoDecayMessage(int oldamount, int zerfall) { |
| 636 | string msg; |
| 637 | if (oldamount == zerfall) { |
| 638 | if (living(environment())) { |
| 639 | tell_object(environment(), break_string(sprintf( |
| 640 | "Auf einmal %s%s zu Staub!", |
| 641 | (oldamount>1?"zerfallen Deine ":"zerfaellt D"), name(WER)),78)); |
| 642 | } |
| 643 | // das tell_room() muss auf jeden fall sein, weil es "lebende" Raeume |
| 644 | // gibt. Liegt der KRam in einem solchen wuerden die Livings in diesem Raum |
| 645 | // sonst keine meldung kriegen. |
| 646 | tell_room(environment(), break_string(sprintf( |
| 647 | "Auf einmal %s %s zu Staub!", |
| 648 | (oldamount>1?"zerfallen":"zerfaellt"), name(WER)),78), |
| 649 | ({environment()})); |
| 650 | } |
| 651 | else { |
| 652 | if (living(environment())) { |
| 653 | tell_object(environment(), break_string(sprintf( |
| 654 | "Auf einmal %s %d Deiner %s zu Staub!", |
| 655 | (zerfall>1?"zerfallen":"zerfaellt"), |
| 656 | zerfall, Query(P_NAME)[1]),78)); |
| 657 | } |
| 658 | // das tell_room() muss auf jeden fall sein, weil es "lebende" Raeume |
| 659 | // gibt. Liegt der KRam in einem solchen wuerden die Livings in diesem Raum |
| 660 | // sonst keine meldung kriegen. |
| 661 | tell_room(environment(), break_string(sprintf( |
| 662 | "Auf einmal %s %d %s zu Staub!", |
| 663 | (zerfall>1?"zerfallen":"zerfaellt"), |
| 664 | zerfall, Query(P_NAME)[1]),78), ({environment()}) ); |
| 665 | } |
| 666 | } |
| 667 | |
| 668 | static int _query_unit_decay_interval() { |
| 669 | if (clonep()) { |
| 670 | // fuer clones den Wert aus der BP liefern, der Wert in Clones ist eh |
| 671 | // uninteressant. |
| 672 | if (objectp(blueprint())) |
| 673 | return blueprint()->QueryProp(P_UNIT_DECAY_INTERVAL); |
| 674 | else |
| 675 | return load_object(load_name())->QueryProp(P_UNIT_DECAY_INTERVAL); |
| 676 | } |
| 677 | return Query(P_UNIT_DECAY_INTERVAL); |
| 678 | } |
| 679 | |
| 680 | static int _set_unit_decay_interval(int zeit) { |
| 681 | // fuer Clones ist die Prop sinnlos und kann nicht gesetzt werden. |
| 682 | if (clonep()) { |
| 683 | if (objectp(blueprint())) |
| 684 | return blueprint()->QueryProp(P_UNIT_DECAY_INTERVAL); |
| 685 | else |
| 686 | return load_object(load_name())->QueryProp(P_UNIT_DECAY_INTERVAL); |
| 687 | } |
| 688 | // ueber diese Prop liesse sich der Reset relativ beliebig veraendern, |
| 689 | // verhindern, etc. Daher darf sie nur von diesem Objekt gesetzt werden, von |
| 690 | // Weisen+ oder eines Objektes, das die gleiche UID hat wie dieses hier oder |
| 691 | // des Programmieres dieses Objekts oder eines Magiers, der berechtigt fuer |
| 692 | // die jeweilige UID ist. Letztere beiden werden ueber den Ruf von |
| 693 | // QueryUIDsForWizard() ermittelt. |
| 694 | // erstes fremdes Objekt finden und pruefen. |
| 695 | foreach(object po: caller_stack(1)) { |
| 696 | if (po != ME) { |
| 697 | // fremden Aufrufer gefunden |
| 698 | if (getuid(po) != getuid(ME) |
| 699 | && member(master()->QueryUIDsForWizard(getuid(po)),getuid(ME)) == -1 |
| 700 | && (!this_interactive() || !IS_ELDER(this_interactive()) ) ) |
| 701 | // unberechtigt, Abbruch |
| 702 | return QueryProp(P_UNIT_DECAY_INTERVAL); |
| 703 | else |
| 704 | // nur das erste ext. Objekt in der Callkette wird geprueft, raus. |
| 705 | break; |
| 706 | } |
| 707 | } |
| 708 | set_next_reset(zeit); |
| 709 | // falls dies aus dem create() gesetzt wird, muss zeitverzoegert eine |
| 710 | // Funktion von aussen am Objekt gerufen werden, da nach einem create() das |
| 711 | // Objekt als resettet gilt und sonst keinen Reset kriegt. |
| 712 | call_out("_query_unit_decay_interval",1); |
| 713 | return Set(P_UNIT_DECAY_INTERVAL, zeit, F_VALUE); |
| 714 | } |
| 715 | |
| 716 | static int _query_unit_decay_quota() { |
| 717 | if (clonep()) { |
| 718 | // hat dieses Objekt einen eigenen wert gespeichert? |
| 719 | int quota=Query(P_UNIT_DECAY_QUOTA, F_VALUE); |
| 720 | if (quota) |
| 721 | return quota; |
| 722 | // sonst den Wert aus der Blueprint |
| 723 | if (objectp(blueprint())) |
| 724 | return blueprint()->QueryProp(P_UNIT_DECAY_QUOTA); |
| 725 | else |
| 726 | return load_object(load_name())->QueryProp(P_UNIT_DECAY_QUOTA); |
| 727 | } |
| 728 | return Query(P_UNIT_DECAY_QUOTA, F_VALUE); |
| 729 | } |
| 730 | |
| 731 | static int _set_unit_decay_quota(int quota) { |
| 732 | // momentan nur quota zwischen 0 und 10000 |
| 733 | if (quota >= 0 && quota <=10000) |
| 734 | return Set(P_UNIT_DECAY_QUOTA, quota, F_VALUE); |
| 735 | else |
| 736 | return Query(P_UNIT_DECAY_QUOTA, F_VALUE); |
| 737 | } |
| 738 | |
| 739 | static int _query_unit_decay_min() { |
| 740 | if (clonep()) { |
| 741 | // hat dieses Objekt einen eigenen wert gespeichert? |
| 742 | int minimum=Query(P_UNIT_DECAY_MIN, F_VALUE); |
| 743 | if (minimum) |
| 744 | return minimum; |
| 745 | // sonst den Wert aus der Blueprint |
| 746 | if (objectp(blueprint())) |
| 747 | return blueprint()->QueryProp(P_UNIT_DECAY_MIN); |
| 748 | else |
| 749 | return load_object(load_name())->QueryProp(P_UNIT_DECAY_MIN); |
| 750 | } |
| 751 | return Query(P_UNIT_DECAY_MIN, F_VALUE); |
| 752 | } |
| 753 | |
| 754 | static int _query_unit_decay_flags() { |
| 755 | if (clonep()) { |
| 756 | // hat dieses Objekt einen eigenen wert gespeichert? |
| 757 | int flags=Query(P_UNIT_DECAY_FLAGS, F_VALUE); |
| 758 | if (flags) |
| 759 | return flags; |
| 760 | // sonst den Wert aus der Blueprint |
| 761 | if (objectp(blueprint())) |
| 762 | return blueprint()->QueryProp(P_UNIT_DECAY_FLAGS); |
| 763 | else |
| 764 | return load_object(load_name())->QueryProp(P_UNIT_DECAY_FLAGS); |
| 765 | } |
| 766 | // Die BP liefert den Wert auf jeden Fall ohne NO_DECAY_UNTIL_MOVE zurueck, |
| 767 | // weil dieses Flag im Falle der BP nicht zurueckgesetzt wird und bei Clients |
| 768 | // ohne eigene Flags der Zerfall stoppen wuerde. |
| 769 | return Query(P_UNIT_DECAY_FLAGS, F_VALUE) & ~NO_DECAY_UNTIL_MOVE; |
| 770 | } |
| 771 | |