blob: 2cab731531dbb3609b014b33adcc91f1e8a3b4f1 [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)
860{ object *defs;
861
862 if ( !objectp(friend) )
863 return;
864
865 if ( !pointerp(defs=QueryProp(P_DEFENDERS)) )
866 defs=({});
867
868 if ( member(defs,friend)>=0 )
869 return;
870
871 defs+=({friend});
872 SetProp(P_DEFENDERS,defs);
873}
874
875public void RemoveDefender(object friend)
876{ object *defs;
877
878 if ( !objectp(friend) )
879 return;
880
881 if ( !pointerp(defs=QueryProp(P_DEFENDERS)) )
882 defs=({});
883
884 if ( member(defs,friend)==-1 )
885 return;
886
887 defs -= ({friend});
888 SetProp(P_DEFENDERS,defs);
889}
890
891public void InformDefend(object enemy)
892{
893 UseSkill(SK_INFORM_DEFEND,([ SI_ENEMY : enemy,
894 SI_FRIEND : previous_object() ]));
895 // Oh, oh - ich hoffe mal, dass InformDefend wirklich NUR aus Defend
896 // eines befreundeten livings aufgerufen wird... (Silvana)
897 // This is only experimental... ;)
898}
899
900public <int|string*|mapping>* DefendOther(int dam, string|string* dam_type,
901 int|mapping spell, object enemy)
902{
903 <int|string*|mapping>* res;
904
905 if ( (res=UseSkill(SK_DEFEND_OTHER,([ SI_SKILLDAMAGE : dam,
906 SI_SKILLDAMAGE_TYPE : dam_type,
907 SI_SPELL : spell,
908 SI_FRIEND : previous_object(),
909 SI_ENEMY : enemy ])))
910 && pointerp(res) )
911 return res;
912
913 return 0;
914}
915
916public void CheckWimpyAndFlee()
917{
918 if ( (QueryProp(P_WIMPY)>QueryProp(P_HP)) && !TeamFlee()
919 && find_call_out("Flee")<0 )
920 call_out(#'Flee,0,environment());
921}
922
923protected string mess(string msg,object me,object enemy)
924{ closure mname, ename;
Zesstra31e09ab2021-05-10 10:32:05 +0200925 string *parts;
MG Mud User88f12472016-06-24 23:31:02 +0200926 int i;
927
928 mname = symbol_function("name", me);
929 ename = symbol_function("name", enemy);
930
931 parts=regexplode(msg,"@WE[A-Z]*[12]");
932 for ( i=sizeof(parts)-2 ; i>=1 ; i-=2 )
933 {
934 switch(parts[i])
935 {
936 case "@WER1": parts[i]=funcall(mname,WER,1); break;
937 case "@WESSEN1": parts[i]=funcall(mname,WESSEN,1); break;
938 case "@WEM1": parts[i]=funcall(mname,WEM,1); break;
939 case "@WEN1": parts[i]=funcall(mname,WEN,1); break;
940 case "@WER2": parts[i]=funcall(ename,WER,1); break;
941 case "@WESSEN2": parts[i]=funcall(ename,WESSEN,1); break;
942 case "@WEM2": parts[i]=funcall(ename,WEM,1); break;
943 case "@WEN2": parts[i]=funcall(ename,WEN,1); break;
944 default: ;
945 }
946 }
947
948 return break_string(capitalize(implode(parts,"")),78," ",1);
949}
950
951// Fuer evtl. Defend-Aenderungen, ohne dass man gleich das ganze
952// Attack ueberlagern muss:
953protected void InternalModifyDefend(int dam, string* dt, mapping spell, object enemy)
954{
955 return;
956}
957
Zesstrada3ad452019-01-16 22:04:05 +0100958protected nomask void normalize_defend_args(int dam, string|string* dam_type,
959 int|mapping spell, object enemy)
960{
961 // this_player(), wenn kein enemy bekannt...
962 enemy ||= this_player();
963
964 // Schadenstyp ueberpruefen
965 if ( !pointerp(dam_type) )
966 dam_type = ({ dam_type });
967
968 // Und das Spellmapping pruefen, erzeugen, ergaenzen etc.
969 if ( intp(spell) )
970 spell = ([ SP_PHYSICAL_ATTACK : !spell,
971 SP_SHOW_DAMAGE : !spell,
972 SP_REDUCE_ARMOUR : ([ ]),
973 EINFO_DEFEND : ([ORIGINAL_DAM:dam,
974 ORIGINAL_DAMTYPE:dam_type ])
975 ]);
976 else if ( mappingp(spell) )
977 {
978 // testen ob eine erweiterte defendinfo vorhanden ist
979 if(!member(spell,EINFO_DEFEND))
980 {
981 // wenn nicht, koennen wir an den fehlenden Infos wenig machen, aber
982 // zumindest ergaenzen wir es und schreiben die (hier) initialen dam und
983 // dam_type rein.
984 spell[EINFO_DEFEND] = ([ORIGINAL_DAM:dam,
985 ORIGINAL_DAMTYPE:dam_type]);
986 }
987 if ( !mappingp(spell[SP_REDUCE_ARMOUR]) )
988 spell[SP_REDUCE_ARMOUR] = ([]);
989 }
990 else // Illegaler spell-Parameter
991 raise_error(sprintf("Ungueltiger Typ des spell-Arguments: %d\n",
992 get_type_info(spell,0)));
993}
994
MG Mud User88f12472016-06-24 23:31:02 +0200995public int Defend(int dam, string|string* dam_type, int|mapping spell, object enemy)
996{
MG Mud User88f12472016-06-24 23:31:02 +0200997 mixed res,res2;
998 object *armours,tmp;
999 mixed hookData;
1000 mixed hookRes;
Zesstra31e09ab2021-05-10 10:32:05 +02001001 int i;
Zesstrada3ad452019-01-16 22:04:05 +01001002
MG Mud User88f12472016-06-24 23:31:02 +02001003 // string what, how;
1004 string enname, myname;
1005
Zesstrada3ad452019-01-16 22:04:05 +01001006 normalize_defend_args(&dam, &dam_type, &spell, &enemy);
1007
MG Mud User88f12472016-06-24 23:31:02 +02001008 // Testen, ob dieses Lebewesen ueberhaupt angegriffen werden darf
1009 if ( !this_object() || !enemy || QueryProp(P_NO_ATTACK)
1010 || ( query_once_interactive(enemy) && ! interactive(enemy) ) )
1011 return 0;
1012
MG Mud User88f12472016-06-24 23:31:02 +02001013 spell[EINFO_DEFEND][CURRENT_DAMTYPE]=dam_type;
1014 spell[EINFO_DEFEND][CURRENT_DAM]=dam;
1015
1016 // Testen, ob der Angreifer schon als Feind registriert worden ist.
1017 // Wenn nein, registrieren.
1018 if ( !IsEnemy(enemy) && !spell[SP_NO_ENEMY] )
1019 {
1020 spell[EINFO_DEFEND][ENEMY_INSERTED]=1;
1021 InsertEnemy(enemy);
1022 }
1023
1024 // RFR-Taktik abfangen
1025 if ( !QueryProp(P_ENABLE_IN_ATTACK_OUT) )
1026 {
1027 i=time()-(enemy->QueryProp(P_LAST_MOVE));
1028 // Gegner hat sich bewegt, man selbst nicht
1029 if ( (i<3) && (time()-QueryProp(P_LAST_MOVE)>=5) )
1030 {
1031 // Bei Erster Kampfrunde wenige Schaden
1032 dam/=(4-i);
1033 spell[EINFO_DEFEND][RFR_REDUCE]=dam;
1034 spell[EINFO_DEFEND][CURRENT_DAM]=dam;
1035 }
1036 }
1037
1038 // Man kann Verteidiger haben. Diese kommen als erste zum Zuge
1039 if ( res=QueryProp(P_DEFENDERS) )
1040 { object *defs,*defs_here;
1041
1042 defs=({});
1043 defs_here=({});
1044 if ( !pointerp(res) )
1045 res=({res});
1046 // erst alle anwesenden finden.
1047 foreach(object defender: res) {
1048 if ( objectp(defender) && (member(defs,defender)<0) )
1049 {
1050 defs+=({defender});
1051 // Verteidiger muessen im gleichen Raum oder im Living selber
1052 // enthalten sein.
1053 if ( environment(defender) == environment()
1054 || environment(defender) == ME)
1055 {
1056 call_other(defender,"InformDefend",enemy);
1057 if (defender)
1058 defs_here += ({ defender });
1059 }
1060 }
1061 }
1062 //Anwesende Verteidiger eintragen.
1063 spell[EINFO_DEFEND][PRESENT_DEFENDERS]=defs_here;
1064
1065 // P_DEFENDERS auch gleich aktualisieren
1066 if ( sizeof(defs)<1 )
1067 defs=0;
1068 SetProp(P_DEFENDERS,defs);
1069
1070 if ( spell[SP_PHYSICAL_ATTACK] ) {
1071 // Bei physischen Angriffen nur Verteidiger aus Reihe 1
1072 // nehmen (z.B. fuer Rueckendeckung)
1073 foreach(object defender: defs_here) {
1074 if ( (defender->PresentPosition())>1 ) {
1075 defs_here-=({defender});
1076 }
1077 }
1078 }
1079
1080 if ( (i=sizeof(defs_here)) )
1081 {
1082 mixed edefendtmp=({defs_here[random(i)],0,0,0});
1083 res=call_other(edefendtmp[DEF_DEFENDER],"DefendOther",
1084 dam,dam_type,spell,enemy);
1085 if ( pointerp(res) && (sizeof(res)>=3) && intp(res[0])
1086 && pointerp(res[1]))
1087 {
1088 // Helfer koennen den Schaden oder Schadenstyp aendern,
1089 // z.B. Umwandlung von Feuer nach Eis oder so...
1090 dam=res[0];
1091 edefendtmp[DEF_DAM]=dam;
1092 dam_type=res[1];
1093 edefendtmp[DEF_DAMTYPE]=dam_type;
1094
1095 if ( mappingp(res[2]) )
1096 {
1097 spell=res[2];
1098 // teuer, aber geht nicht anders (Rekursion vermeiden)
1099 edefendtmp[DEF_SPELL]=deep_copy(res[2]);
1100 }
1101 spell[EINFO_DEFEND][CURRENT_DAMTYPE]=dam_type;
1102 spell[EINFO_DEFEND][CURRENT_DAM]=dam;
1103 }
1104 spell[EINFO_DEFEND][DEFENDING_DEFENDER]=edefendtmp;
1105 }
1106 } // Ende Defender-Verarbeitung
1107
1108
1109 // Ueber einen P_TMP_DEFEND_HOOK werden z.B. Schutzzauber gehandhabt
1110 spell[EINFO_DEFEND][DEFEND_HOOK]=DI_NOHOOK;
1111 if ( res=QueryProp(P_TMP_DEFEND_HOOK) )
1112 {
1113 if ( pointerp(res) && (sizeof(res)>=3) && intp(res[0]) && (time()<res[0])
1114 && objectp(res[1]) && stringp(res[2]) )
1115 {
1116 if ( !(res=call_other(res[1],res[2],dam,dam_type,spell,enemy)) )
1117 {
1118 // Ein P_TMP_DEFEND_HOOK kann den Schaden vollstaendig abfangen,
1119 // das Defend wird dann hier abgebrochen *SICK* und es wird nur
1120 // noch getestet, ob man in die Flucht geschlagen wurde
1121 spell[EINFO_DEFEND][DEFEND_HOOK]=DI_HOOKINTERRUPT;
1122 CheckWimpyAndFlee();
1123 return 0;
1124 }
1125 else
1126 {
1127 spell[EINFO_DEFEND][DEFEND_HOOK]=DI_HOOK;
1128 if ( pointerp(res) && (sizeof(res)>=3)
1129 && intp(res[0] && pointerp(res[1])) )
1130 {
1131 mixed edefendtmp=({0,0,0});
1132 // Der P_TMP_DEFEND_HOOK kann ebenfalls Schadenshoehe und
1133 // -art sowie die Spell-Infos veraendern
1134 dam=res[0];
1135 edefendtmp[HOOK_DAM]=dam;
1136 dam_type=res[1];
1137 edefendtmp[HOOK_DAMTYPE]=dam_type;
1138
1139 if ( mappingp(res[2]) )
1140 {
1141 spell=res[2];
1142 // Waeh. Teuer. Aber geht nicht anders.
1143 edefendtmp[HOOK_SPELL]=deep_copy(spell);
1144 }
1145 spell[EINFO_DEFEND][DEFEND_HOOK]=edefendtmp;
1146 spell[EINFO_DEFEND][CURRENT_DAMTYPE]=dam_type;
1147 spell[EINFO_DEFEND][CURRENT_DAM]=dam;
1148 }
1149 }
1150 }
1151 else
1152 SetProp(P_TMP_DEFEND_HOOK,0);
1153 } // P_TMP_DEFEND_HOOK
1154
1155 // trigger defend hook
1156 hookData=({dam,dam_type,spell,enemy});
1157 hookRes=HookFlow(H_HOOK_DEFEND,hookData);
1158 if(hookRes && pointerp(hookRes) && sizeof(hookRes)>H_RETDATA){
1159 if(hookRes[H_RETCODE]==H_CANCELLED){
1160 spell[EINFO_DEFEND][DEFEND_HOOK]=DI_HOOKINTERRUPT;
1161 CheckWimpyAndFlee();
1162 return 0;
1163 }
1164 else if(hookRes[H_RETCODE]==H_ALTERED && hookRes[H_RETDATA]){
1165 spell[EINFO_DEFEND][DEFEND_HOOK]=DI_HOOK;
1166 if ( pointerp(hookRes[H_RETDATA]) && (sizeof(hookRes[H_RETDATA])>=3)
1167 && intp(hookRes[H_RETDATA][0] && pointerp(hookRes[H_RETDATA][1])) )
1168 {
1169 mixed edefendtmp=({0,0,0});
1170 // Der P_TMP_DEFEND_HOOK kann ebenfalls Schadenshoehe und
1171 // -art sowie die Spell-Infos veraendern
1172 dam=hookRes[H_RETDATA][0];
1173 edefendtmp[HOOK_DAM]=dam;
1174 dam_type=hookRes[H_RETDATA][1];
1175 edefendtmp[HOOK_DAMTYPE]=dam_type;
1176
1177 if ( mappingp(hookRes[H_RETDATA][2]) )
1178 {
1179 spell=hookRes[H_RETDATA][2];
1180 // Teuer, teuer... :-(
1181 edefendtmp[HOOK_SPELL]=deep_copy(spell);
1182 }
1183 spell[EINFO_DEFEND][DEFEND_HOOK]=edefendtmp;
1184 spell[EINFO_DEFEND][CURRENT_DAMTYPE]=dam_type;
1185 spell[EINFO_DEFEND][CURRENT_DAM]=dam;
1186 }
1187 }
1188 } // Ende Hook-Behandlung
1189
MG Mud User88f12472016-06-24 23:31:02 +02001190 // Es gibt auch Parierwaffen,
1191 if ( objectp(tmp=QueryProp(P_PARRY_WEAPON)) )
1192 {
1193 res2=tmp->QueryDefend(dam_type, spell, enemy);
1194
1195 // Reduzierbare Wirksamkeit der Parierwaffe?
1196 if ( member(spell[SP_REDUCE_ARMOUR],P_PARRY_WEAPON)
1197 && intp(res=spell[SP_REDUCE_ARMOUR][P_PARRY_WEAPON]) && (res>=0) )
1198 {
1199 res2=(res2*res)/100;
1200 }
1201
1202 dam-=res2;
1203 spell[EINFO_DEFEND][CURRENT_DAM]=dam;
1204 }
1205
1206 // Jetzt kommen die Ruestungen des Lebewesens ins Spiel (wenn es denn
1207 // welche traegt)
1208
1209 armours=QueryProp(P_ARMOURS)-({0});
1210 if ( (i=sizeof(armours))>0 ) {
1211 string aty;
1212
1213 tmp=armours[random(i)];
1214
1215 if ( objectp(tmp) )
1216 //Uebergabe des Mappings eh als Pointer/Referenz, daher billig
1217 tmp->TakeFlaw(dam_type,spell[EINFO_DEFEND]);
1218
1219 // pro Ruestung ein Key an Platz reservieren
1220 spell[EINFO_DEFEND][DEFEND_ARMOURS]=m_allocate(i,2);
1221 foreach(object armour : armours) {
1222 if ( objectp(armour) ) {
Zesstrac4052902019-12-29 16:45:21 +01001223 aty=armour->QueryProp(P_ARMOUR_TYPE);
MG Mud User88f12472016-06-24 23:31:02 +02001224
1225 if ( member(spell[SP_REDUCE_ARMOUR],aty)
1226 && intp(res2=spell[SP_REDUCE_ARMOUR][aty]) && (res2>=0) )
1227 dam -= (res2*armour->QueryDefend(dam_type, spell, enemy))/100;
1228 else
Zesstrac4052902019-12-29 16:45:21 +01001229 dam -= armour->QueryDefend(dam_type, spell, enemy);
MG Mud User88f12472016-06-24 23:31:02 +02001230 // Schaden NACH DefendFunc vermerken.
1231 // Schutzwirkung VOR DefendFunc (DEF_ARMOUR_PROT) vermerkt
1232 // das QueryDefend() selber.
1233 spell[EINFO_DEFEND][DEFEND_ARMOURS][armour,DEF_ARMOUR_DAM]=dam;
1234 // akt. Schaden vermerken und Schutz der aktuellen Ruestung (vor
1235 // DefendFunc) wieder auf 0 setzen fuer den naechsten Durchlauf.
1236 spell[EINFO_DEFEND][CURRENT_DAM]=dam;
1237 spell[EINFO_DEFEND][DEFEND_CUR_ARMOUR_PROT]=0;
1238 }
1239 }
1240 }
1241
1242 // Manche Gilden haben einen Verteidigungsskill. Der kommt jetzt zum
1243 // tragen. Der Skill kann die Schadenshoehe und -art beeinflussen.
1244 spell[EINFO_DEFEND]+=([DEFEND_GUILD:({})]);
1245 if ( mappingp(res=UseSkill(SK_MAGIC_DEFENSE,
1246 ([ SI_ENEMY : enemy,
1247 SI_SKILLDAMAGE : dam,
1248 SI_SKILLDAMAGE_TYPE : dam_type,
1249 SI_SPELL : spell ]))) )
1250 {
Zesstrac4052902019-12-29 16:45:21 +01001251 dam=res[SI_SKILLDAMAGE];
MG Mud User88f12472016-06-24 23:31:02 +02001252
1253 if ( pointerp(res[SI_SKILLDAMAGE_TYPE]) )
1254 dam_type=res[SI_SKILLDAMAGE_TYPE];
1255
1256 spell[EINFO_DEFEND][CURRENT_DAMTYPE]=dam_type;
1257 spell[EINFO_DEFEND][CURRENT_DAM]=dam;
1258 spell[EINFO_DEFEND][DEFEND_GUILD]=({dam,dam_type});
1259 }
1260
1261 // Evtl. interne Modifikationen
1262 InternalModifyDefend(&dam, &dam_type, &spell, &enemy);
1263
1264 spell[EINFO_DEFEND][CURRENT_DAMTYPE]=dam_type;
1265 spell[EINFO_DEFEND][CURRENT_DAM]=dam;
1266
1267 // Testen, ob irgendwas im Inventory des Lebewesen auf den Angriff
1268 // "reagiert"
1269 CheckSensitiveAttack(dam,dam_type,spell,enemy);
1270
1271 if ( !objectp(enemy) )
1272 return 0;
1273
1274 // Angriffszeit im Gegner setzen
1275 enemy->SetProp(P_LAST_COMBAT_TIME,time());
1276
1277 // Die Resistenzen des Lebewesens (natuerliche und durch Ruestungen
1278 // gesetzte) beruecksichtigen
1279 dam = to_int(CheckResistance(dam_type)*dam);
1280 spell[EINFO_DEFEND][DEFEND_RESI]=dam;
1281
1282 spell[EINFO_DEFEND][CURRENT_DAM]=dam;
1283
1284 // Bei physikalischen Angriffen wird die natuerliche Panzerung und die
1285 // Geschicklichkeit des Lebewesens beruecksichtigt
1286 object stat = find_object("/d/erzmagier/zesstra/pacstat"); // TODO: remove
1287 if ( spell[SP_PHYSICAL_ATTACK] )
1288 {
Zesstra9033eee2021-05-06 19:57:20 +02001289 // Schutz bestimmen, Minimum 1, aber nur wenn P_BODY > 0
1290 // Um Rundungsverluste zu reduzieren, wird P_BODY anfangs mit 10000
1291 // skaliert. Beim runterskalieren wird aufgerundet (Addition von
1292 // 5000 vor Division).
Zesstra5a142be2021-05-09 12:01:12 +02001293 int body = (QueryProp(P_BODY)+QueryAttribute(A_DEX)) * 10000;
Zesstra9033eee2021-05-06 19:57:20 +02001294 res2 = ((body/4 + random(body*3/4 + 1) + 5000)/10000) || 1;
MG Mud User88f12472016-06-24 23:31:02 +02001295 if (stat)
Zesstra5a142be2021-05-09 12:01:12 +02001296 stat->bodystat(body, res2, (random(body)+1)/10000);
MG Mud User88f12472016-06-24 23:31:02 +02001297
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;
MG Mud User88f12472016-06-24 23:31:02 +02001629
1630 save_enemy=enemies;
1631 if ( !silent )
1632 walk_mapping(enemies, #'StopHuntText); //#');
1633
1634 enemies=m_allocate(0,1);
1635 last_attack_msg=0;
1636
1637 return save_enemy;
1638}
1639
1640public <object*|int*>* QueryEnemies()
1641{
1642 return ({m_indices(enemies),m_values(enemies)});
1643}
1644
1645public mapping GetEnemies()
1646{
1647 return enemies;
1648}
1649
1650public mapping SetEnemies(<object*|int*>* myenemies)
1651{
1652 enemies=mkmapping(myenemies[0],myenemies[1]);
1653 return enemies;
1654}
1655
1656private string _kill_alias( string str )
1657{
1658 return "\\" + str;
1659}
1660
1661public varargs void Flee( object oldenv, int force )
1662{ mixed *exits, exit, dex;
1663 mapping tmp;
1664 int i;
1665 object env;
1666
1667 if ( !environment() )
1668 return;
1669
1670 // mit 'force' kann man die Checks umgehen, damit der Spieler auf jeden
1671 // Fall fluechtet ...
1672 if ( !force && ( oldenv && (oldenv != environment()) ||
1673 query_once_interactive(ME) && (!EnemyPresent() ||
1674 (QueryProp(P_HP) >= QueryProp(P_WIMPY))) ) )
1675 return;
1676
1677 // ... aber Magier fluechten zu lassen ist nicht ganz so einfach ;-)
1678 if ( query_once_interactive(this_object()) && IS_LEARNING(this_object()) )
1679 return;
1680
1681 // Geister brauchen nicht um ihr Leben zu fuerchten
1682 if ( QueryProp(P_GHOST) )
1683 return;
1684
1685 tell_object( ME, "Die Angst ist staerker als Du ... "+
1686 "Du willst nur noch weg hier.\n");
1687
1688 if ( TeamFlee() ) // Erfolgreiche Flucht in hintere Kampfreihe?
1689 return;
1690
1691 env = environment();
1692 tmp = environment()->QueryProp(P_EXITS);
1693 exits = m_indices(tmp);
1694 tmp = environment()->QueryProp(P_SPECIAL_EXITS);
1695 exits += m_indices(tmp);
1696
1697 if ( query_once_interactive(ME) )
1698 exits = map( exits, #'_kill_alias/*'*/ );
1699
1700 // die Fluchtrichtung von Magiern wird aus Sicherheitsgruenden
1701 // nicht ausgewertet
1702 if ( interactive(ME) && IS_SEER(ME) && !IS_LEARNER(ME)
1703 && (dex=QueryProp(P_WIMPY_DIRECTION)) )
1704 {
1705 i = 60 + 4 * (QueryProp(P_LEVEL) - 30) / 3;
1706 exits += ({dex}); // bevorzugte Fluchtrichtung mindestens einmal
1707 }
1708
1709 if ( !sizeof(exits) )
1710 {
1711 tell_object( ME, "Du versuchst zu fliehen, schaffst es aber nicht.\n" );
1712
1713 return;
1714 }
1715
1716 while ( sizeof(exits) && (environment()==env) )
1717 {
1718 if ( dex // Vorzugsweise Fluchtrichtung?
1719 && (member(exits,dex) >= 0) // moeglich?
1720 && (random(100) <= i)) // und Wahrscheinlichkeit gross genug?
1721 exit = dex;
1722 else
1723 exit = exits[random(sizeof(exits))];
1724
1725 catch(command(exit);publish);
1726 exits -= ({exit});
1727 }
1728
1729 if ( environment()==env )
1730 tell_object( ME, "Dein Fluchtversuch ist gescheitert.\n" );
1731}
1732
1733public object EnemyPresent()
1734{
1735 foreach(object en: enemies) {
1736 if (environment()==environment(en))
1737 return en;
1738 }
1739 return 0;
1740}
1741
1742public object InFight()
1743{
1744 return EnemyPresent();
1745}
1746
1747public varargs int StopHuntID(string str, int silent) {
1748
1749 if ( !stringp(str) )
1750 return 0;
1751
1752 int j;
1753 foreach(object en: enemies) {
1754 if (en->id(str)) {
1755 StopHuntFor(en,silent);
1756 j++;
1757 }
1758 }
1759
1760 return j;
1761}
1762
1763public int SpellDefend(object caster, mapping sinfo)
1764{ int re;
1765 mixed res;
1766 string *ind;
1767
1768 re = UseSkill(SK_SPELL_DEFEND,([ SI_SKILLARG : sinfo ,
1769 SI_ENEMY : caster ]) );
1770
1771 if ( (res=QueryProp(P_MAGIC_RESISTANCE_OFFSET)) && mappingp(res)
1772 && pointerp(sinfo[SI_MAGIC_TYPE]))
1773 {
1774 ind = m_indices(res) & sinfo[SI_MAGIC_TYPE];
1775
1776 if (pointerp(ind) && sizeof(ind) ) {
1777 foreach(string index : ind)
1778 re+=res[index];
1779 }
1780 }
1781 else if(res && intp(res))
1782 re+=res;
1783
1784 if ( (re>3333) && query_once_interactive(this_object()) )
1785 re=3333; /* Maximal 33% Abwehrchance bei Spielern */
1786 return re;
1787}
1788
1789// **** this is property-like
1790
1791static int _set_attack_busy(mixed val)
1792{
1793 if ( ((to_int(val))>5) && previous_object(1)
1794 && query_once_interactive(previous_object(1)) )
1795 log_file("ATTACKBUSY",sprintf("%s %d Taeter: %O Opfer: %O\n",
1796 dtime(time()),to_int(val),previous_object(1),this_object()));
1797
1798 attack_busy-=to_int(val*100.0);
1799
1800 if ( attack_busy<-2000 )
1801 attack_busy=-2000;
1802
1803 return attack_busy;
1804}
1805
1806static int _query_attack_busy()
1807{
1808 if (IS_LEARNING(ME))
1809 return 0;
1810
1811 return (attack_busy<100);
1812}
1813
1814// **** local property methods
1815static int _set_wimpy(int i)
1816{
1817 if ( !intp(i) || (i>QueryProp(P_MAX_HP)) || (i<0) )
1818 return 0;
1819
1820 // ggf. Statusreport ausgeben
1821 if (interactive(ME))
1822 status_report(DO_REPORT_WIMPY, i);
1823
1824 return Set(P_WIMPY, i);
1825}
1826
1827static string _set_wimpy_dir(string s) {
1828 // ggf. Statusreport ausgeben
1829 if (interactive(ME))
1830 status_report(DO_REPORT_WIMPY_DIR, s);
1831 return Set(P_WIMPY_DIRECTION, s, F_VALUE);
1832}
1833
1834static mixed _set_hands(mixed h)
1835{
1836 if ( sizeof(h)==2 )
1837 h += ({ ({DT_BLUDGEON}) });
1838 if (!pointerp(h[2]))
1839 h[2] = ({h[2]});
1840 return Set(P_HANDS, h, F_VALUE);
1841}
1842
1843//TODO normalisieren/korrigieren in updates_after_restore().
1844static mixed _query_hands()
1845{
1846 mixed *hands = Query(P_HANDS);
1847 if ( !hands )
1848 return Set(P_HANDS, ({ " mit blossen Haenden", 30, ({DT_BLUDGEON})}));
1849 else if ( sizeof(hands)<3 )
1850 return Set(P_HANDS, ({hands[0], hands[1], ({DT_BLUDGEON})}));
1851 else if ( !pointerp(hands[2]) )
1852 return Set(P_HANDS, ({hands[0], hands[1], ({ hands[2] })}));
1853
1854 return Query(P_HANDS);
1855}
1856
1857static int _query_total_wc()
1858{ mixed res;
1859 int totwc;
1860
1861 if ( objectp(res=QueryProp(P_WEAPON)) )
Zesstrac4052902019-12-29 16:45:21 +01001862 totwc = res->QueryProp(P_WC);
MG Mud User88f12472016-06-24 23:31:02 +02001863 else if ( pointerp(res=QueryProp(P_HANDS)) && sizeof(res)>1
1864 && intp(res[1]) )
Zesstrac4052902019-12-29 16:45:21 +01001865 totwc=res[1];
MG Mud User88f12472016-06-24 23:31:02 +02001866 else
1867 totwc=30;
1868
1869 totwc = ((2*totwc)+(10*QueryAttribute(A_STR)))/3;
1870
1871 return Set(P_TOTAL_WC, totwc, F_VALUE);
1872}
1873
1874static int _query_total_ac() {
1875
1876 int totac = 0;
1877 object *armours = QueryProp(P_ARMOURS);
1878 object parry = QueryProp(P_PARRY_WEAPON);
1879
1880 if ( member(armours,0)>=0 ) {
1881 armours -= ({ 0 });
1882 }
1883
1884 foreach(object armour: armours)
Zesstrac4052902019-12-29 16:45:21 +01001885 totac += armour->QueryProp(P_AC);
MG Mud User88f12472016-06-24 23:31:02 +02001886
1887 if ( objectp(parry) )
1888 totac += parry->QueryProp(P_AC);
1889
1890 totac += (QueryProp(P_BODY)+QueryAttribute(A_DEX));
1891
1892 return Set(P_TOTAL_AC, totac, F_VALUE);
1893}
1894
1895static mapping _query_resistance_strengths() {
1896
1897 UpdateResistanceStrengths();
1898
1899 mapping rstren = copy(Query(P_RESISTANCE_STRENGTHS, F_VALUE));
1900 mapping mod = Query(P_RESISTANCE_MODIFIER, F_VALUE);
1901
1902 if ( !mappingp(rstren) )
1903 rstren = ([]);
1904
1905 if ( !mappingp(mod) || !mappingp(mod=mod["me"]) || !sizeof(mod) )
1906 return rstren;
1907
1908 foreach(string modkey, float modvalue : mod)
1909 rstren[modkey] = ((1.0+rstren[modkey])*(1.0+modvalue))-1.0;
1910
1911 return rstren;
1912}
1913
1914static int _set_disable_attack(int val)
1915{
1916 if (val<-100000)
1917 {
1918 log_file("PARALYSE_TOO_LOW",
1919 sprintf("Wert zu klein: %s, Wert: %d, "
1920 "Aufrufer: %O, Opfer: %O",
1921 ctime(time()),
1922 val,previous_object(1),
1923 this_object()));
1924 }
1925 if ( val>30 )
1926 val=30;
1927 if ( (val>=20) && previous_object(1)!=ME && query_once_interactive(ME) )
1928 log_file("PARALYSE",sprintf("%s %d Taeter: %O Opfer: %O\n",
1929 ctime(time())[4..15],
1930 val,previous_object(1),this_object()));
1931
1932 if (time()<QueryProp(P_NEXT_DISABLE_ATTACK))
1933 {
1934 // gueltige Zeitsperre existiert.
1935 // Erhoehen von P_DISABLE_ATTACK geht dann nicht. (Ja, auch nicht erhoehen
1936 // eines negativen P_DISABLE_ATTACK)
1937 if (val >= QueryProp(P_DISABLE_ATTACK))
1938 return DISABLE_TOO_EARLY;
1939 // val ist kleiner als aktuelles P_DISABLE_ATTACK - das ist erlaubt, ABER es
1940 // darf die bestehende Zeitsperre nicht verringern, daher wird sie nicht
1941 // geaendert.
1942 return Set(P_DISABLE_ATTACK,val,F_VALUE);
1943 }
1944 // es existiert keine gueltige Zeitsperre - dann wird sie nun gesetzt.
1945 // (Sollte val < 0 sein, wird eine Zeitsperre in der Vergangenheit gesetzt,
1946 // die schon abgelaufen ist. Das ist ueberfluessig, schadet aber auch
1947 // nicht.)
1948 SetProp(P_NEXT_DISABLE_ATTACK,time()+val*2*__HEART_BEAT_INTERVAL__);
1949 return Set(P_DISABLE_ATTACK,val);
1950}
1951
1952// Neue Verwaltung der Haende:
1953
1954// P_HANDS_USED_BY enhaelt ein Array mit allen Objekten, die Haende
1955// belegen, jedes kommt so oft vor wie Haende belegt werden.
1956
1957static mixed *_query_hands_used_by()
1958{
1959 return ((Query(P_HANDS_USED_BY, F_VALUE) || ({}) ) - ({0}));
1960}
1961
1962static int _query_used_hands()
1963{
1964 return sizeof(QueryProp(P_HANDS_USED_BY));
1965}
1966
1967static int _query_free_hands()
1968{
1969 return (QueryProp(P_MAX_HANDS)-QueryProp(P_USED_HANDS));
1970}
1971
1972public varargs int UseHands(object ob, int num)
1973{ mixed *h;
1974
1975 if ( !ob && !(ob=previous_object(1)) )
1976 return 0;
1977
1978 if ( (num<=0) && ((num=ob->QueryProp(P_HANDS))<=0) )
1979 return 0;
1980
1981 h=QueryProp(P_HANDS_USED_BY)-({ob});
1982
1983 if ( (sizeof(h)+num)>QueryProp(P_MAX_HANDS) )
1984 return 0;
1985
1986 foreach(int i: num)
1987 h+=({ob});
1988
1989 SetProp(P_HANDS_USED_BY,h);
1990
1991 return 1;
1992}
1993
1994public varargs int FreeHands(object ob)
1995{
1996 if ( !ob && !(ob=previous_object(1)) )
1997 return 0;
1998
1999 SetProp(P_HANDS_USED_BY,QueryProp(P_HANDS_USED_BY)-({ob}));
2000
2001 return 1;
2002}
2003
2004// Kompatiblitaetsfunktionen:
2005
2006static int _set_used_hands(int new_num)
2007{ int old_num, dif;
2008 object ob;
2009
2010 old_num=QueryProp(P_USED_HANDS);
2011
2012 if ( !objectp(ob=previous_object(1)) )
2013 return old_num;
2014
2015 // Meldung ins Debuglog schreiben. Aufrufer sollte gefixt werden.
2016 debug_message(sprintf("P_USED_HANDS in %O wird gesetzt durch %O\n",
2017 this_object(), ob), DMSG_LOGFILE|DMSG_STAMP);
2018
2019 if ( !(dif=new_num-old_num) )
2020 return new_num;
2021
2022 if ( dif>0 )
2023 UseHands(ob,dif);
2024 else
2025 FreeHands(ob);
2026
2027 return QueryProp(P_USED_HANDS);
2028}
2029
2030// Funktionen fuer Begruessungsschlag / Nackenschlag:
2031
2032public int CheckEnemy(object ob)
2033{
2034 return (living(ob) && IsEnemy(ob));
2035}
2036
2037public varargs void ExecuteMissingAttacks(object *remove_attackers)
2038{
2039 if ( !pointerp(missing_attacks) )
2040 missing_attacks=({});
2041
2042 if ( pointerp(remove_attackers) )
2043 missing_attacks-=remove_attackers;
2044
2045 foreach(object ob : missing_attacks) {
2046 if ( objectp(ob) && (environment(ob)==environment()) )
2047 ob->Attack2(ME);
2048 }
2049 missing_attacks=({});
2050}
2051
2052public void InitAttack()
2053{ object ob,next;
2054 closure cb;
2055
2056 if ( !living(ME) )
2057 return;
2058
2059 ExecuteMissingAttacks();
2060 //EMA kann das Living zerstoeren oder toeten...
2061 if (!living(ME) || QueryProp(P_GHOST)) return;
2062
2063 if ( objectp(ob=IsTeamMove()) )
2064 cb=symbol_function("InitAttack_Callback",ob);
2065 else
2066 cb=0;
2067
2068 for ( ob=first_inventory(environment()) ; objectp(ob) ; ob=next)
2069 {
2070 next=next_inventory(ob);
2071
2072 if ( !living(ob) )
2073 continue;
2074
2075 if (ob->IsEnemy(ME))
2076 {
2077 // Das ist nicht so sinnlos wie es aussieht. a) werden die Hunttimes
2078 // aktualisiert und b) werden Teammitglieder von mir bei diesem
2079 // InsertEnemy() ggf. erfasst.
2080 ob->InsertEnemy(ME);
2081
2082 if ( closurep(cb) && funcall(cb,ob) ) // Wird ganzes Team gemoved?
2083 missing_attacks += ({ ob }); // Dann erstmal warten bis alle da sind
2084 else
2085 ob->Attack2(ME);
2086
2087 }
2088 else if ( IsEnemy(ob) )
2089 {
2090 // Das ist nicht so sinnlos wie es aussieht. a) werden die Hunttimes
2091 // aktualisiert und b) werden Teammitglieder von ob bei diesem
2092 // InsertEnemy() ggf. erfasst.
2093 InsertEnemy(ob);
2094 Attack2(ob);
2095 }
2096 //Attack2 kann dieses Objekt zerstoeren oder toeten. Wenn ja: abbruch
2097 if ( !living(ME) || QueryProp(P_GHOST)) break;
2098 }
2099}
2100
2101public void ExitAttack()
2102{
2103 if ( !living(ME) )
2104 return;
2105
2106 // Noch nachzuholende Begruessungsschlaege:
2107 ExecuteMissingAttacks();
2108}
2109
2110public object|object*|mapping QueryArmourByType(string type)
2111{
2112 // Rueckgabewert:
2113 // DIE Ruestung vom Typ <type>, falls <type> nicht AT_MISC,
2114 // Array aller AT_MISC-Ruestungen falls <type> AT_MISC (auch leer),
2115 // Mapping mit allen oben genannten Infos, falls <type> Null
2116
2117 object *armours;
2118 string typ2;
2119
2120 // Wenn Cache vorhanden, dann Cache liefern.
2121 if (mappingp(QABTCache)) {
2122 if (type == AT_MISC)
2123 return QABTCache[AT_MISC] - ({0});
2124 else if (type)
2125 return QABTCache[type];
2126 else
2127 return copy(QABTCache);
2128 }
2129
2130 if ( !pointerp(armours=QueryProp(P_ARMOURS)) )
2131 armours=({});
2132
2133 // Cache erzeugen
2134 QABTCache = ([ AT_MISC: ({}) ]);
2135 foreach(object ob: armours - ({0}) ) {
2136 if ( !stringp(typ2=ob->QueryProp(P_ARMOUR_TYPE)) )
2137 continue;
2138 if ( typ2==AT_MISC )
2139 QABTCache[AT_MISC] += ({ob});
2140 else
2141 QABTCache[typ2] = ob;
2142 }
2143 // Und gewuenschtes Datum liefern.
2144 if (type)
2145 return QABTCache[type];
2146 else
2147 return copy(QABTCache);
2148}
2149
2150//TODO: langfristig waers besser, wenn hier nicht jeder per SetProp() drauf
2151//los schreiben wuerde.
2152static object *_set_armours(object *armours) {
2153 if (pointerp(armours)) {
2154 // Cache wegwerfen
2155 QABTCache = 0;
2156 // armours ggf. unifizieren. Ausserdem Kopie reinschreiben.
2157 return Set(P_ARMOURS, m_indices(mkmapping(armours)), F_VALUE);
2158 }
2159 return QueryProp(P_ARMOURS);
2160}
2161
2162/** Reduziert die Befriedezaehler pro Reset im Durchschnitt um 2.5.
2163 Berechnet ganzzahlige durchschnittliche Resets seit dem letzten Expire und
2164 erhoeht die letzte Expirezeit um Resets*__RESET_TIME__ (Standard Intervall
2165 fuer Reset ist momentan 3600s, im Durchschnitt kommen dann 2700 zwischen 2
2166 Resets bei raus). So wird der unganzzahlige Rest beim naechsten Aufruf
2167 beruecksichtigt.
2168 Diese Variante des Expires wurde gewaehlt, um zu vermeiden, combat.c einen
2169 reset() zu geben und in jedem Reset in jedem Lebewesen ein Expire machen zu
2170 muessen, auch wenn nur in sehr wenigen Lebewesen was gemacht werden muss.
2171 @param[in,out] ph Mapping aus P_PEACE_HISTORY. Wird direkt aktualisiert.
2172 @attention Muss ein gueltiges P_PEACE_HISTORY uebergeben bekommen, anderem
2173 Datentyp inkl. 0 wird die Funktion buggen.
2174 */
2175private void _decay_peace_history(mixed ph) {
2176 // Ganzzahlige resets seit letztem Expire ermitteln. (Durchschnitt)
2177 int resets = (time() - ph[0]) / (__RESET_TIME__*75/100);
2178 // auf Zeitstempel draufrechnen, damit der unganzzahlige Rest nicht
2179 // verlorengeht, der wird beim naechsten Expire dann beruecksichtigt.
2180 ph[0] += resets * (__RESET_TIME__ * 75 / 100);
2181 // pro Reset werden im Durchschnitt 2.5 Versuche abgezogen. (Hier werden
2182 // beim Expire mal 2 und mal 3 Versuche pro Reset gerechnet. Aber falls hier
2183 // viele Resets vergangen sind, ist es vermutlich eh egal, weil die Counter
2184 // auf 0 fallen.)
2185 int expire = resets * (random(2) + 2);
2186 // ueber alle Gilden
2187 mapping tmp=ph[1];
2188 foreach(string key, int count: &tmp ) {
2189 count-=expire;
2190 if (count < 0) count = 0;
2191 }
2192}
2193
2194/** Pacify() dient zur Bestimmung, ob sich ein Lebewesen gerade
2195 * befrieden lassen will.
2196 * Berechnet eine Wahrscheinlichkeit nach unten stehender Formel, welche die
2197 * Intelligenz dieses Lebenwesens, die Intelligenz des Casters und die
2198 * bisherige Anzahl erfolgreicher Befriedungsversuche dieser Gilde eingeht.
2199 * Anschliessend wird aus der Wahrscheinlichkeit und random() bestimmt, ob
2200 * dieser Versuch erfolgreich ist.
2201Formel: w = (INT_CASTER + 10 - ANZ*4) / (INT_ME + 10)
2202INT_CASTER: Caster-Intelligenz, INT_ME: Intelligenz dieses Livings
2203ANZ: Anzahl erfolgreicher Befriedungsversuche
2204
2205Annahme: INT_CASTER === 22, alle Wahrscheinlichkeiten * 100
2206
2207INT_ME Erfolgswahrscheinlichkeiten je nach Anzahl erfolgreicher Versuche
2208 1 2 3 4 5 6 7 8
2209 0 280 240 200 160 120 80 40 0
2210 2 233,33 200 166,67 133,33 100 66,67 33,33 0
2211 4 200 171,43 142,86 114,29 85,71 57,14 28,57 0
2212 6 175 150 125 100 75 50 25 0
2213 8 155,56 133,33 111,11 88,89 66,67 44,44 22,22 0
2214 10 140 120 100 80 60 40 20 0
2215 12 127,27 109,09 90,91 72,73 54,55 36,36 18,18 0
2216 14 116,67 100 83,33 66,67 50 33,33 16,67 0
2217 16 107,69 92,31 76,92 61,54 46,15 30,77 15,38 0
2218 18 100 85,71 71,43 57,14 42,86 28,57 14,29 0
2219 20 93,33 80 66,67 53,33 40 26,67 13,33 0
2220 22 87,5 75 62,5 50 37,5 25 12,5 0
2221 24 82,35 70,59 58,82 47,06 35,29 23,53 11,76 0
2222 26 77,78 66,67 55,56 44,44 33,33 22,22 11,11 0
2223 28 73,68 63,16 52,63 42,11 31,58 21,05 10,53 0
2224 30 70 60 50 40 30 20 10 0
2225 32 66,67 57,14 47,62 38,1 28,57 19,05 9,52 0
2226 34 63,64 54,55 45,45 36,36 27,27 18,18 9,09 0
2227 35 62,22 53,33 44,44 35,56 26,67 17,78 8,89 0
2228 36 60,87 52,17 43,48 34,78 26,09 17,39 8,7 0
2229 38 58,33 50 41,67 33,33 25 16,67 8,33 0
2230 40 56 48 40 32 24 16 8 0
2231 42 53,85 46,15 38,46 30,77 23,08 15,38 7,69 0
2232 44 51,85 44,44 37,04 29,63 22,22 14,81 7,41 0
2233 46 50 42,86 35,71 28,57 21,43 14,29 7,14 0
2234 48 48,28 41,38 34,48 27,59 20,69 13,79 6,9 0
2235 50 46,67 40 33,33 26,67 20 13,33 6,67 0
2236 52 45,16 38,71 32,26 25,81 19,35 12,9 6,45 0
2237 54 43,75 37,5 31,25 25 18,75 12,5 6,25 0
2238 56 42,42 36,36 30,3 24,24 18,18 12,12 6,06 0
2239 58 41,18 35,29 29,41 23,53 17,65 11,76 5,88 0
2240 60 40 34,29 28,57 22,86 17,14 11,43 5,71 0
2241 62 38,89 33,33 27,78 22,22 16,67 11,11 5,56 0
2242 64 37,84 32,43 27,03 21,62 16,22 10,81 5,41 0
2243 66 36,84 31,58 26,32 21,05 15,79 10,53 5,26 0
2244 68 35,9 30,77 25,64 20,51 15,38 10,26 5,13 0
2245 70 35 30 25 20 15 10 5 0
2246 72 34,15 29,27 24,39 19,51 14,63 9,76 4,88 0
2247 74 33,33 28,57 23,81 19,05 14,29 9,52 4,76 0
2248 76 32,56 27,91 23,26 18,6 13,95 9,3 4,65 0
2249 78 31,82 27,27 22,73 18,18 13,64 9,09 4,55 0
2250 80 31,11 26,67 22,22 17,78 13,33 8,89 4,44 0
2251 82 30,43 26,09 21,74 17,39 13,04 8,7 4,35 0
2252 84 29,79 25,53 21,28 17,02 12,77 8,51 4,26 0
2253 86 29,17 25 20,83 16,67 12,5 8,33 4,17 0
2254 88 28,57 24,49 20,41 16,33 12,24 8,16 4,08 0
2255 90 28 24 20 16 12 8 4 0
2256 92 27,45 23,53 19,61 15,69 11,76 7,84 3,92 0
2257 94 26,92 23,08 19,23 15,38 11,54 7,69 3,85 0
2258 96 26,42 22,64 18,87 15,09 11,32 7,55 3,77 0
2259 98 25,93 22,22 18,52 14,81 11,11 7,41 3,7 0
2260 100 25,45 21,82 18,18 14,55 10,91 7,27 3,64 0
2261 * @return 1, falls Befrieden erlaubt ist, 0 sonst.
2262 * @param[in] caster Derjenige, der den Spruch ausfuehrt.
2263 * @attention Wenn acify() 1 zurueckliefert, zaehlt dies als
2264 * erfolgreicher Befriedungsversuch und in diesem Lebewesen wurde
2265 * StopHuntingMode(1) aufgerufen.
2266*/
2267public int Pacify(object caster) {
2268
2269 // wenn das Viech keine Gegner hat, dann witzlos. ;-) Ohne Caster gehts auch
2270 // direkt raus.
2271 if (!mappingp(enemies) || !sizeof(enemies)
2272 || !objectp(caster)) {
2273 return 0;
2274 }
2275
2276 // Wenn P_ACCEPT_PEACE gesetzt ist, altes Verhalten wiederspiegeln
2277 // -> der NPC ist einfach immer befriedbar. Gleiches gilt fuer den Caster
2278 // selber, der wird sich ja nicht gegen das eigene Befriede wehren. Und auch
2279 // im team wehrt man sich nicht gegen das Befriede eines Teamkollegen
2280 if (QueryProp(P_ACCEPT_PEACE)==1 || caster==ME
2281 || member(TeamMembers(), caster) > -1) {
2282 StopHuntingMode(1); // Caster/Gilde sollte eigene Meldung ausgeben
2283 return 1;
2284 }
2285
2286 string gilde = caster->QueryProp(P_GUILD) || "ANY";
2287
2288 // ggf. P_PEACE_HISTORY initialisieren
Zesstrac4052902019-12-29 16:45:21 +01002289 <int|mapping>* ph = QueryProp(P_PEACE_HISTORY);
MG Mud User88f12472016-06-24 23:31:02 +02002290 if (!pointerp(ph))
2291 SetProp(P_PEACE_HISTORY, ph=({time(), ([]) }) );
2292
2293 // ggf. die Zaehler reduzieren.
2294 if ( ph[0] + __RESET_TIME__ * 75/100 < time()) {
2295 _decay_peace_history(&ph);
2296 }
2297
2298 float w = (caster->QueryAttribute(A_INT) + 10 - ph[1][gilde] * 4.0) /
2299 (QueryAttribute(A_INT) + 10);
2300 // auf [0,1] begrenzen.
2301 if (w<0) w=0.0;
2302 else if (w>1) w=1.0;
2303 // w * n ist eine Zahl zwischen 0 und n, wenn w * n > random(n)+1,
2304 // darf befriedet werden. Da das Random fuer grosse Zahlen
2305 // besser verteilt ist, nehm ich n = __INT_MAX__ und vernachlaessige
2306 // ausserdem die +1 beim random().
2307 if (ceil(w * __INT_MAX__) > random(__INT_MAX__) ) {
2308 ph[1][gilde]++;
2309 StopHuntingMode(1);
2310 return 1;
2311 }
2312 // ein SetProp(P_PEACE_HISTORY) kann entfallen, da das Mapping direkt
2313 // geaendert wird. Sollte die Prop allerdings mal ne Querymethode haben,
2314 // welche eine Kopie davon liefert, muss das hier geaendert oder die
2315 // Prop per Query() abgefragt werden.
2316 return 0;
2317}
2318