blob: 06ccda0e8ff0f5ccaae684aec0e26a44d519dfcb [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// invmaster.c by Nachtwind@MG V1.1 (5.3.2001)
2// A small master that provides a graphical display of the player´s
3// equipment.
4#pragma strong_types
5#pragma save_types,rtt_checks
6#pragma range_check
7#pragma no_clone
8#pragma pedantic
9
10#include <input_to.h>
11#include <properties.h>
12#include <ansi.h>
13#include <combat.h>
14#include <language.h>
15#include "invmaster.h"
16
17
18mapping data;
19closure abbreviate;
20
21
22// i'm aware this can be determined with m_indices(VALID_ARMOUR_TYPE),
23// but the position in the arrays is important for the drawing order.
24// first item in the array is drawn last
25static string *armour_types =
26({AT_BELT,
27 AT_SHIELD,
28 AT_HELMET,
29 AT_BOOT,
30 AT_TROUSERS,
31 AT_AMULET,
32 AT_RING,
33 AT_GLOVE,
34 AT_QUIVER,
35 AT_CLOAK,
36 AT_ARMOUR});
37
38static mapping colors =
39([0:ANSI_BLACK,
40 1:ANSI_RED,
41 2:ANSI_GREEN,
42 3:ANSI_YELLOW,
43 4:ANSI_BLUE,
44 5:ANSI_PURPLE,
45 6:ANSI_CYAN,
46 7:ANSI_WHITE,
47 8:""]);
48
49static mapping bgcolors =
50([0:ANSI_BG_BLACK,
51 1:ANSI_BG_RED,
52 2:ANSI_BG_GREEN,
53 3:ANSI_BG_YELLOW,
54 4:ANSI_BG_BLUE,
55 5:ANSI_BG_PURPLE,
56 6:ANSI_BG_CYAN,
57 7:ANSI_BG_WHITE,
58 8:""]);
59
60
61
62static string Mapping2ColoredText(mapping pic, object player);
63static string Mapping2PlainText(mapping pic);
64static void AddDescription(mapping pic, string type, object item);
65static string ComposeDesc(object item);
66static void ConfigureColors(string text);
67
68void ShowInv(object player, string arg);
69
70// ok, let´s just read in the graphics...
71// really takes some time (~250 eval ticks) but is only done
72// once in an uptime
73void create()
74{
75 mapping pic;
76 string *files, *lines, text;
77 int i,j,k, indentx,indenty, color;
78
79 data=([]);
80
81 DB("Trying to fire up master, path is '"+INVPATH+"'...");
82 files=get_dir(INVPATH+"gfx/*")-({".", ".."});
83 DB(sprintf("Files found in 'gfx/': \n%O", files));
84 for (i=sizeof(files)-1;i>=0;i--)
85 {
86 DB("Reading '"+files[i]+"' ...");
87 text=read_file(INVPATH+"gfx/"+files[i]);
88 if (!stringp(text))
89 {
90 DB("Failed to read file.");
91 continue;
92 }
93 lines=explode(text, "\n");
94 if (sizeof(lines) < 4)
95 {
96 DB("File corrupt.");
97 continue;
98 }
99 indentx=to_int(lines[1]);
100 indenty=to_int(lines[2]);
101 color=to_int(lines[0]);
102 pic=([]);
103 for (j=sizeof(lines)-1;j>2;j--)
104 {
105 for (k=sizeof(lines[j])-1;k>=0;k--)
106 {
107 if (lines[j][k..k]!="?")
108 pic+=([(j-3+indenty)*80+k+indentx:lines[j][k..k];color]);
109 }
110 }
111 data+=([files[i]:pic]);
112 DB("File successfully read.");
113 }
114 DB(sprintf("Types covered:\n%O\n", m_indices(data)));
115
116 // create closure only once to save time
117 // needed by ComposeDesc()
118 // the closure ist not as complicated as it seems ;)
119 // it just checks every word of the name, if it does not begin
120 // with a capital letter, it is abbreviated
121 // this happens only if the name length exceeds 20 chars...
122 abbreviate=lambda(({'x}),
123 ({#'?, ({#'>, ({#'member, quote(({"der", "des"})), 'x}), 0}),
124 "d.",
125 ({#'?, ({#'>, ({#'sizeof, 'x}), 3}),
126 ({#'?, ({#',, ({#'=, 'a, ({#'allocate, 1}) }),
127 ({#'=, ({#'[, 'a, 0}), 'x }),
128 ({#'sizeof, ({#'regexp, 'a, "^[a-z].*"}) })
129 }),
130 ({#'+, ({#'[..], 'x, 0, 1}), "."}),
131 'x
132 }),
133 'x
134 })
135 }));
136}
137
138// function that tries to guess a good item name and use abbrevations
139// where possible
140static string ComposeDesc(object item)
141{
142 int i;
143 string text, *buff;
144
145 text=regreplace(item->QueryProp(P_SHORT)
146 ||item->QueryProp(P_NAME)
147 ||"<?>",
148 "^(Ein Paar|Ein|Eine|Der|Die|Das) ","",0);
149
150// try to shorten the name with the closure
151 if (sizeof(text) > 20)
152 return implode(map(explode(text, " "), abbreviate), " ");
153 else
154 return text;
155}
156
157// converts a mapping with characters and color info into a
158// text. data in the mapping is stored in a one-dimensional
159// order with the position as key (ypos is pos/80, xpos pos%80)
160// this setup has a huge advantage: combining several
161// graphics just takes a "+" operator, the rest is handled
162// by the game driver. freakin' fast, much better than doing an
163// iteration over one or more array in lpc.
164static string Mapping2ColoredText(mapping pic, object player)
165{
166 string text;
167 mapping configmap;
168 int i,j,color;
169
170 configmap=default_config+(player->QueryProp(P_INVMASTER_CONFIG)||([]));
171
172 text="";
173 color=0;
174 for (i=0;i<22;i++)
175 {
176 text+=bgcolors[configmap[8]];
177 for (j=0;j<78;j++)
178 {
179 if (pic[i*80+j,1]!=color)
180 {
181 color=pic[i*80+j,1];
182 text+=colors[configmap[color]];
183 }
184 text+=pic[i*80+j];
185 }
186 text+=ANSI_NORMAL+"\n";
187 color=0;
188 }
189 return text;
190}
191
192static string Mapping2PlainText(mapping pic)
193{
194 string text;
195 int i,j;
196
197 text="";
198
199 for (i=0;i<22;i++)
200 {
201 for (j=0;j<78;j++)
202 text+=pic[i*80+j];
203 text+="\n";
204 }
205 return text;
206}
207static void AddDescription(mapping pic, string type, object item)
208{
209 int indentx, indenty, i;
210 string text;
211
212 switch(type)
213 {
214 case AT_HELMET:
215 indentx=47;
216 indenty=0;
217 text=sprintf("%-30s",ComposeDesc(item)[0..30]);break;
218 case AT_QUIVER:
219 indentx=49;
220 indenty=2;
221 text=sprintf("%-28s",ComposeDesc(item)[0..28]);break;
222 case AT_AMULET:
223 indentx=49;
224 indenty=4;
225 text=sprintf("%-27s",ComposeDesc(item)[0..28]);break;
226 case AT_ARMOUR:
227 indentx=53;
228 indenty=7;
229 text=sprintf("%-24s",ComposeDesc(item)[0..25]);break;
230 case AT_SHIELD:
231 indentx=54;
232 indenty=10;
233 text=sprintf("%-20s",ComposeDesc(item)[0..24]);break;
234 case AT_CLOAK:
235 indentx=53;
236 indenty=15;
237 text=sprintf("%-20s",ComposeDesc(item)[0..25]);break;
238 case AT_TROUSERS:
239 indentx=49;
240 indenty=17;
241 text=sprintf("%-20s",ComposeDesc(item)[0..20]);break;
242 case AT_RING:
243 indentx=0;
244 indenty=9;
245 text=sprintf("%14s",ComposeDesc(item)[0..17]);break;
246 case AT_GLOVE:
247 indentx=0;
248 indenty=11;
249 text=sprintf("%14s",ComposeDesc(item)[0..17]);break;
250 case AT_BELT:
251 indentx=1;
252 indenty=13;
253 text=sprintf("%14s",ComposeDesc(item)[0..18]);break;
254 case AT_BOOT:
255 indentx=1;
256 indenty=20;
257 text=sprintf("%18s",ComposeDesc(item)[0..18]);break;
258 case "Waffe":
259 indentx=1;
260 indenty=1;
261 text=sprintf("%18s",ComposeDesc(item)[0..25]);
262 if (item->QueryProp(P_NR_HANDS) > 1 &&
263 this_player() &&
264 !(this_player()->QueryArmorByType(AT_SHIELD)))
265 AddDescription(pic, AT_SHIELD, item);break;
266 default: return;
267 }
268 for (i=0;i<sizeof(text);i++)
269 pic+=([(80*indenty+indentx+i):text[i..i];2]);
270}
271
272varargs static void ConfigureColors(string text)
273{
274 mapping config, display;
275 string *strs;
276
277 if (!objectp(this_player())) return;
278
279 if (this_player()->InFight())
280 {
281 write(break_string(
282 "Im Kampf? Na Du hast Nerven, das lassen wir doch mal lieber! "
283 "Probier es danach nochmal...", 78));
284 return;
285 }
286
287 if (stringp(text)) text=lower_case(text);
288
289 if (text=="ok")
290 {
291 write("Farbkonfiguration beendet.\n");
292 return;
293 }
294
295 //"ansi_config", def in invmaster.h
296 config=this_player()->QueryProp(P_INVMASTER_CONFIG)||([]);
297 display=default_config+config;
298
299 if (!text || text=="")
300 {
301 write(
302 "*** Farbkonfiguration fuer den Ausruestungsbefehl ***\n\n"
303 " Farbe: wird dargestellt mit:\n"
304 "------------------ --------------------\n"
305 " Hintergrund "+COLORNAMES[display[8]]+"\n"
306 " Schwarz "+COLORNAMES[display[0]]+"\n"
307 " Rot "+COLORNAMES[display[1]]+"\n"
308 " Gruen "+COLORNAMES[display[2]]+"\n"
309 " Gelb "+COLORNAMES[display[3]]+"\n"
310 " Blau "+COLORNAMES[display[4]]+"\n"
311 " Magenta "+COLORNAMES[display[5]]+"\n"
312 " Tuerkis "+COLORNAMES[display[6]]+"\n"
313 " Weiss "+COLORNAMES[display[7]]+"\n\n"
314 "Farbe aendern mit '<farbe> <gewuenschte farbe>'.\n"
315 "Beispiel: 'gelb rot'.\n"
316 "Alles, was standardmaessig gelb waere, wuerde dann mit der ANSI-Farbe \n"
317 "Rot dargestellt.\n"
318 "Der Hintergrund kann zusaetzlich die Farbe 'keine' haben, bei der der \n"
319 "Hintergrund eben ueberhaupt nicht gefaerbt wird.\n"
320 "Beispiel: 'hintergrund keine'. Schaltet die Hintergrundfarbe aus.\n\n"
321 "Beenden mit 'ok'. \n"
322 "Wiederholung der Farbliste mit <Return>.\n"
323 "Farbliste auf Standard zuruecksetzen mit 'reset'.\n");
324 }
325 else
326 if (text=="reset")
327 {
328 this_player()->Set(P_INVMASTER_CONFIG, SAVE, F_MODE_AD);
329 this_player()->SetProp(P_INVMASTER_CONFIG, 0);
330 write("Farben zurueckgesetzt!\n");
331 }
332 else
333 {
334 if ( sizeof(strs=explode(text, " ")-({""})) !=2
335 || !member((COLORCODES-(["keine"])), strs[0])
336 || !member((COLORCODES-(["hintergrund"])), strs[1])
337 || ((strs[0]!="hintergrund") && (strs[1]=="keine")) )
338 {
339 write("Falsche Eingabe.\n"
340 "Format: <farbe|hintergrund> <zugewiesene Farbe>\n"
341 "Abbrechen mit 'ok'.\n");
342 }
343 else
344 {
345 if (COLORCODES[strs[1]]==default_config[COLORCODES[strs[0]]])
346 config-=([COLORCODES[strs[0]]]);
347 else
348 config+=([COLORCODES[strs[0]]:COLORCODES[strs[1]]]);
349 if (!sizeof(config))
350 {
351 this_player()->Set(P_INVMASTER_CONFIG, SAVE, F_MODE_AD);
352 this_player()->SetProp(P_INVMASTER_CONFIG, 0);
353 }
354 else
355 {
356 this_player()->SetProp(P_INVMASTER_CONFIG, deep_copy(config));
357 this_player()->Set(P_INVMASTER_CONFIG, SAVE, F_MODE_AS);
358 }
359 write("Ok, Farbe gewaehlt!\n");
360 }
361 }
362 input_to("ConfigureColors", INPUT_PROMPT, "\nEingabe: ");
363}
364
365
366string* armour_order=({
367 AT_HELMET, AT_AMULET, AT_QUIVER, AT_ARMOUR, AT_CLOAK,
368 AT_GLOVE, AT_RING, AT_BELT,
369 AT_TROUSERS, AT_BOOT, AT_SHIELD, AT_MISC});
370
371mapping weapon_names=([
372 WT_SPEAR : "Speer",
373 WT_SWORD : "Schwert",
374 WT_STAFF : "Kampfstab",
375 WT_WHIP : "Peitsche",
376 WT_CLUB : "Keule",
377 WT_KNIFE : "Messer",
378 WT_MISC : "Irgendwas",
379 WT_MAGIC : "Artefakt",
380 WT_AXE : "Axt",
381 WT_RANGED_WEAPON : "Fernwaffe"
382 ]);
383
384string SimpleInv(object player) {
385 object* armours=player->QueryProp(P_ARMOURS);
386 int count=sizeof(armour_order);
387 string* list=allocate(count);
388 string result="Ausruestung\n";
389 int i;
390
391 foreach(object ob: armours) {
392 if (!objectp(ob)) continue;
393 int idx = member(armour_order, ob->QueryProp(P_ARMOUR_TYPE));
394 if (idx>=0)
395 list[idx]=ob->QueryProp(P_SHORT);
396 }
397
398 // AT_MISC (letztes Element in list und armour_order) weglassen.
399 for (i=0;i<count-1;i++) {
400 result+=sprintf("%-20s %-57s\n",armour_order[i],list[i] || "");
401 }
402
403 object ob=ob=player->QueryProp(P_WEAPON);
404 if (objectp(ob)) {
405 result+=sprintf("%-20s %-57s\n",
406 (ob->QueryProp(P_NR_HANDS)==1 ? "Einhand-":"Zweihand-")
407 +weapon_names[ob->QueryProp(P_WEAPON_TYPE)],
408 ob->QueryProp(P_SHORT));
409 } else result+="Keine Waffe\n";
410
411 return result;
412}
413// the main function called by the player object.
414// determines gender, then adds armor/weapon graphics
415// dynamically. still very fast due to the use of the "+" operator,
416// see above.
417void ShowInv(object player, string arg)
418{
419 string gender, type;
420 mapping pic;
421 int i;
422 object item;
423
424 if (!objectp(player)||!interactive(player)) return;
425
426 // split args.
427 string *args;
428 if (stringp(arg))
429 args = explode(lower_case(arg), " ") - ({" "});
430 else
431 args = ({});
432
433 if (member(args, "farben") > -1) {
434 ConfigureColors();
435 return;
436 }
437
438 if (member(args, "-k") > -1 || player->QueryProp(P_NO_ASCII_ART)) {
439 tell_object(player, SimpleInv(player));
440 return;
441 }
442
443 gender=player->QueryProp(P_GENDER)==FEMALE?"_female":"_male";
444 pic=deep_copy(data["base"+gender]);
445 pic+=data["Beschriftung"];
446 for (i=sizeof(armour_types)-1;i>=0;i--)
447 if (objectp(item=player->QueryArmourByType(armour_types[i])))
448 {
449 pic+=data[armour_types[i]+gender];
450 AddDescription(pic, armour_types[i], item);
451 }
452 if (item=player->QueryProp(P_WEAPON))
453 {
454 pic+=data[(VALID_WEAPON_TYPE(type=item->QueryProp(P_WEAPON_TYPE)))?
455 type:WT_MISC];
456 AddDescription(pic, "Waffe", item);
457 }
458 if (player->QueryProp(P_TTY)!="ansi")
459 player->More(Mapping2PlainText(pic));
460 else
461 player->More(Mapping2ColoredText(pic, player));
462 DB(geteuid(player)+" eval cost: "+(1000000-get_eval_cost())+" ticks.\n");
463}
464