1 /*
2 
3 Copyright (C) 2015-2018 Night Dive Studios, LLC.
4 
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 
18 */
19 /*
20  * $Source: r:/prj/cit/src/RCS/tools.c $
21  * $Revision: 1.94 $
22  * $Author: dc $
23  * $Date: 1994/11/25 16:58:28 $
24  */
25 
26 // Source code from random useful tools and utilities
27 
28 #define __TOOLS_SRC
29 
30 #include <string.h>
31 #include <ctype.h>
32 
33 #include "criterr.h"
34 #include "gr2ss.h"
35 #include "tools.h"
36 #include "mainloop.h"
37 #include "gamescr.h"
38 #include "musicai.h"
39 #include "colors.h"
40 #include "gamestrn.h"
41 #include "invdims.h"
42 #include "fullscrn.h"
43 #include "hud.h"
44 #include "canvchek.h"
45 #include "player.h"
46 #include "faketime.h"
47 #include "cit2d.h"
48 
49 #include <SDL.h>
50 #include "OpenGL.h"
51 
52 #ifndef STORE_CLIP
53 #define STORE_CLIP(a, b, c, d) \
54     a = gr_get_clip_l();       \
55     b = gr_get_clip_t();       \
56     c = gr_get_clip_r();       \
57     d = gr_get_clip_b()
58 #endif // !STORE_CLIP
59 
60 #ifndef RESTORE_CLIP
61 #define RESTORE_CLIP(a, b, c, d) gr_set_cliprect(a, b, c, d)
62 #endif // !RESTORE_CLIP
63 
64 //------------
65 //  PROTOTYPES
66 //------------
67 int str_to_hex(char val);
68 void text_button(char *text, int xc, int yc, int col, int shad, int w, int h);
69 void simple_text_button(char *text, int xc, int yc, int col);
70 void Rect_gr_rect(LGRect *r);
71 void Rect_gr_box(LGRect *r);
72 char *itoa_2_10(char *s, int val);
73 void zoom_rect(LGRect *start, LGRect *end);
74 
str_to_hex(char val)75 int str_to_hex(char val) {
76     int retval = 0;
77     if ((val >= '0') && (val <= '9'))
78         retval = val - '0';
79     else if ((val >= 'A') && (val <= 'F'))
80         retval = 10 + val - 'A';
81     else if ((val >= 'a') && (val <= 'f'))
82         retval = 10 + val - 'a';
83     return (retval);
84 }
85 
strtoupper(char * text)86 void strtoupper(char *text) {
87     for (; *text; text++) {
88         if (islower(*text))
89             (*text) += 'A' - 'a';
90     }
91 }
92 
93 #ifdef SVGA_SUPPORT
94 uchar shadow_scale = TRUE;
95 #endif
draw_shadowed_string(char * s,short x,short y,uchar shadow)96 void draw_shadowed_string(char *s, short x, short y, uchar shadow) {
97     LGPoint npt;
98     ubyte color = gr_get_fcolor();
99     npt.x = x;
100     npt.y = y;
101     if (shadow && FONT_IS_MONO(gr_get_font())) // draw a black box
102     {
103 #ifdef SVGA_SUPPORT
104         extern char convert_use_mode;
105         extern uchar perform_svga_conversion(uchar mask);
106         extern void ss_scale_string(char *s, short x, short y);
107         if ((convert_use_mode > 0) && (perform_svga_conversion(OVERRIDE_FONT))) {
108             if (shadow_scale)
109                 ss_point_convert(&(npt.x), &(npt.y), FALSE);
110             gr_set_fcolor(shadow);
111             ss_scale_string(s, npt.x - 1, npt.y - 1);
112             ss_scale_string(s, npt.x, npt.y - 1);
113             ss_scale_string(s, npt.x + 1, npt.y - 1);
114             ss_scale_string(s, npt.x, npt.y + 1);
115             ss_scale_string(s, npt.x - 1, npt.y + 1);
116             ss_scale_string(s, npt.x + 1, npt.y + 1);
117             ss_scale_string(s, npt.x - 1, npt.y);
118             ss_scale_string(s, npt.x + 1, npt.y);
119             gr_set_fcolor(color);
120             ss_scale_string(s, npt.x, npt.y);
121         } else
122 #endif
123         {
124             gr_set_fcolor(shadow);
125             gr_string(s, npt.x - 1, npt.y - 1);
126             gr_string(s, npt.x, npt.y - 1);
127             gr_string(s, npt.x + 1, npt.y - 1);
128             gr_string(s, npt.x, npt.y + 1);
129             gr_string(s, npt.x - 1, npt.y + 1);
130             gr_string(s, npt.x + 1, npt.y + 1);
131             gr_string(s, npt.x - 1, npt.y);
132             gr_string(s, npt.x + 1, npt.y);
133             gr_set_fcolor(color);
134             gr_string(s, npt.x, npt.y);
135         }
136     } else {
137         gr_set_fcolor(color);
138         //      gr_string(s,npt.x,npt.y);
139         ss_string(s, npt.x, npt.y);
140     }
141 }
142 
draw_hires_resource_bm(Ref id,int x,int y)143 void draw_hires_resource_bm(Ref id, int x, int y) {
144     FrameDesc *f = RefLock(id);
145     if (f == NULL)
146         critical_error(CRITERR_MEM | 9);
147     gr_bitmap(&f->bm, x, y);
148     RefUnlock(id);
149 }
150 
draw_hires_halfsize_bm(Ref id,int x,int y)151 void draw_hires_halfsize_bm(Ref id, int x, int y) {
152     FrameDesc *f = RefLock(id);
153     if (f == NULL)
154         critical_error(CRITERR_MEM | 9);
155     gr_scale_bitmap(&f->bm, x, y, (f->bm.w >> 1), (f->bm.h >> 1));
156     RefUnlock(id);
157 }
158 
draw_raw_res_bm_temp(Ref id,int x,int y)159 errtype draw_raw_res_bm_temp(Ref id, int x, int y) {
160     FrameDesc *f = RefLock(id);
161     if (f == NULL) {
162 	return ERR_FREAD;
163     }
164     ss_bitmap(&f->bm, x, y);
165     RefUnlock(id);
166     return OK;
167 }
168 
draw_raw_resource_bm(Ref id,int x,int y)169 errtype draw_raw_resource_bm(Ref id, int x, int y) {
170     FrameDesc *f;
171 
172     f = RefLock(id);
173     if (f == NULL)
174         critical_error(CRITERR_MEM | 9);
175     ss_bitmap(&f->bm, x, y);
176     RefUnlock(id);
177     return (OK);
178 }
179 
draw_res_bm_core(Ref id,int x,int y,uchar scale)180 errtype draw_res_bm_core(Ref id, int x, int y, uchar scale) {
181     FrameDesc *f;
182     LGRect mouse_rect;
183 
184     f = RefLock(id);
185     if (f == NULL)
186         critical_error(CRITERR_MEM | 9);
187     mouse_rect.ul.x = x;
188     mouse_rect.ul.y = y;
189     mouse_rect.lr.x = x + f->bm.w;
190     mouse_rect.lr.y = y + f->bm.h;
191 
192     // Set the palette right, if one is provided....
193     if (is_onscreen())
194         uiHideMouse(&mouse_rect);
195     if (scale)
196         ss_bitmap(&f->bm, x, y);
197     else
198         ss_noscale_bitmap(&f->bm, x, y);
199     if (is_onscreen())
200         uiShowMouse(&mouse_rect);
201     RefUnlock(id);
202     return (OK);
203 }
204 
draw_res_bm(Ref id,int x,int y)205 errtype draw_res_bm(Ref id, int x, int y) { return (draw_res_bm_core(id, x, y, TRUE)); }
206 
207 // Note, does no mouse code!
draw_full_res_bm(Ref id,int x,int y,uchar fade_in)208 errtype draw_full_res_bm(Ref id, int x, int y, uchar fade_in) {
209     FrameDesc *f;
210     short *temp_pall;
211     byte pal_id;
212     extern void finish_pal_effect(byte id);
213     extern byte palfx_start_fade_up(uchar * new_pal);
214 
215     f = RefLock(id);
216     if (f == NULL)
217         critical_error(CRITERR_MEM | 9);
218 
219     // Set the palette right, if one is provided....
220     if (f->pallOff) {
221 	// FIXME nasty hack to get at the private palette; the private palette
222 	// is an offset from the start of the raw on-disc resource, so it
223 	// includes the (raw) reftable as well as any previous ref entries.
224 	RefTable *prt = (RefTable *)ResGet(REFID(id));
225 	// Raw reftable size
226         size_t tabsize = sizeof(RefIndex) + (prt->numRefs+1) * sizeof(uint32_t);
227         temp_pall = (short *)((uchar *)prt->raw_data - tabsize + f->pallOff);
228         gr_set_pal(*temp_pall, *(temp_pall + 1), (uchar *)(temp_pall + 2));
229     }
230 
231     if (fade_in && temp_pall != NULL) {
232         pal_id = palfx_start_fade_up((uchar *)(temp_pall + 2));
233     }
234 
235     f->bm.bits = (uchar *)(f + 1);
236     ss_bitmap(&f->bm, x, y); // KLC  ss_bitmap(&f->bm, x, y);
237     RefUnlock(id);
238     if (fade_in)
239         finish_pal_effect(pal_id);
240     ResDrop(REFID(id));
241     return (OK);
242 }
243 
res_bm_width(Ref id)244 int res_bm_width(Ref id) {
245     FrameDesc *f;
246     int n;
247 
248     f = RefLock(id);
249     if (f == NULL)
250         critical_error(CRITERR_MEM | 9);
251     n = f->bm.w;
252     RefUnlock(id);
253     return (n);
254 }
255 
res_bm_height(Ref id)256 int res_bm_height(Ref id) {
257     FrameDesc *f;
258     int n;
259 
260     f = RefLock(id);
261     if (f == NULL)
262         critical_error(CRITERR_MEM | 9);
263     n = f->bm.h;
264     RefUnlock(id);
265     return (n);
266 }
267 
res_draw_text_shadowed(Id id,char * text,int x,int y,uchar shadow)268 errtype res_draw_text_shadowed(Id id, char *text, int x, int y, uchar shadow) {
269     gr_set_font(ResLock(id));
270     draw_shadowed_string(text, x, y, shadow);
271     ResUnlock(id);
272     return (OK);
273 }
274 
res_draw_string(Id font,int strid,int x,int y)275 errtype res_draw_string(Id font, int strid, int x, int y) { return res_draw_text(font, get_temp_string(strid), x, y); }
276 
277 // have some god damn parameters
278 // xc,yc is position, usually text center
279 // w.h. is rectangle wid+hgt, note <0 means that the x|yc is the upper left, not the center
280 // shad is how much to shade down color (-1 is no shadow)
281 // col is color of rectangle and text
text_button(char * text,int xc,int yc,int col,int shad,int w,int h)282 void text_button(char *text, int xc, int yc, int col, int shad, int w, int h) {
283     int ux, uy;
284     short tw, th;
285 
286     gr_string_size(text, &tw, &th);
287 
288     // do wacked out conversionitis
289     if (w < 0) {
290         ux = xc;
291         w = -w;
292         xc = ux + (w >> 1);
293     } else
294         ux = xc - (w >> 1);
295     if (h < 0) {
296         uy = yc;
297         h = -h;
298         yc = uy + (h >> 1);
299     } else
300         uy = yc - (h >> 1);
301 
302     // two rectangles...
303     gr_set_fcolor(col);
304     ss_rect(ux, uy, ux + w, uy + h);
305     if (shad >= 0) {
306         gr_set_fcolor(col + shad);
307         ss_rect(ux + 1, uy + 1, ux + w - 1, uy + h - 1);
308         gr_set_fcolor(col);
309     }
310     // some text, eh?
311     ss_string(text, xc - (tw >> 1), yc - (th >> 1));
312 }
313 
314 // ok, the easy case...
315 // centered at xc,yc, color base, auto-shadowed, size out setting
simple_text_button(char * text,int xc,int yc,int col)316 void simple_text_button(char *text, int xc, int yc, int col) {
317     short w, h;
318     gr_string_size(text, &w, &h);
319     text_button(text, xc, yc, col, 4, w + 12, h + 8);
320 }
321 
Rect_gr_rect(LGRect * r)322 void Rect_gr_rect(LGRect *r) { ss_rect(r->ul.x, r->ul.y, r->lr.x, r->lr.y); }
323 
Rect_gr_box(LGRect * r)324 void Rect_gr_box(LGRect *r) { ss_box(r->ul.x, r->ul.y, r->lr.x, r->lr.y); }
325 
itoa_2_10(char * s,int val)326 char *itoa_2_10(char *s, int val) {
327     s[0] = '0' + (val / 10);
328     s[1] = '0' + (val % 10);
329     s[2] = '\0';
330     return s;
331 }
332 
333 // max 99 hours...
second_format(int sec_remain,char * s)334 void second_format(int sec_remain, char *s) {
335     int c_l;
336     if (sec_remain >= 3600) {
337         itoa_2_10(s, sec_remain / 3600);
338         sec_remain %= 3600;
339         c_l = 3;
340         s[2] = ':';
341     } else
342         c_l = 0;
343     itoa_2_10(s + c_l, sec_remain / 60);
344     s[c_l + 2] = ':';
345     sec_remain %= 60;
346     itoa_2_10(s + c_l + 3, sec_remain);
347     if (s[0] == '0')
348         s[0] = ' ';
349 }
350 
351 #ifdef NOT_YET // later, dude
352 
353 #define BIG_BUF
354 
355 #pragma disable_message(202)
gifdump_func(short keycode,ulong context,void * data)356 uchar gifdump_func(short keycode, ulong context, void *data) {
357     unsigned char *temp_buf;
358     int giffp;
359     char harold[45];
360 
361     strcpy(harold, "SHOCK000.GIF");
362     giffp = open_gen(harold, O_CREAT | O_BINARY | O_WRONLY | O_TRUNC, S_IWRITE);
363     if (giffp == -1) {
364         message_info("GIF dump failed!");
365         return (ERR_NOEFFECT);
366     }
367     {
368         temp_buf = big_buffer;
369         gd_dump_screen(giffp, temp_buf);
370         strcat(harold, " saved");
371         message_info(harold);
372     }
373     return (TRUE);
374 }
375 #pragma enable_message(202)
376 
377 #endif // NOT_YET
378 
379 #define FULLSCREEN_MESSAGE_X 125
380 #define FULLSCREEN_MESSAGE_Y 8
381 
382 #define MESSAGE_BUFSZ 128
383 
384 #ifdef SVGA_SUPPORT_HATE_HATE
mouse_unconstrain(void)385 void mouse_unconstrain(void) {
386     // Note we are not calling the UI here since we are looking
387     // at actual screen size
388     mouse_constrain_xy(0, 0, grd_cap->w - 1, grd_cap->h - 1);
389 }
390 #endif
391 
string_message_info(int strnum)392 errtype string_message_info(int strnum) {
393     char buf[MESSAGE_BUFSZ];
394     get_string(strnum, buf, MESSAGE_BUFSZ);
395     return message_info(buf);
396 }
397 
398 char last_message[128];
399 ulong message_clear_time;
400 
401 #define MESSAGE_INTERVAL 1200
402 #define CHAR_SOFTCR 0x01 // soft carriage return (wrapped text)
403 #define CHAR_SOFTSP 0x02 // soft space (wrapped text)
404 #define MESSAGE_LEN 80
405 
406 LGRect msg_rect[2] = {
407     {GAME_MESSAGE_X, GAME_MESSAGE_Y, GAME_MESSAGE_X + GAME_MESSAGE_W, GAME_MESSAGE_Y + GAME_MESSAGE_H},
408     {FULLSCREEN_MESSAGE_X, FULLSCREEN_MESSAGE_Y, FULLSCREEN_MESSAGE_X + GAME_MESSAGE_W,
409      FULLSCREEN_MESSAGE_Y + GAME_MESSAGE_H}};
410 
411 uchar message_resend = FALSE;
412 extern uchar game_paused;
413 extern uchar view360_message_obscured;
414 void strip_newlines(char *buf);
415 
416 // Use the string wrapper's secret characters to delete newlines and double spaces.
strip_newlines(char * buf)417 void strip_newlines(char *buf) {
418     char *s;
419     for (s = buf; *s != '\0'; s++) {
420         if (*s == '\n')
421             *s = CHAR_SOFTSP;
422         if (isspace(*s) && isspace(*(s + 1)))
423             *s = CHAR_SOFTSP;
424     }
425 }
426 
message_info(const char * info_text)427 errtype message_info(const char *info_text) {
428     extern errtype inventory_draw_new_page(int pgnum);
429     int x, y;
430     char buf[MESSAGE_LEN];
431 
432     if (info_text != NULL) {
433         strncpy(buf, info_text, MESSAGE_LEN);
434         strip_newlines(buf);
435     } else
436         buf[0] = '\0';
437     if (_current_loop <= FULLSCREEN_LOOP) {
438         short a, b, c, d;
439         LGRect *r = &msg_rect[(full_game_3d && !game_paused) ? 1 : 0];
440 
441         x = r->ul.x;
442         y = r->ul.y;
443         if (is_onscreen())
444             uiHideMouse(r);
445         gr_push_canvas(grd_screen_canvas);
446         STORE_CLIP(a, b, c, d);
447 
448         ss_safe_set_cliprect(r->ul.x, r->ul.y, r->lr.x, r->lr.y);
449         if (!full_game_3d) {
450             y += 1;
451             if (!view360_message_obscured || game_paused) {
452                 draw_raw_resource_bm(REF_IMG_bmBlankMessageLine, x, y);
453                 // draw_hires_resource_bm(REF_IMG_bmBlankMessageLine,
454                 //										 SCONV_X(x),
455                 //SCONV_Y(y));
456             }
457             x += 2;
458         } else if (game_paused) {
459             extern grs_canvas inv_view360_canvas;
460             ss_noscale_bitmap(&inv_view360_canvas.bm, x, y);
461             x += 2;
462             y += 1;
463         }
464         if (!message_resend && info_text != last_message && strcmp(last_message, info_text) == 0) {
465             message_resend = TRUE;
466             message_clear_time = *tmd_ticks + CIT_CYCLE / 10;
467             hud_unset(HUD_MSGLINE);
468         } else {
469             message_resend = FALSE;
470             if ((!full_game_3d && !view360_message_obscured) || game_paused) {
471                 gr_set_fcolor(WHITE);
472                 res_draw_text_shadowed(RES_tinyTechFont, buf, x, y, full_game_3d);
473                 hud_unset(HUD_MSGLINE);
474             } else if (full_game_3d || view360_message_obscured) {
475                 if (buf[0] != '\0') {
476                     hud_set_time(HUD_MSGLINE, 5 << APPROX_CIT_CYCLE_SHFT);
477                 }
478             }
479         }
480         RESTORE_CLIP(a, b, c, d);
481         gr_pop_canvas();
482         if (is_onscreen())
483             uiShowMouse(r);
484     }
485     if (!message_resend && info_text != last_message) {
486         message_clear_time = *tmd_ticks + MESSAGE_INTERVAL;
487         strcpy(last_message, info_text);
488     }
489     return (OK);
490 }
491 
492 uchar message_clear_on = TRUE;
493 
message_clear_check()494 errtype message_clear_check() {
495     // much as I like spews that print every frame.......
496     //   Spew(DSRC_GAMESYS_Messages, ("%d >? %d\n",player_struct.game_time,message_clear_time));
497     if (*tmd_ticks < message_clear_time)
498         return OK;
499     if (message_resend) {
500         char buf[sizeof(last_message)];
501         strcpy(buf, last_message);
502         return message_info(buf);
503     }
504     if (message_clear_on && !full_game_3d && !view360_message_obscured) {
505         errtype retval = message_info("");
506         return retval;
507     }
508     return (OK);
509 }
510 
message_box(char * box_text)511 errtype message_box(char *box_text) {
512     message_info(box_text);
513     return (OK);
514 }
515 
516 #ifdef NOT_YET // later, dude
517 
518 #pragma disable_message(202)
confirm_box(char * confirm_text)519 uchar confirm_box(char *confirm_text) { return (TRUE); }
520 #pragma enable_message(202)
521 
fopen_gen(char * fname,char * t)522 FILE *fopen_gen(char *fname, char *t) {
523     Datapath gen_path;
524     FILE *retval;
525     char temp[64];
526 
527     gen_path.numDatapaths = 0;
528     gen_path.noCurrent = 1;
529     DatapathAddDir(&gen_path, "gen");
530     DatapathAddEnv(&gen_path, "GEN_DIR");
531     strcpy(temp, getenv("CITHOME"));
532     strcat(temp, "\\gen");
533     DatapathAddDir(&gen_path, temp);
534     DatapathNoCurrent(&gen_path);
535     next_number_dpath_fname(&gen_path, fname);
536     retval = DatapathOpen(&gen_path, fname, t);
537     DatapathFree(&gen_path);
538     return retval;
539 }
540 
open_gen(char * fname,int access1,int access2)541 int open_gen(char *fname, int access1, int access2) {
542     Datapath gen_path;
543     int retval;
544 
545     gen_path.numDatapaths = 0;
546     gen_path.noCurrent = 1;
547     DatapathAddDir(&gen_path, "gen");
548     DatapathAddEnv(&gen_path, "GEN_DIR");
549     DatapathNoCurrent(&gen_path);
550     next_number_dpath_fname(&gen_path, fname);
551     retval = DatapathFDOpen(&gen_path, fname, access1, access2);
552     DatapathFree(&gen_path);
553     return retval;
554 }
555 
next_number_dpath_fname(Datapath * dpath,char * fname)556 char *next_number_dpath_fname(Datapath *dpath, char *fname) {
557     char *subname = strrchr(fname, '0');
558     int fhnd, numlen = 1, i, num = 0;
559 
560     if (subname != NULL) {
561         while ((strlen(subname) != strlen(fname)) && (subname[0] == subname[-1])) {
562             subname--;
563             numlen++;
564         }
565         // try them, lets go, rock and roll, so on
566         while ((fhnd = DatapathFDOpen(dpath, fname, O_BINARY | O_RDONLY)) != -1) { /* Check next slot */
567             close(fhnd); /* good idea to, like, close the opened file */
568             ++num;
569             for (i = 0; i < numlen; i++)
570                 subname[numlen - (i + 1)] = '0' + ((num >> (3 * i)) & 7);
571         }
572         close(fhnd);
573     }
574     return fname;
575 }
576 
next_number_fname(char * fname)577 char *next_number_fname(char *fname) {
578     char *subname = strrchr(fname, '0');
579     int fhnd, numlen = 1, i, num = 0;
580 
581     while ((strlen(subname) != strlen(fname)) && (subname[0] == subname[-1])) {
582         subname--;
583         numlen++;
584     }
585     /* Look for files like uwpic000.gif */
586     while ((fhnd = open(fname, O_BINARY | O_RDONLY)) != -1) { /* Check next slot */
587         close(fhnd);                                          /* good idea to, like, close the opened file */
588         ++num;
589         for (i = 0; i < numlen; i++)
590             subname[numlen - (i + 1)] = '0' + ((num >> (3 * i)) & 7);
591     }
592     close(fhnd);
593     return fname;
594 }
595 
596 #endif // NOT_YET
597 
tight_loop(uchar check_input)598 errtype tight_loop(uchar check_input) {
599     if (music_on)
600         mlimbs_do_ai();
601 
602     // KLC - does nothing!
603     //   if (music_on || sfx_on)
604     //      synchronous_update();
605 
606     if (check_input) {
607         uiPoll();
608         kb_flush_bios();
609     }
610     return (OK);
611 }
612 
613 // --------------------------------------------------
614 // STRING WRAPPER
615 
616 #define HYPHEN '-'
617 
618 // FIXME This code duplicates gr_font_string_wrap()
hyphenated_wrap_text(char * ps,char * out,short width)619 int hyphenated_wrap_text(char *ps, char *out, short width) {
620     char *psbase;
621     char *p;
622     char *pmark;
623     short numLines;
624     short currWidth;
625 
626     //      Set up to do wrapping
627 
628     psbase = ps;  // psbase = string beginning
629     numLines = 0; // ps = base of current line
630 
631     //      Do wrapping for each line till hit end
632 
633     while (*ps) {
634         pmark = NULL;  // no SOFTCR insert LGPoint yet
635         currWidth = 0; // and zero width so far
636         p = ps;
637 
638         //      Loop thru each word
639 
640         while (*p) {
641 
642             //      Skip through to next CR or space or '\0', keeping track of width
643 
644             while ((*p != 0) && (*p != '\n') && (*p != ' ') && (*p != CHAR_SOFTSP)) {
645                 currWidth += gr_char_width(*p);
646                 p++;
647             }
648 
649             //      If bypassed width, break out of word loop
650 
651             if (currWidth > width ||
652                 (*p == CHAR_SOFTSP && (currWidth + gr_char_width(HYPHEN)) > width)) {
653                 if ((pmark == NULL) && (*p != 0) && (*p != '\n'))
654                     pmark = p;
655                 break;
656             }
657 
658             //      Else set new mark LGPoint (unless eol or eos, then bust out)
659 
660             else {
661                 if ((*p == 0) || (*p == '\n')) // hit end of line, wipe marker
662                 {
663                     pmark = NULL;
664                     break;
665                 }
666                 pmark = p;                      // else advance marker
667                 currWidth += gr_char_width(*p); // and account for space
668                 p++;
669             }
670         }
671 
672         //      Now insert soft cr if marked one
673 
674         if (pmark) {
675             strncpy(out, ps, pmark - ps);
676             out += pmark - ps;
677             if (*pmark == CHAR_SOFTSP)
678                 *out++ = HYPHEN;
679             *out++ = CHAR_SOFTCR;
680             ps = pmark + 1;
681             if (*ps == ' ') // if wrapped and following space,
682                 ps++;       // turn into (ignored) soft space
683         }
684 
685         //      Otherwise, bump past cr
686         else {
687             strncpy(out, ps, p - ps + 1);
688             out += p - ps + 1;
689 
690             if (*p)
691                 ++p;
692             ps = p;
693         }
694 
695         //      Bump line counter in any case
696 
697         ++numLines;
698     }
699 
700     //      When hit end of string, return # lines encountered
701 
702     return (numLines);
703 }
704 
705 // --------------------------------------------------------------------
706 // WAIT CURSOR
707 
708 char wait_count = 0;
709 
begin_wait()710 errtype begin_wait() {
711     extern LGCursor wait_cursor;
712     errtype retval;
713     if (wait_count == 0) {
714         uiHideMouse(NULL);
715         retval = uiPushGlobalCursor(&wait_cursor);
716         uiShowMouse(NULL);
717 
718         extern void SDLDraw(void);
719         SDLDraw();
720     }
721     wait_count++;
722 
723     return (retval);
724 }
725 
726 #ifdef NOT_YET //
spoof_mouse_event(void)727 errtype spoof_mouse_event(void) {
728     int i;
729     uiMouseEvent ev;
730 
731     uiMakeMotionEvent(&ev);
732     if (ev.buttons == 0)
733         return OK;
734     for (i = 0; i < NUM_MOUSE_BTNS; i++) {
735         if (ev.buttons & (1 << i))
736             ev.action |= MOUSE_BTN2DOWN(i);
737     }
738     ev.type = UI_EVENT_MOUSE;
739     return uiQueueEvent((uiEvent *)&ev);
740 }
741 #endif // NOT_YET
742 
end_wait()743 errtype end_wait() {
744     errtype retval;
745     wait_count--;
746     if (wait_count <= 0) {
747         uiHideMouse(NULL);
748         retval = uiPopGlobalCursor();
749         uiShowMouse(NULL);
750         wait_count = 0;
751         uiFlush();
752         //      spoof_mouse_event();
753     }
754     return (retval);
755 }
756 
757 // --------------------------------------------------------------------
758 // ZOOM BOXES
759 
760 /*
761  * Original zoom timing was 8 * 10 ticks at a timer frequency of 280 Hz, i.e.
762  * around 286 milliseconds. Assuming a refresh rate of 60 Hz that's around 17
763  * frames.
764  */
765 
766 #define ZOOM_MS  286
767 
768 #define INTERP(s, f, i) (((f) * (i) + (s) * (ZOOM_MS - (i)-1)) / (ZOOM_MS - 1))
769 
770 bool ZoomEnable;
771 LGRect ZoomStart, ZoomEnd;
772 Uint32 ZoomTicks, ZoomI;
773 
zoom_rect(LGRect * start,LGRect * end)774 void zoom_rect(LGRect *start, LGRect *end)
775 {
776   //set global variables used by ZoomProc()
777   ZoomEnable = TRUE;
778   ZoomStart = *start;
779   ZoomEnd = *end;
780   ZoomTicks = SDL_GetTicks();
781 }
782 
783 //called just before and after SDLDraw() in mainloop()
ZoomDrawProc(int erase)784 void ZoomDrawProc(int erase)
785 {
786   if (!ZoomEnable) return;
787 
788   if (!erase)
789   {
790     ZoomI = SDL_GetTicks() - ZoomTicks;
791     if (ZoomI >= ZOOM_MS) {ZoomEnable = 0; return;}
792   }
793 
794   int ft = gr_get_fill_type();
795   int c = gr_get_fcolor();
796   gr_set_fill_type(FILL_XOR);
797   gr_set_fcolor(WHITE);
798 
799   // make the zoom rectanle visible in OpenGL as well
800   if(full_game_3d && use_opengl()) {
801     gr_set_fcolor(0x1);
802   }
803 
804   short ulx = INTERP(ZoomStart.ul.x, ZoomEnd.ul.x, ZoomI);
805   short uly = INTERP(ZoomStart.ul.y, ZoomEnd.ul.y, ZoomI);
806   short lrx = INTERP(ZoomStart.lr.x, ZoomEnd.lr.x, ZoomI);
807   short lry = INTERP(ZoomStart.lr.y, ZoomEnd.lr.y, ZoomI);
808   ss_box(ulx, uly, lrx, lry);
809   ss_box(ulx - 1, uly - 1, lrx + 1, lry + 1);
810 
811   gr_set_fill_type(ft);
812   gr_set_fcolor(c);
813 }
814 
815 // Returns the angle of difference between look_facing and the true direction
816 // that looker would have to be facing in order to see target, and puts that
817 // true direction into real_dir.
818 
819 // Wow, I really ought to someday make this not use icky trig
820 // but instead write some fast simple version
point_in_view_arc(fix target_x,fix target_y,fix looker_x,fix looker_y,fixang look_facing,fixang * real_dir)821 fixang point_in_view_arc(fix target_x, fix target_y, fix looker_x, fix looker_y, fixang look_facing, fixang *real_dir) {
822     fix x_diff, y_diff;
823     fixang retval;
824 
825     x_diff = target_x - looker_x;
826     y_diff = target_y - looker_y;
827     *real_dir = fix_atan2(y_diff, x_diff);
828 
829     // Compensate for difference between our coordinate system and fixpoint's
830 
831     // Hmmm, how do fixangs deal with negatives?
832     // Better normalize to absolute difference, just to be sure
833     // After all, they do have *real_dir to figure it out themselves
834     // if they want to.
835     if (*real_dir > look_facing)
836         retval = (*real_dir - look_facing);
837     else
838         retval = (look_facing - *real_dir);
839     if (retval > 0x8000)
840         retval = 0x10000 - retval;
841     return (retval);
842 }
843 
844 // convert occurances of the character "from" to the character "to"
845 // in the string "s"
string_replace_char(char * s,char from,char to)846 void string_replace_char(char *s, char from, char to) {
847     for (; *s; s++) {
848         if (*s == from)
849             *s = to;
850     };
851 }
852 
853 //gamma param not used here; see SetSDLPalette() in Shock.c
gamma_dealfunc(ushort gamma_qvar)854 void gamma_dealfunc(ushort gamma_qvar) {
855     gr_set_gamma_pal(0, 256, 0);
856 }
857