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