1 /**
2 * @file control.cpp
3 *
4 * Implementation of the character and main control panels
5 */
6 #include "all.h"
7
8 #include <cstddef>
9
10 DEVILUTION_BEGIN_NAMESPACE
11
12 namespace {
13
14 CelOutputBuffer pBtmBuff;
15 CelOutputBuffer pLifeBuff;
16 CelOutputBuffer pManaBuff;
17
18 } // namespace
19
20 BYTE sgbNextTalkSave;
21 BYTE sgbTalkSavePos;
22 BYTE *pDurIcons;
23 BYTE *pChrButtons;
24 BOOL drawhpflag;
25 BOOL dropGoldFlag;
26 BOOL panbtn[8];
27 BOOL chrbtn[4];
28 BYTE *pMultiBtns;
29 BYTE *pPanelButtons;
30 BYTE *pChrPanel;
31 BOOL lvlbtndown;
32 char sgszTalkSave[8][80];
33 int dropGoldValue;
34 BOOL drawmanaflag;
35 BOOL chrbtnactive;
36 char sgszTalkMsg[MAX_SEND_STR_LEN];
37 BYTE *pPanelText;
38 BYTE *pTalkBtns;
39 BOOL pstrjust[4];
40 int pnumlines;
41 BOOL pinfoflag;
42 BOOL talkbtndown[3];
43 spell_id pSpell;
44 text_color infoclr;
45 int sgbPlrTalkTbl;
46 BYTE *pGBoxBuff;
47 BYTE *pSBkBtnCel;
48 char tempstr[256];
49 BOOLEAN whisper[MAX_PLRS];
50 int sbooktab;
51 spell_type pSplType;
52 int initialDropGoldIndex;
53 BOOL talkflag;
54 BYTE *pSBkIconCels;
55 BOOL sbookflag;
56 BOOL chrflag;
57 BOOL drawbtnflag;
58 BYTE *pSpellBkCel;
59 char infostr[64];
60 int numpanbtns;
61 char panelstr[4][64];
62 BOOL panelflag;
63 BYTE SplTransTbl[256];
64 int initialDropGoldValue;
65 BYTE *pSpellCels;
66 BOOL panbtndown;
67 BOOL spselflag;
68
69 /** Map of hero class names */
70 const char *const ClassStrTbl[] = {
71 "Warrior",
72 "Rogue",
73 "Sorcerer",
74 "Monk",
75 "Bard",
76 "Barbarian",
77 };
78
79 /** Maps from font index to smaltext.cel frame number. */
80 const BYTE fontframe[128] = {
81 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
82 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
83 0, 54, 44, 57, 58, 56, 55, 47, 40, 41, 59, 39, 50, 37, 51, 52,
84 36, 27, 28, 29, 30, 31, 32, 33, 34, 35, 48, 49, 60, 38, 61, 53,
85 62, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
86 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 42, 63, 43, 64, 65,
87 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
88 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 40, 66, 41, 67, 0
89 };
90
91 /**
92 * Maps from smaltext.cel frame number to character width. Note, the
93 * character width may be distinct from the frame width, which is 13 for every
94 * smaltext.cel frame.
95 */
96 const BYTE fontkern[68] = {
97 8, 10, 7, 9, 8, 7, 6, 8, 8, 3,
98 3, 8, 6, 11, 9, 10, 6, 9, 9, 6,
99 9, 11, 10, 13, 10, 11, 7, 5, 7, 7,
100 8, 7, 7, 7, 7, 7, 10, 4, 5, 6,
101 3, 3, 4, 3, 6, 6, 3, 3, 3, 3,
102 3, 2, 7, 6, 3, 10, 10, 6, 6, 7,
103 4, 4, 9, 6, 6, 12, 3, 7
104 };
105 /**
106 * Line start position for info box text when displaying 1, 2, 3, 4 and 5 lines respectivly
107 */
108 const int lineOffsets[5][5] = {
109 { 82 },
110 { 70, 94 },
111 { 64, 82, 100 },
112 { 60, 75, 89, 104 },
113 { 58, 70, 82, 94, 105 },
114 };
115
116 /**
117 * Maps ASCII character code to font index, as used by the
118 * small, medium and large sized fonts; which corresponds to smaltext.cel,
119 * medtexts.cel and bigtgold.cel respectively.
120 */
121 const BYTE gbFontTransTbl[256] = {
122 // clang-format off
123 '\0', 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
124 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
125 ' ', '!', '\"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/',
126 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
127 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
128 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
129 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
130 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', 0x01,
131 'C', 'u', 'e', 'a', 'a', 'a', 'a', 'c', 'e', 'e', 'e', 'i', 'i', 'i', 'A', 'A',
132 'E', 'a', 'A', 'o', 'o', 'o', 'u', 'u', 'y', 'O', 'U', 'c', 'L', 'Y', 'P', 'f',
133 'a', 'i', 'o', 'u', 'n', 'N', 'a', 'o', '?', 0x01, 0x01, 0x01, 0x01, '!', '<', '>',
134 'o', '+', '2', '3', '\'', 'u', 'P', '.', ',', '1', '0', '>', 0x01, 0x01, 0x01, '?',
135 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'C', 'E', 'E', 'E', 'E', 'I', 'I', 'I', 'I',
136 'D', 'N', 'O', 'O', 'O', 'O', 'O', 'X', '0', 'U', 'U', 'U', 'U', 'Y', 'b', 'B',
137 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'c', 'e', 'e', 'e', 'e', 'i', 'i', 'i', 'i',
138 'o', 'n', 'o', 'o', 'o', 'o', 'o', '/', '0', 'u', 'u', 'u', 'u', 'y', 'b', 'y',
139 // clang-format on
140 };
141
142 /* data */
143
144 /** Maps from spell_id to spelicon.cel frame number. */
145 char SpellITbl[] = {
146 27,
147 1,
148 2,
149 3,
150 4,
151 5,
152 6,
153 7,
154 8,
155 9,
156 28,
157 13,
158 12,
159 18,
160 16,
161 14,
162 18,
163 19,
164 11,
165 20,
166 15,
167 21,
168 23,
169 24,
170 25,
171 22,
172 26,
173 29,
174 37,
175 38,
176 39,
177 42,
178 41,
179 40,
180 10,
181 36,
182 30,
183 51,
184 51,
185 50,
186 46,
187 47,
188 43,
189 45,
190 48,
191 49,
192 44,
193 35,
194 35,
195 35,
196 35,
197 35,
198 };
199 /** Maps from panel_button_id to the position and dimensions of a panel button. */
200 int PanBtnPos[8][5] = {
201 // clang-format off
202 { 9, 9, 71, 19, TRUE }, // char button
203 { 9, 35, 71, 19, FALSE }, // quests button
204 { 9, 75, 71, 19, TRUE }, // map button
205 { 9, 101, 71, 19, FALSE }, // menu button
206 { 560, 9, 71, 19, TRUE }, // inv button
207 { 560, 35, 71, 19, FALSE }, // spells button
208 { 87, 91, 33, 32, TRUE }, // chat button
209 { 527, 91, 33, 32, TRUE }, // friendly fire button
210 // clang-format on
211 };
212 /** Maps from panel_button_id to hotkey name. */
213 const char *const PanBtnHotKey[8] = { "'c'", "'q'", "Tab", "Esc", "'i'", "'b'", "Enter", NULL };
214 /** Maps from panel_button_id to panel button description. */
215 const char *const PanBtnStr[8] = {
216 "Character Information",
217 "Quests log",
218 "Automap",
219 "Main Menu",
220 "Inventory",
221 "Spell book",
222 "Send Message",
223 "Player Attack"
224 };
225 /** Maps from attribute_id to the rectangle on screen used for attribute increment buttons. */
226 RECT32 ChrBtnsRect[4] = {
227 { 137, 138, 41, 22 },
228 { 137, 166, 41, 22 },
229 { 137, 195, 41, 22 },
230 { 137, 223, 41, 22 }
231 };
232
233 /** Maps from spellbook page number and position to spell_id. */
234 spell_id SpellPages[6][7] = {
235 { SPL_NULL, SPL_FIREBOLT, SPL_CBOLT, SPL_HBOLT, SPL_HEAL, SPL_HEALOTHER, SPL_FLAME },
236 { SPL_RESURRECT, SPL_FIREWALL, SPL_TELEKINESIS, SPL_LIGHTNING, SPL_TOWN, SPL_FLASH, SPL_STONE },
237 { SPL_RNDTELEPORT, SPL_MANASHIELD, SPL_ELEMENT, SPL_FIREBALL, SPL_WAVE, SPL_CHAIN, SPL_GUARDIAN },
238 { SPL_NOVA, SPL_GOLEM, SPL_TELEPORT, SPL_APOCA, SPL_BONESPIRIT, SPL_FLARE, SPL_ETHEREALIZE },
239 { SPL_LIGHTWALL, SPL_IMMOLAT, SPL_WARP, SPL_REFLECT, SPL_BERSERK, SPL_FIRERING, SPL_SEARCH },
240 { SPL_INVALID, SPL_INVALID, SPL_INVALID, SPL_INVALID, SPL_INVALID, SPL_INVALID, SPL_INVALID }
241 };
242
243 #define SPLICONLENGTH 56
244 #define SPLROWICONLS 10
245 #define SPLICONLAST (gbIsHellfire ? 52 : 43)
246
247 /**
248 * Draw spell cell onto the given buffer.
249 * @param out Output buffer
250 * @param xp Buffer coordinate
251 * @param yp Buffer coordinate
252 * @param Trans Pointer to the cel buffer.
253 * @param nCel Index of the cel frame to draw. 0 based.
254 * @param w Width of the frame.
255 */
DrawSpellCel(CelOutputBuffer out,int xp,int yp,BYTE * Trans,int nCel,int w)256 static void DrawSpellCel(CelOutputBuffer out, int xp, int yp, BYTE *Trans, int nCel, int w)
257 {
258 CelDrawLightTo(out, xp, yp, Trans, nCel, w, SplTransTbl);
259 }
260
SetSpellTrans(char t)261 void SetSpellTrans(char t)
262 {
263 int i;
264
265 if (t == RSPLTYPE_SKILL) {
266 for (i = 0; i < 128; i++)
267 SplTransTbl[i] = i;
268 }
269 for (i = 128; i < 256; i++)
270 SplTransTbl[i] = i;
271 SplTransTbl[255] = 0;
272
273 switch (t) {
274 case RSPLTYPE_SPELL:
275 SplTransTbl[PAL8_YELLOW] = PAL16_BLUE + 1;
276 SplTransTbl[PAL8_YELLOW + 1] = PAL16_BLUE + 3;
277 SplTransTbl[PAL8_YELLOW + 2] = PAL16_BLUE + 5;
278 for (i = PAL16_BLUE; i < PAL16_BLUE + 16; i++) {
279 SplTransTbl[PAL16_BEIGE - PAL16_BLUE + i] = i;
280 SplTransTbl[PAL16_YELLOW - PAL16_BLUE + i] = i;
281 SplTransTbl[PAL16_ORANGE - PAL16_BLUE + i] = i;
282 }
283 break;
284 case RSPLTYPE_SCROLL:
285 SplTransTbl[PAL8_YELLOW] = PAL16_BEIGE + 1;
286 SplTransTbl[PAL8_YELLOW + 1] = PAL16_BEIGE + 3;
287 SplTransTbl[PAL8_YELLOW + 2] = PAL16_BEIGE + 5;
288 for (i = PAL16_BEIGE; i < PAL16_BEIGE + 16; i++) {
289 SplTransTbl[PAL16_YELLOW - PAL16_BEIGE + i] = i;
290 SplTransTbl[PAL16_ORANGE - PAL16_BEIGE + i] = i;
291 }
292 break;
293 case RSPLTYPE_CHARGES:
294 SplTransTbl[PAL8_YELLOW] = PAL16_ORANGE + 1;
295 SplTransTbl[PAL8_YELLOW + 1] = PAL16_ORANGE + 3;
296 SplTransTbl[PAL8_YELLOW + 2] = PAL16_ORANGE + 5;
297 for (i = PAL16_ORANGE; i < PAL16_ORANGE + 16; i++) {
298 SplTransTbl[PAL16_BEIGE - PAL16_ORANGE + i] = i;
299 SplTransTbl[PAL16_YELLOW - PAL16_ORANGE + i] = i;
300 }
301 break;
302 case RSPLTYPE_INVALID:
303 SplTransTbl[PAL8_YELLOW] = PAL16_GRAY + 1;
304 SplTransTbl[PAL8_YELLOW + 1] = PAL16_GRAY + 3;
305 SplTransTbl[PAL8_YELLOW + 2] = PAL16_GRAY + 5;
306 for (i = PAL16_GRAY; i < PAL16_GRAY + 15; i++) {
307 SplTransTbl[PAL16_BEIGE - PAL16_GRAY + i] = i;
308 SplTransTbl[PAL16_YELLOW - PAL16_GRAY + i] = i;
309 SplTransTbl[PAL16_ORANGE - PAL16_GRAY + i] = i;
310 }
311 SplTransTbl[PAL16_BEIGE + 15] = 0;
312 SplTransTbl[PAL16_YELLOW + 15] = 0;
313 SplTransTbl[PAL16_ORANGE + 15] = 0;
314 break;
315 }
316 }
317
318 /**
319 * Sets the spell frame to draw and its position then draws it.
320 */
DrawSpell(CelOutputBuffer out)321 static void DrawSpell(CelOutputBuffer out)
322 {
323 char st;
324 int spl, tlvl;
325
326 spl = plr[myplr]._pRSpell;
327 st = plr[myplr]._pRSplType;
328
329 // BUGFIX: Move the next line into the if statement to avoid OOB (SPL_INVALID is -1) (fixed)
330 if (st == RSPLTYPE_SPELL && spl != SPL_INVALID) {
331 tlvl = plr[myplr]._pISplLvlAdd + plr[myplr]._pSplLvl[spl];
332 if (!CheckSpell(myplr, spl, RSPLTYPE_SPELL, TRUE))
333 st = RSPLTYPE_INVALID;
334 if (tlvl <= 0)
335 st = RSPLTYPE_INVALID;
336 }
337 if (currlevel == 0 && st != RSPLTYPE_INVALID && !spelldata[spl].sTownSpell)
338 st = RSPLTYPE_INVALID;
339 if (plr[myplr]._pRSpell < 0)
340 st = RSPLTYPE_INVALID;
341 SetSpellTrans(st);
342 if (spl != SPL_INVALID)
343 DrawSpellCel(out, PANEL_X + 565, PANEL_Y + 119, pSpellCels, SpellITbl[spl], SPLICONLENGTH);
344 else
345 DrawSpellCel(out, PANEL_X + 565, PANEL_Y + 119, pSpellCels, 27, SPLICONLENGTH);
346 }
347
DrawSpellList(CelOutputBuffer out)348 void DrawSpellList(CelOutputBuffer out)
349 {
350 int x, y, c, s, t, v, lx, ly, trans;
351 Uint64 mask, spl;
352
353 pSpell = SPL_INVALID;
354 infostr[0] = '\0';
355 x = PANEL_X + 12 + SPLICONLENGTH * SPLROWICONLS;
356 y = PANEL_Y - 17;
357 ClearPanel();
358
359 for (Sint32 i = RSPLTYPE_SKILL; i < RSPLTYPE_INVALID; i++) {
360 switch ((spell_type)i) {
361 case RSPLTYPE_SKILL:
362 SetSpellTrans(RSPLTYPE_SKILL);
363 mask = plr[myplr]._pAblSpells;
364 c = SPLICONLAST + 3;
365 break;
366 case RSPLTYPE_SPELL:
367 mask = plr[myplr]._pMemSpells;
368 c = SPLICONLAST + 4;
369 break;
370 case RSPLTYPE_SCROLL:
371 SetSpellTrans(RSPLTYPE_SCROLL);
372 mask = plr[myplr]._pScrlSpells;
373 c = SPLICONLAST + 1;
374 break;
375 case RSPLTYPE_CHARGES:
376 SetSpellTrans(RSPLTYPE_CHARGES);
377 mask = plr[myplr]._pISpells;
378 c = SPLICONLAST + 2;
379 break;
380 case RSPLTYPE_INVALID:
381 break;
382 }
383 Sint32 j = SPL_FIREBOLT;
384 for (spl = 1; j < MAX_SPELLS; spl <<= 1, j++) {
385 if (!(mask & spl))
386 continue;
387 if (i == RSPLTYPE_SPELL) {
388 s = plr[myplr]._pISplLvlAdd + plr[myplr]._pSplLvl[j];
389 if (s < 0)
390 s = 0;
391 if (s > 0)
392 trans = RSPLTYPE_SPELL;
393 else
394 trans = RSPLTYPE_INVALID;
395 SetSpellTrans(trans);
396 }
397 if (currlevel == 0 && !spelldata[j].sTownSpell)
398 SetSpellTrans(RSPLTYPE_INVALID);
399 DrawSpellCel(out, x, y, pSpellCels, SpellITbl[j], SPLICONLENGTH);
400 lx = x;
401 ly = y - SPLICONLENGTH;
402 if (MouseX >= lx && MouseX < lx + SPLICONLENGTH && MouseY >= ly && MouseY < ly + SPLICONLENGTH) {
403 pSpell = (spell_id)j;
404 pSplType = (spell_type)i;
405 if (plr[myplr]._pClass == PC_MONK && j == SPL_SEARCH)
406 pSplType = RSPLTYPE_SKILL;
407 DrawSpellCel(out, x, y, pSpellCels, c, SPLICONLENGTH);
408 switch (pSplType) {
409 case RSPLTYPE_SKILL:
410 sprintf(infostr, "%s Skill", spelldata[pSpell].sSkillText);
411 break;
412 case RSPLTYPE_SPELL:
413 sprintf(infostr, "%s Spell", spelldata[pSpell].sNameText);
414 if (pSpell == SPL_HBOLT) {
415 sprintf(tempstr, "Damages undead only");
416 AddPanelString(tempstr, TRUE);
417 }
418 if (s == 0)
419 sprintf(tempstr, "Spell Level 0 - Unusable");
420 else
421 sprintf(tempstr, "Spell Level %i", s);
422 AddPanelString(tempstr, TRUE);
423 break;
424 case RSPLTYPE_SCROLL:
425 sprintf(infostr, "Scroll of %s", spelldata[pSpell].sNameText);
426 v = 0;
427 for (t = 0; t < plr[myplr]._pNumInv; t++) {
428 if (!plr[myplr].InvList[t].isEmpty()
429 && (plr[myplr].InvList[t]._iMiscId == IMISC_SCROLL || plr[myplr].InvList[t]._iMiscId == IMISC_SCROLLT)
430 && plr[myplr].InvList[t]._iSpell == pSpell) {
431 v++;
432 }
433 }
434 for (t = 0; t < MAXBELTITEMS; t++) {
435 if (!plr[myplr].SpdList[t].isEmpty()
436 && (plr[myplr].SpdList[t]._iMiscId == IMISC_SCROLL || plr[myplr].SpdList[t]._iMiscId == IMISC_SCROLLT)
437 && plr[myplr].SpdList[t]._iSpell == pSpell) {
438 v++;
439 }
440 }
441 if (v == 1)
442 strcpy(tempstr, "1 Scroll");
443 else
444 sprintf(tempstr, "%i Scrolls", v);
445 AddPanelString(tempstr, TRUE);
446 break;
447 case RSPLTYPE_CHARGES:
448 sprintf(infostr, "Staff of %s", spelldata[pSpell].sNameText);
449 if (plr[myplr].InvBody[INVLOC_HAND_LEFT]._iCharges == 1)
450 strcpy(tempstr, "1 Charge");
451 else
452 sprintf(tempstr, "%i Charges", plr[myplr].InvBody[INVLOC_HAND_LEFT]._iCharges);
453 AddPanelString(tempstr, TRUE);
454 break;
455 case RSPLTYPE_INVALID:
456 break;
457 }
458 for (t = 0; t < 4; t++) {
459 if (plr[myplr]._pSplHotKey[t] == pSpell && plr[myplr]._pSplTHotKey[t] == pSplType) {
460 DrawSpellCel(out, x, y, pSpellCels, t + SPLICONLAST + 5, SPLICONLENGTH);
461 sprintf(tempstr, "Spell Hotkey #F%i", t + 5);
462 AddPanelString(tempstr, TRUE);
463 }
464 }
465 }
466 x -= SPLICONLENGTH;
467 if (x == PANEL_X + 12 - SPLICONLENGTH) {
468 x = PANEL_X + 12 + SPLICONLENGTH * SPLROWICONLS;
469 y -= SPLICONLENGTH;
470 }
471 }
472 if (mask != 0 && x != PANEL_X + 12 + SPLICONLENGTH * SPLROWICONLS)
473 x -= SPLICONLENGTH;
474 if (x == PANEL_X + 12 - SPLICONLENGTH) {
475 x = PANEL_X + 12 + SPLICONLENGTH * SPLROWICONLS;
476 y -= SPLICONLENGTH;
477 }
478 }
479 }
480
SetSpell()481 void SetSpell()
482 {
483 spselflag = FALSE;
484 if (pSpell != SPL_INVALID) {
485 ClearPanel();
486 plr[myplr]._pRSpell = pSpell;
487 plr[myplr]._pRSplType = pSplType;
488 force_redraw = 255;
489 }
490 }
491
SetSpeedSpell(int slot)492 void SetSpeedSpell(int slot)
493 {
494 if (pSpell != SPL_INVALID) {
495 for (int i = 0; i < 4; ++i) {
496 if (plr[myplr]._pSplHotKey[i] == pSpell && plr[myplr]._pSplTHotKey[i] == pSplType)
497 plr[myplr]._pSplHotKey[i] = SPL_INVALID;
498 }
499 plr[myplr]._pSplHotKey[slot] = pSpell;
500 plr[myplr]._pSplTHotKey[slot] = pSplType;
501 }
502 }
503
ToggleSpell(int slot)504 void ToggleSpell(int slot)
505 {
506 Uint64 spells;
507
508 if (plr[myplr]._pSplHotKey[slot] == SPL_INVALID) {
509 return;
510 }
511
512 switch (plr[myplr]._pSplTHotKey[slot]) {
513 case RSPLTYPE_SKILL:
514 spells = plr[myplr]._pAblSpells;
515 break;
516 case RSPLTYPE_SPELL:
517 spells = plr[myplr]._pMemSpells;
518 break;
519 case RSPLTYPE_SCROLL:
520 spells = plr[myplr]._pScrlSpells;
521 break;
522 case RSPLTYPE_CHARGES:
523 spells = plr[myplr]._pISpells;
524 break;
525 case RSPLTYPE_INVALID:
526 return;
527 }
528
529 if (spells & GetSpellBitmask(plr[myplr]._pSplHotKey[slot])) {
530 plr[myplr]._pRSpell = plr[myplr]._pSplHotKey[slot];
531 plr[myplr]._pRSplType = plr[myplr]._pSplTHotKey[slot];
532 force_redraw = 255;
533 }
534 }
535
PrintChar(CelOutputBuffer out,int sx,int sy,int nCel,text_color col)536 void PrintChar(CelOutputBuffer out, int sx, int sy, int nCel, text_color col)
537 {
538 int i;
539 BYTE pix;
540 BYTE tbl[256];
541
542 switch (col) {
543 case COL_WHITE:
544 CelDrawTo(out, sx, sy, pPanelText, nCel, 13);
545 return;
546 case COL_BLUE:
547 for (i = 0; i < 256; i++) {
548 pix = i;
549 if (pix > PAL16_GRAY + 13)
550 pix = PAL16_BLUE + 15;
551 else if (pix >= PAL16_GRAY)
552 pix -= PAL16_GRAY - (PAL16_BLUE + 2);
553 tbl[i] = pix;
554 }
555 break;
556 case COL_RED:
557 for (i = 0; i < 256; i++) {
558 pix = i;
559 if (pix >= PAL16_GRAY)
560 pix -= PAL16_GRAY - PAL16_RED;
561 tbl[i] = pix;
562 }
563 break;
564 case COL_GOLD:
565 for (i = 0; i < 256; i++) {
566 pix = i;
567 if (pix >= PAL16_GRAY) {
568 if (pix >= PAL16_GRAY + 14)
569 pix = PAL16_YELLOW + 15;
570 else
571 pix -= PAL16_GRAY - (PAL16_YELLOW + 2);
572 }
573 tbl[i] = pix;
574 }
575 break;
576 case COL_BLACK:
577 light_table_index = 15;
578 CelDrawLightTo(out, sx, sy, pPanelText, nCel, 13, NULL);
579 return;
580 }
581 CelDrawLightTo(out, sx, sy, pPanelText, nCel, 13, tbl);
582 }
583
AddPanelString(const char * str,BOOL just)584 void AddPanelString(const char *str, BOOL just)
585 {
586 strcpy(panelstr[pnumlines], str);
587 pstrjust[pnumlines] = just;
588
589 if (pnumlines < 4)
590 pnumlines++;
591 }
592
ClearPanel()593 void ClearPanel()
594 {
595 pnumlines = 0;
596 pinfoflag = FALSE;
597 }
598
DrawPanelBox(CelOutputBuffer out,int x,int y,int w,int h,int sx,int sy)599 void DrawPanelBox(CelOutputBuffer out, int x, int y, int w, int h, int sx, int sy)
600 {
601 const BYTE *src = pBtmBuff.at(x, y);
602 BYTE *dst = out.at(sx, sy);
603
604 for (int hgt = h; hgt; hgt--, src += pBtmBuff.pitch(), dst += out.pitch()) {
605 memcpy(dst, src, w);
606 }
607 }
608
609 /**
610 * Draws a section of the empty flask cel on top of the panel to create the illusion
611 * of the flask getting empty. This function takes a cel and draws a
612 * horizontal stripe of height (max-min) onto the given buffer.
613 * @param out Target buffer.
614 * @param sx Buffer coordinate
615 * @param sy Buffer coordinate
616 * @param celBuf Buffer of the empty flask cel.
617 * @param y0 Top of the flask cel section to draw.
618 * @param y1 Bottom of the flask cel section to draw.
619 */
DrawFlaskTop(CelOutputBuffer out,int sx,int sy,CelOutputBuffer celBuf,int y0,int y1)620 static void DrawFlaskTop(CelOutputBuffer out, int sx, int sy, CelOutputBuffer celBuf, int y0, int y1)
621 {
622 const BYTE *src = celBuf.at(0, y0);
623 BYTE *dst = out.at(sx, sy);
624
625 for (int h = y1 - y0; h != 0; --h, src += celBuf.pitch(), dst += out.pitch())
626 memcpy(dst, src, celBuf.w());
627 }
628
629 /**
630 * Draws the dome of the flask that protrudes above the panel top line.
631 * It draws a rectangle of fixed width 59 and height 'h' from the source buffer
632 * into the target buffer.
633 * @param out The target buffer.
634 * @param celBuf Buffer of the empty flask cel.
635 * @param celX Source buffer start coordinate.
636 * @param celY Source buffer start coordinate.
637 * @param sx Target buffer coordinate.
638 * @param sy Target buffer coordinate.
639 * @param h How many lines of the source buffer that will be copied.
640 */
DrawFlask(CelOutputBuffer out,CelOutputBuffer celBuf,int celX,int celY,int x,int y,int h)641 static void DrawFlask(CelOutputBuffer out, CelOutputBuffer celBuf, int celX, int celY, int x, int y, int h)
642 {
643 int wdt, hgt;
644 const BYTE *src = celBuf.at(celX, celY);
645 BYTE *dst = out.at(x, y);
646
647 for (hgt = h; hgt; hgt--, src += celBuf.pitch() - 59, dst += out.pitch() - 59) {
648 for (wdt = 59; wdt; wdt--) {
649 if (*src)
650 *dst = *src;
651 src++;
652 dst++;
653 }
654 }
655 }
656
DrawLifeFlask(CelOutputBuffer out)657 void DrawLifeFlask(CelOutputBuffer out)
658 {
659 double p;
660 int filled;
661
662 p = 0.0;
663 if (plr[myplr]._pMaxHP > 0) {
664 p = (double)plr[myplr]._pHitPoints / (double)plr[myplr]._pMaxHP * 80.0;
665 }
666 plr[myplr]._pHPPer = p;
667 filled = plr[myplr]._pHPPer;
668
669 if (filled > 80)
670 filled = 80;
671
672 filled = 80 - filled;
673 if (filled > 11)
674 filled = 11;
675 filled += 2;
676
677 DrawFlask(out, pLifeBuff, 13, 3, PANEL_LEFT + 109, PANEL_TOP - 13, filled);
678 if (filled != 13)
679 DrawFlask(out, pBtmBuff, 109, filled + 3, PANEL_LEFT + 109, PANEL_TOP - 13 + filled, 13 - filled);
680 }
681
UpdateLifeFlask(CelOutputBuffer out)682 void UpdateLifeFlask(CelOutputBuffer out)
683 {
684 double p;
685 int filled;
686
687 p = 0.0;
688 if (plr[myplr]._pMaxHP > 0) {
689 p = (double)plr[myplr]._pHitPoints / (double)plr[myplr]._pMaxHP * 80.0;
690 }
691 filled = p;
692 plr[myplr]._pHPPer = filled;
693
694 if (filled > 69)
695 filled = 69;
696 else if (filled < 0)
697 filled = 0;
698 if (filled != 69)
699 DrawFlaskTop(out, 96 + PANEL_X, PANEL_Y, pLifeBuff, 16, 85 - filled);
700 if (filled != 0)
701 DrawPanelBox(out, 96, 85 - filled, 88, filled, 96 + PANEL_X, PANEL_Y + 69 - filled);
702 }
703
DrawManaFlask(CelOutputBuffer out)704 void DrawManaFlask(CelOutputBuffer out)
705 {
706 int filled = plr[myplr]._pManaPer;
707 if (filled > 80)
708 filled = 80;
709 filled = 80 - filled;
710 if (filled > 11)
711 filled = 11;
712 filled += 2;
713
714 DrawFlask(out, pManaBuff, 13, 3, PANEL_LEFT + 475, PANEL_TOP - 13, filled);
715 if (filled != 13)
716 DrawFlask(out, pBtmBuff, 475, filled + 3, PANEL_LEFT + 475, PANEL_TOP - 13 + filled, 13 - filled);
717 }
718
control_update_life_mana()719 void control_update_life_mana()
720 {
721 int manaPer;
722 int maxMana = plr[myplr]._pMaxMana;
723 int mana = plr[myplr]._pMana;
724 if (maxMana < 0)
725 maxMana = 0;
726 if (mana < 0)
727 mana = 0;
728 if (maxMana == 0)
729 manaPer = 0;
730 else
731 manaPer = (double)mana / (double)maxMana * 80.0;
732 plr[myplr]._pManaPer = manaPer;
733 plr[myplr]._pHPPer = (double)plr[myplr]._pHitPoints / (double)plr[myplr]._pMaxHP * 80.0;
734 }
735
UpdateManaFlask(CelOutputBuffer out)736 void UpdateManaFlask(CelOutputBuffer out)
737 {
738 int filled;
739 int maxMana = plr[myplr]._pMaxMana;
740 int mana = plr[myplr]._pMana;
741 if (maxMana < 0)
742 maxMana = 0;
743 if (mana < 0)
744 mana = 0;
745
746 if (maxMana == 0)
747 filled = 0;
748 else
749 filled = (double)mana / (double)maxMana * 80.0;
750
751 plr[myplr]._pManaPer = filled;
752
753 if (filled > 69)
754 filled = 69;
755 if (filled != 69)
756 DrawFlaskTop(out, PANEL_X + 464, PANEL_Y, pManaBuff, 16, 85 - filled);
757 if (filled != 0)
758 DrawPanelBox(out, 464, 85 - filled, 88, filled, PANEL_X + 464, PANEL_Y + 69 - filled);
759
760 DrawSpell(out);
761 }
762
InitControlPan()763 void InitControlPan()
764 {
765 int i;
766 pBtmBuff = CelOutputBuffer::Alloc(PANEL_WIDTH, (PANEL_HEIGHT + 16) * (gbIsMultiplayer ? 2 : 1));
767 pManaBuff = CelOutputBuffer::Alloc(88, 88);
768 pLifeBuff = CelOutputBuffer::Alloc(88, 88);
769
770 pPanelText = LoadFileInMem("CtrlPan\\SmalText.CEL", NULL);
771 pChrPanel = LoadFileInMem("Data\\Char.CEL", NULL);
772 if (!gbIsHellfire)
773 pSpellCels = LoadFileInMem("CtrlPan\\SpelIcon.CEL", NULL);
774 else
775 pSpellCels = LoadFileInMem("Data\\SpelIcon.CEL", NULL);
776 SetSpellTrans(RSPLTYPE_SKILL);
777 BYTE *pStatusPanel = LoadFileInMem("CtrlPan\\Panel8.CEL", NULL);
778 CelDrawUnsafeTo(pBtmBuff, 0, (PANEL_HEIGHT + 16) - 1, pStatusPanel, 1, PANEL_WIDTH);
779 MemFreeDbg(pStatusPanel);
780 pStatusPanel = LoadFileInMem("CtrlPan\\P8Bulbs.CEL", NULL);
781 CelDrawUnsafeTo(pLifeBuff, 0, 87, pStatusPanel, 1, 88);
782 CelDrawUnsafeTo(pManaBuff, 0, 87, pStatusPanel, 2, 88);
783 MemFreeDbg(pStatusPanel);
784 talkflag = FALSE;
785 if (gbIsMultiplayer) {
786 BYTE *pTalkPanel = LoadFileInMem("CtrlPan\\TalkPanl.CEL", NULL);
787 CelDrawUnsafeTo(pBtmBuff, 0, (PANEL_HEIGHT + 16) * 2 - 1, pTalkPanel, 1, PANEL_WIDTH);
788 MemFreeDbg(pTalkPanel);
789 pMultiBtns = LoadFileInMem("CtrlPan\\P8But2.CEL", NULL);
790 pTalkBtns = LoadFileInMem("CtrlPan\\TalkButt.CEL", NULL);
791 sgbPlrTalkTbl = 0;
792 sgszTalkMsg[0] = '\0';
793 for (i = 0; i < MAX_PLRS; i++)
794 whisper[i] = TRUE;
795 for (i = 0; i < sizeof(talkbtndown) / sizeof(talkbtndown[0]); i++)
796 talkbtndown[i] = FALSE;
797 }
798 panelflag = FALSE;
799 lvlbtndown = FALSE;
800 pPanelButtons = LoadFileInMem("CtrlPan\\Panel8bu.CEL", NULL);
801 for (i = 0; i < sizeof(panbtn) / sizeof(panbtn[0]); i++)
802 panbtn[i] = FALSE;
803 panbtndown = FALSE;
804 if (!gbIsMultiplayer)
805 numpanbtns = 6;
806 else
807 numpanbtns = 8;
808 pChrButtons = LoadFileInMem("Data\\CharBut.CEL", NULL);
809 for (i = 0; i < sizeof(chrbtn) / sizeof(chrbtn[0]); i++)
810 chrbtn[i] = FALSE;
811 chrbtnactive = FALSE;
812 pDurIcons = LoadFileInMem("Items\\DurIcons.CEL", NULL);
813 strcpy(infostr, "");
814 ClearPanel();
815 drawhpflag = TRUE;
816 drawmanaflag = TRUE;
817 chrflag = FALSE;
818 spselflag = FALSE;
819 pSpellBkCel = LoadFileInMem("Data\\SpellBk.CEL", NULL);
820 pSBkBtnCel = LoadFileInMem("Data\\SpellBkB.CEL", NULL);
821 pSBkIconCels = LoadFileInMem("Data\\SpellI2.CEL", NULL);
822 sbooktab = 0;
823 sbookflag = FALSE;
824 if (plr[myplr]._pClass == PC_WARRIOR) {
825 SpellPages[0][0] = SPL_REPAIR;
826 } else if (plr[myplr]._pClass == PC_ROGUE) {
827 SpellPages[0][0] = SPL_DISARM;
828 } else if (plr[myplr]._pClass == PC_SORCERER) {
829 SpellPages[0][0] = SPL_RECHARGE;
830 } else if (plr[myplr]._pClass == PC_MONK) {
831 SpellPages[0][0] = SPL_SEARCH;
832 } else if (plr[myplr]._pClass == PC_BARD) {
833 SpellPages[0][0] = SPL_IDENTIFY;
834 } else if (plr[myplr]._pClass == PC_BARBARIAN) {
835 SpellPages[0][0] = SPL_BLODBOIL;
836 }
837 pQLogCel = LoadFileInMem("Data\\Quest.CEL", NULL);
838 pGBoxBuff = LoadFileInMem("CtrlPan\\Golddrop.cel", NULL);
839 dropGoldFlag = FALSE;
840 dropGoldValue = 0;
841 initialDropGoldValue = 0;
842 initialDropGoldIndex = 0;
843 }
844
DrawCtrlPan(CelOutputBuffer out)845 void DrawCtrlPan(CelOutputBuffer out)
846 {
847 DrawPanelBox(out, 0, sgbPlrTalkTbl + 16, PANEL_WIDTH, PANEL_HEIGHT, PANEL_X, PANEL_Y);
848 DrawInfoBox(out);
849 }
850
DrawCtrlBtns(CelOutputBuffer out)851 void DrawCtrlBtns(CelOutputBuffer out)
852 {
853 int i;
854
855 for (i = 0; i < 6; i++) {
856 if (!panbtn[i])
857 DrawPanelBox(out, PanBtnPos[i][0], PanBtnPos[i][1] + 16, 71, 20, PanBtnPos[i][0] + PANEL_X, PanBtnPos[i][1] + PANEL_Y);
858 else
859 CelDrawTo(out, PanBtnPos[i][0] + PANEL_X, PanBtnPos[i][1] + PANEL_Y + 18, pPanelButtons, i + 1, 71);
860 }
861 if (numpanbtns == 8) {
862 CelDrawTo(out, 87 + PANEL_X, 122 + PANEL_Y, pMultiBtns, panbtn[6] + 1, 33);
863 if (gbFriendlyMode)
864 CelDrawTo(out, 527 + PANEL_X, 122 + PANEL_Y, pMultiBtns, panbtn[7] + 3, 33);
865 else
866 CelDrawTo(out, 527 + PANEL_X, 122 + PANEL_Y, pMultiBtns, panbtn[7] + 5, 33);
867 }
868 }
869
870 /**
871 * Draws the "Speed Book": the rows of known spells for quick-setting a spell that
872 * show up when you click the spell slot at the control panel.
873 */
DoSpeedBook()874 void DoSpeedBook()
875 {
876 int xo, yo, X, Y, i, j;
877
878 spselflag = TRUE;
879 xo = PANEL_X + 12 + SPLICONLENGTH * 10;
880 yo = PANEL_Y - 17;
881 X = xo + SPLICONLENGTH / 2;
882 Y = yo - SPLICONLENGTH / 2;
883
884 if (plr[myplr]._pRSpell != SPL_INVALID) {
885 for (i = 0; i < 4; i++) {
886 Uint64 spells;
887 switch (i) {
888 case RSPLTYPE_SKILL:
889 spells = plr[myplr]._pAblSpells;
890 break;
891 case RSPLTYPE_SPELL:
892 spells = plr[myplr]._pMemSpells;
893 break;
894 case RSPLTYPE_SCROLL:
895 spells = plr[myplr]._pScrlSpells;
896 break;
897 case RSPLTYPE_CHARGES:
898 spells = plr[myplr]._pISpells;
899 break;
900 }
901 Uint64 spell = 1;
902 for (j = 1; j < MAX_SPELLS; j++) {
903 if (spell & spells) {
904 if (j == plr[myplr]._pRSpell && i == plr[myplr]._pRSplType) {
905 X = xo + SPLICONLENGTH / 2;
906 Y = yo - SPLICONLENGTH / 2;
907 }
908 xo -= SPLICONLENGTH;
909 if (xo == PANEL_X + 12 - SPLICONLENGTH) {
910 xo = PANEL_X + 12 + SPLICONLENGTH * SPLROWICONLS;
911 yo -= SPLICONLENGTH;
912 }
913 }
914 spell <<= 1ULL;
915 }
916 if (spells && xo != PANEL_X + 12 + SPLICONLENGTH * SPLROWICONLS)
917 xo -= SPLICONLENGTH;
918 if (xo == PANEL_X + 12 - SPLICONLENGTH) {
919 xo = PANEL_X + 12 + SPLICONLENGTH * SPLROWICONLS;
920 yo -= SPLICONLENGTH;
921 }
922 }
923 }
924
925 SetCursorPos(X, Y);
926 }
927
928 /**
929 * Checks if the mouse cursor is within any of the panel buttons and flag it if so.
930 */
DoPanBtn()931 void DoPanBtn()
932 {
933 int i;
934
935 for (i = 0; i < numpanbtns; i++) {
936 int x = PanBtnPos[i][0] + PANEL_LEFT + PanBtnPos[i][2];
937 int y = PanBtnPos[i][1] + PANEL_TOP + PanBtnPos[i][3];
938 if (MouseX >= PanBtnPos[i][0] + PANEL_LEFT && MouseX <= x) {
939 if (MouseY >= PanBtnPos[i][1] + PANEL_TOP && MouseY <= y) {
940 panbtn[i] = TRUE;
941 drawbtnflag = TRUE;
942 panbtndown = TRUE;
943 }
944 }
945 }
946 if (!spselflag && MouseX >= 565 + PANEL_LEFT && MouseX < 621 + PANEL_LEFT && MouseY >= 64 + PANEL_TOP && MouseY < 120 + PANEL_TOP) {
947 if (SDL_GetModState() & KMOD_SHIFT) {
948 plr[myplr]._pRSpell = SPL_INVALID;
949 plr[myplr]._pRSplType = RSPLTYPE_INVALID;
950 force_redraw = 255;
951 return;
952 }
953 DoSpeedBook();
954 gamemenu_off();
955 }
956 }
957
control_set_button_down(int btn_id)958 void control_set_button_down(int btn_id)
959 {
960 panbtn[btn_id] = TRUE;
961 drawbtnflag = TRUE;
962 panbtndown = TRUE;
963 }
964
control_check_btn_press()965 void control_check_btn_press()
966 {
967 int x, y;
968
969 x = PanBtnPos[3][0] + PANEL_LEFT + PanBtnPos[3][2];
970 y = PanBtnPos[3][1] + PANEL_TOP + PanBtnPos[3][3];
971 if (MouseX >= PanBtnPos[3][0] + PANEL_LEFT
972 && MouseX <= x
973 && MouseY >= PanBtnPos[3][1] + PANEL_TOP
974 && MouseY <= y) {
975 control_set_button_down(3);
976 }
977 x = PanBtnPos[6][0] + PANEL_LEFT + PanBtnPos[6][2];
978 y = PanBtnPos[6][1] + PANEL_TOP + PanBtnPos[6][3];
979 if (MouseX >= PanBtnPos[6][0] + PANEL_LEFT
980 && MouseX <= x
981 && MouseY >= PanBtnPos[6][1] + PANEL_TOP
982 && MouseY <= y) {
983 control_set_button_down(6);
984 }
985 }
986
DoAutoMap()987 void DoAutoMap()
988 {
989 if (currlevel != 0 || gbIsMultiplayer) {
990 if (!automapflag)
991 StartAutomap();
992 else
993 automapflag = FALSE;
994 } else {
995 InitDiabloMsg(EMSG_NO_AUTOMAP_IN_TOWN);
996 }
997 }
998
999 /**
1000 * Checks the mouse cursor position within the control panel and sets information
1001 * strings if needed.
1002 */
CheckPanelInfo()1003 void CheckPanelInfo()
1004 {
1005 int i, c, s, xend, yend;
1006
1007 panelflag = FALSE;
1008 ClearPanel();
1009 for (i = 0; i < numpanbtns; i++) {
1010 xend = PanBtnPos[i][0] + PANEL_LEFT + PanBtnPos[i][2];
1011 yend = PanBtnPos[i][1] + PANEL_TOP + PanBtnPos[i][3];
1012 if (MouseX >= PanBtnPos[i][0] + PANEL_LEFT && MouseX <= xend && MouseY >= PanBtnPos[i][1] + PANEL_TOP && MouseY <= yend) {
1013 if (i != 7) {
1014 strcpy(infostr, PanBtnStr[i]);
1015 } else {
1016 if (gbFriendlyMode)
1017 strcpy(infostr, "Player friendly");
1018 else
1019 strcpy(infostr, "Player attack");
1020 }
1021 if (PanBtnHotKey[i] != NULL) {
1022 sprintf(tempstr, "Hotkey: %s", PanBtnHotKey[i]);
1023 AddPanelString(tempstr, TRUE);
1024 }
1025 infoclr = COL_WHITE;
1026 panelflag = TRUE;
1027 pinfoflag = TRUE;
1028 }
1029 }
1030 if (!spselflag && MouseX >= 565 + PANEL_LEFT && MouseX < 621 + PANEL_LEFT && MouseY >= 64 + PANEL_TOP && MouseY < 120 + PANEL_TOP) {
1031 strcpy(infostr, "Select current spell button");
1032 infoclr = COL_WHITE;
1033 panelflag = TRUE;
1034 pinfoflag = TRUE;
1035 strcpy(tempstr, "Hotkey: 's'");
1036 AddPanelString(tempstr, TRUE);
1037 spell_id v = plr[myplr]._pRSpell;
1038 if (v != SPL_INVALID) {
1039 switch (plr[myplr]._pRSplType) {
1040 case RSPLTYPE_SKILL:
1041 sprintf(tempstr, "%s Skill", spelldata[v].sSkillText);
1042 AddPanelString(tempstr, TRUE);
1043 break;
1044 case RSPLTYPE_SPELL:
1045 sprintf(tempstr, "%s Spell", spelldata[v].sNameText);
1046 AddPanelString(tempstr, TRUE);
1047 c = plr[myplr]._pISplLvlAdd + plr[myplr]._pSplLvl[v];
1048 if (c < 0)
1049 c = 0;
1050 if (c == 0)
1051 sprintf(tempstr, "Spell Level 0 - Unusable");
1052 else
1053 sprintf(tempstr, "Spell Level %i", c);
1054 AddPanelString(tempstr, TRUE);
1055 break;
1056 case RSPLTYPE_SCROLL:
1057 sprintf(tempstr, "Scroll of %s", spelldata[v].sNameText);
1058 AddPanelString(tempstr, TRUE);
1059 s = 0;
1060 for (i = 0; i < plr[myplr]._pNumInv; i++) {
1061 if (!plr[myplr].InvList[i].isEmpty()
1062 && (plr[myplr].InvList[i]._iMiscId == IMISC_SCROLL || plr[myplr].InvList[i]._iMiscId == IMISC_SCROLLT)
1063 && plr[myplr].InvList[i]._iSpell == v) {
1064 s++;
1065 }
1066 }
1067 for (i = 0; i < MAXBELTITEMS; i++) {
1068 if (!plr[myplr].SpdList[i].isEmpty()
1069 && (plr[myplr].SpdList[i]._iMiscId == IMISC_SCROLL || plr[myplr].SpdList[i]._iMiscId == IMISC_SCROLLT)
1070 && plr[myplr].SpdList[i]._iSpell == v) {
1071 s++;
1072 }
1073 }
1074 if (s == 1)
1075 strcpy(tempstr, "1 Scroll");
1076 else
1077 sprintf(tempstr, "%i Scrolls", s);
1078 AddPanelString(tempstr, TRUE);
1079 break;
1080 case RSPLTYPE_CHARGES:
1081 sprintf(tempstr, "Staff of %s", spelldata[v].sNameText);
1082 AddPanelString(tempstr, TRUE);
1083 if (plr[myplr].InvBody[INVLOC_HAND_LEFT]._iCharges == 1)
1084 strcpy(tempstr, "1 Charge");
1085 else
1086 sprintf(tempstr, "%i Charges", plr[myplr].InvBody[INVLOC_HAND_LEFT]._iCharges);
1087 AddPanelString(tempstr, TRUE);
1088 break;
1089 case RSPLTYPE_INVALID:
1090 break;
1091 }
1092 }
1093 }
1094 if (MouseX > 190 + PANEL_LEFT && MouseX < 437 + PANEL_LEFT && MouseY > 4 + PANEL_TOP && MouseY < 33 + PANEL_TOP)
1095 pcursinvitem = CheckInvHLight();
1096 }
1097
1098 /**
1099 * Check if the mouse is within a control panel button that's flagged.
1100 * Takes apropiate action if so.
1101 */
CheckBtnUp()1102 void CheckBtnUp()
1103 {
1104 int i;
1105 BOOLEAN gamemenuOff;
1106
1107 gamemenuOff = TRUE;
1108 drawbtnflag = TRUE;
1109 panbtndown = FALSE;
1110
1111 for (i = 0; i < 8; i++) {
1112 if (!panbtn[i]) {
1113 continue;
1114 }
1115
1116 panbtn[i] = FALSE;
1117
1118 if (MouseX < PanBtnPos[i][0] + PANEL_LEFT
1119 || MouseX > PanBtnPos[i][0] + PANEL_LEFT + PanBtnPos[i][2]
1120 || MouseY < PanBtnPos[i][1] + PANEL_TOP
1121 || MouseY > PanBtnPos[i][1] + PANEL_TOP + PanBtnPos[i][3]) {
1122 continue;
1123 }
1124
1125 switch (i) {
1126 case PANBTN_CHARINFO:
1127 questlog = FALSE;
1128 chrflag = !chrflag;
1129 break;
1130 case PANBTN_QLOG:
1131 chrflag = FALSE;
1132 if (!questlog)
1133 StartQuestlog();
1134 else
1135 questlog = FALSE;
1136 break;
1137 case PANBTN_AUTOMAP:
1138 DoAutoMap();
1139 break;
1140 case PANBTN_MAINMENU:
1141 qtextflag = FALSE;
1142 gamemenu_handle_previous();
1143 gamemenuOff = FALSE;
1144 break;
1145 case PANBTN_INVENTORY:
1146 sbookflag = FALSE;
1147 invflag = !invflag;
1148 if (dropGoldFlag) {
1149 dropGoldFlag = FALSE;
1150 dropGoldValue = 0;
1151 }
1152 break;
1153 case PANBTN_SPELLBOOK:
1154 invflag = FALSE;
1155 if (dropGoldFlag) {
1156 dropGoldFlag = FALSE;
1157 dropGoldValue = 0;
1158 }
1159 sbookflag = !sbookflag;
1160 break;
1161 case PANBTN_SENDMSG:
1162 if (talkflag)
1163 control_reset_talk();
1164 else
1165 control_type_message();
1166 break;
1167 case PANBTN_FRIENDLY:
1168 gbFriendlyMode = !gbFriendlyMode;
1169 break;
1170 }
1171 }
1172
1173 if (gamemenuOff)
1174 gamemenu_off();
1175 }
1176
FreeControlPan()1177 void FreeControlPan()
1178 {
1179 pBtmBuff.Free();
1180 pManaBuff.Free();
1181 pLifeBuff.Free();
1182 MemFreeDbg(pPanelText);
1183 MemFreeDbg(pChrPanel);
1184 MemFreeDbg(pSpellCels);
1185 MemFreeDbg(pPanelButtons);
1186 MemFreeDbg(pMultiBtns);
1187 MemFreeDbg(pTalkBtns);
1188 MemFreeDbg(pChrButtons);
1189 MemFreeDbg(pDurIcons);
1190 MemFreeDbg(pQLogCel);
1191 MemFreeDbg(pSpellBkCel);
1192 MemFreeDbg(pSBkBtnCel);
1193 MemFreeDbg(pSBkIconCels);
1194 MemFreeDbg(pGBoxBuff);
1195 }
1196
control_WriteStringToBuffer(BYTE * str)1197 BOOL control_WriteStringToBuffer(BYTE *str)
1198 {
1199 int k;
1200 BYTE ichar;
1201
1202 k = 0;
1203 while (*str) {
1204 ichar = gbFontTransTbl[*str];
1205 str++;
1206 k += fontkern[fontframe[ichar]];
1207 if (k >= 125)
1208 return FALSE;
1209 }
1210
1211 return TRUE;
1212 }
1213
CPrintString(CelOutputBuffer out,int y,const char * str,BOOL center,int lines)1214 static void CPrintString(CelOutputBuffer out, int y, const char *str, BOOL center, int lines)
1215 {
1216 BYTE c;
1217 const char *tmp;
1218 int lineOffset, strWidth, sx, sy;
1219
1220 lineOffset = 0;
1221 sx = 177 + PANEL_X;
1222 sy = lineOffsets[lines][y] + PANEL_Y;
1223 if (center == TRUE) {
1224 strWidth = 0;
1225 tmp = str;
1226 while (*tmp) {
1227 c = gbFontTransTbl[(BYTE)*tmp++];
1228 strWidth += fontkern[fontframe[c]] + 2;
1229 }
1230 if (strWidth < 288)
1231 lineOffset = (288 - strWidth) >> 1;
1232 sx += lineOffset;
1233 }
1234 while (*str) {
1235 c = gbFontTransTbl[(BYTE)*str++];
1236 c = fontframe[c];
1237 lineOffset += fontkern[c] + 2;
1238 if (c) {
1239 if (lineOffset < 288) {
1240 PrintChar(out, sx, sy, c, infoclr);
1241 }
1242 }
1243 sx += fontkern[c] + 2;
1244 }
1245 }
1246
PrintInfo(CelOutputBuffer out)1247 static void PrintInfo(CelOutputBuffer out)
1248 {
1249 int yo, lo, i;
1250
1251 if (!talkflag) {
1252 yo = 0;
1253 lo = 1;
1254 if (infostr[0] != '\0') {
1255 CPrintString(out, 0, infostr, TRUE, pnumlines);
1256 yo = 1;
1257 lo = 0;
1258 }
1259
1260 for (i = 0; i < pnumlines; i++) {
1261 CPrintString(out, i + yo, panelstr[i], pstrjust[i], pnumlines - lo);
1262 }
1263 }
1264 }
1265
DrawInfoBox(CelOutputBuffer out)1266 void DrawInfoBox(CelOutputBuffer out)
1267 {
1268 int nGold;
1269
1270 DrawPanelBox(out, 177, 62, 288, 60, PANEL_X + 177, PANEL_Y + 46);
1271 if (!panelflag && !trigflag && pcursinvitem == -1 && !spselflag) {
1272 infostr[0] = '\0';
1273 infoclr = COL_WHITE;
1274 ClearPanel();
1275 }
1276 if (spselflag || trigflag) {
1277 infoclr = COL_WHITE;
1278 } else if (pcurs >= CURSOR_FIRSTITEM) {
1279 if (plr[myplr].HoldItem._itype == ITYPE_GOLD) {
1280 nGold = plr[myplr].HoldItem._ivalue;
1281 sprintf(infostr, "%i gold %s", nGold, get_pieces_str(nGold));
1282 } else if (!plr[myplr].HoldItem._iStatFlag) {
1283 ClearPanel();
1284 AddPanelString("Requirements not met", TRUE);
1285 pinfoflag = TRUE;
1286 } else {
1287 if (plr[myplr].HoldItem._iIdentified)
1288 strcpy(infostr, plr[myplr].HoldItem._iIName);
1289 else
1290 strcpy(infostr, plr[myplr].HoldItem._iName);
1291 if (plr[myplr].HoldItem._iMagical == ITEM_QUALITY_MAGIC)
1292 infoclr = COL_BLUE;
1293 if (plr[myplr].HoldItem._iMagical == ITEM_QUALITY_UNIQUE)
1294 infoclr = COL_GOLD;
1295 }
1296 } else {
1297 if (pcursitem != -1)
1298 GetItemStr(pcursitem);
1299 else if (pcursobj != -1)
1300 GetObjectStr(pcursobj);
1301 if (pcursmonst != -1) {
1302 if (leveltype != DTYPE_TOWN) {
1303 infoclr = COL_WHITE;
1304 strcpy(infostr, monster[pcursmonst].mName);
1305 ClearPanel();
1306 if (monster[pcursmonst]._uniqtype != 0) {
1307 infoclr = COL_GOLD;
1308 PrintUniqueHistory();
1309 } else {
1310 PrintMonstHistory(monster[pcursmonst].MType->mtype);
1311 }
1312 } else if (pcursitem == -1) {
1313 strcpy(infostr, towner[pcursmonst]._tName);
1314 }
1315 }
1316 if (pcursplr != -1) {
1317 infoclr = COL_GOLD;
1318 strcpy(infostr, plr[pcursplr]._pName);
1319 ClearPanel();
1320 sprintf(tempstr, "%s, Level: %i", ClassStrTbl[plr[pcursplr]._pClass], plr[pcursplr]._pLevel);
1321 AddPanelString(tempstr, TRUE);
1322 sprintf(tempstr, "Hit Points %i of %i", plr[pcursplr]._pHitPoints >> 6, plr[pcursplr]._pMaxHP >> 6);
1323 AddPanelString(tempstr, TRUE);
1324 }
1325 }
1326 if (infostr[0] != '\0' || pnumlines != 0)
1327 PrintInfo(out);
1328 }
1329
1330 #define ADD_PlrStringXY(out, x, y, width, pszStr, col) MY_PlrStringXY(out, x, y, width, pszStr, col, 1)
1331
PrintGameStr(CelOutputBuffer out,int x,int y,const char * str,text_color color)1332 void PrintGameStr(CelOutputBuffer out, int x, int y, const char *str, text_color color)
1333 {
1334 while (*str) {
1335 BYTE c = gbFontTransTbl[(BYTE)*str++];
1336 c = fontframe[c];
1337 if (c)
1338 PrintChar(out, x, y, c, color);
1339 x += fontkern[c] + 1;
1340 }
1341 }
1342
1343 /**
1344 * @brief Render text string to the given buffer
1345 * @param out Buffer to render to
1346 * @param x Screen coordinate
1347 * @param y Screen coordinate
1348 * @param endX End of line in screen coordinate
1349 * @param pszStr String to print, in Windows-1252 encoding
1350 * @param col text_color color value
1351 * @param base Letter spacing
1352 */
MY_PlrStringXY(CelOutputBuffer out,int x,int y,int endX,const char * pszStr,text_color col,int base)1353 static void MY_PlrStringXY(CelOutputBuffer out, int x, int y, int endX, const char *pszStr, text_color col, int base)
1354 {
1355 BYTE c;
1356 const char *tmp;
1357 int screen_x, line, widthOffset;
1358
1359 widthOffset = endX - x + 1;
1360 line = 0;
1361 screen_x = 0;
1362 tmp = pszStr;
1363 while (*tmp) {
1364 c = gbFontTransTbl[(BYTE)*tmp++];
1365 screen_x += fontkern[fontframe[c]] + base;
1366 }
1367 if (screen_x < widthOffset)
1368 line = (widthOffset - screen_x) >> 1;
1369 x += line;
1370 while (*pszStr) {
1371 c = gbFontTransTbl[(BYTE)*pszStr++];
1372 c = fontframe[c];
1373 line += fontkern[c] + base;
1374 if (c) {
1375 if (line < widthOffset)
1376 PrintChar(out, x, y, c, col);
1377 }
1378 x += fontkern[c] + base;
1379 }
1380 }
1381
DrawChr(CelOutputBuffer out)1382 void DrawChr(CelOutputBuffer out)
1383 {
1384 text_color col = COL_WHITE;
1385 char chrstr[64];
1386 int mindam, maxdam;
1387
1388 CelDrawTo(out, 0, 351, pChrPanel, 1, SPANEL_WIDTH);
1389 ADD_PlrStringXY(out, 20, 32, 151, plr[myplr]._pName, COL_WHITE);
1390
1391 ADD_PlrStringXY(out, 168, 32, 299, ClassStrTbl[plr[myplr]._pClass], COL_WHITE);
1392
1393 sprintf(chrstr, "%i", plr[myplr]._pLevel);
1394 ADD_PlrStringXY(out, 66, 69, 109, chrstr, COL_WHITE);
1395
1396 sprintf(chrstr, "%i", plr[myplr]._pExperience);
1397 ADD_PlrStringXY(out, 216, 69, 300, chrstr, COL_WHITE);
1398
1399 if (plr[myplr]._pLevel == MAXCHARLEVEL - 1) {
1400 strcpy(chrstr, "None");
1401 col = COL_GOLD;
1402 } else {
1403 sprintf(chrstr, "%i", plr[myplr]._pNextExper);
1404 col = COL_WHITE;
1405 }
1406 ADD_PlrStringXY(out, 216, 97, 300, chrstr, col);
1407
1408 sprintf(chrstr, "%i", plr[myplr]._pGold);
1409 ADD_PlrStringXY(out, 216, 146, 300, chrstr, COL_WHITE);
1410
1411 col = COL_WHITE;
1412 if (plr[myplr]._pIBonusAC > 0)
1413 col = COL_BLUE;
1414 if (plr[myplr]._pIBonusAC < 0)
1415 col = COL_RED;
1416 sprintf(chrstr, "%i", plr[myplr]._pIBonusAC + plr[myplr]._pIAC + plr[myplr]._pDexterity / 5);
1417 ADD_PlrStringXY(out, 258, 183, 301, chrstr, col);
1418
1419 col = COL_WHITE;
1420 if (plr[myplr]._pIBonusToHit > 0)
1421 col = COL_BLUE;
1422 if (plr[myplr]._pIBonusToHit < 0)
1423 col = COL_RED;
1424 sprintf(chrstr, "%i%%", (plr[myplr]._pDexterity >> 1) + plr[myplr]._pIBonusToHit + 50);
1425 ADD_PlrStringXY(out, 258, 211, 301, chrstr, col);
1426
1427 col = COL_WHITE;
1428 if (plr[myplr]._pIBonusDam > 0)
1429 col = COL_BLUE;
1430 if (plr[myplr]._pIBonusDam < 0)
1431 col = COL_RED;
1432 mindam = plr[myplr]._pIMinDam;
1433 mindam += plr[myplr]._pIBonusDam * mindam / 100;
1434 mindam += plr[myplr]._pIBonusDamMod;
1435 if (plr[myplr].InvBody[INVLOC_HAND_LEFT]._itype == ITYPE_BOW) {
1436 if (plr[myplr]._pClass == PC_ROGUE)
1437 mindam += plr[myplr]._pDamageMod;
1438 else
1439 mindam += plr[myplr]._pDamageMod >> 1;
1440 } else {
1441 mindam += plr[myplr]._pDamageMod;
1442 }
1443 maxdam = plr[myplr]._pIMaxDam;
1444 maxdam += plr[myplr]._pIBonusDam * maxdam / 100;
1445 maxdam += plr[myplr]._pIBonusDamMod;
1446 if (plr[myplr].InvBody[INVLOC_HAND_LEFT]._itype == ITYPE_BOW) {
1447 if (plr[myplr]._pClass == PC_ROGUE)
1448 maxdam += plr[myplr]._pDamageMod;
1449 else
1450 maxdam += plr[myplr]._pDamageMod >> 1;
1451 } else {
1452 maxdam += plr[myplr]._pDamageMod;
1453 }
1454 sprintf(chrstr, "%i-%i", mindam, maxdam);
1455 if (mindam >= 100 || maxdam >= 100)
1456 MY_PlrStringXY(out, 254, 239, 305, chrstr, col, -1);
1457 else
1458 MY_PlrStringXY(out, 258, 239, 301, chrstr, col, 0);
1459
1460 if (plr[myplr]._pMagResist == 0)
1461 col = COL_WHITE;
1462 else
1463 col = COL_BLUE;
1464 if (plr[myplr]._pMagResist < MAXRESIST) {
1465 sprintf(chrstr, "%i%%", plr[myplr]._pMagResist);
1466 } else {
1467 col = COL_GOLD;
1468 sprintf(chrstr, "MAX");
1469 }
1470 ADD_PlrStringXY(out, 257, 276, 300, chrstr, col);
1471
1472 if (plr[myplr]._pFireResist == 0)
1473 col = COL_WHITE;
1474 else
1475 col = COL_BLUE;
1476 if (plr[myplr]._pFireResist < MAXRESIST) {
1477 sprintf(chrstr, "%i%%", plr[myplr]._pFireResist);
1478 } else {
1479 col = COL_GOLD;
1480 sprintf(chrstr, "MAX");
1481 }
1482 ADD_PlrStringXY(out, 257, 304, 300, chrstr, col);
1483
1484 if (plr[myplr]._pLghtResist == 0)
1485 col = COL_WHITE;
1486 else
1487 col = COL_BLUE;
1488 if (plr[myplr]._pLghtResist < MAXRESIST) {
1489 sprintf(chrstr, "%i%%", plr[myplr]._pLghtResist);
1490 } else {
1491 col = COL_GOLD;
1492 sprintf(chrstr, "MAX");
1493 }
1494 ADD_PlrStringXY(out, 257, 332, 300, chrstr, col);
1495
1496 col = COL_WHITE;
1497 sprintf(chrstr, "%i", plr[myplr]._pBaseStr);
1498 if (MaxStats[plr[myplr]._pClass][ATTRIB_STR] == plr[myplr]._pBaseStr)
1499 col = COL_GOLD;
1500 ADD_PlrStringXY(out, 95, 155, 126, chrstr, col);
1501
1502 col = COL_WHITE;
1503 sprintf(chrstr, "%i", plr[myplr]._pBaseMag);
1504 if (MaxStats[plr[myplr]._pClass][ATTRIB_MAG] == plr[myplr]._pBaseMag)
1505 col = COL_GOLD;
1506 ADD_PlrStringXY(out, 95, 183, 126, chrstr, col);
1507
1508 col = COL_WHITE;
1509 sprintf(chrstr, "%i", plr[myplr]._pBaseDex);
1510 if (MaxStats[plr[myplr]._pClass][ATTRIB_DEX] == plr[myplr]._pBaseDex)
1511 col = COL_GOLD;
1512 ADD_PlrStringXY(out, 95, 211, 126, chrstr, col);
1513
1514 col = COL_WHITE;
1515 sprintf(chrstr, "%i", plr[myplr]._pBaseVit);
1516 if (MaxStats[plr[myplr]._pClass][ATTRIB_VIT] == plr[myplr]._pBaseVit)
1517 col = COL_GOLD;
1518 ADD_PlrStringXY(out, 95, 239, 126, chrstr, col);
1519
1520 col = COL_WHITE;
1521 if (plr[myplr]._pStrength > plr[myplr]._pBaseStr)
1522 col = COL_BLUE;
1523 if (plr[myplr]._pStrength < plr[myplr]._pBaseStr)
1524 col = COL_RED;
1525 sprintf(chrstr, "%i", plr[myplr]._pStrength);
1526 ADD_PlrStringXY(out, 143, 155, 173, chrstr, col);
1527
1528 col = COL_WHITE;
1529 if (plr[myplr]._pMagic > plr[myplr]._pBaseMag)
1530 col = COL_BLUE;
1531 if (plr[myplr]._pMagic < plr[myplr]._pBaseMag)
1532 col = COL_RED;
1533 sprintf(chrstr, "%i", plr[myplr]._pMagic);
1534 ADD_PlrStringXY(out, 143, 183, 173, chrstr, col);
1535
1536 col = COL_WHITE;
1537 if (plr[myplr]._pDexterity > plr[myplr]._pBaseDex)
1538 col = COL_BLUE;
1539 if (plr[myplr]._pDexterity < plr[myplr]._pBaseDex)
1540 col = COL_RED;
1541 sprintf(chrstr, "%i", plr[myplr]._pDexterity);
1542 ADD_PlrStringXY(out, 143, 211, 173, chrstr, col);
1543
1544 col = COL_WHITE;
1545 if (plr[myplr]._pVitality > plr[myplr]._pBaseVit)
1546 col = COL_BLUE;
1547 if (plr[myplr]._pVitality < plr[myplr]._pBaseVit)
1548 col = COL_RED;
1549 sprintf(chrstr, "%i", plr[myplr]._pVitality);
1550 ADD_PlrStringXY(out, 143, 239, 173, chrstr, col);
1551
1552 if (plr[myplr]._pStatPts > 0) {
1553 if (CalcStatDiff(myplr) < plr[myplr]._pStatPts) {
1554 plr[myplr]._pStatPts = CalcStatDiff(myplr);
1555 }
1556 }
1557 if (plr[myplr]._pStatPts > 0) {
1558 sprintf(chrstr, "%i", plr[myplr]._pStatPts);
1559 ADD_PlrStringXY(out, 95, 266, 126, chrstr, COL_RED);
1560 plr_class pc = plr[myplr]._pClass;
1561 if (plr[myplr]._pBaseStr < MaxStats[pc][ATTRIB_STR])
1562 CelDrawTo(out, 137, 159, pChrButtons, chrbtn[ATTRIB_STR] + 2, 41);
1563 if (plr[myplr]._pBaseMag < MaxStats[pc][ATTRIB_MAG])
1564 CelDrawTo(out, 137, 187, pChrButtons, chrbtn[ATTRIB_MAG] + 4, 41);
1565 if (plr[myplr]._pBaseDex < MaxStats[pc][ATTRIB_DEX])
1566 CelDrawTo(out, 137, 216, pChrButtons, chrbtn[ATTRIB_DEX] + 6, 41);
1567 if (plr[myplr]._pBaseVit < MaxStats[pc][ATTRIB_VIT])
1568 CelDrawTo(out, 137, 244, pChrButtons, chrbtn[ATTRIB_VIT] + 8, 41);
1569 }
1570
1571 if (plr[myplr]._pMaxHP > plr[myplr]._pMaxHPBase)
1572 col = COL_BLUE;
1573 else
1574 col = COL_WHITE;
1575 sprintf(chrstr, "%i", plr[myplr]._pMaxHP >> 6);
1576 ADD_PlrStringXY(out, 95, 304, 126, chrstr, col);
1577 if (plr[myplr]._pHitPoints != plr[myplr]._pMaxHP)
1578 col = COL_RED;
1579 sprintf(chrstr, "%i", plr[myplr]._pHitPoints >> 6);
1580 ADD_PlrStringXY(out, 143, 304, 174, chrstr, col);
1581
1582 if (plr[myplr]._pMaxMana > plr[myplr]._pMaxManaBase)
1583 col = COL_BLUE;
1584 else
1585 col = COL_WHITE;
1586 sprintf(chrstr, "%i", plr[myplr]._pMaxMana >> 6);
1587 ADD_PlrStringXY(out, 95, 332, 126, chrstr, col);
1588 if (plr[myplr]._pMana != plr[myplr]._pMaxMana)
1589 col = COL_RED;
1590 sprintf(chrstr, "%i", plr[myplr]._pMana >> 6);
1591 ADD_PlrStringXY(out, 143, 332, 174, chrstr, col);
1592 }
1593
CheckLvlBtn()1594 void CheckLvlBtn()
1595 {
1596 if (!lvlbtndown && MouseX >= 40 + PANEL_LEFT && MouseX <= 81 + PANEL_LEFT && MouseY >= -39 + PANEL_TOP && MouseY <= -17 + PANEL_TOP)
1597 lvlbtndown = TRUE;
1598 }
1599
ReleaseLvlBtn()1600 void ReleaseLvlBtn()
1601 {
1602 if (MouseX >= 40 + PANEL_LEFT && MouseX <= 81 + PANEL_LEFT && MouseY >= -39 + PANEL_TOP && MouseY <= -17 + PANEL_TOP)
1603 chrflag = TRUE;
1604 lvlbtndown = FALSE;
1605 }
1606
DrawLevelUpIcon(CelOutputBuffer out)1607 void DrawLevelUpIcon(CelOutputBuffer out)
1608 {
1609 int nCel;
1610
1611 if (stextflag == STORE_NONE) {
1612 nCel = lvlbtndown ? 3 : 2;
1613 ADD_PlrStringXY(out, PANEL_LEFT + 0, PANEL_TOP - 49, PANEL_LEFT + 120, "Level Up", COL_WHITE);
1614 CelDrawTo(out, 40 + PANEL_X, -17 + PANEL_Y, pChrButtons, nCel, 41);
1615 }
1616 }
1617
CheckChrBtns()1618 void CheckChrBtns()
1619 {
1620 int i, x, y;
1621
1622 if (!chrbtnactive && plr[myplr]._pStatPts) {
1623 plr_class pc = plr[myplr]._pClass;
1624 for (i = 0; i < 4; i++) {
1625 switch (i) {
1626 case ATTRIB_STR:
1627 if (plr[myplr]._pBaseStr >= MaxStats[pc][ATTRIB_STR])
1628 continue;
1629 break;
1630 case ATTRIB_MAG:
1631 if (plr[myplr]._pBaseMag >= MaxStats[pc][ATTRIB_MAG])
1632 continue;
1633 break;
1634 case ATTRIB_DEX:
1635 if (plr[myplr]._pBaseDex >= MaxStats[pc][ATTRIB_DEX])
1636 continue;
1637 break;
1638 case ATTRIB_VIT:
1639 if (plr[myplr]._pBaseVit >= MaxStats[pc][ATTRIB_VIT])
1640 continue;
1641 break;
1642 default:
1643 continue;
1644 }
1645 x = ChrBtnsRect[i].x + ChrBtnsRect[i].w;
1646 y = ChrBtnsRect[i].y + ChrBtnsRect[i].h;
1647 if (MouseX >= ChrBtnsRect[i].x
1648 && MouseX <= x
1649 && MouseY >= ChrBtnsRect[i].y
1650 && MouseY <= y) {
1651 chrbtn[i] = TRUE;
1652 chrbtnactive = TRUE;
1653 }
1654 }
1655 }
1656 }
1657
CapStatPointsToAdd(int remainingStatPoints,const PlayerStruct & player,attribute_id attribute)1658 int CapStatPointsToAdd(int remainingStatPoints, const PlayerStruct &player, attribute_id attribute)
1659 {
1660 int pointsToReachCap = player.GetMaximumAttributeValue(attribute) - player.GetBaseAttributeValue(attribute);
1661
1662 return std::min(remainingStatPoints, pointsToReachCap);
1663 }
1664
ReleaseChrBtns(bool addAllStatPoints)1665 void ReleaseChrBtns(bool addAllStatPoints)
1666 {
1667 int i;
1668
1669 chrbtnactive = FALSE;
1670 for (i = 0; i < 4; ++i) {
1671 if (chrbtn[i]) {
1672 chrbtn[i] = FALSE;
1673 if (MouseX >= ChrBtnsRect[i].x
1674 && MouseX <= ChrBtnsRect[i].x + ChrBtnsRect[i].w
1675 && MouseY >= ChrBtnsRect[i].y
1676 && MouseY <= ChrBtnsRect[i].y + ChrBtnsRect[i].h) {
1677 PlayerStruct &player = plr[myplr];
1678 int statPointsToAdd = addAllStatPoints ? player._pStatPts : 1;
1679 switch (i) {
1680 case 0:
1681 statPointsToAdd = CapStatPointsToAdd(statPointsToAdd, player, attribute_id::ATTRIB_STR);
1682 NetSendCmdParam1(TRUE, CMD_ADDSTR, statPointsToAdd);
1683 player._pStatPts -= statPointsToAdd;
1684 break;
1685 case 1:
1686 statPointsToAdd = CapStatPointsToAdd(statPointsToAdd, player, attribute_id::ATTRIB_MAG);
1687 NetSendCmdParam1(TRUE, CMD_ADDMAG, statPointsToAdd);
1688 player._pStatPts -= statPointsToAdd;
1689 break;
1690 case 2:
1691 statPointsToAdd = CapStatPointsToAdd(statPointsToAdd, player, attribute_id::ATTRIB_DEX);
1692 NetSendCmdParam1(TRUE, CMD_ADDDEX, statPointsToAdd);
1693 player._pStatPts -= statPointsToAdd;
1694 break;
1695 case 3:
1696 statPointsToAdd = CapStatPointsToAdd(statPointsToAdd, player, attribute_id::ATTRIB_VIT);
1697 NetSendCmdParam1(TRUE, CMD_ADDVIT, statPointsToAdd);
1698 player._pStatPts -= statPointsToAdd;
1699 break;
1700 }
1701 }
1702 }
1703 }
1704 }
1705
DrawDurIcon4Item(CelOutputBuffer out,ItemStruct * pItem,int x,int c)1706 static int DrawDurIcon4Item(CelOutputBuffer out, ItemStruct *pItem, int x, int c)
1707 {
1708 if (pItem->isEmpty())
1709 return x;
1710 if (pItem->_iDurability > 5)
1711 return x;
1712 if (c == 0) {
1713 switch (pItem->_itype) {
1714 case ITYPE_SWORD:
1715 c = 2;
1716 break;
1717 case ITYPE_AXE:
1718 c = 6;
1719 break;
1720 case ITYPE_BOW:
1721 c = 7;
1722 break;
1723 case ITYPE_MACE:
1724 c = 5;
1725 break;
1726 case ITYPE_STAFF:
1727 c = 8;
1728 break;
1729 default:
1730 c = 1;
1731 break;
1732 }
1733 }
1734 if (pItem->_iDurability > 2)
1735 c += 8;
1736 CelDrawTo(out, x, -17 + PANEL_Y, pDurIcons, c, 32);
1737 return x - 32 - 8;
1738 }
1739
DrawDurIcon(CelOutputBuffer out)1740 void DrawDurIcon(CelOutputBuffer out)
1741 {
1742 PlayerStruct *p;
1743 int x;
1744
1745 bool hasRoomBetweenPanels = gnScreenWidth >= PANEL_WIDTH + 16 + (32 + 8 + 32 + 8 + 32 + 8 + 32) + 16;
1746 bool hasRoomUnderPanels = gnScreenHeight >= SPANEL_HEIGHT + PANEL_HEIGHT + 16 + 32 + 16;
1747
1748 if (!hasRoomBetweenPanels && !hasRoomUnderPanels) {
1749 if ((chrflag || questlog) && (invflag || sbookflag))
1750 return;
1751 }
1752
1753 x = PANEL_X + PANEL_WIDTH - 32 - 16;
1754 if (!hasRoomUnderPanels) {
1755 if (invflag || sbookflag)
1756 x -= SPANEL_WIDTH - (gnScreenWidth - PANEL_WIDTH) / 2;
1757 }
1758
1759 p = &plr[myplr];
1760 x = DrawDurIcon4Item(out, &p->InvBody[INVLOC_HEAD], x, 4);
1761 x = DrawDurIcon4Item(out, &p->InvBody[INVLOC_CHEST], x, 3);
1762 x = DrawDurIcon4Item(out, &p->InvBody[INVLOC_HAND_LEFT], x, 0);
1763 DrawDurIcon4Item(out, &p->InvBody[INVLOC_HAND_RIGHT], x, 0);
1764 }
1765
RedBack(CelOutputBuffer out)1766 void RedBack(CelOutputBuffer out)
1767 {
1768 int idx;
1769
1770 idx = light4flag ? 1536 : 4608;
1771 int w, h;
1772 BYTE *dst, *tbl;
1773
1774 if (leveltype != DTYPE_HELL) {
1775 dst = out.begin();
1776 tbl = &pLightTbl[idx];
1777 for (h = gnViewportHeight; h; h--, dst += out.pitch() - gnScreenWidth) {
1778 for (w = gnScreenWidth; w; w--) {
1779 *dst = tbl[*dst];
1780 dst++;
1781 }
1782 }
1783 } else {
1784 dst = out.begin();
1785 tbl = &pLightTbl[idx];
1786 for (h = gnViewportHeight; h; h--, dst += out.pitch() - gnScreenWidth) {
1787 for (w = gnScreenWidth; w; w--) {
1788 if (*dst >= 32)
1789 *dst = tbl[*dst];
1790 dst++;
1791 }
1792 }
1793 }
1794 }
1795
PrintSBookStr(CelOutputBuffer out,int x,int y,BOOL cjustflag,const char * pszStr,text_color col)1796 static void PrintSBookStr(CelOutputBuffer out, int x, int y, BOOL cjustflag, const char *pszStr, text_color col)
1797 {
1798 BYTE c;
1799 const char *tmp;
1800 int screen_x, line, sx;
1801
1802 sx = x + RIGHT_PANEL_X + SPLICONLENGTH;
1803 line = 0;
1804 if (cjustflag) {
1805 screen_x = 0;
1806 tmp = pszStr;
1807 while (*tmp) {
1808 c = gbFontTransTbl[(BYTE)*tmp++];
1809 screen_x += fontkern[fontframe[c]] + 1;
1810 }
1811 if (screen_x < 222)
1812 line = (222 - screen_x) >> 1;
1813 sx += line;
1814 }
1815 while (*pszStr) {
1816 c = gbFontTransTbl[(BYTE)*pszStr++];
1817 c = fontframe[c];
1818 line += fontkern[c] + 1;
1819 if (c) {
1820 if (line <= 222)
1821 PrintChar(out, sx, y, c, col);
1822 }
1823 sx += fontkern[c] + 1;
1824 }
1825 }
1826
GetSBookTrans(int ii,BOOL townok)1827 char GetSBookTrans(int ii, BOOL townok)
1828 {
1829 char st;
1830
1831 if ((plr[myplr]._pClass == PC_MONK) && (ii == SPL_SEARCH))
1832 return RSPLTYPE_SKILL;
1833 st = RSPLTYPE_SPELL;
1834 if (plr[myplr]._pISpells & GetSpellBitmask(ii)) {
1835 st = RSPLTYPE_CHARGES;
1836 }
1837 if (plr[myplr]._pAblSpells & GetSpellBitmask(ii)) {
1838 st = RSPLTYPE_SKILL;
1839 }
1840 if (st == RSPLTYPE_SPELL) {
1841 if (!CheckSpell(myplr, ii, RSPLTYPE_SPELL, TRUE)) {
1842 st = RSPLTYPE_INVALID;
1843 }
1844 if ((char)(plr[myplr]._pSplLvl[ii] + plr[myplr]._pISplLvlAdd) <= 0) {
1845 st = RSPLTYPE_INVALID;
1846 }
1847 }
1848 if (townok && currlevel == 0 && st != RSPLTYPE_INVALID && !spelldata[ii].sTownSpell) {
1849 st = RSPLTYPE_INVALID;
1850 }
1851
1852 return st;
1853 }
1854
DrawSpellBook(CelOutputBuffer out)1855 void DrawSpellBook(CelOutputBuffer out)
1856 {
1857 int i, sn, mana, lvl, yp, min, max;
1858 char st;
1859
1860 CelDrawTo(out, RIGHT_PANEL_X, 351, pSpellBkCel, 1, SPANEL_WIDTH);
1861 if (gbIsHellfire && sbooktab < 5) {
1862 CelDrawTo(out, RIGHT_PANEL_X + 61 * sbooktab + 7, 348, pSBkBtnCel, sbooktab + 1, 61);
1863 } else {
1864 // BUGFIX: rendering of page 3 and page 4 buttons are both off-by-one pixel (fixed).
1865 int sx = RIGHT_PANEL_X + 76 * sbooktab + 7;
1866 if (sbooktab == 2 || sbooktab == 3) {
1867 sx++;
1868 }
1869 CelDrawTo(out, sx, 348, pSBkBtnCel, sbooktab + 1, 76);
1870 }
1871 Uint64 spl = plr[myplr]._pMemSpells | plr[myplr]._pISpells | plr[myplr]._pAblSpells;
1872
1873 yp = 55;
1874 for (i = 1; i < 8; i++) {
1875 sn = SpellPages[sbooktab][i - 1];
1876 if (sn != -1 && spl & GetSpellBitmask(sn)) {
1877 st = GetSBookTrans(sn, TRUE);
1878 SetSpellTrans(st);
1879 DrawSpellCel(out, RIGHT_PANEL_X + 11, yp, pSBkIconCels, SpellITbl[sn], 37);
1880 if (sn == plr[myplr]._pRSpell && st == plr[myplr]._pRSplType) {
1881 SetSpellTrans(RSPLTYPE_SKILL);
1882 DrawSpellCel(out, RIGHT_PANEL_X + 11, yp, pSBkIconCels, SPLICONLAST, 37);
1883 }
1884 PrintSBookStr(out, 10, yp - 23, FALSE, spelldata[sn].sNameText, COL_WHITE);
1885 switch (GetSBookTrans(sn, FALSE)) {
1886 case RSPLTYPE_SKILL:
1887 strcpy(tempstr, "Skill");
1888 break;
1889 case RSPLTYPE_CHARGES:
1890 sprintf(tempstr, "Staff (%i charges)", plr[myplr].InvBody[INVLOC_HAND_LEFT]._iCharges);
1891 break;
1892 default:
1893 mana = GetManaAmount(myplr, sn) >> 6;
1894 GetDamageAmt(sn, &min, &max);
1895 if (min != -1) {
1896 sprintf(tempstr, "Mana: %i Dam: %i - %i", mana, min, max);
1897 } else {
1898 sprintf(tempstr, "Mana: %i Dam: n/a", mana);
1899 }
1900 if (sn == SPL_BONESPIRIT) {
1901 sprintf(tempstr, "Mana: %i Dam: 1/3 tgt hp", mana);
1902 }
1903 PrintSBookStr(out, 10, yp - 1, FALSE, tempstr, COL_WHITE);
1904 lvl = plr[myplr]._pSplLvl[sn] + plr[myplr]._pISplLvlAdd;
1905 if (lvl < 0) {
1906 lvl = 0;
1907 }
1908 if (lvl == 0) {
1909 sprintf(tempstr, "Spell Level 0 - Unusable");
1910 } else {
1911 sprintf(tempstr, "Spell Level %i", lvl);
1912 }
1913 break;
1914 }
1915 PrintSBookStr(out, 10, yp - 12, FALSE, tempstr, COL_WHITE);
1916 }
1917 yp += 43;
1918 }
1919 }
1920
CheckSBook()1921 void CheckSBook()
1922 {
1923 if (MouseX >= RIGHT_PANEL + 11 && MouseX < RIGHT_PANEL + 48 && MouseY >= 18 && MouseY < 314) {
1924 spell_id sn = SpellPages[sbooktab][(MouseY - 18) / 43];
1925 Uint64 spl = plr[myplr]._pMemSpells | plr[myplr]._pISpells | plr[myplr]._pAblSpells;
1926 if (sn != SPL_INVALID && spl & GetSpellBitmask(sn)) {
1927 spell_type st = RSPLTYPE_SPELL;
1928 if (plr[myplr]._pISpells & GetSpellBitmask(sn)) {
1929 st = RSPLTYPE_CHARGES;
1930 }
1931 if (plr[myplr]._pAblSpells & GetSpellBitmask(sn)) {
1932 st = RSPLTYPE_SKILL;
1933 }
1934 plr[myplr]._pRSpell = sn;
1935 plr[myplr]._pRSplType = st;
1936 force_redraw = 255;
1937 }
1938 }
1939 if (MouseX >= RIGHT_PANEL + 7 && MouseX < RIGHT_PANEL + 311 && MouseY >= SPANEL_WIDTH && MouseY < 349) {
1940 sbooktab = (MouseX - (RIGHT_PANEL + 7)) / (gbIsHellfire ? 61 : 76);
1941 }
1942 }
1943
get_pieces_str(int nGold)1944 const char *get_pieces_str(int nGold)
1945 {
1946 const char *result;
1947
1948 result = "piece";
1949 if (nGold != 1)
1950 result = "pieces";
1951 return result;
1952 }
1953
DrawGoldSplit(CelOutputBuffer out,int amount)1954 void DrawGoldSplit(CelOutputBuffer out, int amount)
1955 {
1956 int screen_x, i;
1957
1958 screen_x = 0;
1959 CelDrawTo(out, 351, 178, pGBoxBuff, 1, 261);
1960 sprintf(tempstr, "You have %u gold", initialDropGoldValue);
1961 ADD_PlrStringXY(out, 366, 87, 600, tempstr, COL_GOLD);
1962 sprintf(tempstr, "%s. How many do", get_pieces_str(initialDropGoldValue));
1963 ADD_PlrStringXY(out, 366, 103, 600, tempstr, COL_GOLD);
1964 ADD_PlrStringXY(out, 366, 121, 600, "you want to remove?", COL_GOLD);
1965 if (amount > 0) {
1966 sprintf(tempstr, "%u", amount);
1967 PrintGameStr(out, 388, 140, tempstr, COL_WHITE);
1968 }
1969 if (amount > 0) {
1970 for (i = 0; i < tempstr[i]; i++) {
1971 BYTE c = fontframe[gbFontTransTbl[(BYTE)tempstr[i]]];
1972 screen_x += fontkern[c] + 1;
1973 }
1974 screen_x += 388;
1975 } else {
1976 screen_x = 386;
1977 }
1978 CelDrawTo(out, screen_x, 140, pSPentSpn2Cels, PentSpn2Spin(), 12);
1979 }
1980
control_drop_gold(char vkey)1981 void control_drop_gold(char vkey)
1982 {
1983 char input[6];
1984
1985 if (plr[myplr]._pHitPoints >> 6 <= 0) {
1986 dropGoldFlag = FALSE;
1987 dropGoldValue = 0;
1988 return;
1989 }
1990
1991 memset(input, 0, sizeof(input));
1992 snprintf(input, sizeof(input), "%d", dropGoldValue);
1993 if (vkey == DVL_VK_RETURN) {
1994 if (dropGoldValue > 0)
1995 control_remove_gold(myplr, initialDropGoldIndex);
1996 dropGoldFlag = FALSE;
1997 } else if (vkey == DVL_VK_ESCAPE) {
1998 dropGoldFlag = FALSE;
1999 dropGoldValue = 0;
2000 } else if (vkey == DVL_VK_BACK) {
2001 input[strlen(input) - 1] = '\0';
2002 dropGoldValue = atoi(input);
2003 } else if (vkey - '0' >= 0 && vkey - '0' <= 9) {
2004 if (dropGoldValue != 0 || atoi(input) <= initialDropGoldValue) {
2005 input[strlen(input)] = vkey;
2006 if (atoi(input) > initialDropGoldValue)
2007 return;
2008 if (strlen(input) > strlen(input))
2009 return;
2010 } else {
2011 input[0] = vkey;
2012 }
2013 dropGoldValue = atoi(input);
2014 }
2015 }
2016
control_remove_gold(int pnum,int gold_index)2017 void control_remove_gold(int pnum, int gold_index)
2018 {
2019 int gi;
2020
2021 if (gold_index <= INVITEM_INV_LAST) {
2022 gi = gold_index - INVITEM_INV_FIRST;
2023 plr[pnum].InvList[gi]._ivalue -= dropGoldValue;
2024 if (plr[pnum].InvList[gi]._ivalue > 0)
2025 SetGoldCurs(pnum, gi);
2026 else
2027 RemoveInvItem(pnum, gi);
2028 } else {
2029 gi = gold_index - INVITEM_BELT_FIRST;
2030 plr[pnum].SpdList[gi]._ivalue -= dropGoldValue;
2031 if (plr[pnum].SpdList[gi]._ivalue > 0)
2032 SetSpdbarGoldCurs(pnum, gi);
2033 else
2034 RemoveSpdBarItem(pnum, gi);
2035 }
2036 SetPlrHandItem(&plr[pnum].HoldItem, IDI_GOLD);
2037 GetGoldSeed(pnum, &plr[pnum].HoldItem);
2038 plr[pnum].HoldItem._ivalue = dropGoldValue;
2039 plr[pnum].HoldItem._iStatFlag = TRUE;
2040 control_set_gold_curs(pnum);
2041 plr[pnum]._pGold = CalculateGold(pnum);
2042 dropGoldValue = 0;
2043 }
2044
control_set_gold_curs(int pnum)2045 void control_set_gold_curs(int pnum)
2046 {
2047 SetPlrHandGoldCurs(&plr[pnum].HoldItem);
2048 NewCursor(plr[pnum].HoldItem._iCurs + CURSOR_FIRSTITEM);
2049 }
2050
control_print_talk_msg(CelOutputBuffer out,char * msg,int * x,int y,text_color color)2051 static char *control_print_talk_msg(CelOutputBuffer out, char *msg, int *x, int y, text_color color)
2052 {
2053 BYTE c;
2054 int width;
2055
2056 *x += 200;
2057 y += 22 + PANEL_Y;
2058 width = *x;
2059 while (*msg) {
2060
2061 c = gbFontTransTbl[(BYTE)*msg];
2062 c = fontframe[c];
2063 width += fontkern[c] + 1;
2064 if (width > 450 + PANEL_X)
2065 return msg;
2066 msg++;
2067 if (c != 0) {
2068 PrintChar(out, *x, y, c, color);
2069 }
2070 *x += fontkern[c] + 1;
2071 }
2072 return NULL;
2073 }
2074
DrawTalkPan(CelOutputBuffer out)2075 void DrawTalkPan(CelOutputBuffer out)
2076 {
2077 int i, off, talk_btn, nCel, x;
2078 char *msg;
2079
2080 if (!talkflag)
2081 return;
2082
2083 DrawPanelBox(out, 175, sgbPlrTalkTbl + 20, 294, 5, PANEL_X + 175, PANEL_Y + 4);
2084 off = 0;
2085 for (i = 293; i > 283; off++, i--) {
2086 DrawPanelBox(out, (off >> 1) + 175, sgbPlrTalkTbl + off + 25, i, 1, (off >> 1) + PANEL_X + 175, off + PANEL_Y + 9);
2087 }
2088 DrawPanelBox(out, 185, sgbPlrTalkTbl + 35, 274, 30, PANEL_X + 185, PANEL_Y + 19);
2089 DrawPanelBox(out, 180, sgbPlrTalkTbl + 65, 284, 5, PANEL_X + 180, PANEL_Y + 49);
2090 for (i = 0; i < 10; i++) {
2091 DrawPanelBox(out, 180, sgbPlrTalkTbl + i + 70, i + 284, 1, PANEL_X + 180, i + PANEL_Y + 54);
2092 }
2093 DrawPanelBox(out, 170, sgbPlrTalkTbl + 80, 310, 55, PANEL_X + 170, PANEL_Y + 64);
2094 msg = sgszTalkMsg;
2095 for (i = 0; i < 39; i += 13) {
2096 x = 0 + PANEL_LEFT;
2097 msg = control_print_talk_msg(out, msg, &x, i, COL_WHITE);
2098 if (!msg)
2099 break;
2100 }
2101 if (msg)
2102 *msg = '\0';
2103 CelDrawTo(out, x, i + 22 + PANEL_Y, pSPentSpn2Cels, PentSpn2Spin(), 12);
2104 talk_btn = 0;
2105 for (i = 0; i < 4; i++) {
2106 if (i == myplr)
2107 continue;
2108 text_color color = COL_RED;
2109 if (whisper[i]) {
2110 color = COL_GOLD;
2111 if (talkbtndown[talk_btn]) {
2112 if (talk_btn != 0)
2113 nCel = 4;
2114 else
2115 nCel = 3;
2116 CelDrawTo(out, 172 + PANEL_X, 84 + 18 * talk_btn + PANEL_Y, pTalkBtns, nCel, 61);
2117 }
2118 } else {
2119 if (talk_btn != 0)
2120 nCel = 2;
2121 else
2122 nCel = 1;
2123 if (talkbtndown[talk_btn])
2124 nCel += 4;
2125 CelDrawTo(out, 172 + PANEL_X, 84 + 18 * talk_btn + PANEL_Y, pTalkBtns, nCel, 61);
2126 }
2127 if (plr[i].plractive) {
2128 x = 46 + PANEL_LEFT;
2129 control_print_talk_msg(out, plr[i]._pName, &x, 60 + talk_btn * 18, color);
2130 }
2131
2132 talk_btn++;
2133 }
2134 }
2135
control_check_talk_btn()2136 BOOL control_check_talk_btn()
2137 {
2138 int i;
2139
2140 if (!talkflag)
2141 return FALSE;
2142
2143 if (MouseX < 172 + PANEL_LEFT)
2144 return FALSE;
2145 if (MouseY < 69 + PANEL_TOP)
2146 return FALSE;
2147 if (MouseX > 233 + PANEL_LEFT)
2148 return FALSE;
2149 if (MouseY > 123 + PANEL_TOP)
2150 return FALSE;
2151
2152 for (i = 0; i < sizeof(talkbtndown) / sizeof(talkbtndown[0]); i++) {
2153 talkbtndown[i] = FALSE;
2154 }
2155
2156 talkbtndown[(MouseY - (69 + PANEL_TOP)) / 18] = TRUE;
2157
2158 return TRUE;
2159 }
2160
control_release_talk_btn()2161 void control_release_talk_btn()
2162 {
2163 int i, p, off;
2164
2165 if (talkflag) {
2166 for (i = 0; i < sizeof(talkbtndown) / sizeof(talkbtndown[0]); i++)
2167 talkbtndown[i] = FALSE;
2168 if (MouseX >= 172 + PANEL_LEFT && MouseY >= 69 + PANEL_TOP && MouseX <= 233 + PANEL_LEFT && MouseY <= 123 + PANEL_TOP) {
2169 off = (MouseY - (69 + PANEL_TOP)) / 18;
2170
2171 for (p = 0; p < MAX_PLRS && off != -1; p++) {
2172 if (p != myplr)
2173 off--;
2174 }
2175 if (p <= MAX_PLRS)
2176 whisper[p - 1] = !whisper[p - 1];
2177 }
2178 }
2179 }
2180
control_reset_talk_msg(char * msg)2181 void control_reset_talk_msg(char *msg)
2182 {
2183 int i, pmask;
2184 pmask = 0;
2185
2186 for (i = 0; i < MAX_PLRS; i++) {
2187 if (whisper[i])
2188 pmask |= 1 << i;
2189 }
2190 NetSendCmdString(pmask, sgszTalkMsg);
2191 }
2192
control_type_message()2193 void control_type_message()
2194 {
2195 int i;
2196
2197 if (!gbIsMultiplayer) {
2198 return;
2199 }
2200
2201 talkflag = TRUE;
2202 sgszTalkMsg[0] = '\0';
2203 for (i = 0; i < 3; i++) {
2204 talkbtndown[i] = FALSE;
2205 }
2206 sgbPlrTalkTbl = PANEL_HEIGHT + 16;
2207 force_redraw = 255;
2208 sgbTalkSavePos = sgbNextTalkSave;
2209 }
2210
control_reset_talk()2211 void control_reset_talk()
2212 {
2213 talkflag = FALSE;
2214 sgbPlrTalkTbl = 0;
2215 force_redraw = 255;
2216 }
2217
control_press_enter()2218 static void control_press_enter()
2219 {
2220 int i;
2221 BYTE talk_save;
2222
2223 if (sgszTalkMsg[0] != 0) {
2224 control_reset_talk_msg(sgszTalkMsg);
2225 for (i = 0; i < 8; i++) {
2226 if (!strcmp(sgszTalkSave[i], sgszTalkMsg))
2227 break;
2228 }
2229 if (i >= 8) {
2230 strcpy(sgszTalkSave[sgbNextTalkSave], sgszTalkMsg);
2231 sgbNextTalkSave++;
2232 sgbNextTalkSave &= 7;
2233 } else {
2234 talk_save = sgbNextTalkSave - 1;
2235 talk_save &= 7;
2236 if (i != talk_save) {
2237 strcpy(sgszTalkSave[i], sgszTalkSave[talk_save]);
2238 strcpy(sgszTalkSave[talk_save], sgszTalkMsg);
2239 }
2240 }
2241 sgszTalkMsg[0] = '\0';
2242 sgbTalkSavePos = sgbNextTalkSave;
2243 }
2244 control_reset_talk();
2245 }
2246
control_talk_last_key(int vkey)2247 BOOL control_talk_last_key(int vkey)
2248 {
2249 int result;
2250
2251 if (!gbIsMultiplayer)
2252 return FALSE;
2253
2254 if (!talkflag)
2255 return FALSE;
2256
2257 if ((DWORD)vkey < DVL_VK_SPACE)
2258 return FALSE;
2259
2260 result = strlen(sgszTalkMsg);
2261 if (result < 78) {
2262 sgszTalkMsg[result] = vkey;
2263 sgszTalkMsg[result + 1] = '\0';
2264 }
2265 return TRUE;
2266 }
2267
control_up_down(int v)2268 static void control_up_down(int v)
2269 {
2270 int i;
2271
2272 for (i = 0; i < 8; i++) {
2273 sgbTalkSavePos = (v + sgbTalkSavePos) & 7;
2274 if (sgszTalkSave[sgbTalkSavePos][0]) {
2275 strcpy(sgszTalkMsg, sgszTalkSave[sgbTalkSavePos]);
2276 return;
2277 }
2278 }
2279 }
2280
control_presskeys(int vkey)2281 BOOL control_presskeys(int vkey)
2282 {
2283 int len;
2284 BOOL ret;
2285
2286 if (gbIsMultiplayer) {
2287 if (!talkflag) {
2288 ret = FALSE;
2289 } else {
2290 if (vkey == DVL_VK_SPACE) {
2291 } else if (vkey == DVL_VK_ESCAPE) {
2292 control_reset_talk();
2293 } else if (vkey == DVL_VK_RETURN) {
2294 control_press_enter();
2295 } else if (vkey == DVL_VK_BACK) {
2296 len = strlen(sgszTalkMsg);
2297 if (len > 0)
2298 sgszTalkMsg[len - 1] = '\0';
2299 } else if (vkey == DVL_VK_DOWN) {
2300 control_up_down(1);
2301 } else if (vkey == DVL_VK_UP) {
2302 control_up_down(-1);
2303 } else {
2304 return FALSE;
2305 }
2306 ret = TRUE;
2307 }
2308 } else {
2309 ret = FALSE;
2310 }
2311 return ret;
2312 }
2313
2314 DEVILUTION_END_NAMESPACE
2315