blob: 053691a8d694d7de3828225479e805d7c44d8347 [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 {
1292 // Minimum ist auch hier 1.
1293 int body = QueryProp(P_BODY)+QueryAttribute(A_DEX);
1294 res2 = (body/4 + random(body*3/4 + 1)) || 1;
1295 if (stat)
1296 stat->bodystat(body, res2, random(body)+1);
1297
1298 // Reduzierbare Wirksamkeit des Bodies?
1299 if ( member(spell[SP_REDUCE_ARMOUR], P_BODY)
1300 && intp(res=spell[SP_REDUCE_ARMOUR][P_BODY]) && (res>=0) )
1301 res2=(res2*res)/100;
1302
1303 dam-=res2;
1304 }
1305 spell[EINFO_DEFEND][DEFEND_BODY]=dam;
1306 spell[EINFO_DEFEND][CURRENT_DAM]=dam;
1307
1308 // Ist ueberhaupt noch etwas vom Schaden uebrig geblieben?
1309 if ( dam<0 )
1310 dam = 0;
1311 spell[EINFO_DEFEND][CURRENT_DAM]=dam;
1312
1313 // fuer die Statistik
1314 // TODO: entfernen nach Test-Uptime
1315 if (stat)
1316 stat->damagestat(spell[EINFO_DEFEND]);
1317
1318 // Die Anzahl der abzuziehenden Lebenspunkte ist der durch 10 geteilte
1319 // Schadenswert
1320 dam = dam / 10;
1321 spell[EINFO_DEFEND][DEFEND_LOSTLP]=dam;
1322
1323 // evtl. hat entweder der Aufrufer oder das Lebewesen selber eigene
1324 // Schadensmeldungen definiert. Die vom Aufrufer haben Prioritaet.
1325 mixed dam_msg = spell[SP_SHOW_DAMAGE];
1326 // Wenn != 0 (d.h. Treffermeldungen grundsaetzlich erwuenscht), aber kein
1327 // Array, hier im Living fragen.
1328 if (dam_msg && !pointerp(dam_msg))
1329 dam_msg = QueryProp(P_DAMAGE_MSG);
1330
1331 // In den meisten Faellen soll auch eine Schadensmeldung ausgegeben
1332 // werden, die die Hoehe des Schadens (grob) anzeigt
1333 if (spell[SP_SHOW_DAMAGE] && !pointerp(dam_msg)) {
1334 myname=name(WEN);
1335 enname=enemy->Name(WER);
1336 if (enemy->QueryProp(P_PLURAL)) {
1337 switch (dam) {
1338 case 0:
1339 tell_object(enemy,sprintf(" Ihr verfehlt %s.\n",myname));
1340 tell_object(this_object(),sprintf(" %s verfehlen Dich.\n",enname));
1341 tell_room(environment(enemy)||environment(this_object()),
1342 sprintf(" %s verfehlen %s.\n",enname,myname),
1343 ({ enemy, this_object() }));
1344 break;
1345 case 1:
1346 tell_object(enemy,sprintf(" Ihr kitzelt %s am Bauch.\n",myname));
1347 tell_object(this_object(),
1348 sprintf(" %s kitzeln Dich am Bauch.\n",enname));
1349 tell_room(environment(enemy)||environment(this_object()),
1350 sprintf(" %s kitzeln %s am Bauch.\n",enname,myname),
1351 ({ enemy, this_object() }));
1352 break;
1353 case 2..3:
1354 tell_object(enemy,sprintf(" Ihr kratzt %s.\n",myname));
1355 tell_object(this_object(),sprintf(" %s kratzen Dich.\n",enname));
1356 tell_room(environment(enemy)||environment(this_object()),
1357 sprintf(" %s kratzen %s.\n",enname,myname),
1358 ({ enemy, this_object() }));
1359 break;
1360 case 4..5:
1361 tell_object(enemy,sprintf(" Ihr trefft %s.\n",myname));
1362 tell_object(this_object(),sprintf(" %s treffen Dich.\n",enname));
1363 tell_room(environment(enemy)||environment(this_object()),
1364 sprintf(" %s treffen %s.\n",enname,myname),
1365 ({ enemy, this_object() }));
1366 break;
1367 case 6..10:
1368 tell_object(enemy,sprintf(" Ihr trefft %s hart.\n",myname));
1369 tell_object(this_object(),sprintf(" %s treffen Dich hart.\n",enname));
1370 tell_room(environment(enemy)||environment(this_object()),
1371 sprintf(" %s treffen %s hart.\n",enname,myname),
1372 ({ enemy, this_object() }));
1373 break;
1374 case 11..20:
1375 tell_object(enemy,sprintf(" Ihr trefft %s sehr hart.\n",myname));
1376 tell_object(this_object(),
1377 sprintf(" %s treffen Dich sehr hart.\n",enname));
1378 tell_room(environment(enemy)||environment(this_object()),
1379 sprintf(" %s treffen %s sehr hart.\n",enname,myname),
1380 ({ enemy, this_object() }));
1381 break;
1382 case 21..30:
1383 tell_object(enemy,
1384 sprintf(" Ihr schlagt %s mit dem Krachen brechender Knochen.\n",
1385 myname));
1386 tell_object(this_object(),
1387 sprintf(" %s schlagen Dich mit dem Krachen brechender Knochen.\n",
1388 enname));
1389 tell_room(environment(enemy)||environment(this_object()),
1390 sprintf(" %s schlagen %s mit dem Krachen brechender Knochen.\n",
1391 enname,myname), ({ enemy, this_object() }));
1392 break;
1393 case 31..50:
1394 tell_object(enemy,
1395 sprintf(" Ihr zerschmettert %s in kleine Stueckchen.\n",myname));
1396 tell_object(this_object(),
1397 sprintf(" %s zerschmettern Dich in kleine Stueckchen.\n",
1398 enname));
1399 tell_room(environment(enemy)||environment(this_object()),
1400 sprintf(" %s zerschmettern %s in kleine Stueckchen.\n",
1401 enname,myname), ({ enemy, this_object() }));
1402 break;
1403 case 51..75:
1404 tell_object(enemy,sprintf(" Ihr schlagt %s zu Brei.\n",myname));
1405 tell_object(this_object(),
1406 sprintf(" %s schlagen Dich zu Brei.\n",enname));
1407 tell_room(environment(enemy)||environment(this_object()),
1408 sprintf(" %s schlagen %s zu Brei.\n",enname,myname),
1409 ({ enemy, this_object() }));
1410 break;
1411 case 76..100:
1412 tell_object(enemy,sprintf(" Ihr pulverisiert %s.\n",myname));
1413 tell_object(this_object(),sprintf(" %s pulverisieren Dich.\n",enname));
1414 tell_room(environment(enemy)||environment(this_object()),
1415 sprintf(" %s pulverisieren %s.\n",enname,myname),
1416 ({ enemy, this_object() }));
1417 break;
1418 case 101..150:
1419 tell_object(enemy,sprintf(" Ihr zerstaeubt %s.\n",myname));
1420 tell_object(this_object(),sprintf(" %s zerstaeuben Dich.\n",enname));
1421 tell_room(environment(enemy)||environment(this_object()),
1422 sprintf(" %s zerstaeuben %s.\n",enname,myname),
1423 ({ enemy, this_object() }));
1424 break;
1425 case 151..200:
1426 tell_object(enemy,sprintf(" Ihr atomisiert %s.\n",myname));
1427 tell_object(this_object(),sprintf(" %s atomisieren Dich.\n",enname));
1428 tell_room(environment(enemy)||environment(this_object()),
1429 sprintf(" %s atomisieren %s.\n",enname,myname),
1430 ({ enemy, this_object() }));
1431 break;
1432 default:
1433 tell_object(enemy,sprintf(" Ihr vernichtet %s.\n",myname));
1434 tell_object(this_object(),sprintf(" %s vernichten Dich.\n",enname));
1435 tell_room(environment(enemy)||environment(this_object()),
1436 sprintf(" %s vernichten %s.\n",enname,myname),
1437 ({ enemy, this_object() }));
1438 break;
1439 }
1440
1441 }
1442 else {
1443 switch (dam) {
1444 case 0:
1445 tell_object(enemy,sprintf(" Du verfehlst %s.\n",myname));
1446 tell_object(this_object(),sprintf(" %s verfehlt Dich.\n",enname));
1447 tell_room(environment(enemy)||environment(this_object()),
1448 sprintf(" %s verfehlt %s.\n",enname,myname),
1449 ({ enemy, this_object() }));
1450 break;
1451 case 1:
1452 tell_object(enemy,sprintf(" Du kitzelst %s am Bauch.\n",myname));
1453 tell_object(this_object(),
1454 sprintf(" %s kitzelt Dich am Bauch.\n",enname));
1455 tell_room(environment(enemy)||environment(this_object()),
1456 sprintf(" %s kitzelt %s am Bauch.\n",enname,myname),
1457 ({ enemy, this_object() }));
1458 break;
1459 case 2..3:
1460 tell_object(enemy,sprintf(" Du kratzt %s.\n",myname));
1461 tell_object(this_object(),sprintf(" %s kratzt Dich.\n",enname));
1462 tell_room(environment(enemy)||environment(this_object()),
1463 sprintf(" %s kratzt %s.\n",enname,myname),
1464 ({ enemy, this_object() }));
1465 break;
1466 case 4..5:
1467 tell_object(enemy,sprintf(" Du triffst %s.\n",myname));
1468 tell_object(this_object(),sprintf(" %s trifft Dich.\n",enname));
1469 tell_room(environment(enemy)||environment(this_object()),
1470 sprintf(" %s trifft %s.\n",enname,myname),
1471 ({ enemy, this_object() }));
1472 break;
1473 case 6..10:
1474 tell_object(enemy,sprintf(" Du triffst %s hart.\n",myname));
1475 tell_object(this_object(),sprintf(" %s trifft Dich hart.\n",enname));
1476 tell_room(environment(enemy)||environment(this_object()),
1477 sprintf(" %s trifft %s hart.\n",enname,myname),
1478 ({ enemy, this_object() }));
1479 break;
1480 case 11..20:
1481 tell_object(enemy,sprintf(" Du triffst %s sehr hart.\n",myname));
1482 tell_object(this_object(),
1483 sprintf(" %s trifft Dich sehr hart.\n",enname));
1484 tell_room(environment(enemy)||environment(this_object()),
1485 sprintf(" %s trifft %s sehr hart.\n",enname,myname),
1486 ({ enemy, this_object() }));
1487 break;
1488 case 21..30:
1489 tell_object(enemy,
1490 sprintf(" Du schlaegst %s mit dem Krachen brechender Knochen.\n",
1491 myname));
1492 tell_object(this_object(),
1493 sprintf(" %s schlaegt Dich mit dem Krachen brechender Knochen.\n",
1494 enname));
1495 tell_room(environment(enemy)||environment(this_object()),
1496 sprintf(" %s schlaegt %s mit dem Krachen brechender Knochen.\n",
1497 enname,myname), ({ enemy, this_object() }));
1498 break;
1499 case 31..50:
1500 tell_object(enemy,
1501 sprintf(" Du zerschmetterst %s in kleine Stueckchen.\n",myname));
1502 tell_object(this_object(),
1503 sprintf(" %s zerschmettert Dich in kleine Stueckchen.\n",enname));
1504 tell_room(environment(enemy)||environment(this_object()),
1505 sprintf(" %s zerschmettert %s in kleine Stueckchen.\n",
1506 enname,myname), ({ enemy, this_object() }));
1507 break;
1508 case 51..75:
1509 tell_object(enemy,sprintf(" Du schlaegst %s zu Brei.\n",myname));
1510 tell_object(this_object(),
1511 sprintf(" %s schlaegt Dich zu Brei.\n",enname));
1512 tell_room(environment(enemy)||environment(this_object()),
1513 sprintf(" %s schlaegt %s zu Brei.\n",enname,myname),
1514 ({ enemy, this_object() }));
1515 break;
1516 case 76..100:
1517 tell_object(enemy,sprintf(" Du pulverisierst %s.\n",myname));
1518 tell_object(this_object(),sprintf(" %s pulverisiert Dich.\n",enname));
1519 tell_room(environment(enemy)||environment(this_object()),
1520 sprintf(" %s pulverisiert %s.\n",enname,myname),
1521 ({ enemy, this_object() }));
1522 break;
1523 case 101..150:
1524 tell_object(enemy,sprintf(" Du zerstaeubst %s.\n",myname));
1525 tell_object(this_object(),sprintf(" %s zerstaeubt Dich.\n",enname));
1526 tell_room(environment(enemy)||environment(this_object()),
1527 sprintf(" %s zerstaeubt %s.\n",enname,myname),
1528 ({ enemy, this_object() }));
1529 break;
1530 case 151..200:
1531 tell_object(enemy,sprintf(" Du atomisierst %s.\n",myname));
1532 tell_object(this_object(),sprintf(" %s atomisiert Dich.\n",enname));
1533 tell_room(environment(enemy)||environment(this_object()),
1534 sprintf(" %s atomisiert %s.\n",enname,myname),
1535 ({ enemy, this_object() }));
1536 break;
1537 default:
1538 tell_object(enemy,sprintf(" Du vernichtest %s.\n",myname));
1539 tell_object(this_object(),sprintf(" %s vernichtet Dich.\n",enname));
1540 tell_room(environment(enemy)||environment(this_object()),
1541 sprintf(" %s vernichtet %s.\n",enname,myname),
1542 ({ enemy, this_object() }));
1543 break;
1544 }
1545 }
1546 }
1547
1548 // Man kann auch selbst-definierte Schadensmeldungen ausgeben lassen
1549 else if( spell[SP_SHOW_DAMAGE] && pointerp(dam_msg) )
1550 {
1551 for( i=sizeof(dam_msg) ; --i >= 0 ; )
1552 {
1553 if ( dam>dam_msg[i][0] )
1554 {
1555 tell_object(ME,mess(dam_msg[i][1],ME,enemy));
1556 tell_object(enemy,mess(dam_msg[i][2],ME,enemy));
1557 say(mess(dam_msg[i][3],ME,enemy), enemy);
1558 break;
1559 }
1560 }
1561 }
1562 // else (!spell[SP_SHOW_DAMAGE]) keine Schadensmeldung.
1563
1564 // Informationen ueber den letzten Angriff festhalten
1565 Set(P_LAST_DAMTYPES, dam_type);
1566 Set(P_LAST_DAMTIME, time());
1567 Set(P_LAST_DAMAGE, dam);
1568
1569 // Bei Angriffen mit SP_NO_ENEMY-Flag kann man nicht sterben ...
1570 if ( spell[SP_NO_ENEMY] )
1571 reduce_hit_points(dam);
1572 // ... bei allen anderen natuerlich schon
1573 else
1574 do_damage(dam,enemy);
1575
1576 // evtl. ist dies Objekt hier tot...
1577 if (!objectp(ME)) return dam;
1578
1579 // Testen, ob man in die Fucht geschlagen wird
1580 CheckWimpyAndFlee();
1581
1582 // Verursachten Schaden (in LP) zurueckgeben
1583 return dam;
1584}
1585
1586public float CheckResistance(string *dam_type) {
1587 //funktion kriegt die schadensarten uebergeben, schaut sich dieses
1588 //sowie P_RESISTENCE_STRENGTH und P_RESITENCE_MODFIFIER an und berechnet
1589 //einen Faktor, mit dem die urspruengliche Schadensmenge multipliziert
1590 //wird, um den Schaden nach Beruecksichtigung der Resis/Anfaelligkeiten zu
1591 //erhalten. Rueckgabewert normalerweise (s.u.) >=0.
1592 mapping rstren, mod;
1593 float faktor,n;
1594 int i;
1595
1596 mod = Query(P_RESISTANCE_MODIFIER);
1597 if ( mappingp(mod) )
1598 mod = mod["me"];
1599
1600 if ( !mappingp(rstren=Query(P_RESISTANCE_STRENGTHS)) )
1601 {
1602 if (!mappingp(mod))
1603 return 1.0;
1604 else
1605 rstren = ([]);
1606 }
1607
1608 if ( !mappingp(mod) )
1609 mod = ([]);
1610
1611 if ( (i=sizeof(dam_type))<1 )
1612 return 1.0;
1613
1614 n=to_float(i);
1615 faktor=0.0;
1616
1617 //dies hier tut nicht mehr das gewuenschte, wenn in P_RESISTENCE_STRENGTHS
1618 //Faktoren <-1.0 angegeben werden. Rueckgabewerte <0 sind dann moeglich und
1619 //leider werden die Schadensarten ohne Resis nicht mehr richtig verwurstet. :-/
1620 foreach(string dt: dam_type) {
1621 faktor = faktor + (1.0 + to_float(rstren[dt]))
1622 * (1.0 + to_float(mod[dt]))-1.0;
1623 }
1624 return 1.0+(faktor/n);
1625}
1626
1627public varargs mapping StopHuntingMode(int silent)
1628{ mapping save_enemy;
1629 int i;
1630
1631 save_enemy=enemies;
1632 if ( !silent )
1633 walk_mapping(enemies, #'StopHuntText); //#');
1634
1635 enemies=m_allocate(0,1);
1636 last_attack_msg=0;
1637
1638 return save_enemy;
1639}
1640
1641public <object*|int*>* QueryEnemies()
1642{
1643 return ({m_indices(enemies),m_values(enemies)});
1644}
1645
1646public mapping GetEnemies()
1647{
1648 return enemies;
1649}
1650
1651public mapping SetEnemies(<object*|int*>* myenemies)
1652{
1653 enemies=mkmapping(myenemies[0],myenemies[1]);
1654 return enemies;
1655}
1656
1657private string _kill_alias( string str )
1658{
1659 return "\\" + str;
1660}
1661
1662public varargs void Flee( object oldenv, int force )
1663{ mixed *exits, exit, dex;
1664 mapping tmp;
1665 int i;
1666 object env;
1667
1668 if ( !environment() )
1669 return;
1670
1671 // mit 'force' kann man die Checks umgehen, damit der Spieler auf jeden
1672 // Fall fluechtet ...
1673 if ( !force && ( oldenv && (oldenv != environment()) ||
1674 query_once_interactive(ME) && (!EnemyPresent() ||
1675 (QueryProp(P_HP) >= QueryProp(P_WIMPY))) ) )
1676 return;
1677
1678 // ... aber Magier fluechten zu lassen ist nicht ganz so einfach ;-)
1679 if ( query_once_interactive(this_object()) && IS_LEARNING(this_object()) )
1680 return;
1681
1682 // Geister brauchen nicht um ihr Leben zu fuerchten
1683 if ( QueryProp(P_GHOST) )
1684 return;
1685
1686 tell_object( ME, "Die Angst ist staerker als Du ... "+
1687 "Du willst nur noch weg hier.\n");
1688
1689 if ( TeamFlee() ) // Erfolgreiche Flucht in hintere Kampfreihe?
1690 return;
1691
1692 env = environment();
1693 tmp = environment()->QueryProp(P_EXITS);
1694 exits = m_indices(tmp);
1695 tmp = environment()->QueryProp(P_SPECIAL_EXITS);
1696 exits += m_indices(tmp);
1697
1698 if ( query_once_interactive(ME) )
1699 exits = map( exits, #'_kill_alias/*'*/ );
1700
1701 // die Fluchtrichtung von Magiern wird aus Sicherheitsgruenden
1702 // nicht ausgewertet
1703 if ( interactive(ME) && IS_SEER(ME) && !IS_LEARNER(ME)
1704 && (dex=QueryProp(P_WIMPY_DIRECTION)) )
1705 {
1706 i = 60 + 4 * (QueryProp(P_LEVEL) - 30) / 3;
1707 exits += ({dex}); // bevorzugte Fluchtrichtung mindestens einmal
1708 }
1709
1710 if ( !sizeof(exits) )
1711 {
1712 tell_object( ME, "Du versuchst zu fliehen, schaffst es aber nicht.\n" );
1713
1714 return;
1715 }
1716
1717 while ( sizeof(exits) && (environment()==env) )
1718 {
1719 if ( dex // Vorzugsweise Fluchtrichtung?
1720 && (member(exits,dex) >= 0) // moeglich?
1721 && (random(100) <= i)) // und Wahrscheinlichkeit gross genug?
1722 exit = dex;
1723 else
1724 exit = exits[random(sizeof(exits))];
1725
1726 catch(command(exit);publish);
1727 exits -= ({exit});
1728 }
1729
1730 if ( environment()==env )
1731 tell_object( ME, "Dein Fluchtversuch ist gescheitert.\n" );
1732}
1733
1734public object EnemyPresent()
1735{
1736 foreach(object en: enemies) {
1737 if (environment()==environment(en))
1738 return en;
1739 }
1740 return 0;
1741}
1742
1743public object InFight()
1744{
1745 return EnemyPresent();
1746}
1747
1748public varargs int StopHuntID(string str, int silent) {
1749
1750 if ( !stringp(str) )
1751 return 0;
1752
1753 int j;
1754 foreach(object en: enemies) {
1755 if (en->id(str)) {
1756 StopHuntFor(en,silent);
1757 j++;
1758 }
1759 }
1760
1761 return j;
1762}
1763
1764public int SpellDefend(object caster, mapping sinfo)
1765{ int re;
1766 mixed res;
1767 string *ind;
1768
1769 re = UseSkill(SK_SPELL_DEFEND,([ SI_SKILLARG : sinfo ,
1770 SI_ENEMY : caster ]) );
1771
1772 if ( (res=QueryProp(P_MAGIC_RESISTANCE_OFFSET)) && mappingp(res)
1773 && pointerp(sinfo[SI_MAGIC_TYPE]))
1774 {
1775 ind = m_indices(res) & sinfo[SI_MAGIC_TYPE];
1776
1777 if (pointerp(ind) && sizeof(ind) ) {
1778 foreach(string index : ind)
1779 re+=res[index];
1780 }
1781 }
1782 else if(res && intp(res))
1783 re+=res;
1784
1785 if ( (re>3333) && query_once_interactive(this_object()) )
1786 re=3333; /* Maximal 33% Abwehrchance bei Spielern */
1787 return re;
1788}
1789
1790// **** this is property-like
1791
1792static int _set_attack_busy(mixed val)
1793{
1794 if ( ((to_int(val))>5) && previous_object(1)
1795 && query_once_interactive(previous_object(1)) )
1796 log_file("ATTACKBUSY",sprintf("%s %d Taeter: %O Opfer: %O\n",
1797 dtime(time()),to_int(val),previous_object(1),this_object()));
1798
1799 attack_busy-=to_int(val*100.0);
1800
1801 if ( attack_busy<-2000 )
1802 attack_busy=-2000;
1803
1804 return attack_busy;
1805}
1806
1807static int _query_attack_busy()
1808{
1809 if (IS_LEARNING(ME))
1810 return 0;
1811
1812 return (attack_busy<100);
1813}
1814
1815// **** local property methods
1816static int _set_wimpy(int i)
1817{
1818 if ( !intp(i) || (i>QueryProp(P_MAX_HP)) || (i<0) )
1819 return 0;
1820
1821 // ggf. Statusreport ausgeben
1822 if (interactive(ME))
1823 status_report(DO_REPORT_WIMPY, i);
1824
1825 return Set(P_WIMPY, i);
1826}
1827
1828static string _set_wimpy_dir(string s) {
1829 // ggf. Statusreport ausgeben
1830 if (interactive(ME))
1831 status_report(DO_REPORT_WIMPY_DIR, s);
1832 return Set(P_WIMPY_DIRECTION, s, F_VALUE);
1833}
1834
1835static mixed _set_hands(mixed h)
1836{
1837 if ( sizeof(h)==2 )
1838 h += ({ ({DT_BLUDGEON}) });
1839 if (!pointerp(h[2]))
1840 h[2] = ({h[2]});
1841 return Set(P_HANDS, h, F_VALUE);
1842}
1843
1844//TODO normalisieren/korrigieren in updates_after_restore().
1845static mixed _query_hands()
1846{
1847 mixed *hands = Query(P_HANDS);
1848 if ( !hands )
1849 return Set(P_HANDS, ({ " mit blossen Haenden", 30, ({DT_BLUDGEON})}));
1850 else if ( sizeof(hands)<3 )
1851 return Set(P_HANDS, ({hands[0], hands[1], ({DT_BLUDGEON})}));
1852 else if ( !pointerp(hands[2]) )
1853 return Set(P_HANDS, ({hands[0], hands[1], ({ hands[2] })}));
1854
1855 return Query(P_HANDS);
1856}
1857
1858static int _query_total_wc()
1859{ mixed res;
1860 int totwc;
1861
1862 if ( objectp(res=QueryProp(P_WEAPON)) )
Zesstrac4052902019-12-29 16:45:21 +01001863 totwc = res->QueryProp(P_WC);
MG Mud User88f12472016-06-24 23:31:02 +02001864 else if ( pointerp(res=QueryProp(P_HANDS)) && sizeof(res)>1
1865 && intp(res[1]) )
Zesstrac4052902019-12-29 16:45:21 +01001866 totwc=res[1];
MG Mud User88f12472016-06-24 23:31:02 +02001867 else
1868 totwc=30;
1869
1870 totwc = ((2*totwc)+(10*QueryAttribute(A_STR)))/3;
1871
1872 return Set(P_TOTAL_WC, totwc, F_VALUE);
1873}
1874
1875static int _query_total_ac() {
1876
1877 int totac = 0;
1878 object *armours = QueryProp(P_ARMOURS);
1879 object parry = QueryProp(P_PARRY_WEAPON);
1880
1881 if ( member(armours,0)>=0 ) {
1882 armours -= ({ 0 });
1883 }
1884
1885 foreach(object armour: armours)
Zesstrac4052902019-12-29 16:45:21 +01001886 totac += armour->QueryProp(P_AC);
MG Mud User88f12472016-06-24 23:31:02 +02001887
1888 if ( objectp(parry) )
1889 totac += parry->QueryProp(P_AC);
1890
1891 totac += (QueryProp(P_BODY)+QueryAttribute(A_DEX));
1892
1893 return Set(P_TOTAL_AC, totac, F_VALUE);
1894}
1895
1896static mapping _query_resistance_strengths() {
1897
1898 UpdateResistanceStrengths();
1899
1900 mapping rstren = copy(Query(P_RESISTANCE_STRENGTHS, F_VALUE));
1901 mapping mod = Query(P_RESISTANCE_MODIFIER, F_VALUE);
1902
1903 if ( !mappingp(rstren) )
1904 rstren = ([]);
1905
1906 if ( !mappingp(mod) || !mappingp(mod=mod["me"]) || !sizeof(mod) )
1907 return rstren;
1908
1909 foreach(string modkey, float modvalue : mod)
1910 rstren[modkey] = ((1.0+rstren[modkey])*(1.0+modvalue))-1.0;
1911
1912 return rstren;
1913}
1914
1915static int _set_disable_attack(int val)
1916{
1917 if (val<-100000)
1918 {
1919 log_file("PARALYSE_TOO_LOW",
1920 sprintf("Wert zu klein: %s, Wert: %d, "
1921 "Aufrufer: %O, Opfer: %O",
1922 ctime(time()),
1923 val,previous_object(1),
1924 this_object()));
1925 }
1926 if ( val>30 )
1927 val=30;
1928 if ( (val>=20) && previous_object(1)!=ME && query_once_interactive(ME) )
1929 log_file("PARALYSE",sprintf("%s %d Taeter: %O Opfer: %O\n",
1930 ctime(time())[4..15],
1931 val,previous_object(1),this_object()));
1932
1933 if (time()<QueryProp(P_NEXT_DISABLE_ATTACK))
1934 {
1935 // gueltige Zeitsperre existiert.
1936 // Erhoehen von P_DISABLE_ATTACK geht dann nicht. (Ja, auch nicht erhoehen
1937 // eines negativen P_DISABLE_ATTACK)
1938 if (val >= QueryProp(P_DISABLE_ATTACK))
1939 return DISABLE_TOO_EARLY;
1940 // val ist kleiner als aktuelles P_DISABLE_ATTACK - das ist erlaubt, ABER es
1941 // darf die bestehende Zeitsperre nicht verringern, daher wird sie nicht
1942 // geaendert.
1943 return Set(P_DISABLE_ATTACK,val,F_VALUE);
1944 }
1945 // es existiert keine gueltige Zeitsperre - dann wird sie nun gesetzt.
1946 // (Sollte val < 0 sein, wird eine Zeitsperre in der Vergangenheit gesetzt,
1947 // die schon abgelaufen ist. Das ist ueberfluessig, schadet aber auch
1948 // nicht.)
1949 SetProp(P_NEXT_DISABLE_ATTACK,time()+val*2*__HEART_BEAT_INTERVAL__);
1950 return Set(P_DISABLE_ATTACK,val);
1951}
1952
1953// Neue Verwaltung der Haende:
1954
1955// P_HANDS_USED_BY enhaelt ein Array mit allen Objekten, die Haende
1956// belegen, jedes kommt so oft vor wie Haende belegt werden.
1957
1958static mixed *_query_hands_used_by()
1959{
1960 return ((Query(P_HANDS_USED_BY, F_VALUE) || ({}) ) - ({0}));
1961}
1962
1963static int _query_used_hands()
1964{
1965 return sizeof(QueryProp(P_HANDS_USED_BY));
1966}
1967
1968static int _query_free_hands()
1969{
1970 return (QueryProp(P_MAX_HANDS)-QueryProp(P_USED_HANDS));
1971}
1972
1973public varargs int UseHands(object ob, int num)
1974{ mixed *h;
1975
1976 if ( !ob && !(ob=previous_object(1)) )
1977 return 0;
1978
1979 if ( (num<=0) && ((num=ob->QueryProp(P_HANDS))<=0) )
1980 return 0;
1981
1982 h=QueryProp(P_HANDS_USED_BY)-({ob});
1983
1984 if ( (sizeof(h)+num)>QueryProp(P_MAX_HANDS) )
1985 return 0;
1986
1987 foreach(int i: num)
1988 h+=({ob});
1989
1990 SetProp(P_HANDS_USED_BY,h);
1991
1992 return 1;
1993}
1994
1995public varargs int FreeHands(object ob)
1996{
1997 if ( !ob && !(ob=previous_object(1)) )
1998 return 0;
1999
2000 SetProp(P_HANDS_USED_BY,QueryProp(P_HANDS_USED_BY)-({ob}));
2001
2002 return 1;
2003}
2004
2005// Kompatiblitaetsfunktionen:
2006
2007static int _set_used_hands(int new_num)
2008{ int old_num, dif;
2009 object ob;
2010
2011 old_num=QueryProp(P_USED_HANDS);
2012
2013 if ( !objectp(ob=previous_object(1)) )
2014 return old_num;
2015
2016 // Meldung ins Debuglog schreiben. Aufrufer sollte gefixt werden.
2017 debug_message(sprintf("P_USED_HANDS in %O wird gesetzt durch %O\n",
2018 this_object(), ob), DMSG_LOGFILE|DMSG_STAMP);
2019
2020 if ( !(dif=new_num-old_num) )
2021 return new_num;
2022
2023 if ( dif>0 )
2024 UseHands(ob,dif);
2025 else
2026 FreeHands(ob);
2027
2028 return QueryProp(P_USED_HANDS);
2029}
2030
2031// Funktionen fuer Begruessungsschlag / Nackenschlag:
2032
2033public int CheckEnemy(object ob)
2034{
2035 return (living(ob) && IsEnemy(ob));
2036}
2037
2038public varargs void ExecuteMissingAttacks(object *remove_attackers)
2039{
2040 if ( !pointerp(missing_attacks) )
2041 missing_attacks=({});
2042
2043 if ( pointerp(remove_attackers) )
2044 missing_attacks-=remove_attackers;
2045
2046 foreach(object ob : missing_attacks) {
2047 if ( objectp(ob) && (environment(ob)==environment()) )
2048 ob->Attack2(ME);
2049 }
2050 missing_attacks=({});
2051}
2052
2053public void InitAttack()
2054{ object ob,next;
2055 closure cb;
2056
2057 if ( !living(ME) )
2058 return;
2059
2060 ExecuteMissingAttacks();
2061 //EMA kann das Living zerstoeren oder toeten...
2062 if (!living(ME) || QueryProp(P_GHOST)) return;
2063
2064 if ( objectp(ob=IsTeamMove()) )
2065 cb=symbol_function("InitAttack_Callback",ob);
2066 else
2067 cb=0;
2068
2069 for ( ob=first_inventory(environment()) ; objectp(ob) ; ob=next)
2070 {
2071 next=next_inventory(ob);
2072
2073 if ( !living(ob) )
2074 continue;
2075
2076 if (ob->IsEnemy(ME))
2077 {
2078 // Das ist nicht so sinnlos wie es aussieht. a) werden die Hunttimes
2079 // aktualisiert und b) werden Teammitglieder von mir bei diesem
2080 // InsertEnemy() ggf. erfasst.
2081 ob->InsertEnemy(ME);
2082
2083 if ( closurep(cb) && funcall(cb,ob) ) // Wird ganzes Team gemoved?
2084 missing_attacks += ({ ob }); // Dann erstmal warten bis alle da sind
2085 else
2086 ob->Attack2(ME);
2087
2088 }
2089 else if ( IsEnemy(ob) )
2090 {
2091 // Das ist nicht so sinnlos wie es aussieht. a) werden die Hunttimes
2092 // aktualisiert und b) werden Teammitglieder von ob bei diesem
2093 // InsertEnemy() ggf. erfasst.
2094 InsertEnemy(ob);
2095 Attack2(ob);
2096 }
2097 //Attack2 kann dieses Objekt zerstoeren oder toeten. Wenn ja: abbruch
2098 if ( !living(ME) || QueryProp(P_GHOST)) break;
2099 }
2100}
2101
2102public void ExitAttack()
2103{
2104 if ( !living(ME) )
2105 return;
2106
2107 // Noch nachzuholende Begruessungsschlaege:
2108 ExecuteMissingAttacks();
2109}
2110
2111public object|object*|mapping QueryArmourByType(string type)
2112{
2113 // Rueckgabewert:
2114 // DIE Ruestung vom Typ <type>, falls <type> nicht AT_MISC,
2115 // Array aller AT_MISC-Ruestungen falls <type> AT_MISC (auch leer),
2116 // Mapping mit allen oben genannten Infos, falls <type> Null
2117
2118 object *armours;
2119 string typ2;
2120
2121 // Wenn Cache vorhanden, dann Cache liefern.
2122 if (mappingp(QABTCache)) {
2123 if (type == AT_MISC)
2124 return QABTCache[AT_MISC] - ({0});
2125 else if (type)
2126 return QABTCache[type];
2127 else
2128 return copy(QABTCache);
2129 }
2130
2131 if ( !pointerp(armours=QueryProp(P_ARMOURS)) )
2132 armours=({});
2133
2134 // Cache erzeugen
2135 QABTCache = ([ AT_MISC: ({}) ]);
2136 foreach(object ob: armours - ({0}) ) {
2137 if ( !stringp(typ2=ob->QueryProp(P_ARMOUR_TYPE)) )
2138 continue;
2139 if ( typ2==AT_MISC )
2140 QABTCache[AT_MISC] += ({ob});
2141 else
2142 QABTCache[typ2] = ob;
2143 }
2144 // Und gewuenschtes Datum liefern.
2145 if (type)
2146 return QABTCache[type];
2147 else
2148 return copy(QABTCache);
2149}
2150
2151//TODO: langfristig waers besser, wenn hier nicht jeder per SetProp() drauf
2152//los schreiben wuerde.
2153static object *_set_armours(object *armours) {
2154 if (pointerp(armours)) {
2155 // Cache wegwerfen
2156 QABTCache = 0;
2157 // armours ggf. unifizieren. Ausserdem Kopie reinschreiben.
2158 return Set(P_ARMOURS, m_indices(mkmapping(armours)), F_VALUE);
2159 }
2160 return QueryProp(P_ARMOURS);
2161}
2162
2163/** Reduziert die Befriedezaehler pro Reset im Durchschnitt um 2.5.
2164 Berechnet ganzzahlige durchschnittliche Resets seit dem letzten Expire und
2165 erhoeht die letzte Expirezeit um Resets*__RESET_TIME__ (Standard Intervall
2166 fuer Reset ist momentan 3600s, im Durchschnitt kommen dann 2700 zwischen 2
2167 Resets bei raus). So wird der unganzzahlige Rest beim naechsten Aufruf
2168 beruecksichtigt.
2169 Diese Variante des Expires wurde gewaehlt, um zu vermeiden, combat.c einen
2170 reset() zu geben und in jedem Reset in jedem Lebewesen ein Expire machen zu
2171 muessen, auch wenn nur in sehr wenigen Lebewesen was gemacht werden muss.
2172 @param[in,out] ph Mapping aus P_PEACE_HISTORY. Wird direkt aktualisiert.
2173 @attention Muss ein gueltiges P_PEACE_HISTORY uebergeben bekommen, anderem
2174 Datentyp inkl. 0 wird die Funktion buggen.
2175 */
2176private void _decay_peace_history(mixed ph) {
2177 // Ganzzahlige resets seit letztem Expire ermitteln. (Durchschnitt)
2178 int resets = (time() - ph[0]) / (__RESET_TIME__*75/100);
2179 // auf Zeitstempel draufrechnen, damit der unganzzahlige Rest nicht
2180 // verlorengeht, der wird beim naechsten Expire dann beruecksichtigt.
2181 ph[0] += resets * (__RESET_TIME__ * 75 / 100);
2182 // pro Reset werden im Durchschnitt 2.5 Versuche abgezogen. (Hier werden
2183 // beim Expire mal 2 und mal 3 Versuche pro Reset gerechnet. Aber falls hier
2184 // viele Resets vergangen sind, ist es vermutlich eh egal, weil die Counter
2185 // auf 0 fallen.)
2186 int expire = resets * (random(2) + 2);
2187 // ueber alle Gilden
2188 mapping tmp=ph[1];
2189 foreach(string key, int count: &tmp ) {
2190 count-=expire;
2191 if (count < 0) count = 0;
2192 }
2193}
2194
2195/** Pacify() dient zur Bestimmung, ob sich ein Lebewesen gerade
2196 * befrieden lassen will.
2197 * Berechnet eine Wahrscheinlichkeit nach unten stehender Formel, welche die
2198 * Intelligenz dieses Lebenwesens, die Intelligenz des Casters und die
2199 * bisherige Anzahl erfolgreicher Befriedungsversuche dieser Gilde eingeht.
2200 * Anschliessend wird aus der Wahrscheinlichkeit und random() bestimmt, ob
2201 * dieser Versuch erfolgreich ist.
2202Formel: w = (INT_CASTER + 10 - ANZ*4) / (INT_ME + 10)
2203INT_CASTER: Caster-Intelligenz, INT_ME: Intelligenz dieses Livings
2204ANZ: Anzahl erfolgreicher Befriedungsversuche
2205
2206Annahme: INT_CASTER === 22, alle Wahrscheinlichkeiten * 100
2207
2208INT_ME Erfolgswahrscheinlichkeiten je nach Anzahl erfolgreicher Versuche
2209 1 2 3 4 5 6 7 8
2210 0 280 240 200 160 120 80 40 0
2211 2 233,33 200 166,67 133,33 100 66,67 33,33 0
2212 4 200 171,43 142,86 114,29 85,71 57,14 28,57 0
2213 6 175 150 125 100 75 50 25 0
2214 8 155,56 133,33 111,11 88,89 66,67 44,44 22,22 0
2215 10 140 120 100 80 60 40 20 0
2216 12 127,27 109,09 90,91 72,73 54,55 36,36 18,18 0
2217 14 116,67 100 83,33 66,67 50 33,33 16,67 0
2218 16 107,69 92,31 76,92 61,54 46,15 30,77 15,38 0
2219 18 100 85,71 71,43 57,14 42,86 28,57 14,29 0
2220 20 93,33 80 66,67 53,33 40 26,67 13,33 0
2221 22 87,5 75 62,5 50 37,5 25 12,5 0
2222 24 82,35 70,59 58,82 47,06 35,29 23,53 11,76 0
2223 26 77,78 66,67 55,56 44,44 33,33 22,22 11,11 0
2224 28 73,68 63,16 52,63 42,11 31,58 21,05 10,53 0
2225 30 70 60 50 40 30 20 10 0
2226 32 66,67 57,14 47,62 38,1 28,57 19,05 9,52 0
2227 34 63,64 54,55 45,45 36,36 27,27 18,18 9,09 0
2228 35 62,22 53,33 44,44 35,56 26,67 17,78 8,89 0
2229 36 60,87 52,17 43,48 34,78 26,09 17,39 8,7 0
2230 38 58,33 50 41,67 33,33 25 16,67 8,33 0
2231 40 56 48 40 32 24 16 8 0
2232 42 53,85 46,15 38,46 30,77 23,08 15,38 7,69 0
2233 44 51,85 44,44 37,04 29,63 22,22 14,81 7,41 0
2234 46 50 42,86 35,71 28,57 21,43 14,29 7,14 0
2235 48 48,28 41,38 34,48 27,59 20,69 13,79 6,9 0
2236 50 46,67 40 33,33 26,67 20 13,33 6,67 0
2237 52 45,16 38,71 32,26 25,81 19,35 12,9 6,45 0
2238 54 43,75 37,5 31,25 25 18,75 12,5 6,25 0
2239 56 42,42 36,36 30,3 24,24 18,18 12,12 6,06 0
2240 58 41,18 35,29 29,41 23,53 17,65 11,76 5,88 0
2241 60 40 34,29 28,57 22,86 17,14 11,43 5,71 0
2242 62 38,89 33,33 27,78 22,22 16,67 11,11 5,56 0
2243 64 37,84 32,43 27,03 21,62 16,22 10,81 5,41 0
2244 66 36,84 31,58 26,32 21,05 15,79 10,53 5,26 0
2245 68 35,9 30,77 25,64 20,51 15,38 10,26 5,13 0
2246 70 35 30 25 20 15 10 5 0
2247 72 34,15 29,27 24,39 19,51 14,63 9,76 4,88 0
2248 74 33,33 28,57 23,81 19,05 14,29 9,52 4,76 0
2249 76 32,56 27,91 23,26 18,6 13,95 9,3 4,65 0
2250 78 31,82 27,27 22,73 18,18 13,64 9,09 4,55 0
2251 80 31,11 26,67 22,22 17,78 13,33 8,89 4,44 0
2252 82 30,43 26,09 21,74 17,39 13,04 8,7 4,35 0
2253 84 29,79 25,53 21,28 17,02 12,77 8,51 4,26 0
2254 86 29,17 25 20,83 16,67 12,5 8,33 4,17 0
2255 88 28,57 24,49 20,41 16,33 12,24 8,16 4,08 0
2256 90 28 24 20 16 12 8 4 0
2257 92 27,45 23,53 19,61 15,69 11,76 7,84 3,92 0
2258 94 26,92 23,08 19,23 15,38 11,54 7,69 3,85 0
2259 96 26,42 22,64 18,87 15,09 11,32 7,55 3,77 0
2260 98 25,93 22,22 18,52 14,81 11,11 7,41 3,7 0
2261 100 25,45 21,82 18,18 14,55 10,91 7,27 3,64 0
2262 * @return 1, falls Befrieden erlaubt ist, 0 sonst.
2263 * @param[in] caster Derjenige, der den Spruch ausfuehrt.
2264 * @attention Wenn acify() 1 zurueckliefert, zaehlt dies als
2265 * erfolgreicher Befriedungsversuch und in diesem Lebewesen wurde
2266 * StopHuntingMode(1) aufgerufen.
2267*/
2268public int Pacify(object caster) {
2269
2270 // wenn das Viech keine Gegner hat, dann witzlos. ;-) Ohne Caster gehts auch
2271 // direkt raus.
2272 if (!mappingp(enemies) || !sizeof(enemies)
2273 || !objectp(caster)) {
2274 return 0;
2275 }
2276
2277 // Wenn P_ACCEPT_PEACE gesetzt ist, altes Verhalten wiederspiegeln
2278 // -> der NPC ist einfach immer befriedbar. Gleiches gilt fuer den Caster
2279 // selber, der wird sich ja nicht gegen das eigene Befriede wehren. Und auch
2280 // im team wehrt man sich nicht gegen das Befriede eines Teamkollegen
2281 if (QueryProp(P_ACCEPT_PEACE)==1 || caster==ME
2282 || member(TeamMembers(), caster) > -1) {
2283 StopHuntingMode(1); // Caster/Gilde sollte eigene Meldung ausgeben
2284 return 1;
2285 }
2286
2287 string gilde = caster->QueryProp(P_GUILD) || "ANY";
2288
2289 // ggf. P_PEACE_HISTORY initialisieren
Zesstrac4052902019-12-29 16:45:21 +01002290 <int|mapping>* ph = QueryProp(P_PEACE_HISTORY);
MG Mud User88f12472016-06-24 23:31:02 +02002291 if (!pointerp(ph))
2292 SetProp(P_PEACE_HISTORY, ph=({time(), ([]) }) );
2293
2294 // ggf. die Zaehler reduzieren.
2295 if ( ph[0] + __RESET_TIME__ * 75/100 < time()) {
2296 _decay_peace_history(&ph);
2297 }
2298
2299 float w = (caster->QueryAttribute(A_INT) + 10 - ph[1][gilde] * 4.0) /
2300 (QueryAttribute(A_INT) + 10);
2301 // auf [0,1] begrenzen.
2302 if (w<0) w=0.0;
2303 else if (w>1) w=1.0;
2304 // w * n ist eine Zahl zwischen 0 und n, wenn w * n > random(n)+1,
2305 // darf befriedet werden. Da das Random fuer grosse Zahlen
2306 // besser verteilt ist, nehm ich n = __INT_MAX__ und vernachlaessige
2307 // ausserdem die +1 beim random().
2308 if (ceil(w * __INT_MAX__) > random(__INT_MAX__) ) {
2309 ph[1][gilde]++;
2310 StopHuntingMode(1);
2311 return 1;
2312 }
2313 // ein SetProp(P_PEACE_HISTORY) kann entfallen, da das Mapping direkt
2314 // geaendert wird. Sollte die Prop allerdings mal ne Querymethode haben,
2315 // welche eine Kopie davon liefert, muss das hier geaendert oder die
2316 // Prop per Query() abgefragt werden.
2317 return 0;
2318}
2319