blob: ada4ccbca28946122483979efb01d03da124c69e [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// inpc/select.c -- Beste Ausruestung suchen
4//
5// $Id: select.c 6998 2008-08-24 17:17:46Z 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
11#define NEED_PROTOTYPES
12
13#include <thing.h>
14#include <living.h>
15#include <inpc.h>
16#include <properties.h>
17#include <combat.h>
18#include <language.h>
19
20#define ME this_object()
21
22mapping scan_objects(mixed src) {
23 // Ermittelt zu jedem Typ (Waffe, Ruestungstyp...) alle Objekte diesen Typs
24 // Gesucht wird: - Im Inventory, falls Objekt angegeben
25 // - Im Array, falls Array angegeben
26 object *obs;
27 mapping res;
Arathorn443fe7f2021-04-03 13:06:12 +020028 string x;
Arathorn0ec05092021-04-03 14:32:17 +020029 int cost,cost_limit;
MG Mud User88f12472016-06-24 23:31:02 +020030
31 if (mappingp(src))
32 return src;
33 res=([]);
34 if (objectp(src))
35 src=all_inventory(src);
36 if (!pointerp(src))
37 src=({src});
38
39 cost_limit = get_eval_cost()/3;
40 foreach(object ob: src)
41 {
42 if ( (cost=get_eval_cost()) < cost_limit )
43 {
44 log_file("rochus/raeuber_eval",
45 ctime()+"select::scan_objects(). Rest "+to_string(cost)+
Arathorn0ec05092021-04-03 14:32:17 +020046 ", i="+member(src, ob)+", Size "+to_string(sizeof(src))+".\n");
MG Mud User88f12472016-06-24 23:31:02 +020047 return res;
MG Mud User88f12472016-06-24 23:31:02 +020048 }
49 if (!objectp(ob))
50 continue;
51 if (x=ob->QueryProp(P_ARMOUR_TYPE))
52 ;
53 else if (ob->QueryProp(P_WEAPON_TYPE))
54 x=OT_WEAPON;
55 else if (ob->QueryProp(P_COMBATCMDS))
56 x=OT_COMBAT_OBJECT;
57 else
58 x=OT_MISC;
59 if (!pointerp(obs=res[x]))
60 obs=({});
61 obs+=({ob});
62 res[x]=obs;
63 }
64 return res;
65}
66
67mixed *find_object_by_type(mixed from, mixed typ) {
68 // Findet all Objekte eines Typs, z.B. alle Waffen im Monster
69 // <from> kann auch ein Mapping sein, das schon die mit scan_objects
70 // erstellt Klassifikation enthaelt, damit diese nicht mehrfach
71 // erstellt werden muss.
72 mixed res;
73
74 if (!mappingp(from))
75 from=scan_objects(from);
76 if (!mappingp(from) ||
77 !pointerp(res=from[typ]))
78 return ({});
79 return res;
80}
81
82int eval_weapon(object ob) {
83 return ob->QueryProp(P_WC);
84}
85
86object find_best_weapon(mixed from) {
87 // Findet die beste Waffe
88 int i,wc,bwc,cost,cost_limit;
89 object *res,bob;
90
91 if (!pointerp(res=find_object_by_type(from, OT_WEAPON)))
92 return 0;
93 bwc=-1;bob=0;
94
95 cost_limit = get_eval_cost()/3;
96 for (i=sizeof(res);i--;)
97 foreach(object ob: res)
98 {
99 if (!objectp(ob)) continue;
100 wc=eval_weapon(ob);
101 if (wc>bwc)
102 {
103 bob=ob;
104 bwc=wc;
105 }
106
107 if ( (cost=get_eval_cost()) < cost_limit )
108 {
109 log_file("rochus/raeuber_eval",
110 "Break in select::find_best_weapon(). Rest-Ticks "+to_string(cost)+
111 ", i = "+to_string(i)+", Size "+to_string(sizeof(res))+".\n");
112 return bob; // zurueckgeben, was bisher drinsteht.
MG Mud User88f12472016-06-24 23:31:02 +0200113 }
114 }
115 return bob;
116}
117
118varargs int wield_best_weapon(mixed from) {
119 // Zueckt die beste Waffe.
120 // Sollte mit command("zuecke_beste_waffe") aufgerufen werden,
121 // damit this_player() richtig gesetzt wird.
122 object ob,old;
123
124 // Einige NPC moegen keine Waffen. Zum Beispiel Karate-Gilden-NPC
125 // Durch Setzen von INPC_DONT_WIELD_WEAPONS kann man das Zuecken verhindern
126 if(QueryProp(INPC_DONT_WIELD_WEAPONS)) return 0;
127
128 if (!from)
129 from=ME;
130
131 if (!objectp(ob=find_best_weapon(from)))
132 return 0;
133 if (objectp(old=QueryProp(P_WEAPON)) && old!=ob) {
134 old->RemoveId(INPC_BEST_WEAPON_ID);
135 old->DoUnwield();
136 }
137 if (!ob->id(INPC_BEST_WEAPON_ID))
138 ob->AddId(INPC_BEST_WEAPON_ID);
139
140 return ob->DoWield();
141}
142
143int eval_armour(object ob) {
144 return ob->QueryProp(P_AC);
145}
146
147object find_best_armour(mixed from, mixed typ) {
148 // Findet die beste Ruestung eines Typs
Arathorn0ec05092021-04-03 14:32:17 +0200149 int ac,bac;
MG Mud User88f12472016-06-24 23:31:02 +0200150 object *res,bob;
151
152 if (!pointerp(res=find_object_by_type(from, typ)))
153 return 0;
154 bac=-1;
155 bob=0;
156 foreach(object ob: res)
157 {
158 if (!objectp(ob)) continue;
159 ac=eval_armour(ob);
160 if (ac>bac)
161 {
162 bob=ob;
163 bac=ac;
164 }
165 }
166 return bob;
167}
168
169object *find_best_armours(mixed from) {
170 // Findet die besten Ruestungen, die gleichzeitig getragen werden koennen
Arathorn443fe7f2021-04-03 13:06:12 +0200171 mapping ol = scan_objects(from);
172 if (!mappingp(ol))
MG Mud User88f12472016-06-24 23:31:02 +0200173 return ({});
Arathorn443fe7f2021-04-03 13:06:12 +0200174
175 object* res = ol[AT_MISC];
176 if (!pointerp(res))
177 res = ({});
178
179 object ob;
180 foreach(string typ: m_indices(ol))
181 {
MG Mud User88f12472016-06-24 23:31:02 +0200182 if (VALID_ARMOUR_CLASS[typ]) // anderer Armour-Typ ausser AT_MISC?
183 {
Arathorn443fe7f2021-04-03 13:06:12 +0200184 ob = find_best_armour(from, typ);
MG Mud User88f12472016-06-24 23:31:02 +0200185 if (objectp(ob))
Arathorn443fe7f2021-04-03 13:06:12 +0200186 res += ({ob});
MG Mud User88f12472016-06-24 23:31:02 +0200187 }
188 }
189 return res;
190}
191
192varargs int wear_best_armours(mixed from) {
193 // Die besten Ruestungen werden angezogen
194 // Sollte mit command("trage_beste_ruestungen") aufgerufen werden,
195 // damit this_player() richtig gesetzt wird.
196 object *na,*oa,*diff;
Arathorn0ec05092021-04-03 14:32:17 +0200197 int cost,cost_limit;
MG Mud User88f12472016-06-24 23:31:02 +0200198
199 if (!from)
200 from=ME;
201 if (!pointerp(na=find_best_armours(from)))
202 return 0;
203 if (!pointerp(oa=QueryProp(P_ARMOURS)))
204 oa=({});
205 diff=oa-na;
206 cost_limit = get_eval_cost()/3;
207 foreach(object di: diff)
208 {
209 di->RemoveId(INPC_BEST_SHIELD_ID);
210 di->DoUnwear();
211 if ( (cost=get_eval_cost()) < cost_limit )
212 {
213 log_file("rochus/raeuber_eval",
214 ctime()+"Break 1 in select::wear_best_armours(). Rest "+
Arathorn0ec05092021-04-03 14:32:17 +0200215 to_string(cost)+", i="+member(diff, di)+", Size "+
MG Mud User88f12472016-06-24 23:31:02 +0200216 to_string(sizeof(diff))+".\n");
217 return 1; // zurueckgeben, was bisher drinsteht.
MG Mud User88f12472016-06-24 23:31:02 +0200218 }
219 }
220 diff=na-oa;
221 cost_limit = get_eval_cost()/3;
222 foreach(object di: diff)
223 {
224 if ( di->QueryProp(P_ARMOUR_TYPE)==AT_SHIELD
225 && !di->id(INPC_BEST_SHIELD_ID))
226 di->AddId(INPC_BEST_SHIELD_ID);
227 di->do_wear("alles");
228 if ( (cost=get_eval_cost()) < cost_limit )
229 {
230 log_file("rochus/raeuber_eval",
231 ctime()+"Break 2 in select::wear_best_armours(). Rest "+
Arathorn0ec05092021-04-03 14:32:17 +0200232 to_string(cost)+", i="+member(diff, di)+", Size "+
MG Mud User88f12472016-06-24 23:31:02 +0200233 to_string(sizeof(diff))+".\n");
234 return 1; // zurueckgeben, was bisher drinsteht.
MG Mud User88f12472016-06-24 23:31:02 +0200235 }
236 }
237 return 1;
238}
239
240int eval_combat_object(object ob, mapping vals, object enemy) {
241 return 0;
242}
243
244varargs string
245find_best_combat_command(mixed from, object enemy, mapping pref) {
246 // Sucht den guenstigsten Befehl zur Benutzung einer Sonderwaffe heraus
247 object *obs;
Arathorn0ec05092021-04-03 14:32:17 +0200248 mixed *ind,y;
MG Mud User88f12472016-06-24 23:31:02 +0200249 mapping x;
250 string best;
Arathorn0ec05092021-04-03 14:32:17 +0200251 int j,max,val,mhp;
MG Mud User88f12472016-06-24 23:31:02 +0200252
253 if (!from)
254 from=ME;
255 if (!enemy)
256 enemy=SelectEnemy();
257 if (!mappingp(pref)) // bevorzugte Eigenschaften
258 pref=([C_MIN:5,
259 C_AVG:10,
260 C_MAX:2,
261 C_HEAL:10
262 ]);
263 best=0;max=-1;
264 if (!pointerp(obs=find_object_by_type(from,OT_COMBAT_OBJECT)))
265 return best;
266 mhp=QueryProp(P_MAX_HP)-QueryProp(P_HP);
Arathorn0ec05092021-04-03 14:32:17 +0200267 // Auskommentiert, da Resistenzen bisher nicht verwendet werden.
268 //mixed vul;
269 //if (objectp(enemy))
270 // vul=enemy->QueryProp(P_RESISTANCE_STRENGTHS);
MG Mud User88f12472016-06-24 23:31:02 +0200271 if (mhp<0) mhp=0;
272 foreach(object ob: obs)
273 {
274 if (!objectp(ob))
275 continue;
276 if (!mappingp(x=ob->QueryProp(P_COMBATCMDS)))
277 continue;
278 ind=m_indices(x);
279 for (j=sizeof(ind)-1;j>=0;j--)
280 {
281 if (!stringp(ind[j])) continue;
282 y=x[ind[j]];
283 if (mappingp(y))
284 {
285 if (val=y[C_HEAL])
286 {
287 if (val>mhp) val=mhp; // Nur MOEGLICHE Heilung beruecksichtigen
288 val*=pref[C_HEAL];
289 }
290 else
291 {
292 val=y[C_MIN]*pref[C_MIN]
293 + y[C_AVG]*pref[C_AVG]
294 + y[C_MAX]*pref[C_MAX];
295 // auskommentiert, da eval_resistance() bisher nicht implementiert
296 // ist. Zesstra, 06.08.2007
297 //if (y[C_DTYPES] && vul) // Vulnerability des Gegners bedenken...
298 // val=(int)(((float)val)*(1.0+eval_resistance(y[C_DTYPES],vul)));
299 }
300 }
301 else
302 {
303 val=1;
304 }
Arathorn0ec05092021-04-03 14:32:17 +0200305 val+=eval_combat_object(ob,y,enemy);
MG Mud User88f12472016-06-24 23:31:02 +0200306 if (val<max) continue;
307 max=val;
308 if (mappingp(y) && y[C_HEAL]>0)
309 best=sprintf(ind[j],name(RAW)); // Heilung auf sich selbst
310 else if (objectp(enemy))
311 best=sprintf(ind[j],enemy->name(RAW)); // Schaden auf Gegner
312 }
313 }
314
315 return best;
316}
317
318varargs int use_best_combat_command(mixed enemy, mixed from, mapping pref) {
319 // Fuehrt moeglichst guten Kampfeinsatz mit einer Sonderwaffe aus
320 string str;
321
322 if (stringp(enemy) && environment())
323 enemy=present(enemy,environment());
324 if (str=find_best_combat_command(from,enemy,pref))
325 return command(str);
326 return 0;
327}
328
329void add_select_commands() {
330 add_action("wield_best_weapon","zuecke_beste_waffe");
331 add_action("wear_best_armours","trage_beste_ruestungen");
332 add_action("use_best_combat_command","benutze_beste_sonderwaffe");
333}