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