1 // Copyright (c) Warwick Allison, 1999.
2 // Qt4 conversion copyright (c) Ray Chason, 2012-2014.
3 // NetHack may be freely redistributed.  See license for details.
4 
5 // qt4stat.cpp -- bindings between the Qt 4 interface and the main code
6 
7 extern "C" {
8 #include "hack.h"
9 }
10 #undef Invisible
11 #undef Warning
12 #undef index
13 #undef msleep
14 #undef rindex
15 #undef wizard
16 #undef yn
17 #undef min
18 #undef max
19 
20 #include <QtGui/QtGui>
21 #if QT_VERSION >= 0x050000
22 #include <QtWidgets/QtWidgets>
23 #endif
24 #include "qt4stat.h"
25 #include "qt4stat.moc"
26 #include "qt4set.h"
27 #include "qt4str.h"
28 #include "qt_xpms.h"
29 
30 extern const char *enc_stat[]; /* from botl.c */
31 extern const char *hu_stat[]; /* from eat.c */
32 
33 namespace nethack_qt4 {
34 
NetHackQtStatusWindow()35 NetHackQtStatusWindow::NetHackQtStatusWindow() :
36     // Notes:
37     //  Alignment needs -2 init value, because -1 is an alignment.
38     //  Armor Class is an schar, so 256 is out of range.
39     //  Blank value is 0 and should never change.
40     name(this,"(name)"),
41     dlevel(this,"(dlevel)"),
42     str(this,"STR"),
43     dex(this,"DEX"),
44     con(this,"CON"),
45     intel(this,"INT"),
46     wis(this,"WIS"),
47     cha(this,"CHA"),
48     gold(this,"Gold"),
49     hp(this,"Hit Points"),
50     power(this,"Power"),
51     ac(this,"Armour Class"),
52     level(this,"Level"),
53     exp(this,"Experience"),
54     align(this,"Alignment"),
55     time(this,"Time"),
56     score(this,"Score"),
57     hunger(this,""),
58     confused(this,"Confused"),
59     sick_fp(this,"Sick"),
60     sick_il(this,"Ill"),
61     blind(this,""),
62     stunned(this,"Stunned"),
63     hallu(this,"Hallu"),
64     encumber(this,""),
65     hline1(this),
66     hline2(this),
67     hline3(this),
68     first_set(true)
69 {
70     p_str = QPixmap(str_xpm);
71     p_str = QPixmap(str_xpm);
72     p_dex = QPixmap(dex_xpm);
73     p_con = QPixmap(cns_xpm);
74     p_int = QPixmap(int_xpm);
75     p_wis = QPixmap(wis_xpm);
76     p_cha = QPixmap(cha_xpm);
77 
78     p_chaotic = QPixmap(chaotic_xpm);
79     p_neutral = QPixmap(neutral_xpm);
80     p_lawful = QPixmap(lawful_xpm);
81 
82     p_satiated = QPixmap(satiated_xpm);
83     p_hungry = QPixmap(hungry_xpm);
84 
85     p_confused = QPixmap(confused_xpm);
86     p_sick_fp = QPixmap(sick_fp_xpm);
87     p_sick_il = QPixmap(sick_il_xpm);
88     p_blind = QPixmap(blind_xpm);
89     p_stunned = QPixmap(stunned_xpm);
90     p_hallu = QPixmap(hallu_xpm);
91 
92     p_encumber[0] = QPixmap(slt_enc_xpm);
93     p_encumber[1] = QPixmap(mod_enc_xpm);
94     p_encumber[2] = QPixmap(hvy_enc_xpm);
95     p_encumber[3] = QPixmap(ext_enc_xpm);
96     p_encumber[4] = QPixmap(ovr_enc_xpm);
97 
98     str.setIcon(p_str);
99     dex.setIcon(p_dex);
100     con.setIcon(p_con);
101     intel.setIcon(p_int);
102     wis.setIcon(p_wis);
103     cha.setIcon(p_cha);
104 
105     align.setIcon(p_neutral);
106     hunger.setIcon(p_hungry);
107 
108     confused.setIcon(p_confused);
109     sick_fp.setIcon(p_sick_fp);
110     sick_il.setIcon(p_sick_il);
111     blind.setIcon(p_blind);
112     stunned.setIcon(p_stunned);
113     hallu.setIcon(p_hallu);
114 
115     encumber.setIcon(p_encumber[0]);
116 
117     hline1.setFrameStyle(QFrame::HLine|QFrame::Sunken);
118     hline2.setFrameStyle(QFrame::HLine|QFrame::Sunken);
119     hline3.setFrameStyle(QFrame::HLine|QFrame::Sunken);
120     hline1.setLineWidth(1);
121     hline2.setLineWidth(1);
122     hline3.setLineWidth(1);
123 
124 #if 1 //RLC
125     name.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
126     dlevel.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
127     QVBoxLayout *vbox = new QVBoxLayout();
128     vbox->setSpacing(0);
129     vbox->addWidget(&name);
130     vbox->addWidget(&dlevel);
131     vbox->addWidget(&hline1);
132     QHBoxLayout *atr1box = new QHBoxLayout();
133 	atr1box->addWidget(&str);
134 	atr1box->addWidget(&dex);
135 	atr1box->addWidget(&con);
136 	atr1box->addWidget(&intel);
137 	atr1box->addWidget(&wis);
138 	atr1box->addWidget(&cha);
139     vbox->addLayout(atr1box);
140     vbox->addWidget(&hline2);
141     QHBoxLayout *atr2box = new QHBoxLayout();
142 	atr2box->addWidget(&gold);
143 	atr2box->addWidget(&hp);
144 	atr2box->addWidget(&power);
145 	atr2box->addWidget(&ac);
146 	atr2box->addWidget(&level);
147 	atr2box->addWidget(&exp);
148     vbox->addLayout(atr2box);
149     vbox->addWidget(&hline3);
150     QHBoxLayout *timebox = new QHBoxLayout();
151 	timebox->addWidget(&time);
152 	timebox->addWidget(&score);
153     vbox->addLayout(timebox);
154     QHBoxLayout *statbox = new QHBoxLayout();
155 	statbox->addWidget(&align);
156 	statbox->addWidget(&hunger);
157 	statbox->addWidget(&confused);
158 	statbox->addWidget(&sick_fp);
159 	statbox->addWidget(&sick_il);
160 	statbox->addWidget(&blind);
161 	statbox->addWidget(&stunned);
162 	statbox->addWidget(&hallu);
163 	statbox->addWidget(&encumber);
164     statbox->setAlignment(Qt::AlignLeft|Qt::AlignVCenter);
165     vbox->addLayout(statbox);
166     setLayout(vbox);
167 #endif
168 
169     connect(qt_settings,SIGNAL(fontChanged()),this,SLOT(doUpdate()));
170     doUpdate();
171 }
172 
doUpdate()173 void NetHackQtStatusWindow::doUpdate()
174 {
175     const QFont& large=qt_settings->largeFont();
176     name.setFont(large);
177     dlevel.setFont(large);
178 
179     const QFont& normal=qt_settings->normalFont();
180     str.setFont(normal);
181     dex.setFont(normal);
182     con.setFont(normal);
183     intel.setFont(normal);
184     wis.setFont(normal);
185     cha.setFont(normal);
186     gold.setFont(normal);
187     hp.setFont(normal);
188     power.setFont(normal);
189     ac.setFont(normal);
190     level.setFont(normal);
191     exp.setFont(normal);
192     align.setFont(normal);
193     time.setFont(normal);
194     score.setFont(normal);
195     hunger.setFont(normal);
196     confused.setFont(normal);
197     sick_fp.setFont(normal);
198     sick_il.setFont(normal);
199     blind.setFont(normal);
200     stunned.setFont(normal);
201     hallu.setFont(normal);
202     encumber.setFont(normal);
203 
204     updateStats();
205 }
206 
Widget()207 QWidget* NetHackQtStatusWindow::Widget() { return this; }
208 
Clear()209 void NetHackQtStatusWindow::Clear()
210 {
211 }
Display(bool block)212 void NetHackQtStatusWindow::Display(bool block)
213 {
214 }
CursorTo(int,int y)215 void NetHackQtStatusWindow::CursorTo(int,int y)
216 {
217     cursy=y;
218 }
PutStr(int attr,const QString & text)219 void NetHackQtStatusWindow::PutStr(int attr, const QString& text)
220 {
221     // do a complete update when line 0 is done (as per X11 fancy status)
222     if (cursy==0) updateStats();
223 }
224 
225 #if 0 // RLC
226 void NetHackQtStatusWindow::resizeEvent(QResizeEvent*)
227 {
228 #if 0
229     const float SP_name=0.13; //     <Name> the <Class> (large)
230     const float SP_dlev=0.13; //   Level 3 in The Dungeons of Doom (large)
231     const float SP_atr1=0.25; //  STR   DEX   CON   INT   WIS   CHA
232     const float SP_hln1=0.02; // ---
233     const float SP_atr2=0.09; //  Au    HP    PW    AC    LVL   EXP
234     const float SP_hln2=0.02; // ---
235     const float SP_time=0.09; //      time    score
236     const float SP_hln3=0.02; // ---
237     const float SP_stat=0.25; // Alignment, Poisoned, Hungry, Sick, etc.
238 
239     int h=height();
240     int x=0,y=0;
241 
242     int iw; // Width of an item across line
243     int lh; // Height of a line of values
244 
245     lh=int(h*SP_name);
246     name.setGeometry(0,0,width(),lh); y+=lh;
247     lh=int(h*SP_dlev);
248     dlevel.setGeometry(0,y,width(),lh); y+=lh;
249 
250     lh=int(h*SP_hln1);
251     hline1.setGeometry(0,y,width(),lh); y+=lh;
252 
253     lh=int(h*SP_atr1);
254     iw=width()/6;
255     str.setGeometry(x,y,iw,lh); x+=iw;
256     dex.setGeometry(x,y,iw,lh); x+=iw;
257     con.setGeometry(x,y,iw,lh); x+=iw;
258     intel.setGeometry(x,y,iw,lh); x+=iw;
259     wis.setGeometry(x,y,iw,lh); x+=iw;
260     cha.setGeometry(x,y,iw,lh); x+=iw;
261     x=0; y+=lh;
262 
263     lh=int(h*SP_hln2);
264     hline2.setGeometry(0,y,width(),lh); y+=lh;
265 
266     lh=int(h*SP_atr2);
267     iw=width()/6;
268     gold.setGeometry(x,y,iw,lh); x+=iw;
269     hp.setGeometry(x,y,iw,lh); x+=iw;
270     power.setGeometry(x,y,iw,lh); x+=iw;
271     ac.setGeometry(x,y,iw,lh); x+=iw;
272     level.setGeometry(x,y,iw,lh); x+=iw;
273     exp.setGeometry(x,y,iw,lh); x+=iw;
274     x=0; y+=lh;
275 
276     lh=int(h*SP_hln3);
277     hline3.setGeometry(0,y,width(),lh); y+=lh;
278 
279     lh=int(h*SP_time);
280     iw=width()/3; x+=iw/2;
281     time.setGeometry(x,y,iw,lh); x+=iw;
282     score.setGeometry(x,y,iw,lh); x+=iw;
283     x=0; y+=lh;
284 
285     lh=int(h*SP_stat);
286     iw=width()/9;
287     align.setGeometry(x,y,iw,lh); x+=iw;
288     hunger.setGeometry(x,y,iw,lh); x+=iw;
289     confused.setGeometry(x,y,iw,lh); x+=iw;
290     sick_fp.setGeometry(x,y,iw,lh); x+=iw;
291     sick_il.setGeometry(x,y,iw,lh); x+=iw;
292     blind.setGeometry(x,y,iw,lh); x+=iw;
293     stunned.setGeometry(x,y,iw,lh); x+=iw;
294     hallu.setGeometry(x,y,iw,lh); x+=iw;
295     encumber.setGeometry(x,y,iw,lh); x+=iw;
296     x=0; y+=lh;
297 #else
298     // This is clumsy.  But QLayout objects are proving balky.
299 
300     int row[10];
301 
302     row[0] = name.sizeHint().height();
303     row[1] = dlevel.sizeHint().height();
304     row[2] = h.sizeHint().height();
305 #endif
306 }
307 #endif
308 
309 
310 /*
311  * Set all widget values to a null string.  This is used after all spacings
312  * have been calculated so that when the window is popped up we don't get all
313  * kinds of funny values being displayed.
314  */
nullOut()315 void NetHackQtStatusWindow::nullOut()
316 {
317 }
318 
fadeHighlighting()319 void NetHackQtStatusWindow::fadeHighlighting()
320 {
321     name.dissipateHighlight();
322     dlevel.dissipateHighlight();
323 
324     str.dissipateHighlight();
325     dex.dissipateHighlight();
326     con.dissipateHighlight();
327     intel.dissipateHighlight();
328     wis.dissipateHighlight();
329     cha.dissipateHighlight();
330 
331     gold.dissipateHighlight();
332     hp.dissipateHighlight();
333     power.dissipateHighlight();
334     ac.dissipateHighlight();
335     level.dissipateHighlight();
336     exp.dissipateHighlight();
337     align.dissipateHighlight();
338 
339     time.dissipateHighlight();
340     score.dissipateHighlight();
341 
342     hunger.dissipateHighlight();
343     confused.dissipateHighlight();
344     sick_fp.dissipateHighlight();
345     sick_il.dissipateHighlight();
346     blind.dissipateHighlight();
347     stunned.dissipateHighlight();
348     hallu.dissipateHighlight();
349     encumber.dissipateHighlight();
350 }
351 
352 /*
353  * Update the displayed status.  The current code in botl.c updates
354  * two lines of information.  Both lines are always updated one after
355  * the other.  So only do our update when we update the second line.
356  *
357  * Information on the first line:
358  *    name, attributes, alignment, score
359  *
360  * Information on the second line:
361  *    dlvl, gold, hp, power, ac, {level & exp or HD **}
362  *    status (hunger, conf, halu, stun, sick, blind), time, encumbrance
363  *
364  * [**] HD is shown instead of level and exp if mtimedone is non-zero.
365  */
updateStats()366 void NetHackQtStatusWindow::updateStats()
367 {
368     if (!parentWidget()) return;
369 
370     QString buf;
371     const char *text;
372 
373     if (cursy != 0) return;    /* do a complete update when line 0 is done */
374 
375     if (ACURR(A_STR) > 118) {
376 	buf.sprintf("STR:%d",ACURR(A_STR)-100);
377     } else if (ACURR(A_STR)==118) {
378 	buf.sprintf("STR:18/**");
379     } else if(ACURR(A_STR) > 18) {
380 	buf.sprintf("STR:18/%02d",ACURR(A_STR)-18);
381     } else {
382 	buf.sprintf("STR:%d",ACURR(A_STR));
383     }
384     str.setLabel(buf,NetHackQtLabelledIcon::NoNum,ACURR(A_STR));
385 
386     dex.setLabel("DEX:",(long)ACURR(A_DEX));
387     con.setLabel("CON:",(long)ACURR(A_CON));
388     intel.setLabel("INT:",(long)ACURR(A_INT));
389     wis.setLabel("WIS:",(long)ACURR(A_WIS));
390     cha.setLabel("CHA:",(long)ACURR(A_CHA));
391     const char* hung=hu_stat[u.uhs];
392     if (hung[0]==' ') {
393 	hunger.hide();
394     } else {
395 	hunger.setIcon(u.uhs ? p_hungry : p_satiated);
396 	hunger.setLabel(hung);
397 	hunger.show();
398     }
399     if (Confusion) confused.show(); else confused.hide();
400     if (Sick) {
401 	if (u.usick_type & SICK_VOMITABLE) {
402 	    sick_fp.show();
403 	} else {
404 	    sick_fp.hide();
405 	}
406 	if (u.usick_type & SICK_NONVOMITABLE) {
407 	    sick_il.show();
408 	} else {
409 	    sick_il.hide();
410 	}
411     } else {
412 	sick_fp.hide();
413 	sick_il.hide();
414     }
415     if (Blind) {
416 	blind.setLabel("Blind");
417 	blind.show();
418     } else {
419 	blind.hide();
420     }
421     if (Stunned) stunned.show(); else stunned.hide();
422     if (Hallucination) hallu.show(); else hallu.hide();
423     const char* enc=enc_stat[near_capacity()];
424     if (enc[0]==' ' || !enc[0]) {
425 	encumber.hide();
426     } else {
427 	encumber.setIcon(p_encumber[near_capacity()-1]);
428 	encumber.setLabel(enc);
429 	encumber.show();
430     }
431     if (u.mtimedone) {
432 	buf = nh_capitalize_words(mons[u.umonnum].mname);
433     } else {
434 	buf = rank_of(u.ulevel, pl_character[0], ::flags.female);
435     }
436     QString buf2;
437     buf2.sprintf("%s the %s", plname, buf.toLatin1().constData());
438     name.setLabel(buf2, NetHackQtLabelledIcon::NoNum, u.ulevel);
439 
440     char buf3[BUFSZ];
441     if (describe_level(buf3)) {
442 	dlevel.setLabel(buf3,true);
443     } else {
444 	buf.sprintf("%s, level ", dungeons[u.uz.dnum].dname);
445 	dlevel.setLabel(buf,(long)::depth(&u.uz));
446     }
447 
448     gold.setLabel("Au:", money_cnt(invent));
449 
450     if (u.mtimedone) {
451 	// You're a monster!
452 
453 	buf.sprintf("/%d", u.mhmax);
454 	hp.setLabel("HP:", u.mh  > 0 ? u.mh  : 0, buf);
455 	level.setLabel("HD:",(long)mons[u.umonnum].mlevel);
456     } else {
457 	// You're normal.
458 
459 	buf.sprintf("/%d", u.uhpmax);
460 	hp.setLabel("HP:", u.uhp > 0 ? u.uhp : 0, buf);
461 	level.setLabel("Level:",(long)u.ulevel);
462     }
463     buf.sprintf("/%d", u.uenmax);
464     power.setLabel("Pow:", u.uen, buf);
465     ac.setLabel("AC:",(long)u.uac);
466 #ifdef EXP_ON_BOTL
467     if (::flags.showexp) {
468 	exp.setLabel("Exp:",(long)u.uexp);
469     } else
470 #endif
471     {
472 	exp.setLabel("");
473     }
474     if (u.ualign.type==A_CHAOTIC) {
475 	align.setIcon(p_chaotic);
476 	text = "Chaotic";
477     } else if (u.ualign.type==A_NEUTRAL) {
478 	align.setIcon(p_neutral);
479 	text = "Neutral";
480     } else {
481 	align.setIcon(p_lawful);
482 	text = "Lawful";
483     }
484     align.setLabel(text);
485 
486     if (::flags.time) time.setLabel("Time:",(long)moves);
487     else time.setLabel("");
488 #ifdef SCORE_ON_BOTL
489     if (::flags.showscore) {
490 	score.setLabel("Score:",(long)botl_score());
491     } else
492 #endif
493     {
494 	score.setLabel("");
495     }
496 
497     if (first_set)
498     {
499 	first_set=false;
500 
501 	name.highlightWhenChanging();
502 	dlevel.highlightWhenChanging();
503 
504 	str.highlightWhenChanging();
505 	dex.highlightWhenChanging();
506 	con.highlightWhenChanging();
507 	intel.highlightWhenChanging();
508 	wis.highlightWhenChanging();
509 	cha.highlightWhenChanging();
510 
511 	gold.highlightWhenChanging();
512 	hp.highlightWhenChanging();
513 	power.highlightWhenChanging();
514 	ac.highlightWhenChanging(); ac.lowIsGood();
515 	level.highlightWhenChanging();
516 	exp.highlightWhenChanging();
517 	align.highlightWhenChanging();
518 
519 	//time.highlightWhenChanging();
520 	score.highlightWhenChanging();
521 
522 	hunger.highlightWhenChanging();
523 	confused.highlightWhenChanging();
524 	sick_fp.highlightWhenChanging();
525 	sick_il.highlightWhenChanging();
526 	blind.highlightWhenChanging();
527 	stunned.highlightWhenChanging();
528 	hallu.highlightWhenChanging();
529 	encumber.highlightWhenChanging();
530     }
531 }
532 
533 /*
534  * Turn off hilighted status values after a certain amount of turns.
535  */
checkTurnEvents()536 void NetHackQtStatusWindow::checkTurnEvents()
537 {
538 }
539 
540 } // namespace nethack_qt4
541