blob: b769822785e1e0d5340e5774a1033b22c9ae7517 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// invmaster.c by Nachtwind@MG V1.1 (5.3.2001)
Zesstra1c7da1d2019-10-24 23:01:15 +02002// A small master that provides a graphical display of the player´s
MG Mud User88f12472016-06-24 23:31:02 +02003// 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
Zesstra1c7da1d2019-10-24 23:01:15 +020070// ok, let´s just read in the graphics...
MG Mud User88f12472016-06-24 23:31:02 +020071// 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...
Arathorndc28afc2018-11-26 22:20:59 +0100122 abbreviate = function string (string x) {
123 if ( member(({"der","des"}), x) > 0 )
124 return "d.";
125 else if ( sizeof(x) > 3)
126 return regreplace(x, "^[a-z].+", x[0..0]+".", 0x1);
127 else
128 return x;
129 };
MG Mud User88f12472016-06-24 23:31:02 +0200130}
131
132// function that tries to guess a good item name and use abbrevations
133// where possible
134static string ComposeDesc(object item)
135{
136 int i;
137 string text, *buff;
138
139 text=regreplace(item->QueryProp(P_SHORT)
Arathorn74cbee22018-01-05 23:44:25 +0100140 ||item->name(RAW)
MG Mud User88f12472016-06-24 23:31:02 +0200141 ||"<?>",
142 "^(Ein Paar|Ein|Eine|Der|Die|Das) ","",0);
143
144// try to shorten the name with the closure
145 if (sizeof(text) > 20)
146 return implode(map(explode(text, " "), abbreviate), " ");
147 else
148 return text;
149}
150
151// converts a mapping with characters and color info into a
152// text. data in the mapping is stored in a one-dimensional
153// order with the position as key (ypos is pos/80, xpos pos%80)
154// this setup has a huge advantage: combining several
155// graphics just takes a "+" operator, the rest is handled
156// by the game driver. freakin' fast, much better than doing an
157// iteration over one or more array in lpc.
158static string Mapping2ColoredText(mapping pic, object player)
159{
160 string text;
161 mapping configmap;
162 int i,j,color;
163
164 configmap=default_config+(player->QueryProp(P_INVMASTER_CONFIG)||([]));
165
166 text="";
167 color=0;
168 for (i=0;i<22;i++)
169 {
170 text+=bgcolors[configmap[8]];
171 for (j=0;j<78;j++)
172 {
173 if (pic[i*80+j,1]!=color)
174 {
175 color=pic[i*80+j,1];
176 text+=colors[configmap[color]];
177 }
178 text+=pic[i*80+j];
179 }
180 text+=ANSI_NORMAL+"\n";
181 color=0;
182 }
183 return text;
184}
185
186static string Mapping2PlainText(mapping pic)
187{
188 string text;
189 int i,j;
190
191 text="";
192
193 for (i=0;i<22;i++)
194 {
195 for (j=0;j<78;j++)
196 text+=pic[i*80+j];
197 text+="\n";
198 }
199 return text;
200}
201static void AddDescription(mapping pic, string type, object item)
202{
203 int indentx, indenty, i;
204 string text;
205
206 switch(type)
207 {
208 case AT_HELMET:
209 indentx=47;
210 indenty=0;
211 text=sprintf("%-30s",ComposeDesc(item)[0..30]);break;
212 case AT_QUIVER:
213 indentx=49;
214 indenty=2;
215 text=sprintf("%-28s",ComposeDesc(item)[0..28]);break;
216 case AT_AMULET:
217 indentx=49;
218 indenty=4;
219 text=sprintf("%-27s",ComposeDesc(item)[0..28]);break;
220 case AT_ARMOUR:
221 indentx=53;
222 indenty=7;
223 text=sprintf("%-24s",ComposeDesc(item)[0..25]);break;
224 case AT_SHIELD:
225 indentx=54;
226 indenty=10;
227 text=sprintf("%-20s",ComposeDesc(item)[0..24]);break;
228 case AT_CLOAK:
229 indentx=53;
230 indenty=15;
231 text=sprintf("%-20s",ComposeDesc(item)[0..25]);break;
232 case AT_TROUSERS:
233 indentx=49;
234 indenty=17;
235 text=sprintf("%-20s",ComposeDesc(item)[0..20]);break;
236 case AT_RING:
237 indentx=0;
238 indenty=9;
239 text=sprintf("%14s",ComposeDesc(item)[0..17]);break;
240 case AT_GLOVE:
241 indentx=0;
242 indenty=11;
243 text=sprintf("%14s",ComposeDesc(item)[0..17]);break;
244 case AT_BELT:
245 indentx=1;
246 indenty=13;
247 text=sprintf("%14s",ComposeDesc(item)[0..18]);break;
248 case AT_BOOT:
249 indentx=1;
250 indenty=20;
251 text=sprintf("%18s",ComposeDesc(item)[0..18]);break;
252 case "Waffe":
253 indentx=1;
254 indenty=1;
255 text=sprintf("%18s",ComposeDesc(item)[0..25]);
256 if (item->QueryProp(P_NR_HANDS) > 1 &&
257 this_player() &&
258 !(this_player()->QueryArmorByType(AT_SHIELD)))
259 AddDescription(pic, AT_SHIELD, item);break;
260 default: return;
261 }
262 for (i=0;i<sizeof(text);i++)
263 pic+=([(80*indenty+indentx+i):text[i..i];2]);
264}
265
266varargs static void ConfigureColors(string text)
267{
268 mapping config, display;
269 string *strs;
270
271 if (!objectp(this_player())) return;
272
273 if (this_player()->InFight())
274 {
275 write(break_string(
276 "Im Kampf? Na Du hast Nerven, das lassen wir doch mal lieber! "
277 "Probier es danach nochmal...", 78));
278 return;
279 }
280
281 if (stringp(text)) text=lower_case(text);
282
283 if (text=="ok")
284 {
285 write("Farbkonfiguration beendet.\n");
286 return;
287 }
288
289 //"ansi_config", def in invmaster.h
290 config=this_player()->QueryProp(P_INVMASTER_CONFIG)||([]);
291 display=default_config+config;
292
293 if (!text || text=="")
294 {
295 write(
296 "*** Farbkonfiguration fuer den Ausruestungsbefehl ***\n\n"
297 " Farbe: wird dargestellt mit:\n"
298 "------------------ --------------------\n"
299 " Hintergrund "+COLORNAMES[display[8]]+"\n"
300 " Schwarz "+COLORNAMES[display[0]]+"\n"
301 " Rot "+COLORNAMES[display[1]]+"\n"
302 " Gruen "+COLORNAMES[display[2]]+"\n"
303 " Gelb "+COLORNAMES[display[3]]+"\n"
304 " Blau "+COLORNAMES[display[4]]+"\n"
305 " Magenta "+COLORNAMES[display[5]]+"\n"
306 " Tuerkis "+COLORNAMES[display[6]]+"\n"
307 " Weiss "+COLORNAMES[display[7]]+"\n\n"
308 "Farbe aendern mit '<farbe> <gewuenschte farbe>'.\n"
309 "Beispiel: 'gelb rot'.\n"
310 "Alles, was standardmaessig gelb waere, wuerde dann mit der ANSI-Farbe \n"
311 "Rot dargestellt.\n"
312 "Der Hintergrund kann zusaetzlich die Farbe 'keine' haben, bei der der \n"
313 "Hintergrund eben ueberhaupt nicht gefaerbt wird.\n"
314 "Beispiel: 'hintergrund keine'. Schaltet die Hintergrundfarbe aus.\n\n"
315 "Beenden mit 'ok'. \n"
316 "Wiederholung der Farbliste mit <Return>.\n"
317 "Farbliste auf Standard zuruecksetzen mit 'reset'.\n");
318 }
319 else
320 if (text=="reset")
321 {
322 this_player()->Set(P_INVMASTER_CONFIG, SAVE, F_MODE_AD);
323 this_player()->SetProp(P_INVMASTER_CONFIG, 0);
324 write("Farben zurueckgesetzt!\n");
325 }
326 else
327 {
328 if ( sizeof(strs=explode(text, " ")-({""})) !=2
329 || !member((COLORCODES-(["keine"])), strs[0])
330 || !member((COLORCODES-(["hintergrund"])), strs[1])
331 || ((strs[0]!="hintergrund") && (strs[1]=="keine")) )
332 {
333 write("Falsche Eingabe.\n"
334 "Format: <farbe|hintergrund> <zugewiesene Farbe>\n"
335 "Abbrechen mit 'ok'.\n");
336 }
337 else
338 {
339 if (COLORCODES[strs[1]]==default_config[COLORCODES[strs[0]]])
340 config-=([COLORCODES[strs[0]]]);
341 else
342 config+=([COLORCODES[strs[0]]:COLORCODES[strs[1]]]);
343 if (!sizeof(config))
344 {
345 this_player()->Set(P_INVMASTER_CONFIG, SAVE, F_MODE_AD);
346 this_player()->SetProp(P_INVMASTER_CONFIG, 0);
347 }
348 else
349 {
350 this_player()->SetProp(P_INVMASTER_CONFIG, deep_copy(config));
351 this_player()->Set(P_INVMASTER_CONFIG, SAVE, F_MODE_AS);
352 }
353 write("Ok, Farbe gewaehlt!\n");
354 }
355 }
356 input_to("ConfigureColors", INPUT_PROMPT, "\nEingabe: ");
357}
358
359
360string* armour_order=({
361 AT_HELMET, AT_AMULET, AT_QUIVER, AT_ARMOUR, AT_CLOAK,
362 AT_GLOVE, AT_RING, AT_BELT,
363 AT_TROUSERS, AT_BOOT, AT_SHIELD, AT_MISC});
364
365mapping weapon_names=([
366 WT_SPEAR : "Speer",
367 WT_SWORD : "Schwert",
368 WT_STAFF : "Kampfstab",
369 WT_WHIP : "Peitsche",
370 WT_CLUB : "Keule",
371 WT_KNIFE : "Messer",
372 WT_MISC : "Irgendwas",
373 WT_MAGIC : "Artefakt",
374 WT_AXE : "Axt",
Arathorn74cbee22018-01-05 23:44:25 +0100375 WT_RANGED_WEAPON : "Fernwaffe"
MG Mud User88f12472016-06-24 23:31:02 +0200376 ]);
377
378string SimpleInv(object player) {
379 object* armours=player->QueryProp(P_ARMOURS);
380 int count=sizeof(armour_order);
381 string* list=allocate(count);
382 string result="Ausruestung\n";
383 int i;
384
385 foreach(object ob: armours) {
386 if (!objectp(ob)) continue;
387 int idx = member(armour_order, ob->QueryProp(P_ARMOUR_TYPE));
388 if (idx>=0)
Arathorn74cbee22018-01-05 23:44:25 +0100389 list[idx]=ob->QueryProp(P_SHORT)||ob->name(RAW);
MG Mud User88f12472016-06-24 23:31:02 +0200390 }
391
392 // AT_MISC (letztes Element in list und armour_order) weglassen.
393 for (i=0;i<count-1;i++) {
394 result+=sprintf("%-20s %-57s\n",armour_order[i],list[i] || "");
395 }
396
397 object ob=ob=player->QueryProp(P_WEAPON);
Arathorn74cbee22018-01-05 23:44:25 +0100398 if (objectp(ob))
399 {
400 string waffentyp;
401 if ( ob->QueryProp(P_WEAPON_TYPE)!=WT_HANDS )
402 {
403 waffentyp = (ob->QueryProp(P_NR_HANDS)==1 ? "Einhand-":"Zweihand-")
404 +weapon_names[ob->QueryProp(P_WEAPON_TYPE)];
405 }
406 else
407 {
408 waffentyp = "Waffe";
409 }
410 result+=sprintf("%-20s %-57s\n", waffentyp,
411 ob->QueryProp(P_SHORT)||ob->name(RAW));
412 }
413 else
414 result+="Keine Waffe\n";
MG Mud User88f12472016-06-24 23:31:02 +0200415
416 return result;
417}
418// the main function called by the player object.
419// determines gender, then adds armor/weapon graphics
420// dynamically. still very fast due to the use of the "+" operator,
421// see above.
422void ShowInv(object player, string arg)
423{
424 string gender, type;
425 mapping pic;
426 int i;
427 object item;
428
429 if (!objectp(player)||!interactive(player)) return;
430
431 // split args.
432 string *args;
433 if (stringp(arg))
434 args = explode(lower_case(arg), " ") - ({" "});
435 else
436 args = ({});
437
438 if (member(args, "farben") > -1) {
439 ConfigureColors();
440 return;
441 }
442
443 if (member(args, "-k") > -1 || player->QueryProp(P_NO_ASCII_ART)) {
444 tell_object(player, SimpleInv(player));
445 return;
446 }
447
448 gender=player->QueryProp(P_GENDER)==FEMALE?"_female":"_male";
449 pic=deep_copy(data["base"+gender]);
450 pic+=data["Beschriftung"];
451 for (i=sizeof(armour_types)-1;i>=0;i--)
452 if (objectp(item=player->QueryArmourByType(armour_types[i])))
453 {
454 pic+=data[armour_types[i]+gender];
455 AddDescription(pic, armour_types[i], item);
456 }
457 if (item=player->QueryProp(P_WEAPON))
458 {
459 pic+=data[(VALID_WEAPON_TYPE(type=item->QueryProp(P_WEAPON_TYPE)))?
460 type:WT_MISC];
461 AddDescription(pic, "Waffe", item);
462 }
463 if (player->QueryProp(P_TTY)!="ansi")
464 player->More(Mapping2PlainText(pic));
465 else
466 player->More(Mapping2ColoredText(pic, player));
467 DB(geteuid(player)+" eval cost: "+(1000000-get_eval_cost())+" ticks.\n");
468}
469