| // MorgenGrauen MUDlib |
| // |
| // living/combat.c -- Basis-Kampfmodul |
| // |
| // $Id: combat.c 9568 2016-06-05 18:53:10Z Zesstra $ |
| #pragma strong_types |
| #pragma save_types |
| #pragma range_check |
| #pragma no_clone |
| |
| inherit "/std/living/skill_utils"; |
| inherit "/std/living/inventory"; |
| inherit "/std/living/team"; |
| |
| #include <sys_debug.h> |
| #include <debug_message.h> |
| |
| #define NEED_PROTOTYPES |
| #include <hook.h> |
| #include <living/skills.h> |
| #include <thing/properties.h> |
| #include <player/comm.h> |
| #include <living/skill_attributes.h> |
| #include <combat.h> |
| #include <living.h> |
| #undef NEED_PROTOTYPES |
| |
| #include <config.h> |
| #include <properties.h> |
| #include <language.h> |
| #include <wizlevels.h> |
| #include <attributes.h> |
| #include <new_skills.h> |
| |
| #include <defines.h> |
| |
| #include <sensitive.h> |
| |
| #define HUNTTIME 300 //300 HBs sind 10 Minuten |
| #define RNAME(x) capitalize(getuid(x)) |
| |
| // 'private'-Prototypes |
| private string _kill_alias( string str ); |
| |
| // globale Variablen |
| nosave mapping enemies; |
| private nosave int attack_busy; |
| nosave int no_more_attacks; |
| private nosave int remaining_heart_beats; |
| private nosave int att2_time; |
| private nosave string last_attack_msg; |
| private nosave object *missing_attacks; |
| // Cache fuer QueryArmourByType() |
| private nosave mapping QABTCache; |
| |
| protected void create() |
| { |
| Set(P_WIMPY, SAVE, F_MODE_AS); |
| Set(P_TOTAL_AC, PROTECTED, F_MODE_AS); |
| Set(P_HANDS, SAVE, F_MODE_AS); |
| Set(P_SHOW_ATTACK_MSG,SAVE,F_MODE_AS); |
| Set(P_RESISTANCE, ({})); |
| Set(P_VULNERABILITY, ({})); |
| Set(P_GUILD_PREPAREBLOCK, SAVE, F_MODE_AS); |
| // Kein Setzen von P_ARMOURS von aussen per Set(). (Per SetProp() geht es |
| // durch die Setmethode). |
| Set(P_ARMOURS,PROTECTED,F_MODE_AS); |
| SetProp(P_ARMOURS, ({})); |
| // Kein direktes Setzen von P_DEFENDERS, Setzen nur ueber |
| // die Zugriffsmethoden. |
| Set(P_DEFENDERS, PROTECTED, F_MODE_AS); |
| SetProp(P_DEFENDERS, ({})); |
| attack_busy=100; |
| att2_time=0; |
| enemies=([]); |
| team::create(); |
| offerHook(H_HOOK_DEFEND,1); |
| offerHook(H_HOOK_ATTACK,1); |
| offerHook(H_HOOK_ATTACK_MOD,1); |
| } |
| |
| #undef DEBUG |
| #define DEBUG(x) if (find_object("zesstra")) \ |
| tell_object(find_player("zesstra"),x) |
| |
| public void UpdateResistanceStrengths() { |
| mapping resmods, strmap; |
| |
| if ( !mappingp(resmods=Query(P_RESISTANCE_MODIFIER)) ) |
| return; |
| |
| //erstmal die alte Aufsummation loeschen |
| m_delete(resmods,"me"); |
| |
| //wenn jetzt leer: Abbruch, keine Res-Modifier da. |
| if (!sizeof(resmods)) |
| return(Set(P_RESISTANCE_MODIFIER,0)); |
| |
| strmap = ([]); |
| |
| // ueber alle gesetzten ResModifier gehen |
| foreach(string mod, mapping resmap, object ob: resmods) { |
| if ( !mappingp(resmap) || !sizeof(resmap) |
| || !objectp(ob) ) { |
| m_delete(resmods, mod); |
| continue; // Resi ungueltig, weg damit. |
| } |
| // jetzt noch ueber die Submappings laufen, die die Resis der Objekte |
| // beinhalten. |
| foreach(string reskey, float resi: resmap) { |
| strmap[reskey] = ((strmap[reskey]+1.0)*(resi+1.0))-1.0; |
| } |
| } |
| |
| if ( !sizeof(strmap) ) |
| Set(P_RESISTANCE_MODIFIER, 0); |
| else |
| Set(P_RESISTANCE_MODIFIER, resmods+([ "me" : strmap; 0 ]) ); |
| } |
| |
| public varargs int AddResistanceModifier(mapping mod, string add) |
| { string key; |
| mapping res; |
| |
| if ( !mappingp(mod) || !sizeof(mod) || !previous_object() ) |
| return 0; |
| |
| key = explode(object_name(previous_object()),"#")[0]; |
| |
| if ( add ) |
| key += ("#"+add); |
| |
| res = Query(P_RESISTANCE_MODIFIER); |
| mod = deep_copy(mod); |
| |
| if ( !mappingp(res) ) |
| res = ([ key : mod; previous_object() ]); |
| else |
| res += ([ key : mod; previous_object() ]); |
| |
| Set(P_RESISTANCE_MODIFIER, res); |
| UpdateResistanceStrengths(); |
| |
| return 1; |
| } |
| |
| public varargs void RemoveResistanceModifier(string add) |
| { string key; |
| mapping res; |
| |
| if ( !previous_object() ) |
| return; |
| |
| key = explode(object_name(previous_object()),"#")[0]; |
| |
| if ( add ) |
| key += ("#"+add); |
| |
| if ( !mappingp(res = Query(P_RESISTANCE_MODIFIER)) ) |
| return; |
| |
| m_delete(res, key); |
| Set(P_RESISTANCE_MODIFIER, res); |
| UpdateResistanceStrengths(); |
| } |
| |
| // veraltete Prop, aus Kompatibilitaetsgruenden noch vorhanden. |
| static mixed _set_resistance(mixed arg) |
| { int i; |
| mapping resimap; |
| mixed old; |
| |
| if ( !pointerp(arg) ) |
| arg=({arg}); |
| |
| if ( !mappingp(resimap=Query(P_RESISTANCE_STRENGTHS)) ) |
| resimap=([]); |
| |
| if ( pointerp(old=QueryProp(P_RESISTANCE)) ) |
| for ( i=sizeof(old)-1 ; i>=0 ; i-- ) |
| resimap[old[i]]=(1.0+resimap[old[i]])*2.0-1.0; |
| |
| for ( i=sizeof(arg)-1 ; i>=0 ; i-- ) |
| resimap[arg[i]]=(1.0+resimap[arg[i]])*0.5-1.0; |
| |
| SetProp(P_RESISTANCE_STRENGTHS,resimap); |
| |
| return Set(P_RESISTANCE,arg); |
| } |
| |
| // veraltete Prop, aus Kompatibilitaetsgruenden noch vorhanden. |
| static mixed _set_vulnerability(mixed arg) |
| { int i; |
| mapping resimap; |
| mixed old; |
| |
| if ( !pointerp(arg) ) |
| arg=({arg}); |
| |
| if ( !mappingp(resimap=Query(P_RESISTANCE_STRENGTHS)) ) |
| resimap=([]); |
| |
| if ( pointerp(old=QueryProp(P_VULNERABILITY)) ) |
| for ( i=sizeof(old)-1 ; i>=0 ; i-- ) |
| resimap[old[i]]=(1.0+resimap[old[i]])*0.5-1.0; |
| |
| for ( i=sizeof(arg)-1 ; i>=0 ; i-- ) |
| resimap[arg[i]]=(1.0+resimap[arg[i]])*2.0-1.0; |
| |
| SetProp(P_RESISTANCE_STRENGTHS,resimap); |
| |
| return Set(P_VULNERABILITY,arg); |
| } |
| |
| |
| /** kill - Kampf starten. |
| * Fuegt ob der Feindesliste hinzu. |
| */ |
| public int Kill(object ob) |
| { int res; |
| int|string no_attack; |
| |
| if ( !objectp(ob) ) |
| return 0; |
| |
| if ( ob->QueryProp(P_GHOST) ) |
| { |
| tell_object(ME,ob->Name(WER)+" ist doch schon tot!\n"); |
| return -1; |
| } |
| |
| if ( no_attack = ob->QueryProp(P_NO_ATTACK) ) |
| { |
| if ( stringp(no_attack) ) |
| tell_object(ME, no_attack); |
| else |
| tell_object(ME, ob->Name(WER,1)+" laesst sich nicht angreifen!\n"); |
| |
| return -2; |
| } |
| |
| if ( QueryProp(P_NO_ATTACK) ) |
| return -3; |
| |
| res=InsertEnemy(ob); |
| if (res) |
| tell_object(ME, "Ok.\n"); |
| else if (IsEnemy(ob)) |
| tell_object(ME, "Jajaja, machst Du doch schon!\n"); |
| //else //kein gueltiger Feind, ob wurde nicht eingetragen. |
| |
| if ( !res ) |
| return -4; // nicht aendern ohne Kill in player/combat.c |
| |
| return 1; |
| } |
| |
| public int InsertSingleEnemy(object ob) |
| { |
| if ( !living(ob) ) |
| return 0; |
| |
| // Wie lange verfolgt dieses Living? Wenn Prop nicht gesetzt oder |
| // ungueltig, d.h. kein int oder <= 0, dann wird die Defaultzeit genommen. |
| int hunttime = QueryProp(P_HUNTTIME); |
| hunttime = (intp(hunttime) && hunttime > 0) ? |
| hunttime / __HEART_BEAT_INTERVAL__ : HUNTTIME; |
| |
| // Auch wenn ein Objekt schon als Gegner eingetragen ist, trotzdem die |
| // HUNTTIME erneuern. Das "return 0;" muss aber bleiben, da der Gegner |
| // ja nicht neu ist. |
| // |
| // 09.12.2000, Tiamak |
| if ( member( enemies, ob ) ) { |
| enemies[ob,ENEMY_HUNTTIME] = hunttime; |
| return 0; |
| } |
| |
| if ( (ob==ME) || QueryProp(P_NO_ATTACK) |
| || !objectp(ob) || (ob->QueryProp(P_NO_ATTACK)) |
| || (ob->QueryProp(P_GHOST)) ) |
| return 0; |
| |
| object team; |
| if ( ( query_once_interactive(ME) || query_once_interactive(ob) ) |
| && objectp(team=Query(P_TEAM)) && (team==(ob->Query(P_TEAM))) ) |
| return 0; |
| |
| set_heart_beat(1); |
| |
| last_attack_msg=0; |
| |
| enemies[ob,ENEMY_HUNTTIME] = hunttime; |
| Set(P_LAST_COMBAT_TIME,time()); |
| |
| return 1; |
| } |
| |
| public int InsertEnemy(object ob) |
| { int res; |
| |
| if ( res=InsertSingleEnemy(ob) ) |
| InsertEnemyTeam(ob); |
| |
| return res; |
| } |
| |
| public object QueryPreferedEnemy() |
| { int sz,r; |
| <int|object>* pref; |
| object enemy; |
| |
| enemy=0; |
| if ( pointerp(pref=QueryProp(P_PREFERED_ENEMY)) && ((sz=sizeof(pref))>1) |
| && intp(r=pref[0]) && (random(100)<r) ) |
| { |
| enemy=pref[1+random(sz-1)]; |
| |
| if ( !objectp(enemy) ) |
| { |
| pref-=({enemy}); |
| |
| if ( sizeof(pref)<2 ) |
| pref=0; |
| |
| SetProp(P_PREFERED_ENEMY,pref); |
| |
| return 0; |
| } |
| |
| if ( !IsEnemy(enemy) ) |
| return 0; |
| } |
| |
| return enemy; |
| } |
| |
| public object *PresentEnemies() { |
| |
| object *here=({}); |
| object *netdead=({}); |
| mixed no_attack; |
| foreach(object pl: enemies) { |
| if (no_attack = pl->QueryProp(P_NO_ATTACK)) { |
| m_delete(enemies,pl); |
| // Und auch im Gegner austragen, sonst haut der weiter zu und dieses |
| // Living wehrt sich nicht. (Zesstra, 26.11.2006) |
| if (stringp(no_attack)) { |
| tell_object(ME, no_attack); |
| pl->StopHuntFor(ME, 1); |
| } |
| else pl->StopHuntFor(ME, 0); |
| } |
| else if ( environment()==environment(pl) ) |
| { |
| if ( interactive(pl) || !query_once_interactive(pl) ) |
| here+=({pl}); |
| else |
| netdead+=({pl}); |
| } |
| } |
| |
| if ( !sizeof(here) ) // Netztote sind nur Feinde, falls keine anderen da sind |
| return netdead; |
| |
| return here; |
| } |
| |
| public varargs object SelectEnemy(object *here) |
| { object enemy; |
| |
| if ( !pointerp(here) ) |
| here=PresentEnemies(); |
| |
| if ( !sizeof(here) ) |
| return 0; |
| |
| if ( !objectp(enemy=QueryPreferedEnemy()) |
| || (environment(enemy)!=environment()) ) |
| enemy=here[random(sizeof(here))]; |
| |
| return enemy; |
| } |
| |
| protected void heart_beat() { |
| int hbs; |
| // leider rufen viele Leute einfach das geerbte heart_beat() obwohl sie |
| // schon tot sind und damit das Objekt zerstoert ist. |
| if (!living(this_object())) |
| return; |
| |
| // Paralyse pruefen, ggf. reduzieren |
| int dis_attack = QueryProp(P_DISABLE_ATTACK); |
| if ( intp(dis_attack) && (dis_attack > 0) ) |
| { |
| SetProp(P_DISABLE_ATTACK, --dis_attack); |
| } |
| |
| // Attacken ermitteln: SA_SPEED + Rest aus dem letzten HB |
| hbs = remaining_heart_beats + QuerySkillAttribute(SA_SPEED); |
| if ( hbs <= 0 ) |
| hbs = 100; |
| |
| // P_ATTACK_BUSY bestimmen |
| if ( attack_busy > 99) |
| attack_busy = 100 + hbs; |
| else |
| attack_busy += 100 + hbs; |
| // mehr fuer Seher... |
| if ( IS_SEER(ME) ) |
| attack_busy+=(100+QueryProp(P_LEVEL)); |
| // max. 500, d.h. 5 Attacken |
| if ( attack_busy>500 ) |
| attack_busy=500; |
| |
| // unganzzahligen Rest fuer naechsten HB speichern |
| remaining_heart_beats = hbs % 100; |
| hbs /= 100; // ganze Attacken. ;-) |
| |
| if ( hbs > 10 ) // nicht mehr als 10 Attacken. |
| hbs = 10; |
| |
| // Falls jemand seit dem letzten HB in diesem Living von aussen Attack() |
| // gerufen hat (nicht ExtraAttack()), wird jetzt was abgezogen. |
| hbs -= no_more_attacks; |
| no_more_attacks = 0; |
| |
| // Wie lange verfolgt dieser NPC? Wenn Prop nicht gesetzt oder ungueltig, |
| // d.h. kein int oder <= 0, dann wird die Defaultzeit genommen. |
| int hunttime = QueryProp(P_HUNTTIME); |
| hunttime = (intp(hunttime) && hunttime > 0) ? |
| hunttime / __HEART_BEAT_INTERVAL__ : HUNTTIME; |
| // erstmal die Hunttimes der Gegner aktualisieren und nebenbei die |
| // anwesenden Feinde merken. Ausserdem ggf. Kampf abbrechen, wenn |
| // P_NO_ATTACK gesetzt ist. |
| // eigentlich ist diese ganze Schleife fast das gleiche wie |
| // PresentEnemies(), aber leider muessen ja die Hunttimes aktulisiert werde, |
| // daher kann man nicht direkt PresentEnemies() nehmen. |
| // TODO: Diese Schleife und PresentEnmies() irgendwie vereinigen. |
| object *netdead=({}); |
| object *here=({}); |
| object enemy; |
| mixed no_attack; |
| foreach(enemy, int htime: &enemies) { // Keys in enemy |
| // ggf. Meldungen ausgeben und Gegner austragen und dieses Living beim |
| // Gegner austragen. |
| if (no_attack=enemy->QueryProp(P_NO_ATTACK)) { |
| m_delete(enemies,enemy); |
| if ( stringp(no_attack) ) { |
| write( no_attack ); |
| enemy->StopHuntFor( ME, 1 ); |
| } |
| else |
| enemy->StopHuntFor( ME, 0 ); |
| } |
| else if ( environment()==environment(enemy) ) { |
| // Gegner anwesend, als Feind merken... |
| if ( interactive(enemy) || !query_once_interactive(enemy) ) |
| here+=({enemy}); |
| else |
| netdead+=({enemy}); |
| // ... und Hunttime erneuern. |
| htime = hunttime; |
| } |
| // Feind nicht anwesend. Timer dekrementieren und Feind austragen, wenn |
| // Jagdzeit abgelaufen. |
| else if ( (--htime) <= 0 ) |
| StopHuntFor(enemy); |
| } |
| // Netztote sind nur Feinde, wenn sonst keine da sind. |
| if ( !sizeof(here) ) |
| here=netdead; |
| |
| // schonmal abfragen, ob ein Spell vorbereitet wird. |
| mixed pspell=QueryProp(P_PREPARED_SPELL); |
| |
| //Da hbs 0 und no_more_attacks durch einen manuellen Aufruf von Attack() im |
| //Spieler >0 sein kann, kann jetzt hbs <0 sein. (s.o.) |
| if (hbs > 0 && sizeof(here)) { |
| foreach(int i: hbs) { |
| // Feind in Nahkampfreichweite finden |
| if ( objectp(enemy=SelectNearEnemy(here)) ) { |
| // Flucht erwuenscht? |
| if ( QueryProp(P_WIMPY) > QueryProp(P_HP) ) { |
| Flee(); |
| // Flucht gelungen? |
| if ( enemy && (environment(enemy)!=environment()) ) { |
| here = 0; // naechste Runde neue Feinde suchen |
| enemy = 0; |
| continue; // kein Kampf, Runde ueberspringen |
| } |
| } |
| } |
| else { |
| // keine Feinde gefunden... Abbrechen. |
| break; |
| } |
| // Paralyse gesetzt? -> Abbrechen. Dieses Pruefung muss hier passieren, |
| // damit ggf. die automatische Flucht des Lebewesens durchgefuehrt wird, |
| // auch wenn es paralysiert ist. |
| if (dis_attack > 0) break; |
| |
| // Kampf durch Spell-Prepare blockiert? |
| // Keine genaue Abfrage, wird spaeter noch genau geprueft. |
| if ( pspell && QueryProp(P_GUILD_PREPAREBLOCK) ) |
| break; // keine Angriffe in diesem HB. |
| // wenn Feind da: hit'em hard. |
| if ( objectp(enemy) ) |
| Attack(enemy); |
| // ggf. hat Attack() no_more_attacks gesetzt. Zuruecksetzen |
| no_more_attacks = 0; |
| // naechste Kampfrunde neue Feinde suchen |
| here = 0; |
| } // foreach |
| } |
| |
| no_more_attacks=0; |
| |
| // Ist ein Spell in Vorbereitung und evtl. jetzt fertig vorbereitet? |
| if ( pointerp(pspell) |
| && (sizeof(pspell)>=3) && intp(pspell[0]) && stringp(pspell[1]) ) { |
| if ( time()>=pspell[0] ) // Kann der Spruch jetzt ausgefuehrt werden? |
| { |
| UseSpell(pspell[2],pspell[1]); // Dann los |
| SetProp(P_PREPARED_SPELL,0); |
| } |
| } |
| else if ( pspell ) // Unbrauchbarer Wert, loeschen |
| SetProp(P_PREPARED_SPELL,0); |
| |
| } // Ende heart_beat |
| |
| // wird von NPC gerufen, die Heartbeats nachholen und dabei die Hunttimes um |
| // die Anzahl verpasster Heartbeats reduzieren muessen. |
| // Wird von Spielerobjekten gerufen, wenn sie nach 'schlafe ein' aufwachen, um |
| // die notwendige Anzahl an HB hier abzuziehen und ggf. die Feinde zu expiren, |
| // da Spieler ja netztot keinen Heartbeat haben. |
| protected void update_hunt_times(int beats) { |
| if (!mappingp(enemies)) return; |
| foreach(object en, int htime: &enemies) { // Mapping-Keys in en |
| htime -= beats; |
| if ( htime <= 0 ) |
| StopHuntFor(en); |
| } |
| } |
| |
| public int IsEnemy(object wer) |
| { |
| return (member(enemies,wer)); |
| } |
| |
| public void StopHuntText(object arg) |
| { |
| tell_object(arg, |
| Name(WER,1)+" "+(QueryProp(P_PLURAL)?"jagen ":"jagt ")+ |
| (arg->QueryProp(P_PLURAL)?"Euch":"Dich")+" nicht mehr.\n"); |
| tell_object(ME,(QueryProp(P_PLURAL)?"Ihr jagt ":"Du jagst ")+ |
| arg->name(WEN,1)+" nicht mehr.\n"); |
| } |
| |
| public varargs int StopHuntFor(object arg, int silent) |
| { |
| if ( !objectp(arg) || !IsEnemy(arg) ) |
| return 0; |
| |
| if (!silent) |
| StopHuntText(arg); |
| |
| m_delete(enemies,arg); |
| last_attack_msg=0; |
| |
| return 1; |
| } |
| |
| // Begruessungsschlag nur einmal pro HB |
| public void Attack2(object enemy) |
| { |
| if ( att2_time > time() ) |
| return; |
| |
| att2_time=time() + __HEART_BEAT_INTERVAL__; |
| Attack(enemy); |
| } |
| |
| // Fuer evtl. Attack-Aenderungen, ohne dass man gleich das ganze |
| // Attack ueberlagern muss: |
| protected void InternalModifyAttack(mapping ainfo) |
| { |
| return; // Vorerst! |
| /* |
| int fac; |
| mixed res; |
| |
| fac=100; |
| |
| if (CannotSee(1)) |
| fac -= 50; |
| |
| // Weitere Mali? |
| |
| if (fac<100) |
| ainfo[SI_SKILLDAMAGE] = ainfo[SI_SKILLDAMAGE]*fac/100; |
| // Fertig |
| */ |
| } |
| |
| // Ausfuehren Eines Angriffs auch wenn es bereits einen Angriff |
| // in dieser Runde gegeben hat. Dieser Angriff wird auch nicht |
| // gezaehlt. Die Nutzung dieser Funktion ist nur fuer Spezialfaelle |
| // gedacht und immer BALANCEPFLICHTIG! |
| varargs public void ExtraAttack(object enemy, int ignore_previous) |
| { |
| int saved_no_more_attacks; |
| |
| // Erstschlagsinfo speichern. |
| saved_no_more_attacks = no_more_attacks; |
| |
| // Bei Bedarf bisher schon durchgefuehrte Erstschlaege ignorieren |
| if (ignore_previous) no_more_attacks=0; |
| |
| // Normalen Angriff durchfuehren |
| Attack (enemy); |
| |
| // Gespeicherten Wert zuruecksetzen |
| no_more_attacks = saved_no_more_attacks; |
| |
| return; |
| } |
| |
| public void Attack(object enemy) |
| { |
| closure cl; |
| mixed res; |
| mapping ainfo; |
| mapping edefendinfo; // erweiterte Defend-Infos |
| mixed hookData; |
| mixed hookRes; |
| |
| if ( no_more_attacks || QueryProp(P_GHOST) || |
| !objectp(enemy) || !objectp(this_object()) || |
| (QueryProp(P_DISABLE_ATTACK) > 0) || enemy->QueryProp(P_NO_ATTACK) || |
| (query_once_interactive(this_object()) && !interactive(this_object())) ) |
| return; |
| |
| edefendinfo=([]); |
| |
| Set(P_LAST_COMBAT_TIME,time()); |
| |
| // inkrementieren. Diese Variable wird im HB nach den Angriffen genullt. |
| // Diese Variable wird im naechsten Heartbeat von der Anzahl an verfuegbaren |
| // Angriffen des Livings abgezogen. Dies beruecksichtigt also zwischen den |
| // HBs (z.B. extern) gerufene Attacks(). |
| no_more_attacks++; |
| |
| // Wird das Attack durch einen temporaeren Attack-Hook ersetzt? |
| if ( res=QueryProp(P_TMP_ATTACK_HOOK) ) |
| { |
| if ( pointerp(res) && (sizeof(res)>=3) && intp(res[0]) && (time()<res[0]) |
| && objectp(res[1]) && stringp(res[2]) ) |
| { |
| if ( !(res=call_other(res[1],res[2],enemy)) ) |
| return; |
| } |
| else |
| SetProp(P_TMP_ATTACK_HOOK,0); |
| } |
| |
| // trigger attack hook |
| hookData=({enemy}); |
| hookRes=HookFlow(H_HOOK_ATTACK,hookData); |
| if(hookRes && pointerp(hookRes) && sizeof(hookRes)>H_RETDATA){ |
| if(hookRes[H_RETCODE]==H_CANCELLED){ |
| return; |
| } |
| } |
| |
| ainfo = ([ SI_ENEMY : enemy, |
| SI_SPELL : 0, |
| ]); |
| |
| if ( objectp(ainfo[P_WEAPON]=QueryProp(P_WEAPON)) ) |
| { |
| ainfo[P_WEAPON]->TakeFlaw(enemy); |
| |
| // Abfrage fuer den Fall, dass Waffe durch das TakeFlaw() zerstoert |
| // wurde. Dann wird das Attack abgebrochen. |
| if ( !objectp(ainfo[P_WEAPON]) ) |
| return; |
| |
| cl=symbol_function("name",ainfo[P_WEAPON]); |
| ainfo[SI_SKILLDAMAGE_MSG] = (" mit "+funcall(cl,WEM,0)); |
| ainfo[SI_SKILLDAMAGE_MSG2] = (" mit "+funcall(cl,WEM,1)); |
| |
| ainfo[SI_SKILLDAMAGE] = ainfo[P_WEAPON]->QueryDamage(enemy); |
| |
| cl=symbol_function("QueryProp",ainfo[P_WEAPON]); |
| ainfo[SI_SKILLDAMAGE_TYPE] = funcall(cl,P_DAM_TYPE); |
| ainfo[P_WEAPON_TYPE] = funcall(cl,P_WEAPON_TYPE); |
| ainfo[P_NR_HANDS] = funcall(cl,P_NR_HANDS); |
| ainfo[P_WC] = funcall(cl,P_WC); |
| |
| // Zweihaendige Waffe? |
| if ( ainfo[P_NR_HANDS]==2 |
| && mappingp(res=UseSkill(SK_TWOHANDED,deep_copy(ainfo))) |
| && member(res,SI_SKILLDAMAGE) ) |
| { |
| // Nur den neuen Schadenswert uebernehmen. |
| ainfo[SI_SKILLDAMAGE]=res[SI_SKILLDAMAGE]; |
| } |
| } |
| else // Keine Waffe gezueckt |
| { |
| /* Check if there is a magical attack */ |
| if ( mappingp(res=UseSkill(SK_MAGIC_ATTACK,([SI_ENEMY:enemy]))) ) |
| { |
| ainfo[SI_SKILLDAMAGE]=res[SI_SKILLDAMAGE]; |
| ainfo[SI_SKILLDAMAGE_TYPE]=res[SI_SKILLDAMAGE_TYPE]; |
| |
| if ( stringp(res[SI_SKILLDAMAGE_MSG]) ) |
| ainfo[SI_SKILLDAMAGE_MSG] = " mit "+res[SI_SKILLDAMAGE_MSG]; |
| else |
| ainfo[SI_SKILLDAMAGE_MSG] = " mit magischen Faehigkeiten"; |
| |
| if ( stringp(res[SI_SKILLDAMAGE_MSG2]) ) |
| ainfo[SI_SKILLDAMAGE_MSG2] = " mit "+res[SI_SKILLDAMAGE_MSG2]; |
| else |
| ainfo[SI_SKILLDAMAGE_MSG2] = ainfo[SI_SKILLDAMAGE_MSG]; |
| |
| if ( !(ainfo[P_WEAPON_TYPE]=res[P_WEAPON_TYPE]) ) |
| ainfo[P_WEAPON_TYPE]=WT_MAGIC; |
| |
| if ( member(res,SI_SPELL) ) |
| ainfo[SI_SPELL]=res[SI_SPELL]; |
| } |
| else |
| { |
| /* Ohne (freie) Haende wird auch nicht angegriffen */ |
| if ( interactive(this_object()) |
| && (QueryProp(P_USED_HANDS) >= QueryProp(P_MAX_HANDS)) ) |
| return ; |
| |
| if ( !pointerp(res=QueryProp(P_HANDS)) || (sizeof(res)<2) ) |
| return; |
| |
| ainfo[SI_SKILLDAMAGE] = (( 2*random(res[1]+1) |
| + 10*(QueryAttribute(A_STR)) )/3); |
| ainfo[SI_SKILLDAMAGE_TYPE] = res[2]; |
| ainfo[SI_SKILLDAMAGE_MSG] = ainfo[SI_SKILLDAMAGE_MSG2] |
| = res[0]; |
| ainfo[P_WEAPON_TYPE] = WT_HANDS; |
| ainfo[P_WC] = res[1]; |
| } |
| } // besondere Faehigkeiten mit diesem Waffentyp? |
| if ( mappingp(res=UseSkill(FIGHT(ainfo[P_WEAPON_TYPE]), |
| deep_copy(ainfo))) ) |
| SkillResTransfer(res,ainfo); |
| |
| // besondere allgemeine Kampffaehigkeiten? |
| if ( mappingp(res=UseSkill(SK_FIGHT,deep_copy(ainfo))) ) |
| SkillResTransfer(res,ainfo); |
| |
| // Veraenderungen durch einen Attack-Modifier? |
| if ( (res=QueryProp(P_TMP_ATTACK_MOD)) ) |
| { |
| if ( pointerp(res) && (sizeof(res)>=3) && intp(res[0]) |
| && (time()<res[0]) && objectp(res[1]) && stringp(res[2]) ) |
| { |
| if ( !(res=call_other(res[1],res[2],deep_copy(ainfo))) |
| || !mappingp(res) ) |
| return; |
| else |
| SkillResTransfer(res,ainfo); |
| } |
| else |
| SetProp(P_TMP_ATTACK_MOD,0); |
| } |
| |
| // trigger attack mod hook |
| hookData=deep_copy(ainfo); |
| hookRes=HookFlow(H_HOOK_ATTACK_MOD,hookData); |
| if(hookRes && pointerp(hookRes) && sizeof(hookRes)>H_RETDATA){ |
| if(hookRes[H_RETCODE]==H_CANCELLED){ |
| return; |
| } |
| else if(hookRes[H_RETCODE]==H_ALTERED && hookRes[H_RETDATA] && |
| mappingp(hookRes[H_RETDATA])){ |
| SkillResTransfer(hookRes[H_RETDATA],ainfo); |
| } |
| } |
| |
| // Interne Modifikationen der Angriffswerte |
| InternalModifyAttack(ainfo); |
| |
| if ( !objectp(enemy) ) |
| return; |
| |
| // hier mal bewusst nicht auf P_PLURAL zuerst testen. in 90% der Faelle |
| // wird eh keine Angriffsmeldung mehr ausgegeben, also pruefen wir das |
| // lieber zuerst. Plural testen zieht schon genug Rechenzeit :/ |
| // Nicht meine Idee! Nicht meine Idee! Nachtwind 7.7.2001 |
| if ( ainfo[SI_SKILLDAMAGE_MSG2]!=last_attack_msg ) |
| { |
| last_attack_msg = ainfo[SI_SKILLDAMAGE_MSG2]; |
| if (QueryProp(P_PLURAL)) |
| { |
| tell_object( ME, " Ihr greift " + enemy->name(WEN,1) |
| + ainfo[SI_SKILLDAMAGE_MSG2] + " an.\n" ); |
| say(" "+(Name(WER,1))+" greifen "+(enemy->name(WEN,1))+ |
| ainfo[SI_SKILLDAMAGE_MSG]+" an.\n", enemy ); |
| tell_object( enemy, " "+(Name(WER,1))+" greifen "+ |
| (enemy->QueryProp(P_PLURAL)?"Euch":"Dich")+ |
| ainfo[SI_SKILLDAMAGE_MSG2]+" an.\n" ); |
| } |
| else |
| { |
| tell_object( ME, " Du greifst " + enemy->name(WEN,1) |
| + ainfo[SI_SKILLDAMAGE_MSG2] + " an.\n" ); |
| say(" "+(Name(WER,2))+" greift "+(enemy->name(WEN,1))+ |
| ainfo[SI_SKILLDAMAGE_MSG]+" an.\n", enemy ); |
| tell_object( enemy, " "+(Name(WER,1))+" greift "+ |
| (enemy->QueryProp(P_PLURAL)?"Euch":"Dich")+ |
| ainfo[SI_SKILLDAMAGE_MSG2]+" an.\n" ); } |
| } |
| else if ( Query(P_SHOW_ATTACK_MSG) ) |
| if (QueryProp(P_PLURAL)) |
| tell_object( ME, " Ihr greift " + enemy->name(WEN,1) |
| + ainfo[SI_SKILLDAMAGE_MSG2] + " an.\n" ); |
| else |
| tell_object( ME, " Du greifst " + enemy->name(WEN,1) |
| + ainfo[SI_SKILLDAMAGE_MSG2] + " an.\n" ); |
| |
| // ainfo in defendinfo merken |
| edefendinfo[ ORIGINAL_AINFO]= deep_copy(ainfo); |
| edefendinfo[ ORIGINAL_DAM]= ainfo[SI_SKILLDAMAGE]; |
| edefendinfo[ ORIGINAL_DAMTYPE]= ainfo[SI_SKILLDAMAGE_TYPE]; |
| edefendinfo[ CURRENT_DAM]= ainfo[SI_SKILLDAMAGE]; |
| edefendinfo[ CURRENT_DAMTYPE]= ainfo[SI_SKILLDAMAGE_TYPE]; |
| |
| // ainfo[SI_SPELL] auf ein mapping normieren |
| if ( intp(ainfo[SI_SPELL]) ) |
| { |
| ainfo[SI_SPELL] = ([ SP_PHYSICAL_ATTACK : !ainfo[SI_SPELL], |
| SP_SHOW_DAMAGE : !ainfo[SI_SPELL], |
| SP_REDUCE_ARMOUR : ([ AT_DEFAULT_REDUCE: 100 ]) ]); |
| } |
| |
| // defendinfo anhaengen, falls ainfo[SI_SPELL] ein mapping ist |
| if( mappingp(ainfo[SI_SPELL])) |
| { |
| ainfo[SI_SPELL][EINFO_DEFEND]=edefendinfo; |
| } |
| |
| |
| enemy->Defend(ainfo[SI_SKILLDAMAGE], ainfo[SI_SKILLDAMAGE_TYPE], |
| ainfo[SI_SPELL], this_object()); |
| |
| |
| //edefendinfo=([]); |
| |
| /* Done attacking */ |
| } |
| |
| public void AddDefender(object friend) |
| { |
| if(!objectp(friend)) |
| return; |
| |
| object* defs = QueryProp(P_DEFENDERS) || ({}); |
| defs -= ({0}); |
| |
| if(friend in defs) |
| return; |
| |
| defs += ({friend}); |
| SetProp(P_DEFENDERS, defs); |
| } |
| |
| public void RemoveDefender(object friend) |
| { |
| if ( !objectp(friend) ) |
| return; |
| |
| object* defs = QueryProp(P_DEFENDERS); |
| defs -= ({0}); |
| |
| if(!(friend in defs)) |
| return; |
| |
| defs -= ({friend}); |
| SetProp(P_DEFENDERS, defs); |
| } |
| |
| public object* QueryDefenders() |
| { |
| return filter(QueryProp(P_DEFENDERS), #'objectp); |
| } |
| |
| public object* QueryPresentDefenders(object* defenders = |
| QueryProp(P_DEFENDERS) || ({})) |
| { |
| // Alles selbst pruefen, um Durchlaeufe zu sparen. |
| return filter(defenders, |
| function int(object o) |
| { |
| return (objectp(o) && |
| (environment(o) == environment(ME) || environment(o) == ME)); |
| }); |
| } |
| |
| public object* QueryNearDefenders(object* defenders = |
| QueryProp(P_DEFENDERS) || ({})) |
| { |
| // alles selbst pruefen, um Durchlaeufe zu sparen. |
| return filter(defenders, |
| function int(object o) |
| { |
| // Nahe sind: Objekte im eigenen Inventar oder Objekt im Environment, |
| // welche in Teamreihe 1 oder 0 (haben einfach keine Teamreihe) sind. |
| return (objectp(o) && |
| ( environment(o) == ME || |
| (environment(o) == environment(ME) && o->PresentPosition() <= 1) ) |
| ); |
| } |
| ); |
| } |
| |
| public void InformDefend(object enemy) |
| { |
| UseSkill(SK_INFORM_DEFEND,([ SI_ENEMY : enemy, |
| SI_FRIEND : previous_object() ])); |
| // Oh, oh - ich hoffe mal, dass InformDefend wirklich NUR aus Defend |
| // eines befreundeten livings aufgerufen wird... (Silvana) |
| // This is only experimental... ;) |
| } |
| |
| public <int|string*|mapping>* DefendOther(int dam, string|string* dam_type, |
| int|mapping spell, object enemy) |
| { |
| <int|string*|mapping>* res; |
| |
| if ( (res=UseSkill(SK_DEFEND_OTHER,([ SI_SKILLDAMAGE : dam, |
| SI_SKILLDAMAGE_TYPE : dam_type, |
| SI_SPELL : spell, |
| SI_FRIEND : previous_object(), |
| SI_ENEMY : enemy ]))) |
| && pointerp(res) ) |
| return res; |
| |
| return 0; |
| } |
| |
| public void CheckWimpyAndFlee() |
| { |
| if ( (QueryProp(P_WIMPY)>QueryProp(P_HP)) && !TeamFlee() |
| && find_call_out("Flee")<0 ) |
| call_out(#'Flee,0,environment()); |
| } |
| |
| protected string mess(string msg,object me,object enemy) |
| { closure mname, ename; |
| string *parts; |
| int i; |
| |
| mname = symbol_function("name", me); |
| ename = symbol_function("name", enemy); |
| |
| parts=regexplode(msg,"@WE[A-Z]*[12]"); |
| for ( i=sizeof(parts)-2 ; i>=1 ; i-=2 ) |
| { |
| switch(parts[i]) |
| { |
| case "@WER1": parts[i]=funcall(mname,WER,1); break; |
| case "@WESSEN1": parts[i]=funcall(mname,WESSEN,1); break; |
| case "@WEM1": parts[i]=funcall(mname,WEM,1); break; |
| case "@WEN1": parts[i]=funcall(mname,WEN,1); break; |
| case "@WER2": parts[i]=funcall(ename,WER,1); break; |
| case "@WESSEN2": parts[i]=funcall(ename,WESSEN,1); break; |
| case "@WEM2": parts[i]=funcall(ename,WEM,1); break; |
| case "@WEN2": parts[i]=funcall(ename,WEN,1); break; |
| default: ; |
| } |
| } |
| |
| return break_string(capitalize(implode(parts,"")),78," ",1); |
| } |
| |
| // Fuer evtl. Defend-Aenderungen, ohne dass man gleich das ganze |
| // Attack ueberlagern muss: |
| protected void InternalModifyDefend(int dam, string* dt, mapping spell, object enemy) |
| { |
| return; |
| } |
| |
| protected nomask void normalize_defend_args(int dam, string|string* dam_type, |
| int|mapping spell, object enemy) |
| { |
| // this_player(), wenn kein enemy bekannt... |
| enemy ||= this_player(); |
| |
| // Schadenstyp ueberpruefen |
| if ( !pointerp(dam_type) ) |
| dam_type = ({ dam_type }); |
| |
| // Und das Spellmapping pruefen, erzeugen, ergaenzen etc. |
| if ( intp(spell) ) |
| spell = ([ SP_PHYSICAL_ATTACK : !spell, |
| SP_SHOW_DAMAGE : !spell, |
| SP_REDUCE_ARMOUR : ([ AT_DEFAULT_REDUCE: 100 ]), |
| EINFO_DEFEND : ([ORIGINAL_DAM:dam, |
| ORIGINAL_DAMTYPE:dam_type ]) |
| ]); |
| else if ( mappingp(spell) ) |
| { |
| // testen ob eine erweiterte defendinfo vorhanden ist |
| if(!member(spell,EINFO_DEFEND)) |
| { |
| // wenn nicht, koennen wir an den fehlenden Infos wenig machen, aber |
| // zumindest ergaenzen wir es und schreiben die (hier) initialen dam und |
| // dam_type rein. |
| spell[EINFO_DEFEND] = ([ORIGINAL_DAM:dam, |
| ORIGINAL_DAMTYPE:dam_type]); |
| } |
| // SP_REDUCE_ARMOUR muss auch mit zumindest dem Defaultwert |
| // AT_DEFAULT_REDUCE vorhanden sein. |
| if ( !mappingp(spell[SP_REDUCE_ARMOUR]) ) |
| spell[SP_REDUCE_ARMOUR] = ([AT_DEFAULT_REDUCE: 100]); |
| else if (!(AT_DEFAULT_REDUCE in spell[SP_REDUCE_ARMOUR]) |
| || !intp(spell[SP_REDUCE_ARMOUR][AT_DEFAULT_REDUCE]) ) |
| { |
| spell[SP_REDUCE_ARMOUR][AT_DEFAULT_REDUCE] = 100; |
| } |
| } |
| else // Illegaler spell-Parameter |
| raise_error(sprintf("Ungueltiger Typ des spell-Arguments: %d\n", |
| get_type_info(spell,0))); |
| } |
| |
| public int Defend(int dam, string|string* dam_type, int|mapping spell, object enemy) |
| { |
| mixed res,res2; |
| object *armours,tmp; |
| mixed hookData; |
| mixed hookRes; |
| int i; |
| |
| // string what, how; |
| string enname, myname; |
| |
| normalize_defend_args(&dam, &dam_type, &spell, &enemy); |
| |
| // Testen, ob dieses Lebewesen ueberhaupt angegriffen werden darf |
| if ( !this_object() || !enemy || QueryProp(P_NO_ATTACK) |
| || ( query_once_interactive(enemy) && ! interactive(enemy) ) ) |
| return 0; |
| |
| spell[EINFO_DEFEND][CURRENT_DAMTYPE]=dam_type; |
| spell[EINFO_DEFEND][CURRENT_DAM]=dam; |
| |
| // Testen, ob der Angreifer schon als Feind registriert worden ist. |
| // Wenn nein, registrieren. |
| if ( !IsEnemy(enemy) && !spell[SP_NO_ENEMY] ) |
| { |
| spell[EINFO_DEFEND][ENEMY_INSERTED]=1; |
| InsertEnemy(enemy); |
| } |
| |
| // RFR-Taktik abfangen |
| if ( !QueryProp(P_ENABLE_IN_ATTACK_OUT) ) |
| { |
| i=time()-(enemy->QueryProp(P_LAST_MOVE)); |
| // Gegner hat sich bewegt, man selbst nicht |
| if ( (i<3) && (time()-QueryProp(P_LAST_MOVE)>=5) ) |
| { |
| // Bei Erster Kampfrunde wenige Schaden |
| dam/=(4-i); |
| spell[EINFO_DEFEND][RFR_REDUCE]=dam; |
| spell[EINFO_DEFEND][CURRENT_DAM]=dam; |
| } |
| } |
| |
| // Man kann Verteidiger haben. Diese kommen als erste zum Zuge, falls sie |
| // anwesend sind. |
| object* defs_here = QueryPresentDefenders(); |
| if (sizeof(defs_here)) |
| { |
| // Die werden erstmal alle informiert. |
| defs_here->InformDefend(enemy); |
| // Leider koennten dabei Objekte zerstoert worden sein. *seufz* |
| defs_here -= ({0}); |
| |
| // Anwesende Verteidiger eintragen. |
| spell[EINFO_DEFEND][PRESENT_DEFENDERS] = defs_here; |
| |
| // Bei physischen Angriffen koennen nur Verteidiger aus Reihe 1 helfen |
| // (z.B. fuer Rueckendeckung der Kaempfer) |
| if(spell[SP_PHYSICAL_ATTACK]) |
| { |
| defs_here = QueryNearDefenders(defs_here); |
| } |
| |
| if(sizeof(defs_here)) |
| { |
| // Wenn in Frage kommende Verteidiger anwesend sind, einen davon |
| // auswaehlen... |
| <object|int|string*|mapping>* edefendtmp = ({defs_here[random(sizeof(defs_here))], 0, 0, 0}); |
| // ... und DefendOther() darin rufen. Das wirkt wie ein Defend-Hook-Light |
| res = call_other(edefendtmp[DEF_DEFENDER], "DefendOther", |
| dam, dam_type, spell, enemy); |
| // Wenn das Defender valide Daten geliefert hat und den Schaden oder |
| // Schadenstyp geaendert hat (z.B. Umwandlung von Feuer nach Eis), |
| // werden die neuen Daten uebernommen. |
| if(pointerp(res) && (sizeof(res)>=3) && intp(res[0]) |
| && pointerp(res[1])) |
| { |
| dam = res[0]; |
| edefendtmp[DEF_DAM] = dam; |
| dam_type = res[1]; |
| edefendtmp[DEF_DAMTYPE] = dam_type; |
| if(mappingp(res[2])) |
| { |
| spell = res[2]; |
| // teuer, aber geht nicht anders (Rekursion vermeiden) |
| edefendtmp[DEF_SPELL] = deep_copy(res[2]); |
| } |
| // Die Werte sind jetzt auch die aktuell wirksamen Werte. |
| spell[EINFO_DEFEND][CURRENT_DAMTYPE] = dam_type; |
| spell[EINFO_DEFEND][CURRENT_DAM] = dam; |
| } |
| spell[EINFO_DEFEND][DEFENDING_DEFENDER] = edefendtmp; |
| } |
| } // Ende Defender-Verarbeitung |
| |
| |
| // Ueber einen P_TMP_DEFEND_HOOK werden z.B. Schutzzauber gehandhabt |
| spell[EINFO_DEFEND][DEFEND_HOOK]=DI_NOHOOK; |
| if ( res=QueryProp(P_TMP_DEFEND_HOOK) ) |
| { |
| if ( pointerp(res) && (sizeof(res)>=3) && intp(res[0]) && (time()<res[0]) |
| && objectp(res[1]) && stringp(res[2]) ) |
| { |
| if ( !(res=call_other(res[1],res[2],dam,dam_type,spell,enemy)) ) |
| { |
| // Ein P_TMP_DEFEND_HOOK kann den Schaden vollstaendig abfangen, |
| // das Defend wird dann hier abgebrochen *SICK* und es wird nur |
| // noch getestet, ob man in die Flucht geschlagen wurde |
| spell[EINFO_DEFEND][DEFEND_HOOK]=DI_HOOKINTERRUPT; |
| CheckWimpyAndFlee(); |
| return 0; |
| } |
| else |
| { |
| spell[EINFO_DEFEND][DEFEND_HOOK]=DI_HOOK; |
| if ( pointerp(res) && (sizeof(res)>=3) |
| && intp(res[0] && pointerp(res[1])) ) |
| { |
| mixed edefendtmp=({0,0,0}); |
| // Der P_TMP_DEFEND_HOOK kann ebenfalls Schadenshoehe und |
| // -art sowie die Spell-Infos veraendern |
| dam=res[0]; |
| edefendtmp[HOOK_DAM]=dam; |
| dam_type=res[1]; |
| edefendtmp[HOOK_DAMTYPE]=dam_type; |
| |
| if ( mappingp(res[2]) ) |
| { |
| spell=res[2]; |
| // Waeh. Teuer. Aber geht nicht anders. |
| edefendtmp[HOOK_SPELL]=deep_copy(spell); |
| } |
| spell[EINFO_DEFEND][DEFEND_HOOK]=edefendtmp; |
| spell[EINFO_DEFEND][CURRENT_DAMTYPE]=dam_type; |
| spell[EINFO_DEFEND][CURRENT_DAM]=dam; |
| } |
| } |
| } |
| else |
| SetProp(P_TMP_DEFEND_HOOK,0); |
| } // P_TMP_DEFEND_HOOK |
| |
| // trigger defend hook |
| hookData=({dam,dam_type,spell,enemy}); |
| hookRes=HookFlow(H_HOOK_DEFEND,hookData); |
| if(hookRes && pointerp(hookRes) && sizeof(hookRes)>H_RETDATA){ |
| if(hookRes[H_RETCODE]==H_CANCELLED){ |
| spell[EINFO_DEFEND][DEFEND_HOOK]=DI_HOOKINTERRUPT; |
| CheckWimpyAndFlee(); |
| return 0; |
| } |
| else if(hookRes[H_RETCODE]==H_ALTERED && hookRes[H_RETDATA]){ |
| spell[EINFO_DEFEND][DEFEND_HOOK]=DI_HOOK; |
| if ( pointerp(hookRes[H_RETDATA]) && (sizeof(hookRes[H_RETDATA])>=3) |
| && intp(hookRes[H_RETDATA][0] && pointerp(hookRes[H_RETDATA][1])) ) |
| { |
| mixed edefendtmp=({0,0,0}); |
| // Der P_TMP_DEFEND_HOOK kann ebenfalls Schadenshoehe und |
| // -art sowie die Spell-Infos veraendern |
| dam=hookRes[H_RETDATA][0]; |
| edefendtmp[HOOK_DAM]=dam; |
| dam_type=hookRes[H_RETDATA][1]; |
| edefendtmp[HOOK_DAMTYPE]=dam_type; |
| |
| if ( mappingp(hookRes[H_RETDATA][2]) ) |
| { |
| spell=hookRes[H_RETDATA][2]; |
| // Teuer, teuer... :-( |
| edefendtmp[HOOK_SPELL]=deep_copy(spell); |
| } |
| spell[EINFO_DEFEND][DEFEND_HOOK]=edefendtmp; |
| spell[EINFO_DEFEND][CURRENT_DAMTYPE]=dam_type; |
| spell[EINFO_DEFEND][CURRENT_DAM]=dam; |
| } |
| } |
| } // Ende Hook-Behandlung |
| |
| // Es gibt auch Parierwaffen, |
| if ( objectp(tmp=QueryProp(P_PARRY_WEAPON)) ) |
| { |
| int schutz=tmp->QueryDefend(dam_type, spell, enemy); |
| // Reduzierbare Wirksamkeit der Parierwaffe? |
| int gewicht; |
| if ( (P_PARRY_WEAPON in spell[SP_REDUCE_ARMOUR]) |
| && intp(gewicht=spell[SP_REDUCE_ARMOUR][P_PARRY_WEAPON]) |
| && (gewicht>=0) ) |
| { |
| schutz=(schutz*gewicht)/100; |
| } |
| else |
| schutz=(schutz*spell[SP_REDUCE_ARMOUR][AT_DEFAULT_REDUCE])/100; |
| |
| dam-=schutz; |
| spell[EINFO_DEFEND][CURRENT_DAM]=dam; |
| } |
| |
| // Jetzt kommen die Ruestungen des Lebewesens ins Spiel (wenn es denn |
| // welche traegt) |
| |
| armours=QueryProp(P_ARMOURS)-({0}); |
| if ( (i=sizeof(armours))>0 ) { |
| string aty; |
| |
| tmp=armours[random(i)]; |
| |
| if ( objectp(tmp) ) |
| //Uebergabe des Mappings eh als Pointer/Referenz, daher billig |
| tmp->TakeFlaw(dam_type,spell[EINFO_DEFEND]); |
| |
| // pro Ruestung ein Key an Platz reservieren |
| spell[EINFO_DEFEND][DEFEND_ARMOURS]=m_allocate(i,2); |
| foreach(object armour : armours) { |
| if ( objectp(armour) ) |
| { |
| aty=armour->QueryProp(P_ARMOUR_TYPE); |
| int schutz=armour->QueryDefend(dam_type, spell, enemy); |
| int gewicht; |
| if ( (aty in spell[SP_REDUCE_ARMOUR]) |
| && intp(gewicht=spell[SP_REDUCE_ARMOUR][aty]) && (gewicht>=0) ) |
| dam -= (gewicht*schutz)/100; |
| else |
| dam -= (schutz*spell[SP_REDUCE_ARMOUR][AT_DEFAULT_REDUCE])/100; |
| // Schaden NACH DefendFunc vermerken. |
| // Schutzwirkung VOR DefendFunc (DEF_ARMOUR_PROT) vermerkt |
| // das QueryDefend() selber. |
| spell[EINFO_DEFEND][DEFEND_ARMOURS][armour,DEF_ARMOUR_DAM]=dam; |
| // akt. Schaden vermerken und Schutz der aktuellen Ruestung (vor |
| // DefendFunc) wieder auf 0 setzen fuer den naechsten Durchlauf. |
| spell[EINFO_DEFEND][CURRENT_DAM]=dam; |
| spell[EINFO_DEFEND][DEFEND_CUR_ARMOUR_PROT]=0; |
| } |
| } |
| } |
| |
| // Manche Gilden haben einen Verteidigungsskill. Der kommt jetzt zum |
| // tragen. Der Skill kann die Schadenshoehe und -art beeinflussen. |
| spell[EINFO_DEFEND]+=([DEFEND_GUILD:({})]); |
| if ( mappingp(res=UseSkill(SK_MAGIC_DEFENSE, |
| ([ SI_ENEMY : enemy, |
| SI_SKILLDAMAGE : dam, |
| SI_SKILLDAMAGE_TYPE : dam_type, |
| SI_SPELL : spell ]))) ) |
| { |
| dam=res[SI_SKILLDAMAGE]; |
| |
| if ( pointerp(res[SI_SKILLDAMAGE_TYPE]) ) |
| dam_type=res[SI_SKILLDAMAGE_TYPE]; |
| |
| spell[EINFO_DEFEND][CURRENT_DAMTYPE]=dam_type; |
| spell[EINFO_DEFEND][CURRENT_DAM]=dam; |
| spell[EINFO_DEFEND][DEFEND_GUILD]=({dam,dam_type}); |
| } |
| |
| // Evtl. interne Modifikationen |
| InternalModifyDefend(&dam, &dam_type, &spell, &enemy); |
| |
| spell[EINFO_DEFEND][CURRENT_DAMTYPE]=dam_type; |
| spell[EINFO_DEFEND][CURRENT_DAM]=dam; |
| |
| // Testen, ob irgendwas im Inventory des Lebewesen auf den Angriff |
| // "reagiert" |
| CheckSensitiveAttack(dam,dam_type,spell,enemy); |
| |
| if ( !objectp(enemy) ) |
| return 0; |
| |
| // Angriffszeit im Gegner setzen |
| enemy->SetProp(P_LAST_COMBAT_TIME,time()); |
| |
| // Die Resistenzen des Lebewesens (natuerliche und durch Ruestungen |
| // gesetzte) beruecksichtigen |
| dam = to_int(CheckResistance(dam_type)*dam); |
| spell[EINFO_DEFEND][DEFEND_RESI]=dam; |
| |
| spell[EINFO_DEFEND][CURRENT_DAM]=dam; |
| |
| // Bei physikalischen Angriffen wird die natuerliche Panzerung und die |
| // Geschicklichkeit des Lebewesens beruecksichtigt |
| object stat = find_object("/d/erzmagier/zesstra/pacstat"); // TODO: remove |
| if ( spell[SP_PHYSICAL_ATTACK] ) |
| { |
| // Schutz bestimmen, Minimum 1, aber nur wenn P_BODY > 0 |
| // Um Rundungsverluste zu reduzieren, wird P_BODY anfangs mit 10000 |
| // skaliert. Beim runterskalieren wird aufgerundet (Addition von |
| // 5000 vor Division). |
| int body = (QueryProp(P_BODY)+QueryAttribute(A_DEX)) * 10000; |
| int schutz = ((body/4 + random(body*3/4 + 1) + 5000)/10000) || 1; |
| if (stat) |
| stat->bodystat(body, schutz, (random(body)+1)/10000); |
| |
| // Reduzierbare Wirksamkeit des Bodies? |
| int gewicht; |
| if ( (P_BODY in spell[SP_REDUCE_ARMOUR]) |
| && intp(gewicht=spell[SP_REDUCE_ARMOUR][P_BODY]) && (gewicht>=0) ) |
| schutz=(schutz*gewicht)/100; |
| else |
| schutz=(schutz*spell[SP_REDUCE_ARMOUR][AT_DEFAULT_REDUCE])/100; |
| |
| dam-=schutz; |
| } |
| spell[EINFO_DEFEND][DEFEND_BODY]=dam; |
| spell[EINFO_DEFEND][CURRENT_DAM]=dam; |
| |
| // Ist ueberhaupt noch etwas vom Schaden uebrig geblieben? |
| if ( dam<0 ) |
| dam = 0; |
| spell[EINFO_DEFEND][CURRENT_DAM]=dam; |
| |
| // fuer die Statistik |
| // TODO: entfernen nach Test-Uptime |
| if (stat) |
| stat->damagestat(spell[EINFO_DEFEND]); |
| |
| // Die Anzahl der abzuziehenden Lebenspunkte ist der durch 10 geteilte |
| // Schadenswert |
| dam = dam / 10; |
| spell[EINFO_DEFEND][DEFEND_LOSTLP]=dam; |
| |
| // evtl. hat entweder der Aufrufer oder das Lebewesen selber eigene |
| // Schadensmeldungen definiert. Die vom Aufrufer haben Prioritaet. |
| mixed dam_msg = spell[SP_SHOW_DAMAGE]; |
| // Wenn != 0 (d.h. Treffermeldungen grundsaetzlich erwuenscht), aber kein |
| // Array, hier im Living fragen. |
| if (dam_msg && !pointerp(dam_msg)) |
| dam_msg = QueryProp(P_DAMAGE_MSG); |
| |
| // In den meisten Faellen soll auch eine Schadensmeldung ausgegeben |
| // werden, die die Hoehe des Schadens (grob) anzeigt |
| if (spell[SP_SHOW_DAMAGE] && !pointerp(dam_msg)) { |
| myname=name(WEN); |
| enname=enemy->Name(WER); |
| if (enemy->QueryProp(P_PLURAL)) { |
| switch (dam) { |
| case 0: |
| tell_object(enemy,sprintf(" Ihr verfehlt %s.\n",myname)); |
| tell_object(this_object(),sprintf(" %s verfehlen Dich.\n",enname)); |
| tell_room(environment(enemy)||environment(this_object()), |
| sprintf(" %s verfehlen %s.\n",enname,myname), |
| ({ enemy, this_object() })); |
| break; |
| case 1: |
| tell_object(enemy,sprintf(" Ihr kitzelt %s am Bauch.\n",myname)); |
| tell_object(this_object(), |
| sprintf(" %s kitzeln Dich am Bauch.\n",enname)); |
| tell_room(environment(enemy)||environment(this_object()), |
| sprintf(" %s kitzeln %s am Bauch.\n",enname,myname), |
| ({ enemy, this_object() })); |
| break; |
| case 2..3: |
| tell_object(enemy,sprintf(" Ihr kratzt %s.\n",myname)); |
| tell_object(this_object(),sprintf(" %s kratzen Dich.\n",enname)); |
| tell_room(environment(enemy)||environment(this_object()), |
| sprintf(" %s kratzen %s.\n",enname,myname), |
| ({ enemy, this_object() })); |
| break; |
| case 4..5: |
| tell_object(enemy,sprintf(" Ihr trefft %s.\n",myname)); |
| tell_object(this_object(),sprintf(" %s treffen Dich.\n",enname)); |
| tell_room(environment(enemy)||environment(this_object()), |
| sprintf(" %s treffen %s.\n",enname,myname), |
| ({ enemy, this_object() })); |
| break; |
| case 6..10: |
| tell_object(enemy,sprintf(" Ihr trefft %s hart.\n",myname)); |
| tell_object(this_object(),sprintf(" %s treffen Dich hart.\n",enname)); |
| tell_room(environment(enemy)||environment(this_object()), |
| sprintf(" %s treffen %s hart.\n",enname,myname), |
| ({ enemy, this_object() })); |
| break; |
| case 11..20: |
| tell_object(enemy,sprintf(" Ihr trefft %s sehr hart.\n",myname)); |
| tell_object(this_object(), |
| sprintf(" %s treffen Dich sehr hart.\n",enname)); |
| tell_room(environment(enemy)||environment(this_object()), |
| sprintf(" %s treffen %s sehr hart.\n",enname,myname), |
| ({ enemy, this_object() })); |
| break; |
| case 21..30: |
| tell_object(enemy, |
| sprintf(" Ihr schlagt %s mit dem Krachen brechender Knochen.\n", |
| myname)); |
| tell_object(this_object(), |
| sprintf(" %s schlagen Dich mit dem Krachen brechender Knochen.\n", |
| enname)); |
| tell_room(environment(enemy)||environment(this_object()), |
| sprintf(" %s schlagen %s mit dem Krachen brechender Knochen.\n", |
| enname,myname), ({ enemy, this_object() })); |
| break; |
| case 31..50: |
| tell_object(enemy, |
| sprintf(" Ihr zerschmettert %s in kleine Stueckchen.\n",myname)); |
| tell_object(this_object(), |
| sprintf(" %s zerschmettern Dich in kleine Stueckchen.\n", |
| enname)); |
| tell_room(environment(enemy)||environment(this_object()), |
| sprintf(" %s zerschmettern %s in kleine Stueckchen.\n", |
| enname,myname), ({ enemy, this_object() })); |
| break; |
| case 51..75: |
| tell_object(enemy,sprintf(" Ihr schlagt %s zu Brei.\n",myname)); |
| tell_object(this_object(), |
| sprintf(" %s schlagen Dich zu Brei.\n",enname)); |
| tell_room(environment(enemy)||environment(this_object()), |
| sprintf(" %s schlagen %s zu Brei.\n",enname,myname), |
| ({ enemy, this_object() })); |
| break; |
| case 76..100: |
| tell_object(enemy,sprintf(" Ihr pulverisiert %s.\n",myname)); |
| tell_object(this_object(),sprintf(" %s pulverisieren Dich.\n",enname)); |
| tell_room(environment(enemy)||environment(this_object()), |
| sprintf(" %s pulverisieren %s.\n",enname,myname), |
| ({ enemy, this_object() })); |
| break; |
| case 101..150: |
| tell_object(enemy,sprintf(" Ihr zerstaeubt %s.\n",myname)); |
| tell_object(this_object(),sprintf(" %s zerstaeuben Dich.\n",enname)); |
| tell_room(environment(enemy)||environment(this_object()), |
| sprintf(" %s zerstaeuben %s.\n",enname,myname), |
| ({ enemy, this_object() })); |
| break; |
| case 151..200: |
| tell_object(enemy,sprintf(" Ihr atomisiert %s.\n",myname)); |
| tell_object(this_object(),sprintf(" %s atomisieren Dich.\n",enname)); |
| tell_room(environment(enemy)||environment(this_object()), |
| sprintf(" %s atomisieren %s.\n",enname,myname), |
| ({ enemy, this_object() })); |
| break; |
| default: |
| tell_object(enemy,sprintf(" Ihr vernichtet %s.\n",myname)); |
| tell_object(this_object(),sprintf(" %s vernichten Dich.\n",enname)); |
| tell_room(environment(enemy)||environment(this_object()), |
| sprintf(" %s vernichten %s.\n",enname,myname), |
| ({ enemy, this_object() })); |
| break; |
| } |
| |
| } |
| else { |
| switch (dam) { |
| case 0: |
| tell_object(enemy,sprintf(" Du verfehlst %s.\n",myname)); |
| tell_object(this_object(),sprintf(" %s verfehlt Dich.\n",enname)); |
| tell_room(environment(enemy)||environment(this_object()), |
| sprintf(" %s verfehlt %s.\n",enname,myname), |
| ({ enemy, this_object() })); |
| break; |
| case 1: |
| tell_object(enemy,sprintf(" Du kitzelst %s am Bauch.\n",myname)); |
| tell_object(this_object(), |
| sprintf(" %s kitzelt Dich am Bauch.\n",enname)); |
| tell_room(environment(enemy)||environment(this_object()), |
| sprintf(" %s kitzelt %s am Bauch.\n",enname,myname), |
| ({ enemy, this_object() })); |
| break; |
| case 2..3: |
| tell_object(enemy,sprintf(" Du kratzt %s.\n",myname)); |
| tell_object(this_object(),sprintf(" %s kratzt Dich.\n",enname)); |
| tell_room(environment(enemy)||environment(this_object()), |
| sprintf(" %s kratzt %s.\n",enname,myname), |
| ({ enemy, this_object() })); |
| break; |
| case 4..5: |
| tell_object(enemy,sprintf(" Du triffst %s.\n",myname)); |
| tell_object(this_object(),sprintf(" %s trifft Dich.\n",enname)); |
| tell_room(environment(enemy)||environment(this_object()), |
| sprintf(" %s trifft %s.\n",enname,myname), |
| ({ enemy, this_object() })); |
| break; |
| case 6..10: |
| tell_object(enemy,sprintf(" Du triffst %s hart.\n",myname)); |
| tell_object(this_object(),sprintf(" %s trifft Dich hart.\n",enname)); |
| tell_room(environment(enemy)||environment(this_object()), |
| sprintf(" %s trifft %s hart.\n",enname,myname), |
| ({ enemy, this_object() })); |
| break; |
| case 11..20: |
| tell_object(enemy,sprintf(" Du triffst %s sehr hart.\n",myname)); |
| tell_object(this_object(), |
| sprintf(" %s trifft Dich sehr hart.\n",enname)); |
| tell_room(environment(enemy)||environment(this_object()), |
| sprintf(" %s trifft %s sehr hart.\n",enname,myname), |
| ({ enemy, this_object() })); |
| break; |
| case 21..30: |
| tell_object(enemy, |
| sprintf(" Du schlaegst %s mit dem Krachen brechender Knochen.\n", |
| myname)); |
| tell_object(this_object(), |
| sprintf(" %s schlaegt Dich mit dem Krachen brechender Knochen.\n", |
| enname)); |
| tell_room(environment(enemy)||environment(this_object()), |
| sprintf(" %s schlaegt %s mit dem Krachen brechender Knochen.\n", |
| enname,myname), ({ enemy, this_object() })); |
| break; |
| case 31..50: |
| tell_object(enemy, |
| sprintf(" Du zerschmetterst %s in kleine Stueckchen.\n",myname)); |
| tell_object(this_object(), |
| sprintf(" %s zerschmettert Dich in kleine Stueckchen.\n",enname)); |
| tell_room(environment(enemy)||environment(this_object()), |
| sprintf(" %s zerschmettert %s in kleine Stueckchen.\n", |
| enname,myname), ({ enemy, this_object() })); |
| break; |
| case 51..75: |
| tell_object(enemy,sprintf(" Du schlaegst %s zu Brei.\n",myname)); |
| tell_object(this_object(), |
| sprintf(" %s schlaegt Dich zu Brei.\n",enname)); |
| tell_room(environment(enemy)||environment(this_object()), |
| sprintf(" %s schlaegt %s zu Brei.\n",enname,myname), |
| ({ enemy, this_object() })); |
| break; |
| case 76..100: |
| tell_object(enemy,sprintf(" Du pulverisierst %s.\n",myname)); |
| tell_object(this_object(),sprintf(" %s pulverisiert Dich.\n",enname)); |
| tell_room(environment(enemy)||environment(this_object()), |
| sprintf(" %s pulverisiert %s.\n",enname,myname), |
| ({ enemy, this_object() })); |
| break; |
| case 101..150: |
| tell_object(enemy,sprintf(" Du zerstaeubst %s.\n",myname)); |
| tell_object(this_object(),sprintf(" %s zerstaeubt Dich.\n",enname)); |
| tell_room(environment(enemy)||environment(this_object()), |
| sprintf(" %s zerstaeubt %s.\n",enname,myname), |
| ({ enemy, this_object() })); |
| break; |
| case 151..200: |
| tell_object(enemy,sprintf(" Du atomisierst %s.\n",myname)); |
| tell_object(this_object(),sprintf(" %s atomisiert Dich.\n",enname)); |
| tell_room(environment(enemy)||environment(this_object()), |
| sprintf(" %s atomisiert %s.\n",enname,myname), |
| ({ enemy, this_object() })); |
| break; |
| default: |
| tell_object(enemy,sprintf(" Du vernichtest %s.\n",myname)); |
| tell_object(this_object(),sprintf(" %s vernichtet Dich.\n",enname)); |
| tell_room(environment(enemy)||environment(this_object()), |
| sprintf(" %s vernichtet %s.\n",enname,myname), |
| ({ enemy, this_object() })); |
| break; |
| } |
| } |
| } |
| |
| // Man kann auch selbst-definierte Schadensmeldungen ausgeben lassen |
| else if( spell[SP_SHOW_DAMAGE] && pointerp(dam_msg) ) |
| { |
| for( i=sizeof(dam_msg) ; --i >= 0 ; ) |
| { |
| if ( dam>dam_msg[i][0] ) |
| { |
| tell_object(ME,mess(dam_msg[i][1],ME,enemy)); |
| tell_object(enemy,mess(dam_msg[i][2],ME,enemy)); |
| say(mess(dam_msg[i][3],ME,enemy), enemy); |
| break; |
| } |
| } |
| } |
| // else (!spell[SP_SHOW_DAMAGE]) keine Schadensmeldung. |
| |
| // Informationen ueber den letzten Angriff festhalten |
| Set(P_LAST_DAMTYPES, dam_type); |
| Set(P_LAST_DAMTIME, time()); |
| Set(P_LAST_DAMAGE, dam); |
| |
| // Bei Angriffen mit SP_NO_ENEMY-Flag kann man nicht sterben ... |
| if ( spell[SP_NO_ENEMY] ) |
| reduce_hit_points(dam); |
| // ... bei allen anderen natuerlich schon |
| else |
| do_damage(dam,enemy); |
| |
| // evtl. ist dies Objekt hier tot... |
| if (!objectp(ME)) return dam; |
| |
| // Testen, ob man in die Fucht geschlagen wird |
| CheckWimpyAndFlee(); |
| |
| // Verursachten Schaden (in LP) zurueckgeben |
| return dam; |
| } |
| |
| public float CheckResistance(string *dam_type) { |
| //funktion kriegt die schadensarten uebergeben, schaut sich dieses |
| //sowie P_RESISTENCE_STRENGTH und P_RESITENCE_MODFIFIER an und berechnet |
| //einen Faktor, mit dem die urspruengliche Schadensmenge multipliziert |
| //wird, um den Schaden nach Beruecksichtigung der Resis/Anfaelligkeiten zu |
| //erhalten. Rueckgabewert normalerweise (s.u.) >=0. |
| mapping rstren, mod; |
| float faktor,n; |
| int i; |
| |
| mod = Query(P_RESISTANCE_MODIFIER); |
| if ( mappingp(mod) ) |
| mod = mod["me"]; |
| |
| if ( !mappingp(rstren=Query(P_RESISTANCE_STRENGTHS)) ) |
| { |
| if (!mappingp(mod)) |
| return 1.0; |
| else |
| rstren = ([]); |
| } |
| |
| if ( !mappingp(mod) ) |
| mod = ([]); |
| |
| if ( (i=sizeof(dam_type))<1 ) |
| return 1.0; |
| |
| n=to_float(i); |
| faktor=0.0; |
| |
| //dies hier tut nicht mehr das gewuenschte, wenn in P_RESISTENCE_STRENGTHS |
| //Faktoren <-1.0 angegeben werden. Rueckgabewerte <0 sind dann moeglich und |
| //leider werden die Schadensarten ohne Resis nicht mehr richtig verwurstet. :-/ |
| foreach(string dt: dam_type) { |
| faktor = faktor + (1.0 + to_float(rstren[dt])) |
| * (1.0 + to_float(mod[dt]))-1.0; |
| } |
| return 1.0+(faktor/n); |
| } |
| |
| public varargs mapping StopHuntingMode(int silent) |
| { mapping save_enemy; |
| |
| save_enemy=enemies; |
| if ( !silent ) |
| walk_mapping(enemies, #'StopHuntText); //#'); |
| |
| enemies=m_allocate(0,1); |
| last_attack_msg=0; |
| |
| return save_enemy; |
| } |
| |
| public <object*|int*>* QueryEnemies() |
| { |
| return ({m_indices(enemies),m_values(enemies)}); |
| } |
| |
| public mapping GetEnemies() |
| { |
| return enemies; |
| } |
| |
| public mapping SetEnemies(<object*|int*>* myenemies) |
| { |
| enemies=mkmapping(myenemies[0],myenemies[1]); |
| return enemies; |
| } |
| |
| private string _kill_alias( string str ) |
| { |
| return "\\" + str; |
| } |
| |
| public varargs void Flee( object oldenv, int force ) |
| { mixed *exits, exit, dex; |
| mapping tmp; |
| int i; |
| object env; |
| |
| if ( !environment() ) |
| return; |
| |
| // mit 'force' kann man die Checks umgehen, damit der Spieler auf jeden |
| // Fall fluechtet ... |
| if ( !force && ( oldenv && (oldenv != environment()) || |
| query_once_interactive(ME) && (!EnemyPresent() || |
| (QueryProp(P_HP) >= QueryProp(P_WIMPY))) ) ) |
| return; |
| |
| // ... aber Magier fluechten zu lassen ist nicht ganz so einfach ;-) |
| if ( query_once_interactive(this_object()) && IS_LEARNING(this_object()) ) |
| return; |
| |
| // Geister brauchen nicht um ihr Leben zu fuerchten |
| if ( QueryProp(P_GHOST) ) |
| return; |
| |
| tell_object( ME, "Die Angst ist staerker als Du ... "+ |
| "Du willst nur noch weg hier.\n"); |
| |
| if ( TeamFlee() ) // Erfolgreiche Flucht in hintere Kampfreihe? |
| return; |
| |
| env = environment(); |
| tmp = environment()->QueryProp(P_EXITS); |
| exits = m_indices(tmp); |
| tmp = environment()->QueryProp(P_SPECIAL_EXITS); |
| exits += m_indices(tmp); |
| |
| if ( query_once_interactive(ME) ) |
| exits = map( exits, #'_kill_alias/*'*/ ); |
| |
| // die Fluchtrichtung von Magiern wird aus Sicherheitsgruenden |
| // nicht ausgewertet |
| if ( interactive(ME) && IS_SEER(ME) && !IS_LEARNER(ME) |
| && (dex=QueryProp(P_WIMPY_DIRECTION)) ) |
| { |
| i = 60 + 4 * (QueryProp(P_LEVEL) - 30) / 3; |
| exits += ({dex}); // bevorzugte Fluchtrichtung mindestens einmal |
| } |
| |
| if ( !sizeof(exits) ) |
| { |
| tell_object( ME, "Du versuchst zu fliehen, schaffst es aber nicht.\n" ); |
| |
| return; |
| } |
| |
| while ( sizeof(exits) && (environment()==env) ) |
| { |
| if ( dex // Vorzugsweise Fluchtrichtung? |
| && (member(exits,dex) >= 0) // moeglich? |
| && (random(100) <= i)) // und Wahrscheinlichkeit gross genug? |
| exit = dex; |
| else |
| exit = exits[random(sizeof(exits))]; |
| |
| catch(command(exit);publish); |
| exits -= ({exit}); |
| } |
| |
| if ( environment()==env ) |
| tell_object( ME, "Dein Fluchtversuch ist gescheitert.\n" ); |
| } |
| |
| public object EnemyPresent() |
| { |
| foreach(object en: enemies) { |
| if (environment()==environment(en)) |
| return en; |
| } |
| return 0; |
| } |
| |
| public object InFight() |
| { |
| return EnemyPresent(); |
| } |
| |
| public varargs int StopHuntID(string str, int silent) { |
| |
| if ( !stringp(str) ) |
| return 0; |
| |
| int j; |
| foreach(object en: enemies) { |
| if (en->id(str)) { |
| StopHuntFor(en,silent); |
| j++; |
| } |
| } |
| |
| return j; |
| } |
| |
| public int SpellDefend(object caster, mapping sinfo) |
| { int re; |
| mixed res; |
| string *ind; |
| |
| re = UseSkill(SK_SPELL_DEFEND,([ SI_SKILLARG : sinfo , |
| SI_ENEMY : caster ]) ); |
| |
| if ( (res=QueryProp(P_MAGIC_RESISTANCE_OFFSET)) && mappingp(res) |
| && pointerp(sinfo[SI_MAGIC_TYPE])) |
| { |
| ind = m_indices(res) & sinfo[SI_MAGIC_TYPE]; |
| |
| if (pointerp(ind) && sizeof(ind) ) { |
| foreach(string index : ind) |
| re+=res[index]; |
| } |
| } |
| else if(res && intp(res)) |
| re+=res; |
| |
| if ( (re>3333) && query_once_interactive(this_object()) ) |
| re=3333; /* Maximal 33% Abwehrchance bei Spielern */ |
| return re; |
| } |
| |
| // **** this is property-like |
| |
| static int _set_attack_busy(mixed val) |
| { |
| if ( ((to_int(val))>5) && previous_object(1) |
| && query_once_interactive(previous_object(1)) ) |
| log_file("ATTACKBUSY",sprintf("%s %d Taeter: %O Opfer: %O\n", |
| dtime(time()),to_int(val),previous_object(1),this_object())); |
| |
| attack_busy-=to_int(val*100.0); |
| |
| if ( attack_busy<-2000 ) |
| attack_busy=-2000; |
| |
| return attack_busy; |
| } |
| |
| static int _query_attack_busy() |
| { |
| if (IS_LEARNING(ME)) |
| return 0; |
| |
| return (attack_busy<100); |
| } |
| |
| // **** local property methods |
| static int _set_wimpy(int i) |
| { |
| if ( !intp(i) || (i>QueryProp(P_MAX_HP)) || (i<0) ) |
| return 0; |
| |
| // ggf. Statusreport ausgeben |
| if (interactive(ME)) |
| status_report(DO_REPORT_WIMPY, i); |
| |
| return Set(P_WIMPY, i); |
| } |
| |
| static string _set_wimpy_dir(string s) { |
| // ggf. Statusreport ausgeben |
| if (interactive(ME)) |
| status_report(DO_REPORT_WIMPY_DIR, s); |
| return Set(P_WIMPY_DIRECTION, s, F_VALUE); |
| } |
| |
| static mixed _set_hands(mixed h) |
| { |
| if ( sizeof(h)==2 ) |
| h += ({ ({DT_BLUDGEON}) }); |
| if (!pointerp(h[2])) |
| h[2] = ({h[2]}); |
| return Set(P_HANDS, h, F_VALUE); |
| } |
| |
| //TODO normalisieren/korrigieren in updates_after_restore(). |
| static mixed _query_hands() |
| { |
| mixed *hands = Query(P_HANDS); |
| if ( !hands ) |
| return Set(P_HANDS, ({ " mit blossen Haenden", 30, ({DT_BLUDGEON})})); |
| else if ( sizeof(hands)<3 ) |
| return Set(P_HANDS, ({hands[0], hands[1], ({DT_BLUDGEON})})); |
| else if ( !pointerp(hands[2]) ) |
| return Set(P_HANDS, ({hands[0], hands[1], ({ hands[2] })})); |
| |
| return Query(P_HANDS); |
| } |
| |
| static int _query_total_wc() |
| { mixed res; |
| int totwc; |
| |
| if ( objectp(res=QueryProp(P_WEAPON)) ) |
| totwc = res->QueryProp(P_WC); |
| else if ( pointerp(res=QueryProp(P_HANDS)) && sizeof(res)>1 |
| && intp(res[1]) ) |
| totwc=res[1]; |
| else |
| totwc=30; |
| |
| totwc = ((2*totwc)+(10*QueryAttribute(A_STR)))/3; |
| |
| return Set(P_TOTAL_WC, totwc, F_VALUE); |
| } |
| |
| static int _query_total_ac() { |
| |
| int totac = 0; |
| object *armours = QueryProp(P_ARMOURS); |
| object parry = QueryProp(P_PARRY_WEAPON); |
| |
| if ( member(armours,0)>=0 ) { |
| armours -= ({ 0 }); |
| } |
| |
| foreach(object armour: armours) |
| totac += armour->QueryProp(P_AC); |
| |
| if ( objectp(parry) ) |
| totac += parry->QueryProp(P_AC); |
| |
| totac += (QueryProp(P_BODY)+QueryAttribute(A_DEX)); |
| |
| return Set(P_TOTAL_AC, totac, F_VALUE); |
| } |
| |
| static mapping _query_resistance_strengths() { |
| |
| UpdateResistanceStrengths(); |
| |
| mapping rstren = copy(Query(P_RESISTANCE_STRENGTHS, F_VALUE)); |
| mapping mod = Query(P_RESISTANCE_MODIFIER, F_VALUE); |
| |
| if ( !mappingp(rstren) ) |
| rstren = ([]); |
| |
| if ( !mappingp(mod) || !mappingp(mod=mod["me"]) || !sizeof(mod) ) |
| return rstren; |
| |
| foreach(string modkey, float modvalue : mod) |
| rstren[modkey] = ((1.0+rstren[modkey])*(1.0+modvalue))-1.0; |
| |
| return rstren; |
| } |
| |
| static int _set_disable_attack(int val) |
| { |
| if (val<-100000) |
| { |
| log_file("PARALYSE_TOO_LOW", |
| sprintf("Wert zu klein: %s, Wert: %d, " |
| "Aufrufer: %O, Opfer: %O", |
| ctime(time()), |
| val,previous_object(1), |
| this_object())); |
| } |
| if ( val>30 ) |
| val=30; |
| if ( (val>=20) && previous_object(1)!=ME && query_once_interactive(ME) ) |
| log_file("PARALYSE",sprintf("%s %d Taeter: %O Opfer: %O\n", |
| ctime(time())[4..15], |
| val,previous_object(1),this_object())); |
| |
| if (time()<QueryProp(P_NEXT_DISABLE_ATTACK)) |
| { |
| // gueltige Zeitsperre existiert. |
| // Erhoehen von P_DISABLE_ATTACK geht dann nicht. (Ja, auch nicht erhoehen |
| // eines negativen P_DISABLE_ATTACK) |
| if (val >= QueryProp(P_DISABLE_ATTACK)) |
| return DISABLE_TOO_EARLY; |
| // val ist kleiner als aktuelles P_DISABLE_ATTACK - das ist erlaubt, ABER es |
| // darf die bestehende Zeitsperre nicht verringern, daher wird sie nicht |
| // geaendert. |
| return Set(P_DISABLE_ATTACK,val,F_VALUE); |
| } |
| // es existiert keine gueltige Zeitsperre - dann wird sie nun gesetzt. |
| // (Sollte val < 0 sein, wird eine Zeitsperre in der Vergangenheit gesetzt, |
| // die schon abgelaufen ist. Das ist ueberfluessig, schadet aber auch |
| // nicht.) |
| SetProp(P_NEXT_DISABLE_ATTACK,time()+val*2*__HEART_BEAT_INTERVAL__); |
| return Set(P_DISABLE_ATTACK,val); |
| } |
| |
| // Neue Verwaltung der Haende: |
| |
| // P_HANDS_USED_BY enhaelt ein Array mit allen Objekten, die Haende |
| // belegen, jedes kommt so oft vor wie Haende belegt werden. |
| |
| static mixed *_query_hands_used_by() |
| { |
| return ((Query(P_HANDS_USED_BY, F_VALUE) || ({}) ) - ({0})); |
| } |
| |
| static int _query_used_hands() |
| { |
| return sizeof(QueryProp(P_HANDS_USED_BY)); |
| } |
| |
| static int _query_free_hands() |
| { |
| return (QueryProp(P_MAX_HANDS)-QueryProp(P_USED_HANDS)); |
| } |
| |
| public varargs int UseHands(object ob, int num) |
| { mixed *h; |
| |
| if ( !ob && !(ob=previous_object(1)) ) |
| return 0; |
| |
| if ( (num<=0) && ((num=ob->QueryProp(P_HANDS))<=0) ) |
| return 0; |
| |
| h=QueryProp(P_HANDS_USED_BY)-({ob}); |
| |
| if ( (sizeof(h)+num)>QueryProp(P_MAX_HANDS) ) |
| return 0; |
| |
| foreach(int i: num) |
| h+=({ob}); |
| |
| SetProp(P_HANDS_USED_BY,h); |
| |
| return 1; |
| } |
| |
| public varargs int FreeHands(object ob) |
| { |
| if ( !ob && !(ob=previous_object(1)) ) |
| return 0; |
| |
| SetProp(P_HANDS_USED_BY,QueryProp(P_HANDS_USED_BY)-({ob})); |
| |
| return 1; |
| } |
| |
| // Kompatiblitaetsfunktionen: |
| |
| static int _set_used_hands(int new_num) |
| { int old_num, dif; |
| object ob; |
| |
| old_num=QueryProp(P_USED_HANDS); |
| |
| if ( !objectp(ob=previous_object(1)) ) |
| return old_num; |
| |
| // Meldung ins Debuglog schreiben. Aufrufer sollte gefixt werden. |
| debug_message(sprintf("P_USED_HANDS in %O wird gesetzt durch %O\n", |
| this_object(), ob), DMSG_LOGFILE|DMSG_STAMP); |
| |
| if ( !(dif=new_num-old_num) ) |
| return new_num; |
| |
| if ( dif>0 ) |
| UseHands(ob,dif); |
| else |
| FreeHands(ob); |
| |
| return QueryProp(P_USED_HANDS); |
| } |
| |
| // Funktionen fuer Begruessungsschlag / Nackenschlag: |
| |
| public int CheckEnemy(object ob) |
| { |
| return (living(ob) && IsEnemy(ob)); |
| } |
| |
| public varargs void ExecuteMissingAttacks(object *remove_attackers) |
| { |
| if ( !pointerp(missing_attacks) ) |
| missing_attacks=({}); |
| |
| if ( pointerp(remove_attackers) ) |
| missing_attacks-=remove_attackers; |
| |
| foreach(object ob : missing_attacks) { |
| if ( objectp(ob) && (environment(ob)==environment()) ) |
| ob->Attack2(ME); |
| } |
| missing_attacks=({}); |
| } |
| |
| public void InitAttack() |
| { object ob,next; |
| closure cb; |
| |
| if ( !living(ME) ) |
| return; |
| |
| ExecuteMissingAttacks(); |
| //EMA kann das Living zerstoeren oder toeten... |
| if (!living(ME) || QueryProp(P_GHOST)) return; |
| |
| if ( objectp(ob=IsTeamMove()) ) |
| cb=symbol_function("InitAttack_Callback",ob); |
| else |
| cb=0; |
| |
| for ( ob=first_inventory(environment()) ; objectp(ob) ; ob=next) |
| { |
| next=next_inventory(ob); |
| |
| if ( !living(ob) ) |
| continue; |
| |
| if (ob->IsEnemy(ME)) |
| { |
| // Das ist nicht so sinnlos wie es aussieht. a) werden die Hunttimes |
| // aktualisiert und b) werden Teammitglieder von mir bei diesem |
| // InsertEnemy() ggf. erfasst. |
| ob->InsertEnemy(ME); |
| |
| if ( closurep(cb) && funcall(cb,ob) ) // Wird ganzes Team gemoved? |
| missing_attacks += ({ ob }); // Dann erstmal warten bis alle da sind |
| else |
| ob->Attack2(ME); |
| |
| } |
| else if ( IsEnemy(ob) ) |
| { |
| // Das ist nicht so sinnlos wie es aussieht. a) werden die Hunttimes |
| // aktualisiert und b) werden Teammitglieder von ob bei diesem |
| // InsertEnemy() ggf. erfasst. |
| InsertEnemy(ob); |
| Attack2(ob); |
| } |
| //Attack2 kann dieses Objekt zerstoeren oder toeten. Wenn ja: abbruch |
| if ( !living(ME) || QueryProp(P_GHOST)) break; |
| } |
| } |
| |
| public void ExitAttack() |
| { |
| if ( !living(ME) ) |
| return; |
| |
| // Noch nachzuholende Begruessungsschlaege: |
| ExecuteMissingAttacks(); |
| } |
| |
| public object|object*|mapping QueryArmourByType(string type) |
| { |
| // Rueckgabewert: |
| // DIE Ruestung vom Typ <type>, falls <type> nicht AT_MISC, |
| // Array aller AT_MISC-Ruestungen falls <type> AT_MISC (auch leer), |
| // Mapping mit allen oben genannten Infos, falls <type> Null |
| |
| object *armours; |
| string typ2; |
| |
| // Wenn Cache vorhanden, dann Cache liefern. |
| if (mappingp(QABTCache)) { |
| if (type == AT_MISC) |
| return QABTCache[AT_MISC] - ({0}); |
| else if (type) |
| return QABTCache[type]; |
| else |
| return copy(QABTCache); |
| } |
| |
| if ( !pointerp(armours=QueryProp(P_ARMOURS)) ) |
| armours=({}); |
| |
| // Cache erzeugen |
| QABTCache = ([ AT_MISC: ({}) ]); |
| foreach(object ob: armours - ({0}) ) { |
| if ( !stringp(typ2=ob->QueryProp(P_ARMOUR_TYPE)) ) |
| continue; |
| if ( typ2==AT_MISC ) |
| QABTCache[AT_MISC] += ({ob}); |
| else |
| QABTCache[typ2] = ob; |
| } |
| // Und gewuenschtes Datum liefern. |
| if (type) |
| return QABTCache[type]; |
| else |
| return copy(QABTCache); |
| } |
| |
| //TODO: langfristig waers besser, wenn hier nicht jeder per SetProp() drauf |
| //los schreiben wuerde. |
| static object *_set_armours(object *armours) { |
| if (pointerp(armours)) { |
| // Cache wegwerfen |
| QABTCache = 0; |
| // armours ggf. unifizieren. Ausserdem Kopie reinschreiben. |
| return Set(P_ARMOURS, m_indices(mkmapping(armours)), F_VALUE); |
| } |
| return QueryProp(P_ARMOURS); |
| } |
| |
| /** Reduziert die Befriedezaehler pro Reset im Durchschnitt um 2.5. |
| Berechnet ganzzahlige durchschnittliche Resets seit dem letzten Expire und |
| erhoeht die letzte Expirezeit um Resets*__RESET_TIME__ (Standard Intervall |
| fuer Reset ist momentan 3600s, im Durchschnitt kommen dann 2700 zwischen 2 |
| Resets bei raus). So wird der unganzzahlige Rest beim naechsten Aufruf |
| beruecksichtigt. |
| Diese Variante des Expires wurde gewaehlt, um zu vermeiden, combat.c einen |
| reset() zu geben und in jedem Reset in jedem Lebewesen ein Expire machen zu |
| muessen, auch wenn nur in sehr wenigen Lebewesen was gemacht werden muss. |
| @param[in,out] ph Mapping aus P_PEACE_HISTORY. Wird direkt aktualisiert. |
| @attention Muss ein gueltiges P_PEACE_HISTORY uebergeben bekommen, anderem |
| Datentyp inkl. 0 wird die Funktion buggen. |
| */ |
| private void _decay_peace_history(mixed ph) { |
| // Ganzzahlige resets seit letztem Expire ermitteln. (Durchschnitt) |
| int resets = (time() - ph[0]) / (__RESET_TIME__*75/100); |
| // auf Zeitstempel draufrechnen, damit der unganzzahlige Rest nicht |
| // verlorengeht, der wird beim naechsten Expire dann beruecksichtigt. |
| ph[0] += resets * (__RESET_TIME__ * 75 / 100); |
| // pro Reset werden im Durchschnitt 2.5 Versuche abgezogen. (Hier werden |
| // beim Expire mal 2 und mal 3 Versuche pro Reset gerechnet. Aber falls hier |
| // viele Resets vergangen sind, ist es vermutlich eh egal, weil die Counter |
| // auf 0 fallen.) |
| int expire = resets * (random(2) + 2); |
| // ueber alle Gilden |
| mapping tmp=ph[1]; |
| foreach(string key, int count: &tmp ) { |
| count-=expire; |
| if (count < 0) count = 0; |
| } |
| } |
| |
| /** Pacify() dient zur Bestimmung, ob sich ein Lebewesen gerade |
| * befrieden lassen will. |
| * Berechnet eine Wahrscheinlichkeit nach unten stehender Formel, welche die |
| * Intelligenz dieses Lebenwesens, die Intelligenz des Casters und die |
| * bisherige Anzahl erfolgreicher Befriedungsversuche dieser Gilde eingeht. |
| * Anschliessend wird aus der Wahrscheinlichkeit und random() bestimmt, ob |
| * dieser Versuch erfolgreich ist. |
| Formel: w = (INT_CASTER + 10 - ANZ*4) / (INT_ME + 10) |
| INT_CASTER: Caster-Intelligenz, INT_ME: Intelligenz dieses Livings |
| ANZ: Anzahl erfolgreicher Befriedungsversuche |
| |
| Annahme: INT_CASTER === 22, alle Wahrscheinlichkeiten * 100 |
| |
| INT_ME Erfolgswahrscheinlichkeiten je nach Anzahl erfolgreicher Versuche |
| 1 2 3 4 5 6 7 8 |
| 0 280 240 200 160 120 80 40 0 |
| 2 233,33 200 166,67 133,33 100 66,67 33,33 0 |
| 4 200 171,43 142,86 114,29 85,71 57,14 28,57 0 |
| 6 175 150 125 100 75 50 25 0 |
| 8 155,56 133,33 111,11 88,89 66,67 44,44 22,22 0 |
| 10 140 120 100 80 60 40 20 0 |
| 12 127,27 109,09 90,91 72,73 54,55 36,36 18,18 0 |
| 14 116,67 100 83,33 66,67 50 33,33 16,67 0 |
| 16 107,69 92,31 76,92 61,54 46,15 30,77 15,38 0 |
| 18 100 85,71 71,43 57,14 42,86 28,57 14,29 0 |
| 20 93,33 80 66,67 53,33 40 26,67 13,33 0 |
| 22 87,5 75 62,5 50 37,5 25 12,5 0 |
| 24 82,35 70,59 58,82 47,06 35,29 23,53 11,76 0 |
| 26 77,78 66,67 55,56 44,44 33,33 22,22 11,11 0 |
| 28 73,68 63,16 52,63 42,11 31,58 21,05 10,53 0 |
| 30 70 60 50 40 30 20 10 0 |
| 32 66,67 57,14 47,62 38,1 28,57 19,05 9,52 0 |
| 34 63,64 54,55 45,45 36,36 27,27 18,18 9,09 0 |
| 35 62,22 53,33 44,44 35,56 26,67 17,78 8,89 0 |
| 36 60,87 52,17 43,48 34,78 26,09 17,39 8,7 0 |
| 38 58,33 50 41,67 33,33 25 16,67 8,33 0 |
| 40 56 48 40 32 24 16 8 0 |
| 42 53,85 46,15 38,46 30,77 23,08 15,38 7,69 0 |
| 44 51,85 44,44 37,04 29,63 22,22 14,81 7,41 0 |
| 46 50 42,86 35,71 28,57 21,43 14,29 7,14 0 |
| 48 48,28 41,38 34,48 27,59 20,69 13,79 6,9 0 |
| 50 46,67 40 33,33 26,67 20 13,33 6,67 0 |
| 52 45,16 38,71 32,26 25,81 19,35 12,9 6,45 0 |
| 54 43,75 37,5 31,25 25 18,75 12,5 6,25 0 |
| 56 42,42 36,36 30,3 24,24 18,18 12,12 6,06 0 |
| 58 41,18 35,29 29,41 23,53 17,65 11,76 5,88 0 |
| 60 40 34,29 28,57 22,86 17,14 11,43 5,71 0 |
| 62 38,89 33,33 27,78 22,22 16,67 11,11 5,56 0 |
| 64 37,84 32,43 27,03 21,62 16,22 10,81 5,41 0 |
| 66 36,84 31,58 26,32 21,05 15,79 10,53 5,26 0 |
| 68 35,9 30,77 25,64 20,51 15,38 10,26 5,13 0 |
| 70 35 30 25 20 15 10 5 0 |
| 72 34,15 29,27 24,39 19,51 14,63 9,76 4,88 0 |
| 74 33,33 28,57 23,81 19,05 14,29 9,52 4,76 0 |
| 76 32,56 27,91 23,26 18,6 13,95 9,3 4,65 0 |
| 78 31,82 27,27 22,73 18,18 13,64 9,09 4,55 0 |
| 80 31,11 26,67 22,22 17,78 13,33 8,89 4,44 0 |
| 82 30,43 26,09 21,74 17,39 13,04 8,7 4,35 0 |
| 84 29,79 25,53 21,28 17,02 12,77 8,51 4,26 0 |
| 86 29,17 25 20,83 16,67 12,5 8,33 4,17 0 |
| 88 28,57 24,49 20,41 16,33 12,24 8,16 4,08 0 |
| 90 28 24 20 16 12 8 4 0 |
| 92 27,45 23,53 19,61 15,69 11,76 7,84 3,92 0 |
| 94 26,92 23,08 19,23 15,38 11,54 7,69 3,85 0 |
| 96 26,42 22,64 18,87 15,09 11,32 7,55 3,77 0 |
| 98 25,93 22,22 18,52 14,81 11,11 7,41 3,7 0 |
| 100 25,45 21,82 18,18 14,55 10,91 7,27 3,64 0 |
| * @return 1, falls Befrieden erlaubt ist, 0 sonst. |
| * @param[in] caster Derjenige, der den Spruch ausfuehrt. |
| * @attention Wenn acify() 1 zurueckliefert, zaehlt dies als |
| * erfolgreicher Befriedungsversuch und in diesem Lebewesen wurde |
| * StopHuntingMode(1) aufgerufen. |
| */ |
| public int Pacify(object caster) { |
| |
| // wenn das Viech keine Gegner hat, dann witzlos. ;-) Ohne Caster gehts auch |
| // direkt raus. |
| if (!mappingp(enemies) || !sizeof(enemies) |
| || !objectp(caster)) { |
| return 0; |
| } |
| |
| // Wenn P_ACCEPT_PEACE gesetzt ist, altes Verhalten wiederspiegeln |
| // -> der NPC ist einfach immer befriedbar. Gleiches gilt fuer den Caster |
| // selber, der wird sich ja nicht gegen das eigene Befriede wehren. Und auch |
| // im team wehrt man sich nicht gegen das Befriede eines Teamkollegen |
| if (QueryProp(P_ACCEPT_PEACE)==1 || caster==ME |
| || member(TeamMembers(), caster) > -1) { |
| StopHuntingMode(1); // Caster/Gilde sollte eigene Meldung ausgeben |
| return 1; |
| } |
| |
| string gilde = caster->QueryProp(P_GUILD) || "ANY"; |
| |
| // ggf. P_PEACE_HISTORY initialisieren |
| <int|mapping>* ph = QueryProp(P_PEACE_HISTORY); |
| if (!pointerp(ph)) |
| SetProp(P_PEACE_HISTORY, ph=({time(), ([]) }) ); |
| |
| // ggf. die Zaehler reduzieren. |
| if ( ph[0] + __RESET_TIME__ * 75/100 < time()) { |
| _decay_peace_history(&ph); |
| } |
| |
| float w = (caster->QueryAttribute(A_INT) + 10 - ph[1][gilde] * 4.0) / |
| (QueryAttribute(A_INT) + 10); |
| // auf [0,1] begrenzen. |
| if (w<0) w=0.0; |
| else if (w>1) w=1.0; |
| // w * n ist eine Zahl zwischen 0 und n, wenn w * n > random(n)+1, |
| // darf befriedet werden. Da das Random fuer grosse Zahlen |
| // besser verteilt ist, nehm ich n = __INT_MAX__ und vernachlaessige |
| // ausserdem die +1 beim random(). |
| if (ceil(w * __INT_MAX__) > random(__INT_MAX__) ) { |
| ph[1][gilde]++; |
| StopHuntingMode(1); |
| return 1; |
| } |
| // ein SetProp(P_PEACE_HISTORY) kann entfallen, da das Mapping direkt |
| // geaendert wird. Sollte die Prop allerdings mal ne Querymethode haben, |
| // welche eine Kopie davon liefert, muss das hier geaendert oder die |
| // Prop per Query() abgefragt werden. |
| return 0; |
| } |
| |