blob: abb5a1b7bd51873748cc9161fd8b78ebae115016 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// player/combat.c -- combat statistics
4//
5// $Id: combat.c 9008 2015-01-06 17:20:17Z Zesstra $
6#pragma strong_types
7#pragma save_types
8#pragma range_check
9#pragma no_clone
10#pragma pedantic
11
12inherit "/std/living/combat";
13inherit "/std/player/pklog";
14
15#include <thing/properties.h>
16#include <properties.h>
17#include <wizlevels.h>
18#include <combat.h>
19#include <new_skills.h>
20
21#define ME this_object()
22#define STATMASTER "/p/service/rochus/guildstat/master"
23
24private nosave closure mod_def_stat;
25private nosave string *plAttacked = ({});
26
27protected void create() {
28 combat::create();
29 // P_NO_ATTACK ist nicht fuer Spieler gedacht. Ausnahme: Spieler ist
30 // Geist, dann setzt das Spielerobjekt aber selber.
31 Set(P_NO_ATTACK, SECURED|NOSETMETHOD, F_MODE_AS);
32
33 Set(P_HELPER_NPC, PROTECTED, F_MODE);
34 SetProp(P_HELPER_NPC, ([]) );
35}
36
37// Maske fuer alle moeglichen Klassen von Helfer-NPC
38#define CLASS_MASK 0x1fffffff
39/** registriert den NPC als Helfer von diesem Spieler.
40 @param[in] npc object Helfer-NPC
41 @param[in] flags int Bitfeld von Flags
42 @return int 1, falls der Helfer-NPC registriert wurde und noch nicht
43 registriert war.
44 @attention Nutzt aus, dass QueryProp(P_HELPER_NPC) _keine_ Kopie des
45 Mappings in der Prop liefert.
46 */
47public int RegisterHelperNPC(object npc, int flags) {
48 if (!objectp(npc))
49 raise_error(sprintf( "Wrong argument 1 in RegisterHelperNPC(). "
50 "Expected <object>, got %.100O\n", npc));
51 if (!intp(flags) || flags < 1)
52 raise_error(sprintf( "Wrong argument 2 in RegisterHelperNPC(). "
53 "Expected positive <int>, got %O\n", flags));
54
55 mapping helpers = QueryProp(P_HELPER_NPC);
56
57 // schon registrierte sind witzlos.
58 if (member(helpers, npc))
59 return 0;
60
61 // auf exklusive Helfer pruefen.
62 foreach(object helper, int fl: helpers) {
63 // flags identisch? Dann Klasse und Exklusivitaet identisch
64 if (fl == flags)
65 return 0;
66 // oder einer von beiden exklusiv und beide in der gleichen Klasse?
67 else if ( ((fl & EXCLUSIVE_HELPER) || (flags & EXCLUSIVE_HELPER))
68 && ((fl & CLASS_MASK) == (flags & CLASS_MASK)) )
69 return 0;
70 }
71 // scheint wohl ok zu sein. Registrieren und Prop im NPC setzen.
72 helpers += ([ npc: flags ]);
73 npc->SetProp(P_HELPER_NPC, ({ this_object(), flags }) );
74 // momentan unnoetig, da helpers keine Kopie ist.
75 // SetProp(P_HELPER_NPC, helpers);
76
77 return 1;
78}
79#undef CLASS_MASK
80
81/** de-registriert den NPC als Helfer von diesem Spieler.
82 @param[in] npc object Helfer-NPC
83 @return int 1, falls der Helfer-NPC registriert war und entfernt wurde.
84 @attention Nutzt aus, dass QueryProp(P_HELPER_NPC) _keine_ Kopie des
85 Mappings in der Prop liefert.
86 */
87public int UnregisterHelperNPC(object npc) {
88 if (!objectp(npc))
89 raise_error(sprintf("Wrong argument in UnregisterHelpernNPC(). "
90 "Expected <object>, got %.100O\n", npc));
91
92 mapping helpers = QueryProp(P_HELPER_NPC);
93 if (member(helpers, npc)) {
94 m_delete(helpers, npc);
95 // momentan unnoetig, da helpers keine Kopie ist.
96 // SetProp(P_HELPER_NPC, helpers);
97 npc->SetProp(P_HELPER_NPC, 0);
98 return 1;
99 }
100 return 0;
101}
102
103/** Feind eintragen.
104 * Traegt ob als Feind ein. Dies allerdings nur, wenn ob kein Spieler ist
105 * oder beide Spieler (dieses Objekt und ob) in der Schattenwelt sind oder
106 * beide Spieler Testspieler sind.
107 @param[in] ob potentieller Feind.
108 @return int 1, falls ob als _neuer_ Feind eingetragen wurde.
109 */
110public int InsertEnemy(object ob) {
111 // wenn ob ein Spieler ist und nicht sowohl ich als auch ob in der
112 // Schattenwelt sind, wird ob nicht als Feind eingetragen.
113 if (query_once_interactive(ob)
114 && (strstr(object_name(environment(ob)),"/d/schattenwelt/")!=0
115 || strstr(object_name(environment(ME)),"/d/schattenwelt/")!=0)
116 && (!QueryProp(P_TESTPLAYER) || !ob->QueryProp(P_TESTPLAYER))
117 )
118 {
119 return 0;
120 }
121 return ::InsertEnemy(ob);
122}
123
124/** Hat dieser Spieler den Spieler pl angegriffen?.
125 @param[in] pl object zu pruefender Spieler
126 @return int 1, falls dieser Spieler pl angegriffen hat.
127 @attention Nebeneffekt: bereinigt den internen Speicher von UIDs von
128 zerstoerten Spielern und solchen, die keine Feinde mehr sind.
129 */
130public int QueryPlAttacked(object pl) {
131 object ob;
132
133 if ( !objectp(pl) )
134 return 0;
135
136 foreach(string plname: plAttacked) {
137 if ( !( ob=(find_player(plname)||find_netdead(plname)) )
138 || ( !IsEnemy(ob) && !(ob->IsEnemy(ME)) ) )
139 plAttacked -= ({plname}); // ja, das geht. ;-)
140 }
141 return (member( plAttacked, getuid(pl) ) >= 0);
142}
143
144/** kill - Kampf starten.
145 * Fuegt ob der Feindesliste hinzu.
146 */
147public int Kill(object ob) {
148
149 if (!objectp(ob)) return 0;
150
151 // dies dient nur dazu, das plAttacked mapping zu bereinigen.
152 // TODO: besser machen. ;-)
153 if ( query_once_interactive(ob) && !IsEnemy(ob)) {
154 QueryPlAttacked(ME);
155 ob->QueryPlAttacked(ob); // aktualisieren ...
156 }
157
158 int res = combat::Kill(ob);
159
160 // falls ob nen Spieler ist, pruefen, ob es ein Spieler-Spieler-Angriff
161 // ist.
162 // Dabei ggf. loggen und Magier verstaendigen.
163 if (query_once_interactive(ob) && CheckPlayerAttack(ME, ob, 0))
164 {
165 if (res == -4) // feind wurde nicht eingetragen
166 tell_object(ME, "Ein goettlicher Befehl hindert Dich am Kampf.\n");
167 else
168 plAttacked += ({ getuid(ob) });
169 }
170
171 return res;
172}
173
174public int Defend(int dam, string|string* dam_type, int|mapping spell, object enemy) {
175 int delta_hp,res;
176
177 if (query_once_interactive(ME)
178 && !IS_LEARNER(ME)
179 && !objectp(get_type_info(mod_def_stat,2))) {
180 object ma;
181 if (!objectp(ma=find_object(STATMASTER)))
182 return ::Defend(dam,dam_type,spell,enemy);
183 // Statistik nur aufrufen falls Master geladen
184 mod_def_stat=symbol_function("ModifyDefendStat",ma);
185 }
186
187 if (closurep(mod_def_stat))
188 delta_hp=QueryProp(P_HP);
189
190 res=::Defend(dam,dam_type,spell,enemy);
191
192 if (closurep(mod_def_stat)) {
193 delta_hp-=QueryProp(P_HP);
194 if (delta_hp<0)
195 delta_hp=0;
196 funcall(mod_def_stat,
197 QueryProp(P_GUILD),
198 QueryProp(P_GUILD_LEVEL),
199 dam-10*delta_hp,
200 dam_type,
201 spell);
202 }
203
204 return res;
205}
206
207// Spieler koennen als Geist nicht kämpfen
208// TODO: pruefen, ob das Setzen und Loeschen der Prop in set_ghost() nicht
209// auch ausreichen wuerde. In dem Fall muesste man aber P_NO_ATTACK auch
210// speichern, da P_GHOST gespeichert wird...
211static mixed _query_no_attack()
212{
213 if ( QueryProp(P_GHOST) )
214 return 1;
215
216 return Query(P_NO_ATTACK);
217}