blob: c162a39d6a334e237941b5755c1708aedc0ce301 [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;
MG Mud User88f12472016-06-24 23:31:02 +020029 int i,cost,cost_limit;
30
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)+
46 ", i="+to_string(i)+", Size "+to_string(sizeof(src))+".\n");
47 return res;
48 break;
49 }
50 if (!objectp(ob))
51 continue;
52 if (x=ob->QueryProp(P_ARMOUR_TYPE))
53 ;
54 else if (ob->QueryProp(P_WEAPON_TYPE))
55 x=OT_WEAPON;
56 else if (ob->QueryProp(P_COMBATCMDS))
57 x=OT_COMBAT_OBJECT;
58 else
59 x=OT_MISC;
60 if (!pointerp(obs=res[x]))
61 obs=({});
62 obs+=({ob});
63 res[x]=obs;
64 }
65 return res;
66}
67
68mixed *find_object_by_type(mixed from, mixed typ) {
69 // Findet all Objekte eines Typs, z.B. alle Waffen im Monster
70 // <from> kann auch ein Mapping sein, das schon die mit scan_objects
71 // erstellt Klassifikation enthaelt, damit diese nicht mehrfach
72 // erstellt werden muss.
73 mixed res;
74
75 if (!mappingp(from))
76 from=scan_objects(from);
77 if (!mappingp(from) ||
78 !pointerp(res=from[typ]))
79 return ({});
80 return res;
81}
82
83int eval_weapon(object ob) {
84 return ob->QueryProp(P_WC);
85}
86
87object find_best_weapon(mixed from) {
88 // Findet die beste Waffe
89 int i,wc,bwc,cost,cost_limit;
90 object *res,bob;
91
92 if (!pointerp(res=find_object_by_type(from, OT_WEAPON)))
93 return 0;
94 bwc=-1;bob=0;
95
96 cost_limit = get_eval_cost()/3;
97 for (i=sizeof(res);i--;)
98 foreach(object ob: res)
99 {
100 if (!objectp(ob)) continue;
101 wc=eval_weapon(ob);
102 if (wc>bwc)
103 {
104 bob=ob;
105 bwc=wc;
106 }
107
108 if ( (cost=get_eval_cost()) < cost_limit )
109 {
110 log_file("rochus/raeuber_eval",
111 "Break in select::find_best_weapon(). Rest-Ticks "+to_string(cost)+
112 ", i = "+to_string(i)+", Size "+to_string(sizeof(res))+".\n");
113 return bob; // zurueckgeben, was bisher drinsteht.
114 break;
115 }
116 }
117 return bob;
118}
119
120varargs int wield_best_weapon(mixed from) {
121 // Zueckt die beste Waffe.
122 // Sollte mit command("zuecke_beste_waffe") aufgerufen werden,
123 // damit this_player() richtig gesetzt wird.
124 object ob,old;
125
126 // Einige NPC moegen keine Waffen. Zum Beispiel Karate-Gilden-NPC
127 // Durch Setzen von INPC_DONT_WIELD_WEAPONS kann man das Zuecken verhindern
128 if(QueryProp(INPC_DONT_WIELD_WEAPONS)) return 0;
129
130 if (!from)
131 from=ME;
132
133 if (!objectp(ob=find_best_weapon(from)))
134 return 0;
135 if (objectp(old=QueryProp(P_WEAPON)) && old!=ob) {
136 old->RemoveId(INPC_BEST_WEAPON_ID);
137 old->DoUnwield();
138 }
139 if (!ob->id(INPC_BEST_WEAPON_ID))
140 ob->AddId(INPC_BEST_WEAPON_ID);
141
142 return ob->DoWield();
143}
144
145int eval_armour(object ob) {
146 return ob->QueryProp(P_AC);
147}
148
149object find_best_armour(mixed from, mixed typ) {
150 // Findet die beste Ruestung eines Typs
151 int i,ac,bac;
152 object *res,bob;
153
154 if (!pointerp(res=find_object_by_type(from, typ)))
155 return 0;
156 bac=-1;
157 bob=0;
158 foreach(object ob: res)
159 {
160 if (!objectp(ob)) continue;
161 ac=eval_armour(ob);
162 if (ac>bac)
163 {
164 bob=ob;
165 bac=ac;
166 }
167 }
168 return bob;
169}
170
171object *find_best_armours(mixed from) {
172 // Findet die besten Ruestungen, die gleichzeitig getragen werden koennen
Arathorn443fe7f2021-04-03 13:06:12 +0200173 mapping ol = scan_objects(from);
174 if (!mappingp(ol))
MG Mud User88f12472016-06-24 23:31:02 +0200175 return ({});
Arathorn443fe7f2021-04-03 13:06:12 +0200176
177 object* res = ol[AT_MISC];
178 if (!pointerp(res))
179 res = ({});
180
181 object ob;
182 foreach(string typ: m_indices(ol))
183 {
MG Mud User88f12472016-06-24 23:31:02 +0200184 if (VALID_ARMOUR_CLASS[typ]) // anderer Armour-Typ ausser AT_MISC?
185 {
Arathorn443fe7f2021-04-03 13:06:12 +0200186 ob = find_best_armour(from, typ);
MG Mud User88f12472016-06-24 23:31:02 +0200187 if (objectp(ob))
Arathorn443fe7f2021-04-03 13:06:12 +0200188 res += ({ob});
MG Mud User88f12472016-06-24 23:31:02 +0200189 }
190 }
191 return res;
192}
193
194varargs int wear_best_armours(mixed from) {
195 // Die besten Ruestungen werden angezogen
196 // Sollte mit command("trage_beste_ruestungen") aufgerufen werden,
197 // damit this_player() richtig gesetzt wird.
198 object *na,*oa,*diff;
199 int i,cost,cost_limit;
200
201 if (!from)
202 from=ME;
203 if (!pointerp(na=find_best_armours(from)))
204 return 0;
205 if (!pointerp(oa=QueryProp(P_ARMOURS)))
206 oa=({});
207 diff=oa-na;
208 cost_limit = get_eval_cost()/3;
209 foreach(object di: diff)
210 {
211 di->RemoveId(INPC_BEST_SHIELD_ID);
212 di->DoUnwear();
213 if ( (cost=get_eval_cost()) < cost_limit )
214 {
215 log_file("rochus/raeuber_eval",
216 ctime()+"Break 1 in select::wear_best_armours(). Rest "+
217 to_string(cost)+", i="+to_string(i)+", Size "+
218 to_string(sizeof(diff))+".\n");
219 return 1; // zurueckgeben, was bisher drinsteht.
220 break;
221 }
222 }
223 diff=na-oa;
224 cost_limit = get_eval_cost()/3;
225 foreach(object di: diff)
226 {
227 if ( di->QueryProp(P_ARMOUR_TYPE)==AT_SHIELD
228 && !di->id(INPC_BEST_SHIELD_ID))
229 di->AddId(INPC_BEST_SHIELD_ID);
230 di->do_wear("alles");
231 if ( (cost=get_eval_cost()) < cost_limit )
232 {
233 log_file("rochus/raeuber_eval",
234 ctime()+"Break 2 in select::wear_best_armours(). Rest "+
235 to_string(cost)+", i="+to_string(i)+", Size "+
236 to_string(sizeof(diff))+".\n");
237 return 1; // zurueckgeben, was bisher drinsteht.
238 break;
239 }
240 }
241 return 1;
242}
243
244int eval_combat_object(object ob, mapping vals, object enemy) {
245 return 0;
246}
247
248varargs string
249find_best_combat_command(mixed from, object enemy, mapping pref) {
250 // Sucht den guenstigsten Befehl zur Benutzung einer Sonderwaffe heraus
251 object *obs;
252 mixed *ind,y,vul;
253 mapping x;
254 string best;
255 int i,j,max,val,mhp;
256
257 if (!from)
258 from=ME;
259 if (!enemy)
260 enemy=SelectEnemy();
261 if (!mappingp(pref)) // bevorzugte Eigenschaften
262 pref=([C_MIN:5,
263 C_AVG:10,
264 C_MAX:2,
265 C_HEAL:10
266 ]);
267 best=0;max=-1;
268 if (!pointerp(obs=find_object_by_type(from,OT_COMBAT_OBJECT)))
269 return best;
270 mhp=QueryProp(P_MAX_HP)-QueryProp(P_HP);
271 if (objectp(enemy))
272 vul=enemy->QueryProp(P_RESISTANCE_STRENGTHS);
273 if (mhp<0) mhp=0;
274 foreach(object ob: obs)
275 {
276 if (!objectp(ob))
277 continue;
278 if (!mappingp(x=ob->QueryProp(P_COMBATCMDS)))
279 continue;
280 ind=m_indices(x);
281 for (j=sizeof(ind)-1;j>=0;j--)
282 {
283 if (!stringp(ind[j])) continue;
284 y=x[ind[j]];
285 if (mappingp(y))
286 {
287 if (val=y[C_HEAL])
288 {
289 if (val>mhp) val=mhp; // Nur MOEGLICHE Heilung beruecksichtigen
290 val*=pref[C_HEAL];
291 }
292 else
293 {
294 val=y[C_MIN]*pref[C_MIN]
295 + y[C_AVG]*pref[C_AVG]
296 + y[C_MAX]*pref[C_MAX];
297 // auskommentiert, da eval_resistance() bisher nicht implementiert
298 // ist. Zesstra, 06.08.2007
299 //if (y[C_DTYPES] && vul) // Vulnerability des Gegners bedenken...
300 // val=(int)(((float)val)*(1.0+eval_resistance(y[C_DTYPES],vul)));
301 }
302 }
303 else
304 {
305 val=1;
306 }
307 val+=eval_combat_object(obs[i],y,enemy);
308 if (val<max) continue;
309 max=val;
310 if (mappingp(y) && y[C_HEAL]>0)
311 best=sprintf(ind[j],name(RAW)); // Heilung auf sich selbst
312 else if (objectp(enemy))
313 best=sprintf(ind[j],enemy->name(RAW)); // Schaden auf Gegner
314 }
315 }
316
317 return best;
318}
319
320varargs int use_best_combat_command(mixed enemy, mixed from, mapping pref) {
321 // Fuehrt moeglichst guten Kampfeinsatz mit einer Sonderwaffe aus
322 string str;
323
324 if (stringp(enemy) && environment())
325 enemy=present(enemy,environment());
326 if (str=find_best_combat_command(from,enemy,pref))
327 return command(str);
328 return 0;
329}
330
331void add_select_commands() {
332 add_action("wield_best_weapon","zuecke_beste_waffe");
333 add_action("wear_best_armours","trage_beste_ruestungen");
334 add_action("use_best_combat_command","benutze_beste_sonderwaffe");
335}