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