blob: 8f547a6184d4d7f13ce53e93beb3dc0f729292dc [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// living/combat.c -- Basis-Kampfmodul
4//
5// $Id: combat.c 9568 2016-06-05 18:53:10Z Zesstra $
6#pragma strong_types
7#pragma save_types
8#pragma range_check
9#pragma no_clone
10#pragma pedantic
11
12inherit "/std/living/skill_utils";
13inherit "/std/living/inventory";
14inherit "/std/living/team";
15
16#include <sys_debug.h>
17#include <debug_message.h>
18
19#define NEED_PROTOTYPES
20#include <hook.h>
21#include <living/skills.h>
22#include <thing/properties.h>
23#include <player/comm.h>
24#include <living/skill_attributes.h>
25#include <combat.h>
26#include <living.h>
27#undef NEED_PROTOTYPES
28
29#include <config.h>
30#include <properties.h>
31#include <language.h>
32#include <wizlevels.h>
33#include <attributes.h>
34#include <new_skills.h>
35
36#include <defines.h>
37
38#include <sensitive.h>
39
40#define HUNTTIME 300 //300 HBs sind 10 Minuten
41#define RNAME(x) capitalize(getuid(x))
42
43// 'private'-Prototypes
44private string _kill_alias( string str );
45
46// globale Variablen
47nosave mapping enemies;
48private nosave string magic_attack;
49private nosave int attack_busy;
50nosave int no_more_attacks;
51private nosave int remaining_heart_beats;
52private nosave int att2_time;
53private nosave string last_attack_msg;
54private nosave object *missing_attacks;
55private nosave mapping peace_tries;
56// Cache fuer QueryArmourByType()
57private nosave mapping QABTCache;
58
59protected void create()
60{
61 Set(P_WIMPY, SAVE, F_MODE_AS);
62 Set(P_TOTAL_AC, PROTECTED, F_MODE_AS);
63 Set(P_HANDS, SAVE, F_MODE_AS);
64 Set(P_SHOW_ATTACK_MSG,SAVE,F_MODE_AS);
65 Set(P_RESISTANCE, ({}));
66 Set(P_VULNERABILITY, ({}));
67 Set(P_GUILD_PREPAREBLOCK, SAVE, F_MODE_AS);
68 // Kein Setzen von P_ARMOURS von aussen per Set(). (Per SetProp() geht es
69 // durch die Setmethode).
70 Set(P_ARMOURS,PROTECTED,F_MODE_AS);
71 SetProp(P_ARMOURS, ({}));
72 attack_busy=100;
73 att2_time=0;
74 enemies=([]);
75 peace_tries=([]);
76 team::create();
77 offerHook(H_HOOK_DEFEND,1);
78 offerHook(H_HOOK_ATTACK,1);
79 offerHook(H_HOOK_ATTACK_MOD,1);
80}
81
82#undef DEBUG
83#define DEBUG(x) if (find_object("zesstra")) \
84 tell_object(find_player("zesstra"),x)
85
86public void UpdateResistanceStrengths() {
87 mapping resmods, strmap;
88
89 if ( !mappingp(resmods=Query(P_RESISTANCE_MODIFIER)) )
90 return;
91
92 //erstmal die alte Aufsummation loeschen
93 m_delete(resmods,"me");
94
95 //wenn jetzt leer: Abbruch, keine Res-Modifier da.
96 if (!sizeof(resmods))
97 return(Set(P_RESISTANCE_MODIFIER,0));
98
99 strmap = ([]);
100
101 // ueber alle gesetzten ResModifier gehen
102 foreach(string mod, mapping resmap, object ob: resmods) {
103 if ( !mappingp(resmap) || !sizeof(resmap)
104 || !objectp(ob) ) {
105 m_delete(resmods, mod);
106 continue; // Resi ungueltig, weg damit.
107 }
108 // jetzt noch ueber die Submappings laufen, die die Resis der Objekte
109 // beinhalten.
110 foreach(string reskey, float resi: resmap) {
111 strmap[reskey] = ((strmap[reskey]+1.0)*(resi+1.0))-1.0;
112 }
113 }
114
115 if ( !sizeof(strmap) )
116 Set(P_RESISTANCE_MODIFIER, 0);
117 else
118 Set(P_RESISTANCE_MODIFIER, resmods+([ "me" : strmap; 0 ]) );
119}
120
121public varargs int AddResistanceModifier(mapping mod, string add)
122{ string key;
123 mapping res;
124
125 if ( !mappingp(mod) || !sizeof(mod) || !previous_object() )
126 return 0;
127
128 key = explode(object_name(previous_object()),"#")[0];
129
130 if ( add )
131 key += ("#"+add);
132
133 res = Query(P_RESISTANCE_MODIFIER);
134 mod = deep_copy(mod);
135
136 if ( !mappingp(res) )
137 res = ([ key : mod; previous_object() ]);
138 else
139 res += ([ key : mod; previous_object() ]);
140
141 Set(P_RESISTANCE_MODIFIER, res);
142 UpdateResistanceStrengths();
143
144 return 1;
145}
146
147public varargs void RemoveResistanceModifier(string add)
148{ string key;
149 mapping res;
150
151 if ( !previous_object() )
152 return;
153
154 key = explode(object_name(previous_object()),"#")[0];
155
156 if ( add )
157 key += ("#"+add);
158
159 if ( !mappingp(res = Query(P_RESISTANCE_MODIFIER)) )
160 return;
161
162 m_delete(res, key);
163 Set(P_RESISTANCE_MODIFIER, res);
164 UpdateResistanceStrengths();
165}
166
167// veraltete Prop, aus Kompatibilitaetsgruenden noch vorhanden.
168static mixed _set_resistance(mixed arg)
169{ int i;
170 mapping resimap;
171 mixed old;
172
173 if ( !pointerp(arg) )
174 arg=({arg});
175
176 if ( !mappingp(resimap=Query(P_RESISTANCE_STRENGTHS)) )
177 resimap=([]);
178
179 if ( pointerp(old=QueryProp(P_RESISTANCE)) )
180 for ( i=sizeof(old)-1 ; i>=0 ; i-- )
Zesstrac4052902019-12-29 16:45:21 +0100181 resimap[old[i]]=(1.0+resimap[old[i]])*2.0-1.0;
MG Mud User88f12472016-06-24 23:31:02 +0200182
183 for ( i=sizeof(arg)-1 ; i>=0 ; i-- )
Zesstrac4052902019-12-29 16:45:21 +0100184 resimap[arg[i]]=(1.0+resimap[arg[i]])*0.5-1.0;
MG Mud User88f12472016-06-24 23:31:02 +0200185
186 SetProp(P_RESISTANCE_STRENGTHS,resimap);
187
188 return Set(P_RESISTANCE,arg);
189}
190
191// veraltete Prop, aus Kompatibilitaetsgruenden noch vorhanden.
192static mixed _set_vulnerability(mixed arg)
193{ int i;
194 mapping resimap;
195 mixed old;
196
197 if ( !pointerp(arg) )
198 arg=({arg});
199
200 if ( !mappingp(resimap=Query(P_RESISTANCE_STRENGTHS)) )
201 resimap=([]);
202
203 if ( pointerp(old=QueryProp(P_VULNERABILITY)) )
204 for ( i=sizeof(old)-1 ; i>=0 ; i-- )
Zesstrac4052902019-12-29 16:45:21 +0100205 resimap[old[i]]=(1.0+resimap[old[i]])*0.5-1.0;
MG Mud User88f12472016-06-24 23:31:02 +0200206
207 for ( i=sizeof(arg)-1 ; i>=0 ; i-- )
Zesstrac4052902019-12-29 16:45:21 +0100208 resimap[arg[i]]=(1.0+resimap[arg[i]])*2.0-1.0;
MG Mud User88f12472016-06-24 23:31:02 +0200209
210 SetProp(P_RESISTANCE_STRENGTHS,resimap);
211
212 return Set(P_VULNERABILITY,arg);
213}
214
215
216/** kill - Kampf starten.
217 * Fuegt ob der Feindesliste hinzu.
218 */
219public int Kill(object ob)
220{ int res, arena;
221 int|string no_attack;
222
223 if ( !objectp(ob) )
224 return 0;
225
226 if ( ob->QueryProp(P_GHOST) )
227 {
228 tell_object(ME,ob->Name(WER)+" ist doch schon tot!\n");
229 return -1;
230 }
231
232 if ( no_attack = ob->QueryProp(P_NO_ATTACK) )
233 {
234 if ( stringp(no_attack) )
235 tell_object(ME, no_attack);
236 else
237 tell_object(ME, ob->Name(WER,1)+" laesst sich nicht angreifen!\n");
238
239 return -2;
240 }
241
242 if ( QueryProp(P_NO_ATTACK) )
243 return -3;
244
245 res=InsertEnemy(ob);
246 if (res)
247 tell_object(ME, "Ok.\n");
248 else if (IsEnemy(ob))
249 tell_object(ME, "Jajaja, machst Du doch schon!\n");
250 //else //kein gueltiger Feind, ob wurde nicht eingetragen.
251
252 if ( !res )
253 return -4; // nicht aendern ohne Kill in player/combat.c
254
255 return 1;
256}
257
258public int InsertSingleEnemy(object ob)
259{
260 if ( !living(ob) )
261 return 0;
262
263 // Wie lange verfolgt dieses Living? Wenn Prop nicht gesetzt oder
264 // ungueltig, d.h. kein int oder <= 0, dann wird die Defaultzeit genommen.
Zesstrac4052902019-12-29 16:45:21 +0100265 int hunttime = QueryProp(P_HUNTTIME);
MG Mud User88f12472016-06-24 23:31:02 +0200266 hunttime = (intp(hunttime) && hunttime > 0) ?
267 hunttime / __HEART_BEAT_INTERVAL__ : HUNTTIME;
268
269 // Auch wenn ein Objekt schon als Gegner eingetragen ist, trotzdem die
270 // HUNTTIME erneuern. Das "return 0;" muss aber bleiben, da der Gegner
271 // ja nicht neu ist.
272 //
273 // 09.12.2000, Tiamak
274 if ( member( enemies, ob ) ) {
275 enemies[ob,ENEMY_HUNTTIME] = hunttime;
276 return 0;
277 }
278
279 if ( (ob==ME) || QueryProp(P_NO_ATTACK)
280 || !objectp(ob) || (ob->QueryProp(P_NO_ATTACK))
281 || (ob->QueryProp(P_GHOST)) )
282 return 0;
283
284 object team;
285 if ( ( query_once_interactive(ME) || query_once_interactive(ob) )
286 && objectp(team=Query(P_TEAM)) && (team==(ob->Query(P_TEAM))) )
287 return 0;
288
289 set_heart_beat(1);
290
291 last_attack_msg=0;
292
293 enemies[ob,ENEMY_HUNTTIME] = hunttime;
294 Set(P_LAST_COMBAT_TIME,time());
295
296 return 1;
297}
298
299public int InsertEnemy(object ob)
300{ int res;
301
302 if ( res=InsertSingleEnemy(ob) )
303 InsertEnemyTeam(ob);
304
305 return res;
306}
307
308public object QueryPreferedEnemy()
309{ int sz,r;
310 <int|object>* pref;
311 object enemy;
312
313 enemy=0;
314 if ( pointerp(pref=QueryProp(P_PREFERED_ENEMY)) && ((sz=sizeof(pref))>1)
315 && intp(r=pref[0]) && (random(100)<r) )
316 {
317 enemy=pref[1+random(sz-1)];
318
319 if ( !objectp(enemy) )
320 {
321 pref-=({enemy});
322
323 if ( sizeof(pref)<2 )
324 pref=0;
325
326 SetProp(P_PREFERED_ENEMY,pref);
327
328 return 0;
329 }
330
331 if ( !IsEnemy(enemy) )
332 return 0;
333 }
334
335 return enemy;
336}
337
338public object *PresentEnemies() {
339
340 object *here=({});
341 object *netdead=({});
342 mixed no_attack;
343 foreach(object pl: enemies) {
Zesstrac4052902019-12-29 16:45:21 +0100344 if (no_attack = pl->QueryProp(P_NO_ATTACK)) {
MG Mud User88f12472016-06-24 23:31:02 +0200345 m_delete(enemies,pl);
346 // Und auch im Gegner austragen, sonst haut der weiter zu und dieses
347 // Living wehrt sich nicht. (Zesstra, 26.11.2006)
348 if (stringp(no_attack)) {
349 tell_object(ME, no_attack);
350 pl->StopHuntFor(ME, 1);
351 }
352 else pl->StopHuntFor(ME, 0);
353 }
354 else if ( environment()==environment(pl) )
355 {
356 if ( interactive(pl) || !query_once_interactive(pl) )
357 here+=({pl});
358 else
359 netdead+=({pl});
360 }
361 }
362
363 if ( !sizeof(here) ) // Netztote sind nur Feinde, falls keine anderen da sind
364 return netdead;
365
366 return here;
367}
368
369public varargs object SelectEnemy(object *here)
370{ object enemy;
371
372 if ( !pointerp(here) )
373 here=PresentEnemies();
374
375 if ( !sizeof(here) )
376 return 0;
377
378 if ( !objectp(enemy=QueryPreferedEnemy())
379 || (environment(enemy)!=environment()) )
380 enemy=here[random(sizeof(here))];
381
382 return enemy;
383}
384
385protected void heart_beat() {
386 int hbs;
387 // leider rufen viele Leute einfach das geerbte heart_beat() obwohl sie
388 // schon tot sind und damit das Objekt zerstoert ist.
389 if (!living(this_object()))
390 return;
391
392 // Paralyse pruefen, ggf. reduzieren
393 int dis_attack = QueryProp(P_DISABLE_ATTACK);
394 if ( intp(dis_attack) && (dis_attack > 0) )
395 {
396 SetProp(P_DISABLE_ATTACK, --dis_attack);
397 }
398
399 // Attacken ermitteln: SA_SPEED + Rest aus dem letzten HB
400 hbs = remaining_heart_beats + QuerySkillAttribute(SA_SPEED);
401 if ( hbs <= 0 )
402 hbs = 100;
403
404 // P_ATTACK_BUSY bestimmen
405 if ( attack_busy > 99)
406 attack_busy = 100 + hbs;
407 else
408 attack_busy += 100 + hbs;
409 // mehr fuer Seher...
410 if ( IS_SEER(ME) )
411 attack_busy+=(100+QueryProp(P_LEVEL));
412 // max. 500, d.h. 5 Attacken
413 if ( attack_busy>500 )
414 attack_busy=500;
415
416 // unganzzahligen Rest fuer naechsten HB speichern
417 remaining_heart_beats = hbs % 100;
418 hbs /= 100; // ganze Attacken. ;-)
419
420 if ( hbs > 10 ) // nicht mehr als 10 Attacken.
421 hbs = 10;
422
423 // Falls jemand seit dem letzten HB in diesem Living von aussen Attack()
424 // gerufen hat (nicht ExtraAttack()), wird jetzt was abgezogen.
425 hbs -= no_more_attacks;
426 no_more_attacks = 0;
427
428 // Wie lange verfolgt dieser NPC? Wenn Prop nicht gesetzt oder ungueltig,
429 // d.h. kein int oder <= 0, dann wird die Defaultzeit genommen.
Zesstrac4052902019-12-29 16:45:21 +0100430 int hunttime = QueryProp(P_HUNTTIME);
MG Mud User88f12472016-06-24 23:31:02 +0200431 hunttime = (intp(hunttime) && hunttime > 0) ?
432 hunttime / __HEART_BEAT_INTERVAL__ : HUNTTIME;
433 // erstmal die Hunttimes der Gegner aktualisieren und nebenbei die
434 // anwesenden Feinde merken. Ausserdem ggf. Kampf abbrechen, wenn
435 // P_NO_ATTACK gesetzt ist.
436 // eigentlich ist diese ganze Schleife fast das gleiche wie
437 // PresentEnemies(), aber leider muessen ja die Hunttimes aktulisiert werde,
438 // daher kann man nicht direkt PresentEnemies() nehmen.
439 // TODO: Diese Schleife und PresentEnmies() irgendwie vereinigen.
440 object *netdead=({});
441 object *here=({});
442 object enemy;
443 mixed no_attack;
444 foreach(enemy, int htime: &enemies) { // Keys in enemy
445 // ggf. Meldungen ausgeben und Gegner austragen und dieses Living beim
446 // Gegner austragen.
447 if (no_attack=enemy->QueryProp(P_NO_ATTACK)) {
448 m_delete(enemies,enemy);
449 if ( stringp(no_attack) ) {
450 write( no_attack );
451 enemy->StopHuntFor( ME, 1 );
452 }
453 else
454 enemy->StopHuntFor( ME, 0 );
455 }
456 else if ( environment()==environment(enemy) ) {
457 // Gegner anwesend, als Feind merken...
458 if ( interactive(enemy) || !query_once_interactive(enemy) )
459 here+=({enemy});
460 else
461 netdead+=({enemy});
462 // ... und Hunttime erneuern.
463 htime = hunttime;
464 }
465 // Feind nicht anwesend. Timer dekrementieren und Feind austragen, wenn
466 // Jagdzeit abgelaufen.
467 else if ( (--htime) <= 0 )
468 StopHuntFor(enemy);
469 }
470 // Netztote sind nur Feinde, wenn sonst keine da sind.
471 if ( !sizeof(here) )
472 here=netdead;
473
474 // schonmal abfragen, ob ein Spell vorbereitet wird.
475 mixed pspell=QueryProp(P_PREPARED_SPELL);
476
477 //Da hbs 0 und no_more_attacks durch einen manuellen Aufruf von Attack() im
478 //Spieler >0 sein kann, kann jetzt hbs <0 sein. (s.o.)
479 if (hbs > 0 && sizeof(here)) {
480 foreach(int i: hbs) {
481 // Feind in Nahkampfreichweite finden
482 if ( objectp(enemy=SelectNearEnemy(here)) ) {
483 // Flucht erwuenscht?
484 if ( QueryProp(P_WIMPY) > QueryProp(P_HP) ) {
485 Flee();
486 // Flucht gelungen?
487 if ( enemy && (environment(enemy)!=environment()) ) {
488 here = 0; // naechste Runde neue Feinde suchen
489 enemy = 0;
490 continue; // kein Kampf, Runde ueberspringen
491 }
492 }
493 }
494 else {
495 // keine Feinde gefunden... Abbrechen.
496 break;
497 }
498 // Paralyse gesetzt? -> Abbrechen. Dieses Pruefung muss hier passieren,
499 // damit ggf. die automatische Flucht des Lebewesens durchgefuehrt wird,
500 // auch wenn es paralysiert ist.
501 if (dis_attack > 0) break;
502
503 // Kampf durch Spell-Prepare blockiert?
504 // Keine genaue Abfrage, wird spaeter noch genau geprueft.
505 if ( pspell && QueryProp(P_GUILD_PREPAREBLOCK) )
506 break; // keine Angriffe in diesem HB.
507 // wenn Feind da: hit'em hard.
508 if ( objectp(enemy) )
509 Attack(enemy);
510 // ggf. hat Attack() no_more_attacks gesetzt. Zuruecksetzen
511 no_more_attacks = 0;
512 // naechste Kampfrunde neue Feinde suchen
513 here = 0;
514 } // foreach
515 }
516
517 no_more_attacks=0;
518
519 // Ist ein Spell in Vorbereitung und evtl. jetzt fertig vorbereitet?
520 if ( pointerp(pspell)
521 && (sizeof(pspell)>=3) && intp(pspell[0]) && stringp(pspell[1]) ) {
522 if ( time()>=pspell[0] ) // Kann der Spruch jetzt ausgefuehrt werden?
523 {
524 UseSpell(pspell[2],pspell[1]); // Dann los
525 SetProp(P_PREPARED_SPELL,0);
526 }
527 }
528 else if ( pspell ) // Unbrauchbarer Wert, loeschen
529 SetProp(P_PREPARED_SPELL,0);
530
531} // Ende heart_beat
532
533// wird von NPC gerufen, die Heartbeats nachholen und dabei die Hunttimes um
534// die Anzahl verpasster Heartbeats reduzieren muessen.
535// Wird von Spielerobjekten gerufen, wenn sie nach 'schlafe ein' aufwachen, um
536// die notwendige Anzahl an HB hier abzuziehen und ggf. die Feinde zu expiren,
537// da Spieler ja netztot keinen Heartbeat haben.
538protected void update_hunt_times(int beats) {
539 if (!mappingp(enemies)) return;
540 foreach(object en, int htime: &enemies) { // Mapping-Keys in en
541 htime -= beats;
542 if ( htime <= 0 )
543 StopHuntFor(en);
544 }
545}
546
547public int IsEnemy(object wer)
548{
549 return (member(enemies,wer));
550}
551
552public void StopHuntText(object arg)
553{
554 tell_object(arg,
555 Name(WER,1)+" "+(QueryProp(P_PLURAL)?"jagen ":"jagt ")+
556 (arg->QueryProp(P_PLURAL)?"Euch":"Dich")+" nicht mehr.\n");
557 tell_object(ME,(QueryProp(P_PLURAL)?"Ihr jagt ":"Du jagst ")+
558 arg->name(WEN,1)+" nicht mehr.\n");
559}
560
561public varargs int StopHuntFor(object arg, int silent)
562{
563 if ( !objectp(arg) || !IsEnemy(arg) )
564 return 0;
565
566 if (!silent)
567 StopHuntText(arg);
568
569 m_delete(enemies,arg);
570 last_attack_msg=0;
571
572 return 1;
573}
574
575// Begruessungsschlag nur einmal pro HB
576public void Attack2(object enemy)
577{
578 if ( att2_time > time() )
579 return;
580
581 att2_time=time() + __HEART_BEAT_INTERVAL__;
582 Attack(enemy);
583}
584
585// Fuer evtl. Attack-Aenderungen, ohne dass man gleich das ganze
586// Attack ueberlagern muss:
587protected void InternalModifyAttack(mapping ainfo)
588{
589 return; // Vorerst!
590/*
591 int fac;
592 mixed res;
593
594 fac=100;
595
596 if (CannotSee(1))
597 fac -= 50;
598
599 // Weitere Mali?
600
601 if (fac<100)
602 ainfo[SI_SKILLDAMAGE] = ainfo[SI_SKILLDAMAGE]*fac/100;
603 // Fertig
604*/
605}
606
607// Ausfuehren Eines Angriffs auch wenn es bereits einen Angriff
608// in dieser Runde gegeben hat. Dieser Angriff wird auch nicht
609// gezaehlt. Die Nutzung dieser Funktion ist nur fuer Spezialfaelle
610// gedacht und immer BALANCEPFLICHTIG!
611varargs public void ExtraAttack(object enemy, int ignore_previous)
612{
613 int saved_no_more_attacks;
614
615 // Erstschlagsinfo speichern.
616 saved_no_more_attacks = no_more_attacks;
617
618 // Bei Bedarf bisher schon durchgefuehrte Erstschlaege ignorieren
619 if (ignore_previous) no_more_attacks=0;
620
621 // Normalen Angriff durchfuehren
622 Attack (enemy);
623
624 // Gespeicherten Wert zuruecksetzen
625 no_more_attacks = saved_no_more_attacks;
626
627 return;
628}
629
630public void Attack(object enemy)
631{
632 closure cl;
633 mixed res;
634 mapping ainfo;
635 mapping edefendinfo; // erweiterte Defend-Infos
636 mixed hookData;
637 mixed hookRes;
638
639 if ( no_more_attacks || QueryProp(P_GHOST) ||
640 !objectp(enemy) || !objectp(this_object()) ||
641 (QueryProp(P_DISABLE_ATTACK) > 0) || enemy->QueryProp(P_NO_ATTACK) ||
642 (query_once_interactive(this_object()) && !interactive(this_object())) )
643 return;
644
645 edefendinfo=([]);
646
647 Set(P_LAST_COMBAT_TIME,time());
648
649 // inkrementieren. Diese Variable wird im HB nach den Angriffen genullt.
650 // Diese Variable wird im naechsten Heartbeat von der Anzahl an verfuegbaren
651 // Angriffen des Livings abgezogen. Dies beruecksichtigt also zwischen den
652 // HBs (z.B. extern) gerufene Attacks().
653 no_more_attacks++;
654
655 // Wird das Attack durch einen temporaeren Attack-Hook ersetzt?
656 if ( res=QueryProp(P_TMP_ATTACK_HOOK) )
657 {
658 if ( pointerp(res) && (sizeof(res)>=3) && intp(res[0]) && (time()<res[0])
659 && objectp(res[1]) && stringp(res[2]) )
660 {
661 if ( !(res=call_other(res[1],res[2],enemy)) )
662 return;
663 }
664 else
665 SetProp(P_TMP_ATTACK_HOOK,0);
666 }
667
668 // trigger attack hook
669 hookData=({enemy});
670 hookRes=HookFlow(H_HOOK_ATTACK,hookData);
671 if(hookRes && pointerp(hookRes) && sizeof(hookRes)>H_RETDATA){
672 if(hookRes[H_RETCODE]==H_CANCELLED){
673 return;
674 }
675 }
676
677 ainfo = ([ SI_ENEMY : enemy,
678 SI_SPELL : 0,
679 ]);
680
681 if ( objectp(ainfo[P_WEAPON]=QueryProp(P_WEAPON)) )
682 {
683 ainfo[P_WEAPON]->TakeFlaw(enemy);
684
685 // Abfrage fuer den Fall, dass Waffe durch das TakeFlaw() zerstoert
686 // wurde. Dann wird das Attack abgebrochen.
687 if ( !objectp(ainfo[P_WEAPON]) )
688 return;
689
690 cl=symbol_function("name",ainfo[P_WEAPON]);
Zesstrac4052902019-12-29 16:45:21 +0100691 ainfo[SI_SKILLDAMAGE_MSG] = (" mit "+funcall(cl,WEM,0));
692 ainfo[SI_SKILLDAMAGE_MSG2] = (" mit "+funcall(cl,WEM,1));
MG Mud User88f12472016-06-24 23:31:02 +0200693
Zesstrac4052902019-12-29 16:45:21 +0100694 ainfo[SI_SKILLDAMAGE] = ainfo[P_WEAPON]->QueryDamage(enemy);
MG Mud User88f12472016-06-24 23:31:02 +0200695
696 cl=symbol_function("QueryProp",ainfo[P_WEAPON]);
697 ainfo[SI_SKILLDAMAGE_TYPE] = funcall(cl,P_DAM_TYPE);
Zesstrac4052902019-12-29 16:45:21 +0100698 ainfo[P_WEAPON_TYPE] = funcall(cl,P_WEAPON_TYPE);
699 ainfo[P_NR_HANDS] = funcall(cl,P_NR_HANDS);
700 ainfo[P_WC] = funcall(cl,P_WC);
MG Mud User88f12472016-06-24 23:31:02 +0200701
702 // Zweihaendige Waffe?
703 if ( ainfo[P_NR_HANDS]==2
704 && mappingp(res=UseSkill(SK_TWOHANDED,deep_copy(ainfo)))
705 && member(res,SI_SKILLDAMAGE) )
706 {
707 // Nur den neuen Schadenswert uebernehmen.
Zesstrac4052902019-12-29 16:45:21 +0100708 ainfo[SI_SKILLDAMAGE]=res[SI_SKILLDAMAGE];
MG Mud User88f12472016-06-24 23:31:02 +0200709 }
710 }
711 else // Keine Waffe gezueckt
712 {
713 /* Check if there is a magical attack */
714 if ( mappingp(res=UseSkill(SK_MAGIC_ATTACK,([SI_ENEMY:enemy]))) )
715 {
Zesstrac4052902019-12-29 16:45:21 +0100716 ainfo[SI_SKILLDAMAGE]=res[SI_SKILLDAMAGE];
MG Mud User88f12472016-06-24 23:31:02 +0200717 ainfo[SI_SKILLDAMAGE_TYPE]=res[SI_SKILLDAMAGE_TYPE];
718
719 if ( stringp(res[SI_SKILLDAMAGE_MSG]) )
Zesstrac4052902019-12-29 16:45:21 +0100720 ainfo[SI_SKILLDAMAGE_MSG] = " mit "+res[SI_SKILLDAMAGE_MSG];
MG Mud User88f12472016-06-24 23:31:02 +0200721 else
722 ainfo[SI_SKILLDAMAGE_MSG] = " mit magischen Faehigkeiten";
723
724 if ( stringp(res[SI_SKILLDAMAGE_MSG2]) )
Zesstrac4052902019-12-29 16:45:21 +0100725 ainfo[SI_SKILLDAMAGE_MSG2] = " mit "+res[SI_SKILLDAMAGE_MSG2];
MG Mud User88f12472016-06-24 23:31:02 +0200726 else
Zesstrac4052902019-12-29 16:45:21 +0100727 ainfo[SI_SKILLDAMAGE_MSG2] = ainfo[SI_SKILLDAMAGE_MSG];
MG Mud User88f12472016-06-24 23:31:02 +0200728
729 if ( !(ainfo[P_WEAPON_TYPE]=res[P_WEAPON_TYPE]) )
730 ainfo[P_WEAPON_TYPE]=WT_MAGIC;
731
732 if ( member(res,SI_SPELL) )
733 ainfo[SI_SPELL]=res[SI_SPELL];
734 }
735 else
736 {
737 /* Ohne (freie) Haende wird auch nicht angegriffen */
738 if ( interactive(this_object())
739 && (QueryProp(P_USED_HANDS) >= QueryProp(P_MAX_HANDS)) )
740 return ;
741
742 if ( !pointerp(res=QueryProp(P_HANDS)) || (sizeof(res)<2) )
743 return;
744
Zesstrac4052902019-12-29 16:45:21 +0100745 ainfo[SI_SKILLDAMAGE] = (( 2*random(res[1]+1)
MG Mud User88f12472016-06-24 23:31:02 +0200746 + 10*(QueryAttribute(A_STR)) )/3);
747 ainfo[SI_SKILLDAMAGE_TYPE] = res[2];
748 ainfo[SI_SKILLDAMAGE_MSG] = ainfo[SI_SKILLDAMAGE_MSG2]
Zesstrac4052902019-12-29 16:45:21 +0100749 = res[0];
MG Mud User88f12472016-06-24 23:31:02 +0200750 ainfo[P_WEAPON_TYPE] = WT_HANDS;
Zesstrac4052902019-12-29 16:45:21 +0100751 ainfo[P_WC] = res[1];
MG Mud User88f12472016-06-24 23:31:02 +0200752 }
753 } // besondere Faehigkeiten mit diesem Waffentyp?
754 if ( mappingp(res=UseSkill(FIGHT(ainfo[P_WEAPON_TYPE]),
755 deep_copy(ainfo))) )
756 SkillResTransfer(res,ainfo);
757
758 // besondere allgemeine Kampffaehigkeiten?
759 if ( mappingp(res=UseSkill(SK_FIGHT,deep_copy(ainfo))) )
760 SkillResTransfer(res,ainfo);
761
762 // Veraenderungen durch einen Attack-Modifier?
763 if ( (res=QueryProp(P_TMP_ATTACK_MOD)) )
764 {
765 if ( pointerp(res) && (sizeof(res)>=3) && intp(res[0])
766 && (time()<res[0]) && objectp(res[1]) && stringp(res[2]) )
767 {
768 if ( !(res=call_other(res[1],res[2],deep_copy(ainfo)))
769 || !mappingp(res) )
770 return;
771 else
772 SkillResTransfer(res,ainfo);
773 }
774 else
775 SetProp(P_TMP_ATTACK_MOD,0);
776 }
777
778 // trigger attack mod hook
779 hookData=deep_copy(ainfo);
780 hookRes=HookFlow(H_HOOK_ATTACK_MOD,hookData);
781 if(hookRes && pointerp(hookRes) && sizeof(hookRes)>H_RETDATA){
782 if(hookRes[H_RETCODE]==H_CANCELLED){
783 return;
784 }
785 else if(hookRes[H_RETCODE]==H_ALTERED && hookRes[H_RETDATA] &&
786 mappingp(hookRes[H_RETDATA])){
787 SkillResTransfer(hookRes[H_RETDATA],ainfo);
788 }
789 }
790
791 // Interne Modifikationen der Angriffswerte
792 InternalModifyAttack(ainfo);
793
794 if ( !objectp(enemy) )
795 return;
796
797 // hier mal bewusst nicht auf P_PLURAL zuerst testen. in 90% der Faelle
798 // wird eh keine Angriffsmeldung mehr ausgegeben, also pruefen wir das
799 // lieber zuerst. Plural testen zieht schon genug Rechenzeit :/
800 // Nicht meine Idee! Nicht meine Idee! Nachtwind 7.7.2001
801 if ( ainfo[SI_SKILLDAMAGE_MSG2]!=last_attack_msg )
802 {
803 last_attack_msg = ainfo[SI_SKILLDAMAGE_MSG2];
804 if (QueryProp(P_PLURAL))
805 {
806 tell_object( ME, " Ihr greift " + enemy->name(WEN,1)
807 + ainfo[SI_SKILLDAMAGE_MSG2] + " an.\n" );
808 say(" "+(Name(WER,1))+" greifen "+(enemy->name(WEN,1))+
809 ainfo[SI_SKILLDAMAGE_MSG]+" an.\n", enemy );
810 tell_object( enemy, " "+(Name(WER,1))+" greifen "+
811 (enemy->QueryProp(P_PLURAL)?"Euch":"Dich")+
812 ainfo[SI_SKILLDAMAGE_MSG2]+" an.\n" );
813 }
814 else
815 {
816 tell_object( ME, " Du greifst " + enemy->name(WEN,1)
817 + ainfo[SI_SKILLDAMAGE_MSG2] + " an.\n" );
818 say(" "+(Name(WER,2))+" greift "+(enemy->name(WEN,1))+
819 ainfo[SI_SKILLDAMAGE_MSG]+" an.\n", enemy );
820 tell_object( enemy, " "+(Name(WER,1))+" greift "+
821 (enemy->QueryProp(P_PLURAL)?"Euch":"Dich")+
822 ainfo[SI_SKILLDAMAGE_MSG2]+" an.\n" ); }
823 }
824 else if ( Query(P_SHOW_ATTACK_MSG) )
825 if (QueryProp(P_PLURAL))
826 tell_object( ME, " Ihr greift " + enemy->name(WEN,1)
827 + ainfo[SI_SKILLDAMAGE_MSG2] + " an.\n" );
828 else
829 tell_object( ME, " Du greifst " + enemy->name(WEN,1)
830 + ainfo[SI_SKILLDAMAGE_MSG2] + " an.\n" );
831
832 // ainfo in defendinfo merken
833 edefendinfo[ ORIGINAL_AINFO]= deep_copy(ainfo);
834 edefendinfo[ ORIGINAL_DAM]= ainfo[SI_SKILLDAMAGE];
835 edefendinfo[ ORIGINAL_DAMTYPE]= ainfo[SI_SKILLDAMAGE_TYPE];
836 edefendinfo[ CURRENT_DAM]= ainfo[SI_SKILLDAMAGE];
837 edefendinfo[ CURRENT_DAMTYPE]= ainfo[SI_SKILLDAMAGE_TYPE];
838
839 // ainfo[SI_SPELL] auf ein mapping normieren
840 if ( intp(ainfo[SI_SPELL]) )
841 {
842 ainfo[SI_SPELL] = ([ SP_PHYSICAL_ATTACK : !ainfo[SI_SPELL],
843 SP_SHOW_DAMAGE : !ainfo[SI_SPELL],
844 SP_REDUCE_ARMOUR : ([ ]) ]);
845 }
846
847 // defendinfo anhaengen, falls ainfo[SI_SPELL] ein mapping ist
848 if( mappingp(ainfo[SI_SPELL]))
849 {
850 ainfo[SI_SPELL][EINFO_DEFEND]=edefendinfo;
851 }
852
853
854 enemy->Defend(ainfo[SI_SKILLDAMAGE], ainfo[SI_SKILLDAMAGE_TYPE],
855 ainfo[SI_SPELL], this_object());
856
857
858 //edefendinfo=([]);
859
860 /* Done attacking */
861}
862
863public void AddDefender(object friend)
864{ object *defs;
865
866 if ( !objectp(friend) )
867 return;
868
869 if ( !pointerp(defs=QueryProp(P_DEFENDERS)) )
870 defs=({});
871
872 if ( member(defs,friend)>=0 )
873 return;
874
875 defs+=({friend});
876 SetProp(P_DEFENDERS,defs);
877}
878
879public void RemoveDefender(object friend)
880{ object *defs;
881
882 if ( !objectp(friend) )
883 return;
884
885 if ( !pointerp(defs=QueryProp(P_DEFENDERS)) )
886 defs=({});
887
888 if ( member(defs,friend)==-1 )
889 return;
890
891 defs -= ({friend});
892 SetProp(P_DEFENDERS,defs);
893}
894
895public void InformDefend(object enemy)
896{
897 UseSkill(SK_INFORM_DEFEND,([ SI_ENEMY : enemy,
898 SI_FRIEND : previous_object() ]));
899 // Oh, oh - ich hoffe mal, dass InformDefend wirklich NUR aus Defend
900 // eines befreundeten livings aufgerufen wird... (Silvana)
901 // This is only experimental... ;)
902}
903
904public <int|string*|mapping>* DefendOther(int dam, string|string* dam_type,
905 int|mapping spell, object enemy)
906{
907 <int|string*|mapping>* res;
908
909 if ( (res=UseSkill(SK_DEFEND_OTHER,([ SI_SKILLDAMAGE : dam,
910 SI_SKILLDAMAGE_TYPE : dam_type,
911 SI_SPELL : spell,
912 SI_FRIEND : previous_object(),
913 SI_ENEMY : enemy ])))
914 && pointerp(res) )
915 return res;
916
917 return 0;
918}
919
920public void CheckWimpyAndFlee()
921{
922 if ( (QueryProp(P_WIMPY)>QueryProp(P_HP)) && !TeamFlee()
923 && find_call_out("Flee")<0 )
924 call_out(#'Flee,0,environment());
925}
926
927protected string mess(string msg,object me,object enemy)
928{ closure mname, ename;
929 string *parts,x;
930 int i;
931
932 mname = symbol_function("name", me);
933 ename = symbol_function("name", enemy);
934
935 parts=regexplode(msg,"@WE[A-Z]*[12]");
936 for ( i=sizeof(parts)-2 ; i>=1 ; i-=2 )
937 {
938 switch(parts[i])
939 {
940 case "@WER1": parts[i]=funcall(mname,WER,1); break;
941 case "@WESSEN1": parts[i]=funcall(mname,WESSEN,1); break;
942 case "@WEM1": parts[i]=funcall(mname,WEM,1); break;
943 case "@WEN1": parts[i]=funcall(mname,WEN,1); break;
944 case "@WER2": parts[i]=funcall(ename,WER,1); break;
945 case "@WESSEN2": parts[i]=funcall(ename,WESSEN,1); break;
946 case "@WEM2": parts[i]=funcall(ename,WEM,1); break;
947 case "@WEN2": parts[i]=funcall(ename,WEN,1); break;
948 default: ;
949 }
950 }
951
952 return break_string(capitalize(implode(parts,"")),78," ",1);
953}
954
955// Fuer evtl. Defend-Aenderungen, ohne dass man gleich das ganze
956// Attack ueberlagern muss:
957protected void InternalModifyDefend(int dam, string* dt, mapping spell, object enemy)
958{
959 return;
960}
961
Zesstrada3ad452019-01-16 22:04:05 +0100962protected nomask void normalize_defend_args(int dam, string|string* dam_type,
963 int|mapping spell, object enemy)
964{
965 // this_player(), wenn kein enemy bekannt...
966 enemy ||= this_player();
967
968 // Schadenstyp ueberpruefen
969 if ( !pointerp(dam_type) )
970 dam_type = ({ dam_type });
971
972 // Und das Spellmapping pruefen, erzeugen, ergaenzen etc.
973 if ( intp(spell) )
974 spell = ([ SP_PHYSICAL_ATTACK : !spell,
975 SP_SHOW_DAMAGE : !spell,
976 SP_REDUCE_ARMOUR : ([ ]),
977 EINFO_DEFEND : ([ORIGINAL_DAM:dam,
978 ORIGINAL_DAMTYPE:dam_type ])
979 ]);
980 else if ( mappingp(spell) )
981 {
982 // testen ob eine erweiterte defendinfo vorhanden ist
983 if(!member(spell,EINFO_DEFEND))
984 {
985 // wenn nicht, koennen wir an den fehlenden Infos wenig machen, aber
986 // zumindest ergaenzen wir es und schreiben die (hier) initialen dam und
987 // dam_type rein.
988 spell[EINFO_DEFEND] = ([ORIGINAL_DAM:dam,
989 ORIGINAL_DAMTYPE:dam_type]);
990 }
991 if ( !mappingp(spell[SP_REDUCE_ARMOUR]) )
992 spell[SP_REDUCE_ARMOUR] = ([]);
993 }
994 else // Illegaler spell-Parameter
995 raise_error(sprintf("Ungueltiger Typ des spell-Arguments: %d\n",
996 get_type_info(spell,0)));
997}
998
MG Mud User88f12472016-06-24 23:31:02 +0200999public int Defend(int dam, string|string* dam_type, int|mapping spell, object enemy)
1000{
1001 int i,k;
1002 mixed res,res2;
1003 object *armours,tmp;
1004 mixed hookData;
1005 mixed hookRes;
Zesstrada3ad452019-01-16 22:04:05 +01001006
MG Mud User88f12472016-06-24 23:31:02 +02001007 // string what, how;
1008 string enname, myname;
1009
Zesstrada3ad452019-01-16 22:04:05 +01001010 normalize_defend_args(&dam, &dam_type, &spell, &enemy);
1011
MG Mud User88f12472016-06-24 23:31:02 +02001012 // Testen, ob dieses Lebewesen ueberhaupt angegriffen werden darf
1013 if ( !this_object() || !enemy || QueryProp(P_NO_ATTACK)
1014 || ( query_once_interactive(enemy) && ! interactive(enemy) ) )
1015 return 0;
1016
MG Mud User88f12472016-06-24 23:31:02 +02001017 spell[EINFO_DEFEND][CURRENT_DAMTYPE]=dam_type;
1018 spell[EINFO_DEFEND][CURRENT_DAM]=dam;
1019
1020 // Testen, ob der Angreifer schon als Feind registriert worden ist.
1021 // Wenn nein, registrieren.
1022 if ( !IsEnemy(enemy) && !spell[SP_NO_ENEMY] )
1023 {
1024 spell[EINFO_DEFEND][ENEMY_INSERTED]=1;
1025 InsertEnemy(enemy);
1026 }
1027
1028 // RFR-Taktik abfangen
1029 if ( !QueryProp(P_ENABLE_IN_ATTACK_OUT) )
1030 {
1031 i=time()-(enemy->QueryProp(P_LAST_MOVE));
1032 // Gegner hat sich bewegt, man selbst nicht
1033 if ( (i<3) && (time()-QueryProp(P_LAST_MOVE)>=5) )
1034 {
1035 // Bei Erster Kampfrunde wenige Schaden
1036 dam/=(4-i);
1037 spell[EINFO_DEFEND][RFR_REDUCE]=dam;
1038 spell[EINFO_DEFEND][CURRENT_DAM]=dam;
1039 }
1040 }
1041
1042 // Man kann Verteidiger haben. Diese kommen als erste zum Zuge
1043 if ( res=QueryProp(P_DEFENDERS) )
1044 { object *defs,*defs_here;
1045
1046 defs=({});
1047 defs_here=({});
1048 if ( !pointerp(res) )
1049 res=({res});
1050 // erst alle anwesenden finden.
1051 foreach(object defender: res) {
1052 if ( objectp(defender) && (member(defs,defender)<0) )
1053 {
1054 defs+=({defender});
1055 // Verteidiger muessen im gleichen Raum oder im Living selber
1056 // enthalten sein.
1057 if ( environment(defender) == environment()
1058 || environment(defender) == ME)
1059 {
1060 call_other(defender,"InformDefend",enemy);
1061 if (defender)
1062 defs_here += ({ defender });
1063 }
1064 }
1065 }
1066 //Anwesende Verteidiger eintragen.
1067 spell[EINFO_DEFEND][PRESENT_DEFENDERS]=defs_here;
1068
1069 // P_DEFENDERS auch gleich aktualisieren
1070 if ( sizeof(defs)<1 )
1071 defs=0;
1072 SetProp(P_DEFENDERS,defs);
1073
1074 if ( spell[SP_PHYSICAL_ATTACK] ) {
1075 // Bei physischen Angriffen nur Verteidiger aus Reihe 1
1076 // nehmen (z.B. fuer Rueckendeckung)
1077 foreach(object defender: defs_here) {
1078 if ( (defender->PresentPosition())>1 ) {
1079 defs_here-=({defender});
1080 }
1081 }
1082 }
1083
1084 if ( (i=sizeof(defs_here)) )
1085 {
1086 mixed edefendtmp=({defs_here[random(i)],0,0,0});
1087 res=call_other(edefendtmp[DEF_DEFENDER],"DefendOther",
1088 dam,dam_type,spell,enemy);
1089 if ( pointerp(res) && (sizeof(res)>=3) && intp(res[0])
1090 && pointerp(res[1]))
1091 {
1092 // Helfer koennen den Schaden oder Schadenstyp aendern,
1093 // z.B. Umwandlung von Feuer nach Eis oder so...
1094 dam=res[0];
1095 edefendtmp[DEF_DAM]=dam;
1096 dam_type=res[1];
1097 edefendtmp[DEF_DAMTYPE]=dam_type;
1098
1099 if ( mappingp(res[2]) )
1100 {
1101 spell=res[2];
1102 // teuer, aber geht nicht anders (Rekursion vermeiden)
1103 edefendtmp[DEF_SPELL]=deep_copy(res[2]);
1104 }
1105 spell[EINFO_DEFEND][CURRENT_DAMTYPE]=dam_type;
1106 spell[EINFO_DEFEND][CURRENT_DAM]=dam;
1107 }
1108 spell[EINFO_DEFEND][DEFENDING_DEFENDER]=edefendtmp;
1109 }
1110 } // Ende Defender-Verarbeitung
1111
1112
1113 // Ueber einen P_TMP_DEFEND_HOOK werden z.B. Schutzzauber gehandhabt
1114 spell[EINFO_DEFEND][DEFEND_HOOK]=DI_NOHOOK;
1115 if ( res=QueryProp(P_TMP_DEFEND_HOOK) )
1116 {
1117 if ( pointerp(res) && (sizeof(res)>=3) && intp(res[0]) && (time()<res[0])
1118 && objectp(res[1]) && stringp(res[2]) )
1119 {
1120 if ( !(res=call_other(res[1],res[2],dam,dam_type,spell,enemy)) )
1121 {
1122 // Ein P_TMP_DEFEND_HOOK kann den Schaden vollstaendig abfangen,
1123 // das Defend wird dann hier abgebrochen *SICK* und es wird nur
1124 // noch getestet, ob man in die Flucht geschlagen wurde
1125 spell[EINFO_DEFEND][DEFEND_HOOK]=DI_HOOKINTERRUPT;
1126 CheckWimpyAndFlee();
1127 return 0;
1128 }
1129 else
1130 {
1131 spell[EINFO_DEFEND][DEFEND_HOOK]=DI_HOOK;
1132 if ( pointerp(res) && (sizeof(res)>=3)
1133 && intp(res[0] && pointerp(res[1])) )
1134 {
1135 mixed edefendtmp=({0,0,0});
1136 // Der P_TMP_DEFEND_HOOK kann ebenfalls Schadenshoehe und
1137 // -art sowie die Spell-Infos veraendern
1138 dam=res[0];
1139 edefendtmp[HOOK_DAM]=dam;
1140 dam_type=res[1];
1141 edefendtmp[HOOK_DAMTYPE]=dam_type;
1142
1143 if ( mappingp(res[2]) )
1144 {
1145 spell=res[2];
1146 // Waeh. Teuer. Aber geht nicht anders.
1147 edefendtmp[HOOK_SPELL]=deep_copy(spell);
1148 }
1149 spell[EINFO_DEFEND][DEFEND_HOOK]=edefendtmp;
1150 spell[EINFO_DEFEND][CURRENT_DAMTYPE]=dam_type;
1151 spell[EINFO_DEFEND][CURRENT_DAM]=dam;
1152 }
1153 }
1154 }
1155 else
1156 SetProp(P_TMP_DEFEND_HOOK,0);
1157 } // P_TMP_DEFEND_HOOK
1158
1159 // trigger defend hook
1160 hookData=({dam,dam_type,spell,enemy});
1161 hookRes=HookFlow(H_HOOK_DEFEND,hookData);
1162 if(hookRes && pointerp(hookRes) && sizeof(hookRes)>H_RETDATA){
1163 if(hookRes[H_RETCODE]==H_CANCELLED){
1164 spell[EINFO_DEFEND][DEFEND_HOOK]=DI_HOOKINTERRUPT;
1165 CheckWimpyAndFlee();
1166 return 0;
1167 }
1168 else if(hookRes[H_RETCODE]==H_ALTERED && hookRes[H_RETDATA]){
1169 spell[EINFO_DEFEND][DEFEND_HOOK]=DI_HOOK;
1170 if ( pointerp(hookRes[H_RETDATA]) && (sizeof(hookRes[H_RETDATA])>=3)
1171 && intp(hookRes[H_RETDATA][0] && pointerp(hookRes[H_RETDATA][1])) )
1172 {
1173 mixed edefendtmp=({0,0,0});
1174 // Der P_TMP_DEFEND_HOOK kann ebenfalls Schadenshoehe und
1175 // -art sowie die Spell-Infos veraendern
1176 dam=hookRes[H_RETDATA][0];
1177 edefendtmp[HOOK_DAM]=dam;
1178 dam_type=hookRes[H_RETDATA][1];
1179 edefendtmp[HOOK_DAMTYPE]=dam_type;
1180
1181 if ( mappingp(hookRes[H_RETDATA][2]) )
1182 {
1183 spell=hookRes[H_RETDATA][2];
1184 // Teuer, teuer... :-(
1185 edefendtmp[HOOK_SPELL]=deep_copy(spell);
1186 }
1187 spell[EINFO_DEFEND][DEFEND_HOOK]=edefendtmp;
1188 spell[EINFO_DEFEND][CURRENT_DAMTYPE]=dam_type;
1189 spell[EINFO_DEFEND][CURRENT_DAM]=dam;
1190 }
1191 }
1192 } // Ende Hook-Behandlung
1193
MG Mud User88f12472016-06-24 23:31:02 +02001194 // Es gibt auch Parierwaffen,
1195 if ( objectp(tmp=QueryProp(P_PARRY_WEAPON)) )
1196 {
1197 res2=tmp->QueryDefend(dam_type, spell, enemy);
1198
1199 // Reduzierbare Wirksamkeit der Parierwaffe?
1200 if ( member(spell[SP_REDUCE_ARMOUR],P_PARRY_WEAPON)
1201 && intp(res=spell[SP_REDUCE_ARMOUR][P_PARRY_WEAPON]) && (res>=0) )
1202 {
1203 res2=(res2*res)/100;
1204 }
1205
1206 dam-=res2;
1207 spell[EINFO_DEFEND][CURRENT_DAM]=dam;
1208 }
1209
1210 // Jetzt kommen die Ruestungen des Lebewesens ins Spiel (wenn es denn
1211 // welche traegt)
1212
1213 armours=QueryProp(P_ARMOURS)-({0});
1214 if ( (i=sizeof(armours))>0 ) {
1215 string aty;
1216
1217 tmp=armours[random(i)];
1218
1219 if ( objectp(tmp) )
1220 //Uebergabe des Mappings eh als Pointer/Referenz, daher billig
1221 tmp->TakeFlaw(dam_type,spell[EINFO_DEFEND]);
1222
1223 // pro Ruestung ein Key an Platz reservieren
1224 spell[EINFO_DEFEND][DEFEND_ARMOURS]=m_allocate(i,2);
1225 foreach(object armour : armours) {
1226 if ( objectp(armour) ) {
Zesstrac4052902019-12-29 16:45:21 +01001227 aty=armour->QueryProp(P_ARMOUR_TYPE);
MG Mud User88f12472016-06-24 23:31:02 +02001228
1229 if ( member(spell[SP_REDUCE_ARMOUR],aty)
1230 && intp(res2=spell[SP_REDUCE_ARMOUR][aty]) && (res2>=0) )
1231 dam -= (res2*armour->QueryDefend(dam_type, spell, enemy))/100;
1232 else
Zesstrac4052902019-12-29 16:45:21 +01001233 dam -= armour->QueryDefend(dam_type, spell, enemy);
MG Mud User88f12472016-06-24 23:31:02 +02001234 // Schaden NACH DefendFunc vermerken.
1235 // Schutzwirkung VOR DefendFunc (DEF_ARMOUR_PROT) vermerkt
1236 // das QueryDefend() selber.
1237 spell[EINFO_DEFEND][DEFEND_ARMOURS][armour,DEF_ARMOUR_DAM]=dam;
1238 // akt. Schaden vermerken und Schutz der aktuellen Ruestung (vor
1239 // DefendFunc) wieder auf 0 setzen fuer den naechsten Durchlauf.
1240 spell[EINFO_DEFEND][CURRENT_DAM]=dam;
1241 spell[EINFO_DEFEND][DEFEND_CUR_ARMOUR_PROT]=0;
1242 }
1243 }
1244 }
1245
1246 // Manche Gilden haben einen Verteidigungsskill. Der kommt jetzt zum
1247 // tragen. Der Skill kann die Schadenshoehe und -art beeinflussen.
1248 spell[EINFO_DEFEND]+=([DEFEND_GUILD:({})]);
1249 if ( mappingp(res=UseSkill(SK_MAGIC_DEFENSE,
1250 ([ SI_ENEMY : enemy,
1251 SI_SKILLDAMAGE : dam,
1252 SI_SKILLDAMAGE_TYPE : dam_type,
1253 SI_SPELL : spell ]))) )
1254 {
Zesstrac4052902019-12-29 16:45:21 +01001255 dam=res[SI_SKILLDAMAGE];
MG Mud User88f12472016-06-24 23:31:02 +02001256
1257 if ( pointerp(res[SI_SKILLDAMAGE_TYPE]) )
1258 dam_type=res[SI_SKILLDAMAGE_TYPE];
1259
1260 spell[EINFO_DEFEND][CURRENT_DAMTYPE]=dam_type;
1261 spell[EINFO_DEFEND][CURRENT_DAM]=dam;
1262 spell[EINFO_DEFEND][DEFEND_GUILD]=({dam,dam_type});
1263 }
1264
1265 // Evtl. interne Modifikationen
1266 InternalModifyDefend(&dam, &dam_type, &spell, &enemy);
1267
1268 spell[EINFO_DEFEND][CURRENT_DAMTYPE]=dam_type;
1269 spell[EINFO_DEFEND][CURRENT_DAM]=dam;
1270
1271 // Testen, ob irgendwas im Inventory des Lebewesen auf den Angriff
1272 // "reagiert"
1273 CheckSensitiveAttack(dam,dam_type,spell,enemy);
1274
1275 if ( !objectp(enemy) )
1276 return 0;
1277
1278 // Angriffszeit im Gegner setzen
1279 enemy->SetProp(P_LAST_COMBAT_TIME,time());
1280
1281 // Die Resistenzen des Lebewesens (natuerliche und durch Ruestungen
1282 // gesetzte) beruecksichtigen
1283 dam = to_int(CheckResistance(dam_type)*dam);
1284 spell[EINFO_DEFEND][DEFEND_RESI]=dam;
1285
1286 spell[EINFO_DEFEND][CURRENT_DAM]=dam;
1287
1288 // Bei physikalischen Angriffen wird die natuerliche Panzerung und die
1289 // Geschicklichkeit des Lebewesens beruecksichtigt
1290 object stat = find_object("/d/erzmagier/zesstra/pacstat"); // TODO: remove
1291 if ( spell[SP_PHYSICAL_ATTACK] )
1292 {
1293 // Minimum ist auch hier 1.
1294 int body = QueryProp(P_BODY)+QueryAttribute(A_DEX);
1295 res2 = (body/4 + random(body*3/4 + 1)) || 1;
1296 if (stat)
1297 stat->bodystat(body, res2, random(body)+1);
1298
1299 // Reduzierbare Wirksamkeit des Bodies?
1300 if ( member(spell[SP_REDUCE_ARMOUR], P_BODY)
1301 && intp(res=spell[SP_REDUCE_ARMOUR][P_BODY]) && (res>=0) )
1302 res2=(res2*res)/100;
1303
1304 dam-=res2;
1305 }
1306 spell[EINFO_DEFEND][DEFEND_BODY]=dam;
1307 spell[EINFO_DEFEND][CURRENT_DAM]=dam;
1308
1309 // Ist ueberhaupt noch etwas vom Schaden uebrig geblieben?
1310 if ( dam<0 )
1311 dam = 0;
1312 spell[EINFO_DEFEND][CURRENT_DAM]=dam;
1313
1314 // fuer die Statistik
1315 // TODO: entfernen nach Test-Uptime
1316 if (stat)
1317 stat->damagestat(spell[EINFO_DEFEND]);
1318
1319 // Die Anzahl der abzuziehenden Lebenspunkte ist der durch 10 geteilte
1320 // Schadenswert
1321 dam = dam / 10;
1322 spell[EINFO_DEFEND][DEFEND_LOSTLP]=dam;
1323
1324 // evtl. hat entweder der Aufrufer oder das Lebewesen selber eigene
1325 // Schadensmeldungen definiert. Die vom Aufrufer haben Prioritaet.
1326 mixed dam_msg = spell[SP_SHOW_DAMAGE];
1327 // Wenn != 0 (d.h. Treffermeldungen grundsaetzlich erwuenscht), aber kein
1328 // Array, hier im Living fragen.
1329 if (dam_msg && !pointerp(dam_msg))
1330 dam_msg = QueryProp(P_DAMAGE_MSG);
1331
1332 // In den meisten Faellen soll auch eine Schadensmeldung ausgegeben
1333 // werden, die die Hoehe des Schadens (grob) anzeigt
1334 if (spell[SP_SHOW_DAMAGE] && !pointerp(dam_msg)) {
1335 myname=name(WEN);
1336 enname=enemy->Name(WER);
1337 if (enemy->QueryProp(P_PLURAL)) {
1338 switch (dam) {
1339 case 0:
1340 tell_object(enemy,sprintf(" Ihr verfehlt %s.\n",myname));
1341 tell_object(this_object(),sprintf(" %s verfehlen Dich.\n",enname));
1342 tell_room(environment(enemy)||environment(this_object()),
1343 sprintf(" %s verfehlen %s.\n",enname,myname),
1344 ({ enemy, this_object() }));
1345 break;
1346 case 1:
1347 tell_object(enemy,sprintf(" Ihr kitzelt %s am Bauch.\n",myname));
1348 tell_object(this_object(),
1349 sprintf(" %s kitzeln Dich am Bauch.\n",enname));
1350 tell_room(environment(enemy)||environment(this_object()),
1351 sprintf(" %s kitzeln %s am Bauch.\n",enname,myname),
1352 ({ enemy, this_object() }));
1353 break;
1354 case 2..3:
1355 tell_object(enemy,sprintf(" Ihr kratzt %s.\n",myname));
1356 tell_object(this_object(),sprintf(" %s kratzen Dich.\n",enname));
1357 tell_room(environment(enemy)||environment(this_object()),
1358 sprintf(" %s kratzen %s.\n",enname,myname),
1359 ({ enemy, this_object() }));
1360 break;
1361 case 4..5:
1362 tell_object(enemy,sprintf(" Ihr trefft %s.\n",myname));
1363 tell_object(this_object(),sprintf(" %s treffen Dich.\n",enname));
1364 tell_room(environment(enemy)||environment(this_object()),
1365 sprintf(" %s treffen %s.\n",enname,myname),
1366 ({ enemy, this_object() }));
1367 break;
1368 case 6..10:
1369 tell_object(enemy,sprintf(" Ihr trefft %s hart.\n",myname));
1370 tell_object(this_object(),sprintf(" %s treffen Dich hart.\n",enname));
1371 tell_room(environment(enemy)||environment(this_object()),
1372 sprintf(" %s treffen %s hart.\n",enname,myname),
1373 ({ enemy, this_object() }));
1374 break;
1375 case 11..20:
1376 tell_object(enemy,sprintf(" Ihr trefft %s sehr hart.\n",myname));
1377 tell_object(this_object(),
1378 sprintf(" %s treffen Dich sehr hart.\n",enname));
1379 tell_room(environment(enemy)||environment(this_object()),
1380 sprintf(" %s treffen %s sehr hart.\n",enname,myname),
1381 ({ enemy, this_object() }));
1382 break;
1383 case 21..30:
1384 tell_object(enemy,
1385 sprintf(" Ihr schlagt %s mit dem Krachen brechender Knochen.\n",
1386 myname));
1387 tell_object(this_object(),
1388 sprintf(" %s schlagen Dich mit dem Krachen brechender Knochen.\n",
1389 enname));
1390 tell_room(environment(enemy)||environment(this_object()),
1391 sprintf(" %s schlagen %s mit dem Krachen brechender Knochen.\n",
1392 enname,myname), ({ enemy, this_object() }));
1393 break;
1394 case 31..50:
1395 tell_object(enemy,
1396 sprintf(" Ihr zerschmettert %s in kleine Stueckchen.\n",myname));
1397 tell_object(this_object(),
1398 sprintf(" %s zerschmettern Dich in kleine Stueckchen.\n",
1399 enname));
1400 tell_room(environment(enemy)||environment(this_object()),
1401 sprintf(" %s zerschmettern %s in kleine Stueckchen.\n",
1402 enname,myname), ({ enemy, this_object() }));
1403 break;
1404 case 51..75:
1405 tell_object(enemy,sprintf(" Ihr schlagt %s zu Brei.\n",myname));
1406 tell_object(this_object(),
1407 sprintf(" %s schlagen Dich zu Brei.\n",enname));
1408 tell_room(environment(enemy)||environment(this_object()),
1409 sprintf(" %s schlagen %s zu Brei.\n",enname,myname),
1410 ({ enemy, this_object() }));
1411 break;
1412 case 76..100:
1413 tell_object(enemy,sprintf(" Ihr pulverisiert %s.\n",myname));
1414 tell_object(this_object(),sprintf(" %s pulverisieren Dich.\n",enname));
1415 tell_room(environment(enemy)||environment(this_object()),
1416 sprintf(" %s pulverisieren %s.\n",enname,myname),
1417 ({ enemy, this_object() }));
1418 break;
1419 case 101..150:
1420 tell_object(enemy,sprintf(" Ihr zerstaeubt %s.\n",myname));
1421 tell_object(this_object(),sprintf(" %s zerstaeuben Dich.\n",enname));
1422 tell_room(environment(enemy)||environment(this_object()),
1423 sprintf(" %s zerstaeuben %s.\n",enname,myname),
1424 ({ enemy, this_object() }));
1425 break;
1426 case 151..200:
1427 tell_object(enemy,sprintf(" Ihr atomisiert %s.\n",myname));
1428 tell_object(this_object(),sprintf(" %s atomisieren Dich.\n",enname));
1429 tell_room(environment(enemy)||environment(this_object()),
1430 sprintf(" %s atomisieren %s.\n",enname,myname),
1431 ({ enemy, this_object() }));
1432 break;
1433 default:
1434 tell_object(enemy,sprintf(" Ihr vernichtet %s.\n",myname));
1435 tell_object(this_object(),sprintf(" %s vernichten Dich.\n",enname));
1436 tell_room(environment(enemy)||environment(this_object()),
1437 sprintf(" %s vernichten %s.\n",enname,myname),
1438 ({ enemy, this_object() }));
1439 break;
1440 }
1441
1442 }
1443 else {
1444 switch (dam) {
1445 case 0:
1446 tell_object(enemy,sprintf(" Du verfehlst %s.\n",myname));
1447 tell_object(this_object(),sprintf(" %s verfehlt Dich.\n",enname));
1448 tell_room(environment(enemy)||environment(this_object()),
1449 sprintf(" %s verfehlt %s.\n",enname,myname),
1450 ({ enemy, this_object() }));
1451 break;
1452 case 1:
1453 tell_object(enemy,sprintf(" Du kitzelst %s am Bauch.\n",myname));
1454 tell_object(this_object(),
1455 sprintf(" %s kitzelt Dich am Bauch.\n",enname));
1456 tell_room(environment(enemy)||environment(this_object()),
1457 sprintf(" %s kitzelt %s am Bauch.\n",enname,myname),
1458 ({ enemy, this_object() }));
1459 break;
1460 case 2..3:
1461 tell_object(enemy,sprintf(" Du kratzt %s.\n",myname));
1462 tell_object(this_object(),sprintf(" %s kratzt Dich.\n",enname));
1463 tell_room(environment(enemy)||environment(this_object()),
1464 sprintf(" %s kratzt %s.\n",enname,myname),
1465 ({ enemy, this_object() }));
1466 break;
1467 case 4..5:
1468 tell_object(enemy,sprintf(" Du triffst %s.\n",myname));
1469 tell_object(this_object(),sprintf(" %s trifft Dich.\n",enname));
1470 tell_room(environment(enemy)||environment(this_object()),
1471 sprintf(" %s trifft %s.\n",enname,myname),
1472 ({ enemy, this_object() }));
1473 break;
1474 case 6..10:
1475 tell_object(enemy,sprintf(" Du triffst %s hart.\n",myname));
1476 tell_object(this_object(),sprintf(" %s trifft Dich hart.\n",enname));
1477 tell_room(environment(enemy)||environment(this_object()),
1478 sprintf(" %s trifft %s hart.\n",enname,myname),
1479 ({ enemy, this_object() }));
1480 break;
1481 case 11..20:
1482 tell_object(enemy,sprintf(" Du triffst %s sehr hart.\n",myname));
1483 tell_object(this_object(),
1484 sprintf(" %s trifft Dich sehr hart.\n",enname));
1485 tell_room(environment(enemy)||environment(this_object()),
1486 sprintf(" %s trifft %s sehr hart.\n",enname,myname),
1487 ({ enemy, this_object() }));
1488 break;
1489 case 21..30:
1490 tell_object(enemy,
1491 sprintf(" Du schlaegst %s mit dem Krachen brechender Knochen.\n",
1492 myname));
1493 tell_object(this_object(),
1494 sprintf(" %s schlaegt Dich mit dem Krachen brechender Knochen.\n",
1495 enname));
1496 tell_room(environment(enemy)||environment(this_object()),
1497 sprintf(" %s schlaegt %s mit dem Krachen brechender Knochen.\n",
1498 enname,myname), ({ enemy, this_object() }));
1499 break;
1500 case 31..50:
1501 tell_object(enemy,
1502 sprintf(" Du zerschmetterst %s in kleine Stueckchen.\n",myname));
1503 tell_object(this_object(),
1504 sprintf(" %s zerschmettert Dich in kleine Stueckchen.\n",enname));
1505 tell_room(environment(enemy)||environment(this_object()),
1506 sprintf(" %s zerschmettert %s in kleine Stueckchen.\n",
1507 enname,myname), ({ enemy, this_object() }));
1508 break;
1509 case 51..75:
1510 tell_object(enemy,sprintf(" Du schlaegst %s zu Brei.\n",myname));
1511 tell_object(this_object(),
1512 sprintf(" %s schlaegt Dich zu Brei.\n",enname));
1513 tell_room(environment(enemy)||environment(this_object()),
1514 sprintf(" %s schlaegt %s zu Brei.\n",enname,myname),
1515 ({ enemy, this_object() }));
1516 break;
1517 case 76..100:
1518 tell_object(enemy,sprintf(" Du pulverisierst %s.\n",myname));
1519 tell_object(this_object(),sprintf(" %s pulverisiert Dich.\n",enname));
1520 tell_room(environment(enemy)||environment(this_object()),
1521 sprintf(" %s pulverisiert %s.\n",enname,myname),
1522 ({ enemy, this_object() }));
1523 break;
1524 case 101..150:
1525 tell_object(enemy,sprintf(" Du zerstaeubst %s.\n",myname));
1526 tell_object(this_object(),sprintf(" %s zerstaeubt Dich.\n",enname));
1527 tell_room(environment(enemy)||environment(this_object()),
1528 sprintf(" %s zerstaeubt %s.\n",enname,myname),
1529 ({ enemy, this_object() }));
1530 break;
1531 case 151..200:
1532 tell_object(enemy,sprintf(" Du atomisierst %s.\n",myname));
1533 tell_object(this_object(),sprintf(" %s atomisiert Dich.\n",enname));
1534 tell_room(environment(enemy)||environment(this_object()),
1535 sprintf(" %s atomisiert %s.\n",enname,myname),
1536 ({ enemy, this_object() }));
1537 break;
1538 default:
1539 tell_object(enemy,sprintf(" Du vernichtest %s.\n",myname));
1540 tell_object(this_object(),sprintf(" %s vernichtet Dich.\n",enname));
1541 tell_room(environment(enemy)||environment(this_object()),
1542 sprintf(" %s vernichtet %s.\n",enname,myname),
1543 ({ enemy, this_object() }));
1544 break;
1545 }
1546 }
1547 }
1548
1549 // Man kann auch selbst-definierte Schadensmeldungen ausgeben lassen
1550 else if( spell[SP_SHOW_DAMAGE] && pointerp(dam_msg) )
1551 {
1552 for( i=sizeof(dam_msg) ; --i >= 0 ; )
1553 {
1554 if ( dam>dam_msg[i][0] )
1555 {
1556 tell_object(ME,mess(dam_msg[i][1],ME,enemy));
1557 tell_object(enemy,mess(dam_msg[i][2],ME,enemy));
1558 say(mess(dam_msg[i][3],ME,enemy), enemy);
1559 break;
1560 }
1561 }
1562 }
1563 // else (!spell[SP_SHOW_DAMAGE]) keine Schadensmeldung.
1564
1565 // Informationen ueber den letzten Angriff festhalten
1566 Set(P_LAST_DAMTYPES, dam_type);
1567 Set(P_LAST_DAMTIME, time());
1568 Set(P_LAST_DAMAGE, dam);
1569
1570 // Bei Angriffen mit SP_NO_ENEMY-Flag kann man nicht sterben ...
1571 if ( spell[SP_NO_ENEMY] )
1572 reduce_hit_points(dam);
1573 // ... bei allen anderen natuerlich schon
1574 else
1575 do_damage(dam,enemy);
1576
1577 // evtl. ist dies Objekt hier tot...
1578 if (!objectp(ME)) return dam;
1579
1580 // Testen, ob man in die Fucht geschlagen wird
1581 CheckWimpyAndFlee();
1582
1583 // Verursachten Schaden (in LP) zurueckgeben
1584 return dam;
1585}
1586
1587public float CheckResistance(string *dam_type) {
1588 //funktion kriegt die schadensarten uebergeben, schaut sich dieses
1589 //sowie P_RESISTENCE_STRENGTH und P_RESITENCE_MODFIFIER an und berechnet
1590 //einen Faktor, mit dem die urspruengliche Schadensmenge multipliziert
1591 //wird, um den Schaden nach Beruecksichtigung der Resis/Anfaelligkeiten zu
1592 //erhalten. Rueckgabewert normalerweise (s.u.) >=0.
1593 mapping rstren, mod;
1594 float faktor,n;
1595 int i;
1596
1597 mod = Query(P_RESISTANCE_MODIFIER);
1598 if ( mappingp(mod) )
1599 mod = mod["me"];
1600
1601 if ( !mappingp(rstren=Query(P_RESISTANCE_STRENGTHS)) )
1602 {
1603 if (!mappingp(mod))
1604 return 1.0;
1605 else
1606 rstren = ([]);
1607 }
1608
1609 if ( !mappingp(mod) )
1610 mod = ([]);
1611
1612 if ( (i=sizeof(dam_type))<1 )
1613 return 1.0;
1614
1615 n=to_float(i);
1616 faktor=0.0;
1617
1618 //dies hier tut nicht mehr das gewuenschte, wenn in P_RESISTENCE_STRENGTHS
1619 //Faktoren <-1.0 angegeben werden. Rueckgabewerte <0 sind dann moeglich und
1620 //leider werden die Schadensarten ohne Resis nicht mehr richtig verwurstet. :-/
1621 foreach(string dt: dam_type) {
1622 faktor = faktor + (1.0 + to_float(rstren[dt]))
1623 * (1.0 + to_float(mod[dt]))-1.0;
1624 }
1625 return 1.0+(faktor/n);
1626}
1627
1628public varargs mapping StopHuntingMode(int silent)
1629{ mapping save_enemy;
1630 int i;
1631
1632 save_enemy=enemies;
1633 if ( !silent )
1634 walk_mapping(enemies, #'StopHuntText); //#');
1635
1636 enemies=m_allocate(0,1);
1637 last_attack_msg=0;
1638
1639 return save_enemy;
1640}
1641
1642public <object*|int*>* QueryEnemies()
1643{
1644 return ({m_indices(enemies),m_values(enemies)});
1645}
1646
1647public mapping GetEnemies()
1648{
1649 return enemies;
1650}
1651
1652public mapping SetEnemies(<object*|int*>* myenemies)
1653{
1654 enemies=mkmapping(myenemies[0],myenemies[1]);
1655 return enemies;
1656}
1657
1658private string _kill_alias( string str )
1659{
1660 return "\\" + str;
1661}
1662
1663public varargs void Flee( object oldenv, int force )
1664{ mixed *exits, exit, dex;
1665 mapping tmp;
1666 int i;
1667 object env;
1668
1669 if ( !environment() )
1670 return;
1671
1672 // mit 'force' kann man die Checks umgehen, damit der Spieler auf jeden
1673 // Fall fluechtet ...
1674 if ( !force && ( oldenv && (oldenv != environment()) ||
1675 query_once_interactive(ME) && (!EnemyPresent() ||
1676 (QueryProp(P_HP) >= QueryProp(P_WIMPY))) ) )
1677 return;
1678
1679 // ... aber Magier fluechten zu lassen ist nicht ganz so einfach ;-)
1680 if ( query_once_interactive(this_object()) && IS_LEARNING(this_object()) )
1681 return;
1682
1683 // Geister brauchen nicht um ihr Leben zu fuerchten
1684 if ( QueryProp(P_GHOST) )
1685 return;
1686
1687 tell_object( ME, "Die Angst ist staerker als Du ... "+
1688 "Du willst nur noch weg hier.\n");
1689
1690 if ( TeamFlee() ) // Erfolgreiche Flucht in hintere Kampfreihe?
1691 return;
1692
1693 env = environment();
1694 tmp = environment()->QueryProp(P_EXITS);
1695 exits = m_indices(tmp);
1696 tmp = environment()->QueryProp(P_SPECIAL_EXITS);
1697 exits += m_indices(tmp);
1698
1699 if ( query_once_interactive(ME) )
1700 exits = map( exits, #'_kill_alias/*'*/ );
1701
1702 // die Fluchtrichtung von Magiern wird aus Sicherheitsgruenden
1703 // nicht ausgewertet
1704 if ( interactive(ME) && IS_SEER(ME) && !IS_LEARNER(ME)
1705 && (dex=QueryProp(P_WIMPY_DIRECTION)) )
1706 {
1707 i = 60 + 4 * (QueryProp(P_LEVEL) - 30) / 3;
1708 exits += ({dex}); // bevorzugte Fluchtrichtung mindestens einmal
1709 }
1710
1711 if ( !sizeof(exits) )
1712 {
1713 tell_object( ME, "Du versuchst zu fliehen, schaffst es aber nicht.\n" );
1714
1715 return;
1716 }
1717
1718 while ( sizeof(exits) && (environment()==env) )
1719 {
1720 if ( dex // Vorzugsweise Fluchtrichtung?
1721 && (member(exits,dex) >= 0) // moeglich?
1722 && (random(100) <= i)) // und Wahrscheinlichkeit gross genug?
1723 exit = dex;
1724 else
1725 exit = exits[random(sizeof(exits))];
1726
1727 catch(command(exit);publish);
1728 exits -= ({exit});
1729 }
1730
1731 if ( environment()==env )
1732 tell_object( ME, "Dein Fluchtversuch ist gescheitert.\n" );
1733}
1734
1735public object EnemyPresent()
1736{
1737 foreach(object en: enemies) {
1738 if (environment()==environment(en))
1739 return en;
1740 }
1741 return 0;
1742}
1743
1744public object InFight()
1745{
1746 return EnemyPresent();
1747}
1748
1749public varargs int StopHuntID(string str, int silent) {
1750
1751 if ( !stringp(str) )
1752 return 0;
1753
1754 int j;
1755 foreach(object en: enemies) {
1756 if (en->id(str)) {
1757 StopHuntFor(en,silent);
1758 j++;
1759 }
1760 }
1761
1762 return j;
1763}
1764
1765public int SpellDefend(object caster, mapping sinfo)
1766{ int re;
1767 mixed res;
1768 string *ind;
1769
1770 re = UseSkill(SK_SPELL_DEFEND,([ SI_SKILLARG : sinfo ,
1771 SI_ENEMY : caster ]) );
1772
1773 if ( (res=QueryProp(P_MAGIC_RESISTANCE_OFFSET)) && mappingp(res)
1774 && pointerp(sinfo[SI_MAGIC_TYPE]))
1775 {
1776 ind = m_indices(res) & sinfo[SI_MAGIC_TYPE];
1777
1778 if (pointerp(ind) && sizeof(ind) ) {
1779 foreach(string index : ind)
1780 re+=res[index];
1781 }
1782 }
1783 else if(res && intp(res))
1784 re+=res;
1785
1786 if ( (re>3333) && query_once_interactive(this_object()) )
1787 re=3333; /* Maximal 33% Abwehrchance bei Spielern */
1788 return re;
1789}
1790
1791// **** this is property-like
1792
1793static int _set_attack_busy(mixed val)
1794{
1795 if ( ((to_int(val))>5) && previous_object(1)
1796 && query_once_interactive(previous_object(1)) )
1797 log_file("ATTACKBUSY",sprintf("%s %d Taeter: %O Opfer: %O\n",
1798 dtime(time()),to_int(val),previous_object(1),this_object()));
1799
1800 attack_busy-=to_int(val*100.0);
1801
1802 if ( attack_busy<-2000 )
1803 attack_busy=-2000;
1804
1805 return attack_busy;
1806}
1807
1808static int _query_attack_busy()
1809{
1810 if (IS_LEARNING(ME))
1811 return 0;
1812
1813 return (attack_busy<100);
1814}
1815
1816// **** local property methods
1817static int _set_wimpy(int i)
1818{
1819 if ( !intp(i) || (i>QueryProp(P_MAX_HP)) || (i<0) )
1820 return 0;
1821
1822 // ggf. Statusreport ausgeben
1823 if (interactive(ME))
1824 status_report(DO_REPORT_WIMPY, i);
1825
1826 return Set(P_WIMPY, i);
1827}
1828
1829static string _set_wimpy_dir(string s) {
1830 // ggf. Statusreport ausgeben
1831 if (interactive(ME))
1832 status_report(DO_REPORT_WIMPY_DIR, s);
1833 return Set(P_WIMPY_DIRECTION, s, F_VALUE);
1834}
1835
1836static mixed _set_hands(mixed h)
1837{
1838 if ( sizeof(h)==2 )
1839 h += ({ ({DT_BLUDGEON}) });
1840 if (!pointerp(h[2]))
1841 h[2] = ({h[2]});
1842 return Set(P_HANDS, h, F_VALUE);
1843}
1844
1845//TODO normalisieren/korrigieren in updates_after_restore().
1846static mixed _query_hands()
1847{
1848 mixed *hands = Query(P_HANDS);
1849 if ( !hands )
1850 return Set(P_HANDS, ({ " mit blossen Haenden", 30, ({DT_BLUDGEON})}));
1851 else if ( sizeof(hands)<3 )
1852 return Set(P_HANDS, ({hands[0], hands[1], ({DT_BLUDGEON})}));
1853 else if ( !pointerp(hands[2]) )
1854 return Set(P_HANDS, ({hands[0], hands[1], ({ hands[2] })}));
1855
1856 return Query(P_HANDS);
1857}
1858
1859static int _query_total_wc()
1860{ mixed res;
1861 int totwc;
1862
1863 if ( objectp(res=QueryProp(P_WEAPON)) )
Zesstrac4052902019-12-29 16:45:21 +01001864 totwc = res->QueryProp(P_WC);
MG Mud User88f12472016-06-24 23:31:02 +02001865 else if ( pointerp(res=QueryProp(P_HANDS)) && sizeof(res)>1
1866 && intp(res[1]) )
Zesstrac4052902019-12-29 16:45:21 +01001867 totwc=res[1];
MG Mud User88f12472016-06-24 23:31:02 +02001868 else
1869 totwc=30;
1870
1871 totwc = ((2*totwc)+(10*QueryAttribute(A_STR)))/3;
1872
1873 return Set(P_TOTAL_WC, totwc, F_VALUE);
1874}
1875
1876static int _query_total_ac() {
1877
1878 int totac = 0;
1879 object *armours = QueryProp(P_ARMOURS);
1880 object parry = QueryProp(P_PARRY_WEAPON);
1881
1882 if ( member(armours,0)>=0 ) {
1883 armours -= ({ 0 });
1884 }
1885
1886 foreach(object armour: armours)
Zesstrac4052902019-12-29 16:45:21 +01001887 totac += armour->QueryProp(P_AC);
MG Mud User88f12472016-06-24 23:31:02 +02001888
1889 if ( objectp(parry) )
1890 totac += parry->QueryProp(P_AC);
1891
1892 totac += (QueryProp(P_BODY)+QueryAttribute(A_DEX));
1893
1894 return Set(P_TOTAL_AC, totac, F_VALUE);
1895}
1896
1897static mapping _query_resistance_strengths() {
1898
1899 UpdateResistanceStrengths();
1900
1901 mapping rstren = copy(Query(P_RESISTANCE_STRENGTHS, F_VALUE));
1902 mapping mod = Query(P_RESISTANCE_MODIFIER, F_VALUE);
1903
1904 if ( !mappingp(rstren) )
1905 rstren = ([]);
1906
1907 if ( !mappingp(mod) || !mappingp(mod=mod["me"]) || !sizeof(mod) )
1908 return rstren;
1909
1910 foreach(string modkey, float modvalue : mod)
1911 rstren[modkey] = ((1.0+rstren[modkey])*(1.0+modvalue))-1.0;
1912
1913 return rstren;
1914}
1915
1916static int _set_disable_attack(int val)
1917{
1918 if (val<-100000)
1919 {
1920 log_file("PARALYSE_TOO_LOW",
1921 sprintf("Wert zu klein: %s, Wert: %d, "
1922 "Aufrufer: %O, Opfer: %O",
1923 ctime(time()),
1924 val,previous_object(1),
1925 this_object()));
1926 }
1927 if ( val>30 )
1928 val=30;
1929 if ( (val>=20) && previous_object(1)!=ME && query_once_interactive(ME) )
1930 log_file("PARALYSE",sprintf("%s %d Taeter: %O Opfer: %O\n",
1931 ctime(time())[4..15],
1932 val,previous_object(1),this_object()));
1933
1934 if (time()<QueryProp(P_NEXT_DISABLE_ATTACK))
1935 {
1936 // gueltige Zeitsperre existiert.
1937 // Erhoehen von P_DISABLE_ATTACK geht dann nicht. (Ja, auch nicht erhoehen
1938 // eines negativen P_DISABLE_ATTACK)
1939 if (val >= QueryProp(P_DISABLE_ATTACK))
1940 return DISABLE_TOO_EARLY;
1941 // val ist kleiner als aktuelles P_DISABLE_ATTACK - das ist erlaubt, ABER es
1942 // darf die bestehende Zeitsperre nicht verringern, daher wird sie nicht
1943 // geaendert.
1944 return Set(P_DISABLE_ATTACK,val,F_VALUE);
1945 }
1946 // es existiert keine gueltige Zeitsperre - dann wird sie nun gesetzt.
1947 // (Sollte val < 0 sein, wird eine Zeitsperre in der Vergangenheit gesetzt,
1948 // die schon abgelaufen ist. Das ist ueberfluessig, schadet aber auch
1949 // nicht.)
1950 SetProp(P_NEXT_DISABLE_ATTACK,time()+val*2*__HEART_BEAT_INTERVAL__);
1951 return Set(P_DISABLE_ATTACK,val);
1952}
1953
1954// Neue Verwaltung der Haende:
1955
1956// P_HANDS_USED_BY enhaelt ein Array mit allen Objekten, die Haende
1957// belegen, jedes kommt so oft vor wie Haende belegt werden.
1958
1959static mixed *_query_hands_used_by()
1960{
1961 return ((Query(P_HANDS_USED_BY, F_VALUE) || ({}) ) - ({0}));
1962}
1963
1964static int _query_used_hands()
1965{
1966 return sizeof(QueryProp(P_HANDS_USED_BY));
1967}
1968
1969static int _query_free_hands()
1970{
1971 return (QueryProp(P_MAX_HANDS)-QueryProp(P_USED_HANDS));
1972}
1973
1974public varargs int UseHands(object ob, int num)
1975{ mixed *h;
1976
1977 if ( !ob && !(ob=previous_object(1)) )
1978 return 0;
1979
1980 if ( (num<=0) && ((num=ob->QueryProp(P_HANDS))<=0) )
1981 return 0;
1982
1983 h=QueryProp(P_HANDS_USED_BY)-({ob});
1984
1985 if ( (sizeof(h)+num)>QueryProp(P_MAX_HANDS) )
1986 return 0;
1987
1988 foreach(int i: num)
1989 h+=({ob});
1990
1991 SetProp(P_HANDS_USED_BY,h);
1992
1993 return 1;
1994}
1995
1996public varargs int FreeHands(object ob)
1997{
1998 if ( !ob && !(ob=previous_object(1)) )
1999 return 0;
2000
2001 SetProp(P_HANDS_USED_BY,QueryProp(P_HANDS_USED_BY)-({ob}));
2002
2003 return 1;
2004}
2005
2006// Kompatiblitaetsfunktionen:
2007
2008static int _set_used_hands(int new_num)
2009{ int old_num, dif;
2010 object ob;
2011
2012 old_num=QueryProp(P_USED_HANDS);
2013
2014 if ( !objectp(ob=previous_object(1)) )
2015 return old_num;
2016
2017 // Meldung ins Debuglog schreiben. Aufrufer sollte gefixt werden.
2018 debug_message(sprintf("P_USED_HANDS in %O wird gesetzt durch %O\n",
2019 this_object(), ob), DMSG_LOGFILE|DMSG_STAMP);
2020
2021 if ( !(dif=new_num-old_num) )
2022 return new_num;
2023
2024 if ( dif>0 )
2025 UseHands(ob,dif);
2026 else
2027 FreeHands(ob);
2028
2029 return QueryProp(P_USED_HANDS);
2030}
2031
2032// Funktionen fuer Begruessungsschlag / Nackenschlag:
2033
2034public int CheckEnemy(object ob)
2035{
2036 return (living(ob) && IsEnemy(ob));
2037}
2038
2039public varargs void ExecuteMissingAttacks(object *remove_attackers)
2040{
2041 if ( !pointerp(missing_attacks) )
2042 missing_attacks=({});
2043
2044 if ( pointerp(remove_attackers) )
2045 missing_attacks-=remove_attackers;
2046
2047 foreach(object ob : missing_attacks) {
2048 if ( objectp(ob) && (environment(ob)==environment()) )
2049 ob->Attack2(ME);
2050 }
2051 missing_attacks=({});
2052}
2053
2054public void InitAttack()
2055{ object ob,next;
2056 closure cb;
2057
2058 if ( !living(ME) )
2059 return;
2060
2061 ExecuteMissingAttacks();
2062 //EMA kann das Living zerstoeren oder toeten...
2063 if (!living(ME) || QueryProp(P_GHOST)) return;
2064
2065 if ( objectp(ob=IsTeamMove()) )
2066 cb=symbol_function("InitAttack_Callback",ob);
2067 else
2068 cb=0;
2069
2070 for ( ob=first_inventory(environment()) ; objectp(ob) ; ob=next)
2071 {
2072 next=next_inventory(ob);
2073
2074 if ( !living(ob) )
2075 continue;
2076
2077 if (ob->IsEnemy(ME))
2078 {
2079 // Das ist nicht so sinnlos wie es aussieht. a) werden die Hunttimes
2080 // aktualisiert und b) werden Teammitglieder von mir bei diesem
2081 // InsertEnemy() ggf. erfasst.
2082 ob->InsertEnemy(ME);
2083
2084 if ( closurep(cb) && funcall(cb,ob) ) // Wird ganzes Team gemoved?
2085 missing_attacks += ({ ob }); // Dann erstmal warten bis alle da sind
2086 else
2087 ob->Attack2(ME);
2088
2089 }
2090 else if ( IsEnemy(ob) )
2091 {
2092 // Das ist nicht so sinnlos wie es aussieht. a) werden die Hunttimes
2093 // aktualisiert und b) werden Teammitglieder von ob bei diesem
2094 // InsertEnemy() ggf. erfasst.
2095 InsertEnemy(ob);
2096 Attack2(ob);
2097 }
2098 //Attack2 kann dieses Objekt zerstoeren oder toeten. Wenn ja: abbruch
2099 if ( !living(ME) || QueryProp(P_GHOST)) break;
2100 }
2101}
2102
2103public void ExitAttack()
2104{
2105 if ( !living(ME) )
2106 return;
2107
2108 // Noch nachzuholende Begruessungsschlaege:
2109 ExecuteMissingAttacks();
2110}
2111
2112public object|object*|mapping QueryArmourByType(string type)
2113{
2114 // Rueckgabewert:
2115 // DIE Ruestung vom Typ <type>, falls <type> nicht AT_MISC,
2116 // Array aller AT_MISC-Ruestungen falls <type> AT_MISC (auch leer),
2117 // Mapping mit allen oben genannten Infos, falls <type> Null
2118
2119 object *armours;
2120 string typ2;
2121
2122 // Wenn Cache vorhanden, dann Cache liefern.
2123 if (mappingp(QABTCache)) {
2124 if (type == AT_MISC)
2125 return QABTCache[AT_MISC] - ({0});
2126 else if (type)
2127 return QABTCache[type];
2128 else
2129 return copy(QABTCache);
2130 }
2131
2132 if ( !pointerp(armours=QueryProp(P_ARMOURS)) )
2133 armours=({});
2134
2135 // Cache erzeugen
2136 QABTCache = ([ AT_MISC: ({}) ]);
2137 foreach(object ob: armours - ({0}) ) {
2138 if ( !stringp(typ2=ob->QueryProp(P_ARMOUR_TYPE)) )
2139 continue;
2140 if ( typ2==AT_MISC )
2141 QABTCache[AT_MISC] += ({ob});
2142 else
2143 QABTCache[typ2] = ob;
2144 }
2145 // Und gewuenschtes Datum liefern.
2146 if (type)
2147 return QABTCache[type];
2148 else
2149 return copy(QABTCache);
2150}
2151
2152//TODO: langfristig waers besser, wenn hier nicht jeder per SetProp() drauf
2153//los schreiben wuerde.
2154static object *_set_armours(object *armours) {
2155 if (pointerp(armours)) {
2156 // Cache wegwerfen
2157 QABTCache = 0;
2158 // armours ggf. unifizieren. Ausserdem Kopie reinschreiben.
2159 return Set(P_ARMOURS, m_indices(mkmapping(armours)), F_VALUE);
2160 }
2161 return QueryProp(P_ARMOURS);
2162}
2163
2164/** Reduziert die Befriedezaehler pro Reset im Durchschnitt um 2.5.
2165 Berechnet ganzzahlige durchschnittliche Resets seit dem letzten Expire und
2166 erhoeht die letzte Expirezeit um Resets*__RESET_TIME__ (Standard Intervall
2167 fuer Reset ist momentan 3600s, im Durchschnitt kommen dann 2700 zwischen 2
2168 Resets bei raus). So wird der unganzzahlige Rest beim naechsten Aufruf
2169 beruecksichtigt.
2170 Diese Variante des Expires wurde gewaehlt, um zu vermeiden, combat.c einen
2171 reset() zu geben und in jedem Reset in jedem Lebewesen ein Expire machen zu
2172 muessen, auch wenn nur in sehr wenigen Lebewesen was gemacht werden muss.
2173 @param[in,out] ph Mapping aus P_PEACE_HISTORY. Wird direkt aktualisiert.
2174 @attention Muss ein gueltiges P_PEACE_HISTORY uebergeben bekommen, anderem
2175 Datentyp inkl. 0 wird die Funktion buggen.
2176 */
2177private void _decay_peace_history(mixed ph) {
2178 // Ganzzahlige resets seit letztem Expire ermitteln. (Durchschnitt)
2179 int resets = (time() - ph[0]) / (__RESET_TIME__*75/100);
2180 // auf Zeitstempel draufrechnen, damit der unganzzahlige Rest nicht
2181 // verlorengeht, der wird beim naechsten Expire dann beruecksichtigt.
2182 ph[0] += resets * (__RESET_TIME__ * 75 / 100);
2183 // pro Reset werden im Durchschnitt 2.5 Versuche abgezogen. (Hier werden
2184 // beim Expire mal 2 und mal 3 Versuche pro Reset gerechnet. Aber falls hier
2185 // viele Resets vergangen sind, ist es vermutlich eh egal, weil die Counter
2186 // auf 0 fallen.)
2187 int expire = resets * (random(2) + 2);
2188 // ueber alle Gilden
2189 mapping tmp=ph[1];
2190 foreach(string key, int count: &tmp ) {
2191 count-=expire;
2192 if (count < 0) count = 0;
2193 }
2194}
2195
2196/** Pacify() dient zur Bestimmung, ob sich ein Lebewesen gerade
2197 * befrieden lassen will.
2198 * Berechnet eine Wahrscheinlichkeit nach unten stehender Formel, welche die
2199 * Intelligenz dieses Lebenwesens, die Intelligenz des Casters und die
2200 * bisherige Anzahl erfolgreicher Befriedungsversuche dieser Gilde eingeht.
2201 * Anschliessend wird aus der Wahrscheinlichkeit und random() bestimmt, ob
2202 * dieser Versuch erfolgreich ist.
2203Formel: w = (INT_CASTER + 10 - ANZ*4) / (INT_ME + 10)
2204INT_CASTER: Caster-Intelligenz, INT_ME: Intelligenz dieses Livings
2205ANZ: Anzahl erfolgreicher Befriedungsversuche
2206
2207Annahme: INT_CASTER === 22, alle Wahrscheinlichkeiten * 100
2208
2209INT_ME Erfolgswahrscheinlichkeiten je nach Anzahl erfolgreicher Versuche
2210 1 2 3 4 5 6 7 8
2211 0 280 240 200 160 120 80 40 0
2212 2 233,33 200 166,67 133,33 100 66,67 33,33 0
2213 4 200 171,43 142,86 114,29 85,71 57,14 28,57 0
2214 6 175 150 125 100 75 50 25 0
2215 8 155,56 133,33 111,11 88,89 66,67 44,44 22,22 0
2216 10 140 120 100 80 60 40 20 0
2217 12 127,27 109,09 90,91 72,73 54,55 36,36 18,18 0
2218 14 116,67 100 83,33 66,67 50 33,33 16,67 0
2219 16 107,69 92,31 76,92 61,54 46,15 30,77 15,38 0
2220 18 100 85,71 71,43 57,14 42,86 28,57 14,29 0
2221 20 93,33 80 66,67 53,33 40 26,67 13,33 0
2222 22 87,5 75 62,5 50 37,5 25 12,5 0
2223 24 82,35 70,59 58,82 47,06 35,29 23,53 11,76 0
2224 26 77,78 66,67 55,56 44,44 33,33 22,22 11,11 0
2225 28 73,68 63,16 52,63 42,11 31,58 21,05 10,53 0
2226 30 70 60 50 40 30 20 10 0
2227 32 66,67 57,14 47,62 38,1 28,57 19,05 9,52 0
2228 34 63,64 54,55 45,45 36,36 27,27 18,18 9,09 0
2229 35 62,22 53,33 44,44 35,56 26,67 17,78 8,89 0
2230 36 60,87 52,17 43,48 34,78 26,09 17,39 8,7 0
2231 38 58,33 50 41,67 33,33 25 16,67 8,33 0
2232 40 56 48 40 32 24 16 8 0
2233 42 53,85 46,15 38,46 30,77 23,08 15,38 7,69 0
2234 44 51,85 44,44 37,04 29,63 22,22 14,81 7,41 0
2235 46 50 42,86 35,71 28,57 21,43 14,29 7,14 0
2236 48 48,28 41,38 34,48 27,59 20,69 13,79 6,9 0
2237 50 46,67 40 33,33 26,67 20 13,33 6,67 0
2238 52 45,16 38,71 32,26 25,81 19,35 12,9 6,45 0
2239 54 43,75 37,5 31,25 25 18,75 12,5 6,25 0
2240 56 42,42 36,36 30,3 24,24 18,18 12,12 6,06 0
2241 58 41,18 35,29 29,41 23,53 17,65 11,76 5,88 0
2242 60 40 34,29 28,57 22,86 17,14 11,43 5,71 0
2243 62 38,89 33,33 27,78 22,22 16,67 11,11 5,56 0
2244 64 37,84 32,43 27,03 21,62 16,22 10,81 5,41 0
2245 66 36,84 31,58 26,32 21,05 15,79 10,53 5,26 0
2246 68 35,9 30,77 25,64 20,51 15,38 10,26 5,13 0
2247 70 35 30 25 20 15 10 5 0
2248 72 34,15 29,27 24,39 19,51 14,63 9,76 4,88 0
2249 74 33,33 28,57 23,81 19,05 14,29 9,52 4,76 0
2250 76 32,56 27,91 23,26 18,6 13,95 9,3 4,65 0
2251 78 31,82 27,27 22,73 18,18 13,64 9,09 4,55 0
2252 80 31,11 26,67 22,22 17,78 13,33 8,89 4,44 0
2253 82 30,43 26,09 21,74 17,39 13,04 8,7 4,35 0
2254 84 29,79 25,53 21,28 17,02 12,77 8,51 4,26 0
2255 86 29,17 25 20,83 16,67 12,5 8,33 4,17 0
2256 88 28,57 24,49 20,41 16,33 12,24 8,16 4,08 0
2257 90 28 24 20 16 12 8 4 0
2258 92 27,45 23,53 19,61 15,69 11,76 7,84 3,92 0
2259 94 26,92 23,08 19,23 15,38 11,54 7,69 3,85 0
2260 96 26,42 22,64 18,87 15,09 11,32 7,55 3,77 0
2261 98 25,93 22,22 18,52 14,81 11,11 7,41 3,7 0
2262 100 25,45 21,82 18,18 14,55 10,91 7,27 3,64 0
2263 * @return 1, falls Befrieden erlaubt ist, 0 sonst.
2264 * @param[in] caster Derjenige, der den Spruch ausfuehrt.
2265 * @attention Wenn acify() 1 zurueckliefert, zaehlt dies als
2266 * erfolgreicher Befriedungsversuch und in diesem Lebewesen wurde
2267 * StopHuntingMode(1) aufgerufen.
2268*/
2269public int Pacify(object caster) {
2270
2271 // wenn das Viech keine Gegner hat, dann witzlos. ;-) Ohne Caster gehts auch
2272 // direkt raus.
2273 if (!mappingp(enemies) || !sizeof(enemies)
2274 || !objectp(caster)) {
2275 return 0;
2276 }
2277
2278 // Wenn P_ACCEPT_PEACE gesetzt ist, altes Verhalten wiederspiegeln
2279 // -> der NPC ist einfach immer befriedbar. Gleiches gilt fuer den Caster
2280 // selber, der wird sich ja nicht gegen das eigene Befriede wehren. Und auch
2281 // im team wehrt man sich nicht gegen das Befriede eines Teamkollegen
2282 if (QueryProp(P_ACCEPT_PEACE)==1 || caster==ME
2283 || member(TeamMembers(), caster) > -1) {
2284 StopHuntingMode(1); // Caster/Gilde sollte eigene Meldung ausgeben
2285 return 1;
2286 }
2287
2288 string gilde = caster->QueryProp(P_GUILD) || "ANY";
2289
2290 // ggf. P_PEACE_HISTORY initialisieren
Zesstrac4052902019-12-29 16:45:21 +01002291 <int|mapping>* ph = QueryProp(P_PEACE_HISTORY);
MG Mud User88f12472016-06-24 23:31:02 +02002292 if (!pointerp(ph))
2293 SetProp(P_PEACE_HISTORY, ph=({time(), ([]) }) );
2294
2295 // ggf. die Zaehler reduzieren.
2296 if ( ph[0] + __RESET_TIME__ * 75/100 < time()) {
2297 _decay_peace_history(&ph);
2298 }
2299
2300 float w = (caster->QueryAttribute(A_INT) + 10 - ph[1][gilde] * 4.0) /
2301 (QueryAttribute(A_INT) + 10);
2302 // auf [0,1] begrenzen.
2303 if (w<0) w=0.0;
2304 else if (w>1) w=1.0;
2305 // w * n ist eine Zahl zwischen 0 und n, wenn w * n > random(n)+1,
2306 // darf befriedet werden. Da das Random fuer grosse Zahlen
2307 // besser verteilt ist, nehm ich n = __INT_MAX__ und vernachlaessige
2308 // ausserdem die +1 beim random().
2309 if (ceil(w * __INT_MAX__) > random(__INT_MAX__) ) {
2310 ph[1][gilde]++;
2311 StopHuntingMode(1);
2312 return 1;
2313 }
2314 // ein SetProp(P_PEACE_HISTORY) kann entfallen, da das Mapping direkt
2315 // geaendert wird. Sollte die Prop allerdings mal ne Querymethode haben,
2316 // welche eine Kopie davon liefert, muss das hier geaendert oder die
2317 // Prop per Query() abgefragt werden.
2318 return 0;
2319}
2320