• R/O
  • SSH
  • HTTPS

marathon: コミット


コミットメタ情報

リビジョン488 (tree)
日時2009-03-28 16:09:19
作者ookawa_mi

ログメッセージ

*** empty log message ***

変更サマリ

差分

--- marathon/trunk/AlephOneJP/RenderOther/computer_interface.cpp (revision 487)
+++ marathon/trunk/AlephOneJP/RenderOther/computer_interface.cpp (revision 488)
@@ -288,6 +288,9 @@
288288
289289 static vector<terminal_text_t> map_terminal_text;
290290
291+// ghs: for Lua
292+short number_of_terminal_texts() { return map_terminal_text.size(); }
293+
291294 /* internal global structure */
292295 static struct player_terminal_data *player_terminals;
293296
@@ -298,7 +301,7 @@
298301 #if defined(mac)
299302 extern void UseInterfaceFont(short font_index);
300303 #elif defined(SDL)
301-extern sdl_font_info *GetInterfaceFont(short font_index);
304+extern font_info *GetInterfaceFont(short font_index);
302305 extern uint16 GetInterfaceStyle(short font_index);
303306 #endif
304307
@@ -438,88 +441,41 @@
438441 current_pixel = SDL_MapRGB(/*world_pixels*/draw_surface->format, color.r, color.g, color.b);
439442 }
440443
441-#if 0
442-static bool calculate_line(char *base_text, short width, short start_index, short text_end_index, short *end_index)
443-{
444- bool done = false;
445444
446- if (base_text[start_index]) {
447- int index = start_index, running_width = 0;
448-
449- // terminal_font no longer a global, since it may change
450- sdl_font_info *terminal_font = GetInterfaceFont(_computer_interface_font);
451-
452- while (running_width < width && base_text[index] && base_text[index] != MAC_LINE_END) {
453- running_width += char_width(base_text[index], terminal_font, current_style);
454- index++;
455- }
456-
457- // Now go backwards, looking for whitespace to split on
458- if (base_text[index] == MAC_LINE_END)
459- index++;
460- else if (base_text[index]) {
461- int break_point = index;
462-
463- while (break_point>start_index) {
464- if (base_text[break_point] == ' ')
465- break; // Non printing
466- break_point--; // this needs to be in front of the test
467- }
468-
469- if (break_point != start_index)
470- index = break_point+1; // Space at the end of the line
471- }
472-
473- *end_index= index;
474- } else
475- done = true;
476-
477- return done;
445+static int isJChar(unsigned char text) {
446+ return ( text >= 0x81 && text <= 0xa0 || text >= 0xe0 );
478447 }
479-# else
480-static inline bool IsTwoByte(unsigned char t ) { return (t>0x81 && t<0xa0) || (t>0xe0 && t<0xfd) ; }
481448
449+#include "converter.h"
450+extern "C" uint16 sjisChar(char* in, int* step);
482451 static bool calculate_line(char *base_text, short width, short start_index, short text_end_index, short *end_index)
483452 {
484453 bool done = false;
485-
486454 if (base_text[start_index]) {
487455 int index = start_index, running_width = 0;
488456
489457 // terminal_font no longer a global, since it may change
490- sdl_font_info *terminal_font = GetInterfaceFont(_computer_interface_font);
491- int i = 0;
492- while (running_width + 7< width && base_text[index] && base_text[index] != MAC_LINE_END) {
493- running_width += char_width(base_text[index], terminal_font, current_style);
494- if(IsTwoByte(base_text[++index]))
495- running_width += char_width(base_text[++index],terminal_font, current_style);
458+ font_info *terminal_font = GetInterfaceFont(_computer_interface_font);
459+ uint16 c;
460+ int index_k = start_index;
461+ while (running_width < width && (c = sjisChar(&base_text[index],&index_k)) && c != MAC_LINE_END) {
462+ char* utf8t = sjis2utf8(base_text + start_index,index_k - start_index);
463+ int X;
464+ TTF_SizeUTF8(((ttf_font_info*)terminal_font)->font,utf8t,&running_width,&X);
465+ index = index_k;
496466 }
467+
497468 // Now go backwards, looking for whitespace to split on
498- if (base_text[index] == MAC_LINE_END)
499- index++;
500- else if (base_text[index]) {
501- int break_point = index;
502- for (int i = start_index; i < break_point;i++) {
503- if (base_text[i] == ' ')
504- index = i;
505- if (IsTwoByte(base_text[i])) {
506- index= i+2;
507- i++;
508- }
509- }
510-
511- if (index != start_index && base_text[index] == ' ') {
512- index++; // Space at the end of the line
513- }
514- }
515-
516- *end_index= index;
469+ if( c == MAC_LINE_END )
470+ *end_index= index_k;
471+ else
472+ *end_index = index;
517473 } else
518474 done = true;
519475
520476 return done;
521477 }
522-#endif
478+
523479 /* ------------ code begins */
524480
525481 player_terminal_data *get_player_terminal_data(
@@ -698,6 +654,8 @@
698654 // assert(terminal_data->state != _no_terminal_state);
699655 if(TERMINAL_IS_DIRTY(terminal_data))
700656 {
657+ SET_TERMINAL_IS_DIRTY(terminal_data, false);
658+
701659 terminal_text_t *terminal_text;
702660 struct terminal_groupings *current_group;
703661 Rect bounds;
@@ -776,6 +734,7 @@
776734
777735 case _static_group:
778736 fill_terminal_with_static(&bounds);
737+ SET_TERMINAL_IS_DIRTY(terminal_data, true);
779738 break;
780739
781740 case _camera_group:
@@ -802,8 +761,6 @@
802761 // Disable clipping
803762 set_drawing_clip_rectangle(SHRT_MIN, SHRT_MIN, SHRT_MAX, SHRT_MAX);
804763 #endif
805-
806- SET_TERMINAL_IS_DIRTY(terminal_data, false);
807764 }
808765 }
809766
@@ -898,7 +855,7 @@
898855 SetFont(&old_font);
899856 }
900857 #else
901- sdl_font_info *terminal_font = GetInterfaceFont(_computer_interface_font);
858+ font_info *terminal_font = GetInterfaceFont(_computer_interface_font);
902859 uint16 terminal_style = GetInterfaceStyle(_computer_interface_font);
903860 width = text_width(base_text + current_group->start_index, current_group->length, terminal_font, terminal_style);
904861 // width = text_width(base_text + current_group->start_index, current_group->length, terminal_font, _get_font_spec(_computer_interface_font)->style);
@@ -1120,7 +1077,7 @@
11201077 #ifdef mac
11211078 MoveTo(bounds->left, bounds->top+line_height*(line_number+FUDGE_FACTOR));
11221079 #else
1123- sdl_font_info *terminal_font = GetInterfaceFont(_computer_interface_font);
1080+ font_info *terminal_font = GetInterfaceFont(_computer_interface_font);
11241081 int xpos = bounds->left;
11251082 #endif
11261083
@@ -1239,6 +1196,7 @@
12391196
12401197 #ifdef SDL
12411198 extern int get_pict_header_width(LoadedResource &);
1199+extern int get_pict_header_height(LoadedResource &rsrc);
12421200 #endif
12431201
12441202 static void display_picture(
@@ -1252,7 +1210,7 @@
12521210 #else
12531211 SDL_Surface *s = NULL;
12541212 #endif
1255-
1213+
12561214 if (get_picture_resource_from_scenario(picture_id, PictRsrc))
12571215 {
12581216 #ifdef mac
@@ -1265,7 +1223,7 @@
12651223 #endif
12661224 Rect bounds;
12671225 Rect screen_bounds;
1268-
1226+
12691227 #if defined(mac)
12701228 bounds= (*picture)->picFrame;
12711229 #elif defined(SDL)
@@ -1272,28 +1230,31 @@
12721230 bounds.left = bounds.top = 0;
12731231 bounds.right = s->w;
12741232 bounds.bottom = s->h;
1275-
1233+ bool cinemascopeHack = false;
12761234 int pict_header_width = get_pict_header_width(PictRsrc);
1277- bool cinemascopeHack = false;
12781235 if (bounds.right != pict_header_width)
12791236 {
1280- cinemascopeHack = true;
12811237 bounds.right = pict_header_width;
1238+ bounds.bottom = get_pict_header_height(PictRsrc);
1239+ if( pict_header_width == 307 ) {
1240+ cinemascopeHack = true;
1241+ }
12821242 }
1243+
12831244 #endif
12841245 OffsetRect(&bounds, -bounds.left, -bounds.top);
12851246 calculate_bounds_for_object(frame, flags, &screen_bounds, &bounds);
1286-
1247+
12871248 if(RECTANGLE_WIDTH(&bounds)<=RECTANGLE_WIDTH(&screen_bounds) &&
1288- RECTANGLE_HEIGHT(&bounds)<=RECTANGLE_HEIGHT(&screen_bounds))
1249+ RECTANGLE_HEIGHT(&bounds)<=RECTANGLE_HEIGHT(&screen_bounds))
12891250 {
12901251 /* It fits-> center it. */
12911252 OffsetRect(&bounds, screen_bounds.left+(RECTANGLE_WIDTH(&screen_bounds)-RECTANGLE_WIDTH(&bounds))/2,
1292- screen_bounds.top+(RECTANGLE_HEIGHT(&screen_bounds)-RECTANGLE_HEIGHT(&bounds))/2);
1253+ screen_bounds.top+(RECTANGLE_HEIGHT(&screen_bounds)-RECTANGLE_HEIGHT(&bounds))/2);
12931254 } else {
12941255 /* Doesn't fit. Make it, but preserve the aspect ratio like a good little boy */
12951256 if(RECTANGLE_HEIGHT(&bounds)-RECTANGLE_HEIGHT(&screen_bounds)>=
1296- RECTANGLE_WIDTH(&bounds)-RECTANGLE_WIDTH(&screen_bounds))
1257+ RECTANGLE_WIDTH(&bounds)-RECTANGLE_WIDTH(&screen_bounds))
12971258 {
12981259 short adjusted_width;
12991260
@@ -1308,12 +1269,13 @@
13081269 adjusted_height= RECTANGLE_WIDTH(&screen_bounds)*RECTANGLE_HEIGHT(&bounds)/RECTANGLE_WIDTH(&bounds);
13091270 bounds= screen_bounds;
13101271 InsetRect(&bounds, 0, (RECTANGLE_HEIGHT(&screen_bounds)-adjusted_height)/2);
1272+
13111273 // dprintf("Warning: Not large enough for pict: %d (width);g", picture_id);
13121274 }
13131275 }
1314-
1315-// warn(HGetState((Handle) picture) & 0x40); // assert it is purgable.
1316-
1276+
1277+ // warn(HGetState((Handle) picture) & 0x40); // assert it is purgable.
1278+
13171279 #if defined(mac)
13181280 HLock((Handle) picture);
13191281 DrawPicture(picture, &bounds);
@@ -1320,7 +1282,7 @@
13201282 HUnlock((Handle) picture);
13211283 #elif defined(SDL)
13221284 SDL_Rect r = {bounds.left, bounds.top, bounds.right - bounds.left, bounds.bottom - bounds.top};
1323- if ((s->w == r.w && s->h == r.h) || cinemascopeHack)
1285+ if ((s->w == r.w && s->h == r.h) || (cinemascopeHack ) )
13241286 SDL_BlitSurface(s, NULL, /*world_pixels*/draw_surface, &r);
13251287 else {
13261288 // Rescale picture
@@ -1337,9 +1299,9 @@
13371299 } else {
13381300 Rect bounds;
13391301 char format_string[128];
1340-
1302+
13411303 calculate_bounds_for_object(frame, flags, &bounds, NULL);
1342-
1304+
13431305 #if defined(mac)
13441306 EraseRect(&bounds);
13451307 #elif defined(SDL)
@@ -1346,21 +1308,21 @@
13461308 SDL_Rect r = {bounds.left, bounds.top, bounds.right - bounds.left, bounds.bottom - bounds.top};
13471309 SDL_FillRect(/*world_pixels*/draw_surface, &r, SDL_MapRGB(/*world_pixels*/draw_surface->format, 0, 0, 0));
13481310 #endif
1349-
1311+
13501312 getcstr(format_string, strERRORS, pictureNotFound);
13511313 sprintf(temporary, format_string, picture_id);
1352-
1314+
13531315 #if defined(mac)
13541316 // LP change: setting the font to the OS font
13551317 TextFont(systemFont);
13561318 short width= TextWidth(temporary, 0, strlen(temporary));
1357-
1319+
13581320 /* Center the error message.. */
13591321 MoveTo(bounds.left+(RECTANGLE_WIDTH(&bounds)-width)/2,
1360- bounds.top+RECTANGLE_HEIGHT(&bounds)/2);
1322+ bounds.top+RECTANGLE_HEIGHT(&bounds)/2);
13611323 DrawText(temporary, 0, strlen(temporary));
13621324 #elif defined(SDL)
1363- const sdl_font_info *font = GetInterfaceFont(_computer_interface_title_font);
1325+ const font_info *font = GetInterfaceFont(_computer_interface_title_font);
13641326 int width = text_width(temporary, font, styleNormal);
13651327 draw_text(/*world_pixels*/draw_surface, temporary,
13661328 bounds.left + (RECTANGLE_WIDTH(&bounds) - width) / 2,
@@ -1371,11 +1333,52 @@
13711333 }
13721334 }
13731335
1374-/* Not completed. Remember 16/24 bit & valkyrie */
1336+template <typename T>
1337+static inline T randomize_pixel(uint16 pixel)
1338+{
1339+ return static_cast<T>(pixel);
1340+}
1341+
1342+template <>
1343+inline uint32 randomize_pixel(uint16 pixel)
1344+{
1345+ return (uint32)pixel^(((uint32)pixel)<<8);
1346+}
1347+
1348+template <typename T>
1349+static inline void randomize_line(T* start, uint32 count)
1350+{
1351+ static uint16 random_seed = 6906;
1352+ for (int i = 0; i < count; ++i)
1353+ {
1354+ *start++ = randomize_pixel<T>(random_seed);
1355+ if (random_seed&1) random_seed = (random_seed>>1)^0xb400;
1356+ else random_seed = random_seed >> 1;
1357+ }
1358+}
1359+
13751360 static void fill_terminal_with_static(
13761361 Rect *bounds)
13771362 {
1378- (void) (bounds);
1363+ for (int y = bounds->top; y < bounds->bottom; ++y)
1364+ {
1365+ int bpp = draw_surface->format->BytesPerPixel;
1366+ int width = bounds->right - bounds->left;
1367+
1368+ uint8* p = (uint8*)draw_surface->pixels + y * draw_surface->pitch + bounds->left * bpp;
1369+ if (bpp == 1)
1370+ {
1371+ randomize_line<uint8>(p, width);
1372+ }
1373+ else if (bpp == 2)
1374+ {
1375+ randomize_line<uint16>(reinterpret_cast<uint16*>(p), width);
1376+ }
1377+ else if (bpp == 4)
1378+ {
1379+ randomize_line<uint32>(reinterpret_cast<uint32*>(p), width);
1380+ }
1381+ }
13791382 }
13801383
13811384 #ifndef PREPROCESSING_CODE
@@ -1892,7 +1895,7 @@
18921895 bounds.top+RECTANGLE_HEIGHT(&bounds)/2);
18931896 DrawText(temporary, 0, strlen(temporary));
18941897 #elif defined(SDL)
1895- const sdl_font_info *font = GetInterfaceFont(_computer_interface_title_font);
1898+ const font_info *font = GetInterfaceFont(_computer_interface_title_font);
18961899 // const sdl_font_info *font = load_font(*_get_font_spec(_computer_interface_title_font));
18971900 int width = text_width(temporary, font, styleNormal);
18981901 draw_text(/*world_pixels*/draw_surface, temporary,
--- marathon/trunk/AlephOneJP/RenderOther/sdl_fonts.h (revision 487)
+++ marathon/trunk/AlephOneJP/RenderOther/sdl_fonts.h (revision 488)
@@ -29,25 +29,58 @@
2929 #define SDL_FONTS_H
3030
3131 #include "FileHandler.h"
32+#ifdef HAVE_SDL_TTF
33+#include <SDL_ttf.h>
34+#include <boost/tuple/tuple.hpp>
35+#endif
3236
37+#include <string>
3338
3439 /*
3540 * Definitions
3641 */
3742
43+
44+class font_info {
45+ friend void unload_font(font_info *font);
46+public:
47+ virtual uint16 get_ascent(void) const = 0;
48+ virtual uint16 get_height(void) const = 0;
49+ virtual uint16 get_line_height(void) const = 0;
50+ virtual uint16 get_descent(void) const = 0;
51+ virtual int16 get_leading(void) const = 0;
52+
53+ int draw_text(SDL_Surface *s, const char *text, size_t length, int x, int y, uint32 pixel, uint16 style, bool utf8 = false) const;
54+ uint16 text_width(const char *text, size_t length, uint16 style, bool utf8 = false) const;
55+ uint16 text_width(const char *text, uint16 style, bool utf8 = false) const;
56+ int trunc_text(const char *text, int max_width, uint16 style) const;
57+ virtual int8 char_width(uint16 c, uint16 style) const = 0;
58+
59+ int draw_styled_text(SDL_Surface *s, const std::string& text, size_t length, int x, int y, uint32 pixel, uint16 initial_style, bool utf = false) const;
60+ int styled_text_width(const std::string& text, size_t length, uint16 initial_style, bool utf8 = false) const;
61+ int trunc_styled_text(const std::string& text, int max_width, uint16 style) const;
62+ std::string style_at(const std::string& text, std::string::const_iterator pos, uint16 style) const;
63+
64+protected:
65+ virtual int _draw_text(SDL_Surface *s, const char *text, size_t length, int x, int y, uint32 pixel, uint16 style, bool utf8) const = 0;
66+ virtual uint16 _text_width(const char *text, size_t length, uint16 style, bool utf8) const = 0;
67+ virtual uint16 _text_width(const char *text, uint16 style, bool utf8) const = 0;
68+ virtual int _trunc_text(const char *text, int max_width, uint16 style) const = 0;
69+private:
70+ // wrapped behind unload_font, because we call delete this!
71+ virtual void _unload() = 0;
72+};
73+
3874 // Font information structure
39-class sdl_font_info {
40- friend sdl_font_info *load_font(const TextSpec &spec);
41- friend void unload_font(sdl_font_info *font);
75+class sdl_font_info : public font_info {
76+ friend sdl_font_info *load_sdl_font(const TextSpec &spec);
77+ friend void unload_sdl_font(sdl_font_info *font);
4278
4379 public:
4480 sdl_font_info() : first_character(0), last_character(0),
4581 ascent(0), descent(0), leading(0), pixmap(NULL), ref_count(0) {}
46-#if 0
4782 ~sdl_font_info() {if (pixmap) free(pixmap);}
48-#else
49- ~sdl_font_info() {if (pixmap) delete [] pixmap;}
50-#endif
83+
5184 uint16 get_ascent(void) const {return ascent;}
5285 uint16 get_height(void) const {return ascent + descent;}
5386 uint16 get_line_height(void) const {return ascent + descent + leading;}
@@ -62,19 +95,54 @@
6295
6396 uint8 *pixmap; // Font image (1 byte/pixel)
6497 int bytes_per_row; // Bytes per row in pixmap
65-#if 0
98+
6699 uint16 *location_table; // Table of byte-offsets into pixmap (points into resource)
67-#else
68- uint32 * location_table; // Table of byte-offsets into pixmap (points into resource)
69-#endif
70100 int8 *width_table; // Table of kerning/width info (points into resource)
71101
102+ int8 char_width(uint8 c, uint16 style) const;
103+
104+protected:
105+ virtual int _draw_text(SDL_Surface *s, const char *text, size_t length, int x, int y, uint32 pixel, uint16 style, bool utf8) const;
106+ virtual uint16 _text_width(const char *text, size_t length, uint16 style, bool utf8) const;
107+ virtual uint16 _text_width(const char *text, uint16 style, bool utf8) const;
108+ virtual int _trunc_text(const char *text, int max_width, uint16 style) const;
72109 private:
110+ virtual void _unload();
73111 int ref_count;
74112 LoadedResource rsrc;
75113 };
76114
115+#ifdef HAVE_SDL_TTF
116+typedef boost::tuple<std::string, uint16, int16> ttf_font_key_t;
77117
118+class ttf_font_info : public font_info {
119+public:
120+ uint16 get_ascent() const { return TTF_FontAscent(font); };
121+ uint16 get_height() const { return TTF_FontHeight(font); };
122+ uint16 get_line_height() const { return max(TTF_FontLineSkip(font), TTF_FontHeight(font)); }
123+ uint16 get_descent() const { return -TTF_FontDescent(font); }
124+ int16 get_leading() const { return get_line_height() - get_ascent() - get_descent(); }
125+
126+ TTF_Font* font;
127+
128+
129+ int8 char_width(uint16, uint16) const;
130+
131+ ttf_font_info() {
132+ }
133+protected:
134+ virtual int _draw_text(SDL_Surface *s, const char *text, size_t length, int x, int y, uint32 pixel, uint16 style, bool utf8) const;
135+ virtual uint16 _text_width(const char *text, size_t length, uint16 style, bool utf8) const;
136+ virtual uint16 _text_width(const char *text, uint16 style, bool utf8) const;
137+ virtual int _trunc_text(const char *text, int max_width, uint16 style) const;
138+private:
139+ char *process_printable(const char *src, int len) const;
140+ uint16 *process_macroman(const char *src, int len) const;
141+ TTF_Font *get_ttf(uint16 style) const { return font;}
142+ virtual void _unload();
143+};
144+#endif
145+
78146 /*
79147 * Functions
80148 */
@@ -83,9 +151,9 @@
83151 extern void initialize_fonts(void);
84152
85153 // Load font, return pointer to font info
86-extern sdl_font_info *load_font(const TextSpec &spec);
154+extern font_info *load_font(const TextSpec &spec);
87155
88156 // Unload font
89-extern void unload_font(sdl_font_info *font);
157+extern void unload_font(font_info *font);
90158
91159 #endif
--- marathon/trunk/AlephOneJP/RenderOther/FontHandler.cpp (revision 487)
+++ marathon/trunk/AlephOneJP/RenderOther/FontHandler.cpp (revision 488)
@@ -81,7 +81,6 @@
8181 return *this;
8282 }
8383
84-#if 0
8584 // Initializer: call before using because of difficulties in setting up a proper constructor:
8685
8786 void FontSpecifier::Init()
@@ -91,7 +90,7 @@
9190 #endif
9291 Update();
9392 #ifdef HAVE_OPENGL
94- OGL_Texture = NULL;
93+// OGL_Texture = NULL;
9594 #endif
9695 }
9796
@@ -188,26 +187,31 @@
188187 unload_font(Info);
189188 Info = NULL;
190189 }
191-
190+
191+ TextSpec Spec;
192192 // Simply implements format "#<value>"; may want to generalize this
193- short ID;
194- if (File[0] != '#') {
195- fprintf(stderr, "WARNING: Font file names not (yet) supported\n");
196- return;
193+ if (File[0] == '#')
194+ {
195+ short ID;
196+ sscanf(File+1, "%hd", &ID);
197+
198+ Spec.font = ID;
197199 }
198- sscanf(File+1, "%hd", &ID);
199-
200- // Actual loading
201- TextSpec Spec;
202- Spec.font = ID;
200+ else
201+ {
202+ Spec.font = -1; // no way to fall back :(
203+ Spec.normal = File;
204+ }
205+
203206 Spec.size = Size;
204207 Spec.style = Style;
208+ Spec.adjust_height = AdjustLineHeight;
205209 Info = load_font(Spec);
206210
207211 if (Info) {
208- Ascent = Info->ascent;
209- Descent = Info->descent;
210- Leading = Info->leading;
212+ Ascent = Info->get_ascent();
213+ Descent = Info->get_descent();
214+ Leading = Info->get_leading();
211215 Height = Ascent + Leading;
212216 LineSpacing = Ascent + Descent + Leading;
213217 for (int k=0; k<256; k++)
@@ -221,11 +225,11 @@
221225
222226 int FontSpecifier::TextWidth(const char *text)
223227 {
224- int width = 0;
225- char c;
226- while ((c = *text++) != 0)
227- width += Widths[c];
228- return width;
228+ int w = 0, h;
229+ if ( Info ) {
230+ TTF_SizeUTF8(static_cast<ttf_font_info*>(Info)->font,text,&w, &h);
231+ }
232+ return w;
229233 }
230234
231235 #endif
@@ -237,236 +241,15 @@
237241 {
238242 // Don't delete these if there is no valid texture;
239243 // that indicates that there are no valid texture and display-list ID's.
240- if (!IsStarting && !OGL_Texture)
244+ if (!IsStarting)
241245 {
242246 glDeleteTextures(1,&TxtrID);
243- glDeleteLists(DispList,256);
244247 }
245-
246- // Invalidates whatever texture had been present
247- if (OGL_Texture)
248- {
249- delete[]OGL_Texture;
250- OGL_Texture = NULL;
251- }
252-
253- // Put some padding around each glyph so as to avoid clipping it
254- const int Pad = 1;
255- int ascent_p = Ascent + Pad, descent_p = Descent + Pad;
256- int widths_p[256];
257- for (int i=0; i<256; i++) {
258- widths_p[i] = Widths[i] + 2*Pad;
259- }
260- // Now for the totals and dimensions
261- int TotalWidth = 0;
262- for (int k=0; k<256; k++)
263- TotalWidth += widths_p[k];
264-
265- // For an empty font, clear out
266- if (TotalWidth <= 0) return;
267-
268- int GlyphHeight = ascent_p + descent_p;
269-
270- int EstDim = int(sqrt(static_cast<float>(TotalWidth*GlyphHeight)) + 0.5);
271- TxtrWidth = MAX(128, NextPowerOfTwo(EstDim));
272-
273- // Find the character starting points and counts
274- unsigned char CharStarts[256], CharCounts[256];
275- int LastLine = 0;
276- CharStarts[LastLine] = 0;
277- CharCounts[LastLine] = 0;
278- short Pos = 0;
279- for (int k=0; k<256; k++)
280- {
281- // Over the edge? If so, then start a new line
282- short NewPos = Pos + widths_p[k];
283- if (NewPos > TxtrWidth)
284- {
285- LastLine++;
286- CharStarts[LastLine] = k;
287- Pos = widths_p[k];
288- CharCounts[LastLine] = 1;
289- } else {
290- Pos = NewPos;
291- CharCounts[LastLine]++;
292- }
293- }
294- TxtrHeight = MAX(128, NextPowerOfTwo(GlyphHeight*(LastLine+1)));
295-
296-#if defined(mac)
297-
298- // MacOS-specific: render the font glyphs onto a GWorld,
299- // and use it as the source of the font texture.
300- Rect ImgRect;
301- SetRect(&ImgRect,0,0,TxtrWidth,TxtrHeight);
302- GWorldPtr FTGW;
303- OSErr Err = NewGWorld(&FTGW,32,&ImgRect,0,0,0);
304- if (Err != noErr) return;
305- PixMapHandle Pxls = GetGWorldPixMap(FTGW);
306- LockPixels(Pxls);
307-
308- CGrafPtr OrigPort;
309- GDHandle OrigDevice;
310- GetGWorld(&OrigPort,&OrigDevice);
311- SetGWorld(FTGW,0);
312-
313- BackColor(blackColor);
314- ForeColor(whiteColor);
315- EraseRect(&ImgRect);
316-
317- Use(); // The GWorld needs its font set for it
318-
319- for (int k=0; k<=LastLine; k++)
320- {
321- char Which = CharStarts[k];
322- int VPos = k*GlyphHeight + ascent_p;
323- int HPos = Pad;
324- for (int m=0; m<CharCounts[k]; m++)
325- {
326- MoveTo(HPos,VPos);
327- DrawChar(Which);
328- HPos += widths_p[(unsigned char) (Which++)];
329- }
330- }
331-
332-#elif defined(SDL)
333- // Render the font glyphs into the SDL surface
334- SDL_Surface *FontSurface = SDL_CreateRGBSurface(SDL_SWSURFACE, TxtrWidth, TxtrHeight, 32, 0xff0000, 0x00ff00, 0x0000ff, 0);
335- if (FontSurface == NULL)
336- return;
337-
338- // Set background to black
339- SDL_FillRect(FontSurface, NULL, SDL_MapRGB(FontSurface->format, 0, 0, 0));
340- Uint32 White = SDL_MapRGB(FontSurface->format, 0xFF, 0xFF, 0xFF);
341-
342- // Copy to surface
343- for (int k = 0; k <= LastLine; k++)
344- {
345- char Which = CharStarts[k];
346- int VPos = (k * GlyphHeight) + ascent_p;
347- int HPos = Pad;
348- for (int m = 0; m < CharCounts[k]; m++)
349- {
350-
351- ::draw_text(FontSurface, &Which, 1, HPos, VPos, White, Info, Style);
352- HPos += widths_p[(unsigned char) (Which++)];
353- }
354- }
355-#endif
356-
357- // Non-MacOS-specific: allocate the texture buffer
358- // Its format is LA 88, where L is the luminosity and A is the alpha channel
359- // The font value will go into A.
360- OGL_Texture = new uint8[2*GetTxtrSize()];
361-
362-#if defined(mac)
363- // Now copy from the GWorld into the OpenGL texture
364- uint8 *PixBase = (byte *)GetPixBaseAddr(Pxls);
365- int Stride = int((**Pxls).rowBytes & 0x7fff);
366-
367- for (int k=0; k<TxtrHeight; k++)
368- {
369- uint8 *SrcPxl = PixBase + k*Stride + 1; // Use red channel
370- uint8 *DstPxl = OGL_Texture + 2*k*TxtrWidth;
371- for (int m=0; m<TxtrWidth; m++)
372- {
373- *(DstPxl++) = 0xff; // Base color: white (will be modified with glColorxxx())
374- *(DstPxl++) = *SrcPxl;
375- SrcPxl += 4;
376- }
377- }
378-
379- UnlockPixels(Pxls);
380- SetGWorld(OrigPort,OrigDevice);
381- DisposeGWorld(FTGW);
382-
383-#elif defined(SDL)
384- // Copy the SDL surface into the OpenGL texture
385- uint8 *PixBase = (uint8 *)FontSurface->pixels;
386- int Stride = FontSurface->pitch;
387-
388- for (int k=0; k<TxtrHeight; k++)
389- {
390- uint8 *SrcPxl = PixBase + k*Stride + 1; // Use one of the middle channels (red or green or blue)
391- uint8 *DstPxl = OGL_Texture + 2*k*TxtrWidth;
392- for (int m=0; m<TxtrWidth; m++)
393- {
394- *(DstPxl++) = 0xff; // Base color: white (will be modified with glColorxxx())
395- *(DstPxl++) = *SrcPxl;
396- SrcPxl += 4;
397- }
398- }
399-
400- // Clean up
401- SDL_FreeSurface(FontSurface);
402-#endif
403-
404- // OpenGL stuff starts here
405- // Load texture
406- glGenTextures(1,&TxtrID);
407- glBindTexture(GL_TEXTURE_2D,TxtrID);
408-
409- glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
410- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
411- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
412- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
413- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
414- glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, TxtrWidth, TxtrHeight,
415- 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, OGL_Texture);
416-
417- // Allocate and create display lists of rendering commands
418- DispList = glGenLists(256);
419- GLfloat TWidNorm = GLfloat(1)/TxtrWidth;
420- GLfloat THtNorm = GLfloat(1)/TxtrHeight;
421- for (int k=0; k<=LastLine; k++)
422- {
423- unsigned char Which = CharStarts[k];
424- GLfloat Top = k*(THtNorm*GlyphHeight);
425- GLfloat Bottom = (k+1)*(THtNorm*GlyphHeight);
426- int Pos = 0;
427- for (int m=0; m<CharCounts[k]; m++)
428- {
429- short Width = widths_p[Which];
430- int NewPos = Pos + Width;
431- GLfloat Left = TWidNorm*Pos;
432- GLfloat Right = TWidNorm*NewPos;
433-
434- glNewList(DispList + Which, GL_COMPILE);
435-
436- // Move to the current glyph's (padded) position
437- glTranslatef(-Pad,0,0);
438-
439- // Draw the glyph rectangle
440- // Due to a bug in MacOS X Classic OpenGL, glVertex2s() was changed to glVertex2f()
441- glBegin(GL_POLYGON);
442-
443- glTexCoord2f(Left,Top);
444- glVertex2d(0,-ascent_p);
445-
446- glTexCoord2f(Right,Top);
447- glVertex2d(Width,-ascent_p);
448-
449- glTexCoord2f(Right,Bottom);
450- glVertex2d(Width,descent_p);
451-
452- glTexCoord2f(Left,Bottom);
453- glVertex2d(0,descent_p);
454-
455- glEnd();
456-
457- // Move to the next glyph's position
458- glTranslated(Width-Pad,0,0);
459-
460- glEndList();
461-
462- // For next one
463- Pos = NewPos;
464- Which++;
465- }
466- }
248+ glGenTextures(1,&TxtrID);
467249 }
250+#include <boost/tr1/unordered_map.hpp>
251+boost::unordered_map<std::string, tex_cache> FontSpecifier::texTable;
468252
469-
470253 // Renders a C-style string in OpenGL.
471254 // assumes screen coordinates and that the left baseline point is at (0,0).
472255 // Alters the modelview matrix so that the next characters will be drawn at the proper place.
@@ -473,9 +256,39 @@
473256 // One can surround it with glPushMatrix() and glPopMatrix() to remember the original.
474257 void FontSpecifier::OGL_Render(const char *Text)
475258 {
476- // Bug out if no texture to render
477- if (!OGL_Texture) return;
478-
259+ SDL_Surface *FontSurface;
260+ const int Pad = 0;
261+ int ascent_p = Ascent + Pad, descent_p = Descent + Pad;
262+ int GlyphHeight = ascent_p + descent_p;
263+ if( texTable.find(Text) != texTable.end() ) {
264+ // Cache Hit
265+ FontSurface = texTable[Text].sur;
266+ TxtrWidth = texTable[Text].width;
267+
268+ } else {
269+ if( texTable.size() == 256 ) {
270+ for(boost::unordered_map<std::string, tex_cache>::iterator i = texTable.begin(); i != texTable.end(); i++) {
271+ SDL_Surface* removed = i->second.sur;
272+ SDL_FreeSurface(removed);
273+ texTable.erase(i->first);
274+ }
275+ }
276+ size_t Len = MIN(strlen(Text),255);
277+ // Put some padding around each glyph so as to avoid clipping it
278+
279+ TxtrWidth = NextPowerOfTwo(TextWidth(Text));
280+ TxtrHeight = NextPowerOfTwo(GlyphHeight);
281+
282+ // Render the font glyphs into the SDL surface
283+ FontSurface = SDL_CreateRGBSurface(0, TxtrWidth, TxtrHeight, 32, 0xff0000, 0x00ff00, 0x0000ff, 0xff000000);
284+ if (FontSurface == NULL)
285+ return;
286+ // Set background to black
287+ Uint32 White = SDL_MapRGB(FontSurface->format, 0xFF, 0xFF, 0xFF);
288+ ::draw_text(FontSurface, Text, strlen(Text), 0, ascent_p, White, Info, Style);
289+ texTable[Text].sur = FontSurface;
290+ texTable[Text].width = TxtrWidth;
291+ }
479292 glPushAttrib(GL_ALL_ATTRIB_BITS);
480293
481294 glEnable(GL_TEXTURE_2D);
@@ -482,265 +295,26 @@
482295 glDisable(GL_CULL_FACE);
483296 glEnable(GL_BLEND);
484297 glDisable(GL_ALPHA_TEST);
485-
486- glBindTexture(GL_TEXTURE_2D,TxtrID);
487298
488- size_t Len = MIN(strlen(Text),255);
489- for (size_t k=0; k<Len; k++)
490- {
491- unsigned char c = Text[k];
492- glCallList(DispList+c);
493- }
299+ // OpenGL stuff starts here
300+ // Load texture
494301
495- glPopAttrib();
496-}
497-
498-
499-// Renders text a la _draw_screen_text() (see screen_drawing.h), with
500-// alignment and wrapping. Modelview matrix is unaffected.
501-void FontSpecifier::OGL_DrawText(const char *text, const screen_rectangle &r, short flags)
502-{
503- // Copy the text to draw
504- char text_to_draw[256];
505- strncpy(text_to_draw, text, 256);
506- text_to_draw[255] = 0;
507-
508- // Check for wrapping, and if it occurs, be recursive
509- if (flags & _wrap_text) {
510- int last_non_printing_character = 0, text_width = 0;
511- unsigned count = 0;
512- while (count < strlen(text_to_draw) && text_width < RECTANGLE_WIDTH(&r)) {
513- text_width += CharWidth(text_to_draw[count]);
514- if (text_to_draw[count] == ' ')
515- last_non_printing_character = count;
516- count++;
517- }
518-
519- if( count != strlen(text_to_draw)) {
520- char remaining_text_to_draw[256];
521-
522- // If we ever have to wrap text, we can't also center vertically. Sorry.
523- flags &= ~_center_vertical;
524- flags |= _top_justified;
525-
526- // Pass the rest of it back in, recursively, on the next line
527- memcpy(remaining_text_to_draw, text_to_draw + last_non_printing_character + 1, strlen(text_to_draw + last_non_printing_character + 1) + 1);
302+ glBindTexture(GL_TEXTURE_2D,TxtrID);
528303
529- screen_rectangle new_destination = r;
530- new_destination.top += LineSpacing;
531- OGL_DrawText(remaining_text_to_draw, new_destination, flags);
532-
533- // Now truncate our text to draw
534- text_to_draw[last_non_printing_character] = 0;
535- }
536- }
537-
538- // Truncate text if necessary
539- int t_width = TextWidth(text_to_draw);
540- if (t_width > RECTANGLE_WIDTH(&r)) {
541- int width = 0;
542- int num = 0;
543- char c, *p = text_to_draw;
544- while ((c = *p++) != 0) {
545- width += CharWidth(c);
546- if (width > RECTANGLE_WIDTH(&r))
547- break;
548- num++;
549- }
550- text_to_draw[num] = 0;
551- t_width = TextWidth(text_to_draw);
552- }
553-
554-
555- // Horizontal positioning
556- int x, y;
557- if (flags & _center_horizontal)
558- x = r.left + ((RECTANGLE_WIDTH(&r) - t_width) / 2);
559- else if (flags & _right_justified)
560- x = r.right - t_width;
561- else
562- x = r.left;
563-
564- // Vertical positioning
565- if (flags & _center_vertical) {
566- if (Height > RECTANGLE_HEIGHT(&r))
567- y = r.top;
568- else {
569- y = r.bottom;
570- int offset = RECTANGLE_HEIGHT(&r) - Height;
571- y -= (offset / 2) + (offset & 1) + 1;
572- }
573- } else if (flags & _top_justified) {
574- if (Height > RECTANGLE_HEIGHT(&r))
575- y = r.bottom;
576- else
577- y = r.top + Height;
578- } else
579- y = r.bottom;
580-
581- // Draw text
582- glMatrixMode(GL_MODELVIEW);
583- glPushMatrix();
584- glLoadIdentity();
585- glTranslated(x, y, 0);
586- OGL_Render(text_to_draw);
587- glPopMatrix();
588-}
589-#endif // def HAVE_OPENGL
590-#else
591-
592-uint16 sjis2jis(char** data);
593-
594-// Initializer: call before using because of difficulties in setting up a proper constructor:
595-
596-void FontSpecifier::Init()
597-{
598-#ifdef SDL
599- TextSpec t;
600- Info = load_font(t);
601-#endif
602- Update();
603-#ifdef HAVE_OPENGL
604- // OGL_Texture = NULL;
605-#endif
606-}
607-
608-
609-// MacOS- and SDL-specific stuff
610-
611-
612-void FontSpecifier::Update()
613-{
614- // Clear away
615- if (Info) {
616- unload_font(Info);
617- Info = NULL;
618- }
619-
620- // Simply implements format "#<value>"; may want to generalize this
621- short ID;
622- if (File[0] != '#') {
623- fprintf(stderr, "WARNING: Font file names not (yet) supported\n");
624- return;
625- }
626- sscanf(File+1, "%hd", &ID);
627-
628- // Actual loading
629- TextSpec Spec;
630- Spec.font = ID;
631- Spec.size = Size;
632- Spec.style = Style;
633- Info = load_font(Spec);
634-
635- if (Info) {
636- Ascent = Info->ascent;
637- Descent = Info->descent;
638- Leading = Info->leading;
639- Height = Ascent + Leading;
640- LineSpacing = Ascent + Descent + Leading;
641- } else
642- Ascent = Descent = Leading = Height = LineSpacing = 0;
643-}
644-
645-extern int8 char_width(uint8 c, const sdl_font_info *font, uint16 style);
646-// static inline bool IsValidChar(uint16 t) { return t<128 || IsTwoByte(t>>8); }
647-int FontSpecifier::TextWidth(const char *text)
648-{
649- int width = 0;
650- while (*text) {
651- width += Info->width_table[sjis2jis((char**)&text)] + 1;
652- }
653- return width;
654-}
655-
656-
657-
658-#ifdef HAVE_OPENGL
659-// Reset the OpenGL fonts; its arg indicates whether this is for starting an OpenGL session
660-// (this is to avoid texture and display-list memory leaks and other such things)
661-void FontSpecifier::OGL_Reset(bool IsStarting)
662-{
663- // Don't delete these if there is no valid texture;
664- // that indicates that there are no valid texture and display-list ID's.
665- if (!IsStarting && !OGL_Texture)
666- {
667- glDeleteTextures(1,&TxtrID);
668- }
669-
670- // Invalidates whatever texture had been present
671- if (OGL_Texture)
672- {
673- delete[]OGL_Texture;
674- OGL_Texture = NULL;
675-
676- }
677- glGenTextures(1,&TxtrID);
678-}
679-
680-void FontSpecifier::DrawGryphGLJ(const char* txt) {
681- // Put some padding around each glyph so as to avoid clipping it
682- const int Pad = 0;
683- int ascent_p = Ascent + Pad, descent_p = Descent + Pad;
684- int GlyphHeight = ascent_p + descent_p;
685-
686- TxtrWidth = NextPowerOfTwo(TextWidth(txt));
687- TxtrHeight = NextPowerOfTwo(GlyphHeight);
688-
689- // Render the font glyphs into the SDL surface
690- SDL_Surface *FontSurface = SDL_CreateRGBSurface(SDL_SWSURFACE, TxtrWidth, TxtrHeight, 32, 0xff0000, 0x00ff00, 0x0000ff, 0);
691- if (FontSurface == NULL)
692- return;
693- // Set background to black
694- SDL_FillRect(FontSurface, NULL, SDL_MapRGB(FontSurface->format, 0, 0, 0));
695- Uint32 White = SDL_MapRGB(FontSurface->format, 0xFF, 0xFF, 0xFF);
696- int VPos = ascent_p;
697- int HPos = Pad;
698- ::draw_text(FontSurface, txt, strlen(txt), 0, VPos, White, Info, Style);
699- // Non-MacOS-specific: allocate the texture buffer
700- // Its format is LA 88, where L is the luminosity and A is the alpha channel
701- // The font value will go into A.
702- delete [] OGL_Texture;
703- OGL_Texture = new uint8[2*GetTxtrSize()];
704-
705- // Copy the SDL surface into the OpenGL texture
706- uint8 *PixBase = (uint8 *)FontSurface->pixels;
707- int Stride = FontSurface->pitch;
708-
709- for (int k=0; k<TxtrHeight; k++)
710- {
711- uint8 *SrcPxl = PixBase + k*Stride + 1; // Use one of the middle channels (red or green or blue)
712- int DstPxl = 2*k*TxtrWidth;
713- for (int m=0; m<TxtrWidth; m++)
714- {
715- OGL_Texture[(DstPxl++)] = 0xff; // Base color: white (will be modified with glColorxxx())
716- OGL_Texture[(DstPxl++)] = *SrcPxl;
717- SrcPxl += 4;
718- }
719- }
720-
721- // Clean up
722- SDL_FreeSurface(FontSurface);
723-
724- // OpenGL stuff starts here
725- // Load texture
726-
727- glBindTexture(GL_TEXTURE_2D,TxtrID);
728-
729- glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
304+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
730305 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
731306 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
732- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
733- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
307+
734308 glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, TxtrWidth, TxtrHeight,
735- 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, &OGL_Texture[0]);
736-
737- // Allocate and create display lists of rendering commands
738- GLfloat TWidNorm = GLfloat(1)/TxtrWidth;
739- GLfloat THtNorm = GLfloat(1)/TxtrHeight;
309+ 0, GL_BGRA, GL_UNSIGNED_BYTE, (uint8 *)FontSurface->pixels);
310+
311+ // Allocate and create display lists of rendering commands
312+ GLfloat TWidNorm = GLfloat(1)/TxtrWidth;
313+ GLfloat THtNorm = GLfloat(1)/TxtrHeight;
740314 GLfloat Top = 0;
741315 GLfloat Bottom = (THtNorm*GlyphHeight);
742316 int Pos = 0;
743- short Width = TextWidth(txt);
317+ short Width = TxtrWidth;
744318 int NewPos = Width;
745319 GLfloat Left = 0;
746320 GLfloat Right = TWidNorm*NewPos;
@@ -769,30 +343,6 @@
769343 // Move to the next glyph's position
770344 glTranslated(Width-Pad,0,0);
771345
772-
773-
774-}
775-// Renders a C-style string in OpenGL.
776-// assumes screen coordinates and that the left baseline point is at (0,0).
777-// Alters the modelview matrix so that the next characters will be drawn at the proper place.
778-// One can surround it with glPushMatrix() and glPopMatrix() to remember the original.
779-void FontSpecifier::OGL_Render(const char *Text)
780-{
781- // Bug out if no texture to render
782- // if (!OGL_Texture) return;
783-
784- glPushAttrib(GL_ALL_ATTRIB_BITS);
785-
786- glEnable(GL_TEXTURE_2D);
787- glDisable(GL_CULL_FACE);
788- glEnable(GL_BLEND);
789- glDisable(GL_ALPHA_TEST);
790-
791- glBindTexture(GL_TEXTURE_2D,TxtrID);
792-
793- size_t Len = MIN(strlen(Text),255);
794- DrawGryphGLJ(Text);
795-
796346 glPopAttrib();
797347 }
798348
@@ -805,54 +355,8 @@
805355 char text_to_draw[256];
806356 strncpy(text_to_draw, text, 256);
807357 text_to_draw[255] = 0;
808-
809- // Check for wrapping, and if it occurs, be recursive
810- if (flags & _wrap_text) {
811- int last_non_printing_character = 0, text_width = 0;
812- unsigned count = 0;
813- while (count < strlen(text_to_draw) && text_width < RECTANGLE_WIDTH(&r)) {
814- text_width += CharWidth(text_to_draw[count]);
815- if (text_to_draw[count] == ' ')
816- last_non_printing_character = count;
817- count++;
818- }
819-
820- if( count != strlen(text_to_draw)) {
821- char remaining_text_to_draw[256];
822-
823- // If we ever have to wrap text, we can't also center vertically. Sorry.
824- flags &= ~_center_vertical;
825- flags |= _top_justified;
826-
827- // Pass the rest of it back in, recursively, on the next line
828- memcpy(remaining_text_to_draw, text_to_draw + last_non_printing_character + 1, strlen(text_to_draw + last_non_printing_character + 1) + 1);
829-
830- screen_rectangle new_destination = r;
831- new_destination.top += LineSpacing;
832- OGL_DrawText(remaining_text_to_draw, new_destination, flags);
833-
834- // Now truncate our text to draw
835- text_to_draw[last_non_printing_character] = 0;
836- }
837- }
838-
839- // Truncate text if necessary
358+
840359 int t_width = TextWidth(text_to_draw);
841- if (t_width > RECTANGLE_WIDTH(&r)) {
842- int width = 0;
843- int num = 0;
844- char c, *p = text_to_draw;
845- while ((c = *p++) != 0) {
846- width += CharWidth(c);
847- if (width > RECTANGLE_WIDTH(&r))
848- break;
849- num++;
850- }
851- text_to_draw[num] = 0;
852- t_width = TextWidth(text_to_draw);
853- }
854-
855-
856360 // Horizontal positioning
857361 int x, y;
858362 if (flags & _center_horizontal)
@@ -888,7 +392,7 @@
888392 glPopMatrix();
889393 }
890394 #endif // def HAVE_OPENGL
891-#endif
395+
892396
893397 // Given a pointer to somewhere in a name set, returns the pointer
894398 // to the start of the next name, or NULL if it did not find any
--- marathon/trunk/AlephOneJP/RenderOther/FontHandler.h (revision 487)
+++ marathon/trunk/AlephOneJP/RenderOther/FontHandler.h (revision 488)
@@ -52,9 +52,13 @@
5252 # include <GL/gl.h>
5353 # endif
5454 #endif
55-
56-
55+#include <boost/tr1/unordered_map.hpp>
5756 struct screen_rectangle;
57+struct tex_cache {
58+ SDL_Surface* sur;
59+ int width;
60+};
61+struct screen_rectangle;
5862
5963 class FontSpecifier
6064 {
@@ -72,6 +76,7 @@
7276 char NameSet[NameSetLen];
7377 short Size;
7478 short Style;
79+ short AdjustLineHeight;
7580
7681 // For SDL font support: a font filename
7782 char File[NameSetLen];
@@ -80,16 +85,13 @@
8085 short Height; // How tall is it?
8186 short LineSpacing; // From same positions in each line
8287 short Ascent, Descent, Leading;
83-#if 0
8488 short Widths[256];
85-#else
86- short Widths(uint16 t) const { return Info->width_table[t]; }
87-#endif
89+
8890 // MacOS- and SDL-specific stuff
8991 #if defined(mac)
9092 short ID;
9193 #elif defined(SDL)
92- sdl_font_info *Info;
94+ font_info *Info;
9395 #endif
9496
9597 // Initialize: call this before calling anything else;
@@ -110,11 +112,7 @@
110112 int TextWidth(const char *Text);
111113
112114 // Get width of one character
113-#if 0
114115 int CharWidth(char c) const { return Widths[static_cast<int>(c)]; }
115-#else
116- int CharWidth(uint16 c) const { return Widths(static_cast<const int>(c)); }
117-#endif
118116
119117 #ifdef HAVE_OPENGL
120118 // Reset the OpenGL fonts; its arg indicates whether this is for starting an OpenGL session
@@ -156,16 +154,12 @@
156154 #ifdef HAVE_OPENGL
157155 // Stuff for OpenGL font rendering: the font texture and a display list for font rendering;
158156 // if OGL_Texture is NULL, then there is no OpenGL font texture to render.
159- uint8 *OGL_Texture;
157+ static boost::unordered_map<std::string, tex_cache> texTable;
160158 short TxtrWidth, TxtrHeight;
161159 int GetTxtrSize() {return int(TxtrWidth)*int(TxtrHeight);}
162160 GLuint TxtrID;
163-#if 0
164161 uint32 DispList;
165-#else
166- void DrawGryphGLJ(const char*);
167162 #endif
168-#endif
169163 };
170164
171165
--- marathon/trunk/AlephOneJP/RenderOther/screen_drawing.cpp (revision 487)
+++ marathon/trunk/AlephOneJP/RenderOther/screen_drawing.cpp (revision 488)
@@ -56,6 +56,11 @@
5656 #include "sdl_fonts.h"
5757 #include <string.h>
5858
59+#ifdef HAVE_SDL_TTF
60+#include <SDL_ttf.h>
61+#include "preferences.h"
62+#endif
63+
5964 #define clutSCREEN_COLORS 130
6065 #define finfFONTS 128
6166
@@ -106,16 +111,30 @@
106111 // static struct interface_font_info interface_fonts =
107112 static FontSpecifier InterfaceFonts[NUMBER_OF_INTERFACE_FONTS] =
108113 {
109- {"Monaco", 9, styleBold, "#4"},
110- {"Monaco", 9, styleBold, "#4"},
111- {"Monaco", 9, styleBold, "#4"},
112- {"Monaco", 9, styleNormal,"#4"},
113- {"Courier", 12, styleNormal,"#22"},
114- {"Courier", 14, styleBold, "#22"},
115- {"Monaco", 9, styleNormal,"#4"}
114+ {"Monaco", 9, styleBold, 0, "#4"},
115+ {"Monaco", 9, styleBold, 0, "#4"},
116+ {"Monaco", 9, styleBold, 0, "#4"},
117+ {"Monaco", 9, styleNormal,0, "#4"},
118+ {"Courier", 12, styleNormal,0, "#22"},
119+ {"Courier", 14, styleBold, 0, "#22"},
120+ {"Monaco", 9, styleNormal,0, "#4"}
116121 };
117122
123+#ifdef HAVE_SDL_TTF
124+void fix_missing_interface_fonts()
125+{
126+ for (int i = 0; i < NUMBER_OF_INTERFACE_FONTS; i++)
127+ {
128+ strcpy(InterfaceFonts[i].File, "mono");
129+ }
118130
131+ InterfaceFonts[0].AdjustLineHeight = -1;
132+ InterfaceFonts[3].AdjustLineHeight = -1;
133+ InterfaceFonts[4].Size = 11;
134+ InterfaceFonts[4].AdjustLineHeight = -2;
135+}
136+#endif
137+
119138 // LP change: hardcoding the interface and player colors,
120139 // so as to banish the 'clut' resources
121140 const int NumInterfaceColors = 26;
@@ -207,7 +226,7 @@
207226
208227 // Gets interface font and style;
209228 // used in computer_interface.cpp
210-extern sdl_font_info *GetInterfaceFont(short font_index);
229+extern font_info *GetInterfaceFont(short font_index);
211230 extern uint16 GetInterfaceStyle(short font_index);
212231
213232 bool draw_clip_rect_active = false; // Flag: clipping rect active
@@ -344,55 +363,7 @@
344363 /*
345364 * Draw text
346365 */
347-#if 0
348-// Calculate width of single character
349-int8 char_width(uint8 c, const sdl_font_info *font, uint16 style)
350-{
351- if (font == NULL || c < font->first_character || c > font->last_character)
352- return 0;
353- int8 width = font->width_table[(c - font->first_character) * 2 + 1] + ((style & styleBold) ? 1 : 0);
354- if (width == -1) // non-existant character
355- width = font->width_table[(font->last_character - font->first_character + 1) * 2 + 1] + ((style & styleBold) ? 1 : 0);
356- return width;
357-}
358366
359-// Calculate width of text string
360-uint16 text_width(const char *text, const sdl_font_info *font, uint16 style)
361-{
362- int width = 0;
363- char c;
364- while ((c = *text++) != 0)
365- width += char_width(c, font, style);
366- assert(0 <= width);
367- assert(width == static_cast<int>(static_cast<uint16>(width)));
368- return width;
369-}
370-
371-uint16 text_width(const char *text, size_t length, const sdl_font_info *font, uint16 style)
372-{
373- int width = 0;
374- while (length--)
375- width += char_width(*text++, font, style);
376- assert(0 <= width);
377- assert(width == static_cast<int>(static_cast<uint16>(width)));
378- return width;
379-}
380-
381-// Determine how many characters of a string fit into a given width
382-int trunc_text(const char *text, int max_width, const sdl_font_info *font, uint16 style)
383-{
384- int width = 0;
385- int num = 0;
386- char c;
387- while ((c = *text++) != 0) {
388- width += char_width(c, font, style);
389- if (width > max_width)
390- break;
391- num++;
392- }
393- return num;
394-}
395-
396367 // Draw single glyph at given position in frame buffer, return glyph width
397368 template <class T>
398369 inline static int draw_glyph(uint8 c, int x, int y, T *p, int pitch, int clip_left, int clip_top, int clip_right, int clip_bottom, uint32 pixel, const sdl_font_info *font, bool oblique)
@@ -473,19 +444,7 @@
473444
474445 int width;
475446
476- if(style & styleOutline) {
477- draw_glyph(c, x-1, y-1, p, pitch, clip_left, clip_top, clip_right, clip_bottom, pixel, font, oblique);
478- draw_glyph(c, x , y-1, p, pitch, clip_left, clip_top, clip_right, clip_bottom, pixel, font, oblique);
479- draw_glyph(c, x+1, y-1, p, pitch, clip_left, clip_top, clip_right, clip_bottom, pixel, font, oblique);
480- draw_glyph(c, x-1, y , p, pitch, clip_left, clip_top, clip_right, clip_bottom, pixel, font, oblique);
481- draw_glyph(c, x+1, y , p, pitch, clip_left, clip_top, clip_right, clip_bottom, pixel, font, oblique);
482- draw_glyph(c, x-1, y+1, p, pitch, clip_left, clip_top, clip_right, clip_bottom, pixel, font, oblique);
483- draw_glyph(c, x , y+1, p, pitch, clip_left, clip_top, clip_right, clip_bottom, pixel, font, oblique);
484- width =
485- draw_glyph(c, x+1, y+1, p, pitch, clip_left, clip_top, clip_right, clip_bottom, pixel, font, oblique);
486- }
487- else
488- width = draw_glyph(c, x, y, p, pitch, clip_left, clip_top, clip_right, clip_bottom, pixel, font, oblique);
447+ width = draw_glyph(c, x, y, p, pitch, clip_left, clip_top, clip_right, clip_bottom, pixel, font, oblique);
489448 if (style & styleBold) {
490449 draw_glyph(c, x + 1, y, p, pitch, clip_left, clip_top, clip_right, clip_bottom, pixel, font, oblique);
491450 width++;
@@ -502,11 +461,8 @@
502461 }
503462
504463 // Draw text at given coordinates, return total width
505-int draw_text(SDL_Surface *s, const char *text, size_t length, int x, int y, uint32 pixel, const sdl_font_info *font, uint16 style)
464+int sdl_font_info::_draw_text(SDL_Surface *s, const char *text, size_t length, int x, int y, uint32 pixel, uint16 style, bool) const
506465 {
507- if (font == NULL)
508- return 0;
509-
510466 // Get clipping rectangle
511467 int clip_top, clip_bottom, clip_left, clip_right;
512468 if (draw_clip_rect_active) {
@@ -526,13 +482,13 @@
526482 int width = 0;
527483 switch (s->format->BytesPerPixel) {
528484 case 1:
529- width = draw_text((const uint8 *)text, length, x, y, (pixel8 *)s->pixels, s->pitch, clip_left, clip_top, clip_right, clip_bottom, pixel, font, style);
485+ width = ::draw_text((const uint8 *)text, length, x, y, (pixel8 *)s->pixels, s->pitch, clip_left, clip_top, clip_right, clip_bottom, pixel, this, style);
530486 break;
531487 case 2:
532- width = draw_text((const uint8 *)text, length, x, y, (pixel16 *)s->pixels, s->pitch, clip_left, clip_top, clip_right, clip_bottom, pixel, font, style);
488+ width = ::draw_text((const uint8 *)text, length, x, y, (pixel16 *)s->pixels, s->pitch, clip_left, clip_top, clip_right, clip_bottom, pixel, this, style);
533489 break;
534490 case 4:
535- width = draw_text((const uint8 *)text, length, x, y, (pixel32 *)s->pixels, s->pitch, clip_left, clip_top, clip_right, clip_bottom, pixel, font, style);
491+ width = ::draw_text((const uint8 *)text, length, x, y, (pixel32 *)s->pixels, s->pitch, clip_left, clip_top, clip_right, clip_bottom, pixel, this, style);
536492 break;
537493 }
538494 if (SDL_MUSTLOCK (s)) {
@@ -539,330 +495,110 @@
539495 SDL_UnlockSurface(s);
540496 }
541497 if (s == SDL_GetVideoSurface())
542- SDL_UpdateRect(s, x, y - font->ascent, text_width(text, font, style), font->rect_height);
498+ SDL_UpdateRect(s, x, y - ascent, text_width(text, style, false), rect_height);
543499 return width;
544500 }
545501
546-static void draw_text(const char *text, int x, int y, uint32 pixel, const sdl_font_info *font, uint16 style)
502+#ifdef HAVE_SDL_TTF
503+#include <converter.h>
504+int ttf_font_info::_draw_text(SDL_Surface *s, const char *text, size_t length, int x, int y, uint32 pixel, uint16 style, bool utf8) const
547505 {
548- draw_text(draw_surface, text, strlen(text), x, y, pixel, font, style);
549-}
550-
551-void _draw_screen_text(const char *text, screen_rectangle *destination, short flags, short font_id, short text_color)
552-{
553- int x, y;
554-
555- // Find font information
556- assert(font_id >= 0 && font_id < NUMBER_OF_INTERFACE_FONTS);
557- uint16 style = InterfaceFonts[font_id].Style;
558- const sdl_font_info *font = InterfaceFonts[font_id].Info;
559- if (font == NULL)
560- return;
561-
562- // Get color
563- SDL_Color color;
564- _get_interface_color(text_color, &color);
565-
566- // Copy the text to draw
567- char text_to_draw[256];
568- strncpy(text_to_draw, text, 256);
569- text_to_draw[255] = 0;
570-
571- // Check for wrapping, and if it occurs, be recursive
572- if (flags & _wrap_text) {
573- int last_non_printing_character = 0, text_width = 0;
574- unsigned count = 0;
575- while (count < strlen(text_to_draw) && text_width < RECTANGLE_WIDTH(destination)) {
576- text_width += char_width(text_to_draw[count], font, style);
577- if (text_to_draw[count] == ' ')
578- last_non_printing_character = count;
579- count++;
580- }
581-
582- if( count != strlen(text_to_draw)) {
583- char remaining_text_to_draw[256];
584- screen_rectangle new_destination;
585-
586- // If we ever have to wrap text, we can't also center vertically. Sorry.
587- flags &= ~_center_vertical;
588- flags |= _top_justified;
589-
590- // Pass the rest of it back in, recursively, on the next line
591- memcpy(remaining_text_to_draw, text_to_draw + last_non_printing_character + 1, strlen(text_to_draw + last_non_printing_character + 1) + 1);
592-
593- new_destination = *destination;
594- new_destination.top += InterfaceFonts[font_id].LineSpacing;
595- _draw_screen_text(remaining_text_to_draw, &new_destination, flags, font_id, text_color);
596-
597- // Now truncate our text to draw
598- text_to_draw[last_non_printing_character] = 0;
599- }
506+ int clip_top, clip_bottom, clip_left, clip_right;
507+ if (draw_clip_rect_active) {
508+ clip_top = draw_clip_rect.top;
509+ clip_bottom = draw_clip_rect.bottom;
510+ clip_left = draw_clip_rect.left;
511+ clip_right = draw_clip_rect.right;
512+ } else {
513+ clip_top = clip_left = 0;
514+ clip_right = s->w;
515+ clip_bottom = s->h;
600516 }
601-
602- // Truncate text if necessary
603- int t_width = text_width(text_to_draw, font, style);
604- if (t_width > RECTANGLE_WIDTH(destination)) {
605- text_to_draw[trunc_text(text_to_draw, RECTANGLE_WIDTH(destination), font, style)] = 0;
606- t_width = text_width(text_to_draw, font, style);
607- }
608-
609- // Horizontal positioning
610- if (flags & _center_horizontal)
611- x = destination->left + (((destination->right - destination->left) - t_width) / 2);
612- else if (flags & _right_justified)
613- x = destination->right - t_width;
614- else
615- x = destination->left;
616-
617- // Vertical positioning
618- int t_height = InterfaceFonts[font_id].Height;
619- if (flags & _center_vertical) {
620- if (t_height > RECTANGLE_HEIGHT(destination))
621- y = destination->top;
622- else {
623- y = destination->bottom;
624- int offset = RECTANGLE_HEIGHT(destination) - t_height;
625- y -= (offset / 2) + (offset & 1) + 1;
626- }
627- } else if (flags & _top_justified) {
628- if (t_height > RECTANGLE_HEIGHT(destination))
629- y = destination->bottom;
630- else
631- y = destination->top + t_height;
632- } else
633- y = destination->bottom;
634-
635- // Now draw it
636- draw_text(text_to_draw, x, y, SDL_MapRGB(draw_surface->format, color.r, color.g, color.b), font, style);
637-}
638-#else
639-uint16 sjis2jis(char** data);
640-int8 char_width(uint8 c, const sdl_font_info *font, uint16 style)
641-{
642- static uint8 continued;
643- if (font == NULL || c < 31)
644- return 0;
645- int8 width;
646- if( c < 128 ) { width = font->width_table[c]; }
647- else if( continued ) { continued = 0; return 0; }
648- else { width = font->width_table[48]*2; continued = 1;}
649- if (width == 0) // non-existant character
650- width = font->width_table[48];
651- return width + ((style & styleBold) ? 2 : 0);
652-}
653-
654-// Calculate width of text string
655-uint16 text_width(const char *text, const sdl_font_info *font, uint16 style)
656-{
657- int width = 0;
658- char c;
659- while ((c = *text++) != 0)
660- width += char_width(c, font, style);
661- assert(0 <= width);
662- assert(width == static_cast<int>(static_cast<uint16>(width)));
663- return width;
664-}
665-
666-uint16 text_width(const char *text, size_t length, const sdl_font_info *font, uint16 style)
667-{
668- int width = 0;
669- while (length--)
670- width += char_width(*text++, font, style);
671- assert(0 <= width);
672- assert(width == static_cast<int>(static_cast<uint16>(width)));
673- return width;
674-}
675-
676-// Determine how many characters of a string fit into a given width
677-int trunc_text(const char *text, int max_width, const sdl_font_info *font, uint16 style)
678-{
679- int width = 0;
680- int num = 0;
681- char c;
682- while ((c = *text++) != 0) {
683- width += char_width(c, font, style);
684- if (width > max_width)
685- break;
686- num++;
687- }
688- return num;
689-}
690-
691-// Draw single glyph at given position in frame buffer, return glyph width
692-template <class T>
693-static int draw_glyph(uint16 c, int x, int y, T *p, int pitch, int clip_left, int clip_top, int clip_right, int clip_bottom, uint32 pixel, const sdl_font_info *font, bool oblique)
694-{
695- if( c < 32 ) return 0;
696- // Calculate source and destination pointers (kerning, ascent etc.)
697- uint8 *src = font->pixmap + font->location_table[c];
698- int width = font->width_table[c];
699- int height = font->bytes_per_row;
700- int advance = font->width_table[c];
701- y -= font->ascent;
702- p += y * pitch / sizeof(T) + x;
703- if (oblique)
704- p += font->ascent / 2 - 1;
705- // Clip on top
706- if (y < clip_top) {
707- height -= clip_top - y;
708- if (height <= 0)
709- return advance;
710- p += (clip_top - y) * pitch / sizeof(T);
711- src += (clip_top - y) * font->bytes_per_row;
712- }
713517
714- // Clip on bottom
715- if (y + height - 1 > clip_bottom) {
716- height -= y + height - 1 - clip_bottom;
717- if (height <= 0)
718- return advance;
518+ SDL_Color c;
519+ SDL_GetRGB(pixel, s->format, &c.r, &c.g, &c.b);
520+ SDL_Surface *text_surface = 0;
521+ int st = 0;
522+ if( style & styleBold )
523+ st |= TTF_STYLE_BOLD;
524+ if( style & styleItalic ) {// TTF for small text unsppported instead color change
525+
526+ // st |= TTF_STYLE_ITALIC;
719527 }
720-
721- // Clip on left
722- if (x < clip_left) {
723- width -= (clip_left - x);
724- if (width <= 0)
725- return advance;
726- p += (clip_left - x);
727- src += (clip_left - x);
528+ if( style & styleUnderline )
529+ st |= TTF_STYLE_UNDERLINE;
530+ TTF_SetFontStyle(font,st);
531+ const char* utf8T = sjis2utf8(text,length);
532+ if( style & styleItalic ) {
533+ SDL_Color blk = { 0, 0, 0, 0};
534+ text_surface = TTF_RenderUTF8_Shaded(font, utf8T, blk, c);
535+ } else {
536+ text_surface = TTF_RenderUTF8_Solid(font, utf8T, c);
728537 }
538+ if (!text_surface) return 0;
729539
730- // Clip on right
731- if (x + width - 1 > clip_right) {
732- width -= x + width - 1 - clip_right;
733- if (width <= 0)
734- return advance;
735- }
540+ SDL_Rect dst_rect;
541+ dst_rect.x = x;
542+ dst_rect.y = y - TTF_FontAscent(font);
736543
737- // Blit glyph to screen
738- for (int iy=0; iy<height; iy++) {
739- for (int ix=0; ix<width; ix++) {
740- if (src[ix])
741- p[ix] = pixel;
544+ if (draw_clip_rect_active)
545+ {
546+ SDL_Rect src_rect;
547+ src_rect.x = 0;
548+ src_rect.y = 0;
549+
550+ if (clip_top > dst_rect.y)
551+ {
552+ src_rect.y += dst_rect.y - clip_top;
742553 }
743- if (oblique && (iy % 2) == 1)
744- p--;
745- src += font->bytes_per_row;
746- p += pitch / sizeof(T);
747- }
748-
749- return advance;
750-}
751-static inline bool IsTwoByte(unsigned char t ) { return (t>0x81 && t<0xa0) || (t>0xe0 && t<0xfd) ; }
752-
753-// Draw text at given position in frame buffer, return width
754-template <class T>
755-inline static int draw_text(const uint8 *text2, size_t length, int x, int y, T *p, int pitch, int clip_left, int clip_top, int clip_right, int clip_bottom, uint32 pixel, const sdl_font_info *font, uint16 style)
756-{
757- uint8* texts = (uint8*)calloc(1,length+2), *text = texts;
758- bool oblique = ((style & styleItalic) != 0);
759- strncpy((char*)text,(const char*)text2,length);
760- int total_width = 0;
761- uint16 c;
762- while (length-- ) {
763- int width;
764- if(IsTwoByte(*text)) {
765- if(length) {
766- length--;
767- }
554+
555+ if (clip_left > dst_rect.x)
556+ {
557+ src_rect.x += dst_rect.x - clip_left;
768558 }
769- c = sjis2jis((char**)&text);
770- if(style & styleOutline) {
771- draw_glyph(c, x-1, y-1, p, pitch, clip_left, clip_top, clip_right, clip_bottom, pixel, font, oblique);
772- draw_glyph(c, x , y-1, p, pitch, clip_left, clip_top, clip_right, clip_bottom, pixel, font, oblique);
773- draw_glyph(c, x+1, y-1, p, pitch, clip_left, clip_top, clip_right, clip_bottom, pixel, font, oblique);
774- draw_glyph(c, x-1, y , p, pitch, clip_left, clip_top, clip_right, clip_bottom, pixel, font, oblique);
775- draw_glyph(c, x+1, y , p, pitch, clip_left, clip_top, clip_right, clip_bottom, pixel, font, oblique);
776- draw_glyph(c, x-1, y+1, p, pitch, clip_left, clip_top, clip_right, clip_bottom, pixel, font, oblique);
777- draw_glyph(c, x , y+1, p, pitch, clip_left, clip_top, clip_right, clip_bottom, pixel, font, oblique);
778- width =
779- draw_glyph(c, x+1, y+1, p, pitch, clip_left, clip_top, clip_right, clip_bottom, pixel, font, oblique);
780- }
781- else
782- width = draw_glyph(c, x, y, p, pitch, clip_left, clip_top, clip_right, clip_bottom, pixel, font, oblique);
783- if (style & styleBold) {
784- draw_glyph(c, x + 1, y, p, pitch, clip_left, clip_top, clip_right, clip_bottom, pixel, font, oblique);
785- width++;
786- }
787- if (style & styleUnderline) {
788- for (int i=0; i<width; i++)
789- p[y * pitch / sizeof(T) + x + i] = pixel;
790- }
791559
792- total_width += width;
793- x += width;
560+ src_rect.w = (clip_right > dst_rect.x) ? clip_right - dst_rect.x : 0;
561+ src_rect.h = (clip_bottom > dst_rect.y) ? clip_bottom - dst_rect.y : 0;
562+
563+ SDL_BlitSurface(text_surface, &src_rect, s, &dst_rect);
794564 }
795- free(texts);
796- return total_width;
797-}
798-
799-// Draw text at given coordinates, return total width
800-int draw_text(SDL_Surface *s, const char *text, size_t length, int x, int y, uint32 pixel, const sdl_font_info *font, uint16 style)
801-{
802- if (font == NULL)
803- return 0;
565+ else
566+ SDL_BlitSurface(text_surface, NULL, s, &dst_rect);
804567
805- // Get clipping rectangle
806- int clip_top, clip_bottom, clip_left, clip_right;
807- if (draw_clip_rect_active) {
808- clip_top = draw_clip_rect.top;
809- clip_bottom = draw_clip_rect.bottom - 1;
810- clip_left = draw_clip_rect.left;
811- clip_right = draw_clip_rect.right - 1;
812- } else {
813- clip_top = clip_left = 0;
814- clip_right = s->w - 1;
815- clip_bottom = s->h - 1;
816- }
568+ if (s == SDL_GetVideoSurface())
569+ SDL_UpdateRect(s, x, y - TTF_FontAscent(font), text_width(utf8T, style, utf8), TTF_FontHeight(font));
817570
818- if (SDL_MUSTLOCK (s)) {
819- if (SDL_LockSurface(s) < 0) return 0;
820- }
821- int width = 0;
822- switch (s->format->BytesPerPixel) {
823- case 1:
824- width = draw_text((const uint8 *)text, length, x, y, (pixel8 *)s->pixels, s->pitch, clip_left, clip_top, clip_right, clip_bottom, pixel, font, style);
825- break;
826- case 2:
827- width = draw_text((const uint8 *)text, length, x, y, (pixel16 *)s->pixels, s->pitch, clip_left, clip_top, clip_right, clip_bottom, pixel, font, style);
828- break;
829- case 4:
830- width = draw_text((const uint8 *)text, length, x, y, (pixel32 *)s->pixels, s->pitch, clip_left, clip_top, clip_right, clip_bottom, pixel, font, style);
831- break;
832- }
833- if (SDL_MUSTLOCK (s)) {
834- SDL_UnlockSurface(s);
835- }
836- if (s == SDL_GetVideoSurface())
837- SDL_UpdateRect(s, x, y - font->ascent, text_width(text, font, style), font->rect_height);
571+ int width = text_surface->w;
572+ SDL_FreeSurface(text_surface);
838573 return width;
839574 }
575+#endif
840576
841-static void draw_text(const char *text, int x, int y, uint32 pixel, const sdl_font_info *font, uint16 style)
577+static void draw_text(const char *text, int x, int y, uint32 pixel, const font_info *font, uint16 style)
842578 {
843579 draw_text(draw_surface, text, strlen(text), x, y, pixel, font, style);
844-}
580+}
845581
846582 void _draw_screen_text(const char *text, screen_rectangle *destination, short flags, short font_id, short text_color)
847583 {
848584 int x, y;
849-
585+
850586 // Find font information
851587 assert(font_id >= 0 && font_id < NUMBER_OF_INTERFACE_FONTS);
852588 uint16 style = InterfaceFonts[font_id].Style;
853- const sdl_font_info *font = InterfaceFonts[font_id].Info;
589+ const font_info *font = InterfaceFonts[font_id].Info;
854590 if (font == NULL)
855591 return;
856-
592+
857593 // Get color
858594 SDL_Color color;
859595 _get_interface_color(text_color, &color);
860-
596+
861597 // Copy the text to draw
862598 char text_to_draw[256];
863599 strncpy(text_to_draw, text, 256);
864600 text_to_draw[255] = 0;
865-
601+
866602 // Check for wrapping, and if it occurs, be recursive
867603 if (flags & _wrap_text) {
868604 int last_non_printing_character = 0, text_width = 0;
@@ -869,10 +605,9 @@
869605 unsigned count = 0;
870606 while (count < strlen(text_to_draw) && text_width < RECTANGLE_WIDTH(destination)) {
871607 text_width += char_width(text_to_draw[count], font, style);
872- if (text_to_draw[count] == ' ' )
608+ if (text_to_draw[count] == ' ')
873609 last_non_printing_character = count;
874610 count++;
875- if(IsTwoByte(text_to_draw[count])) { count++; }
876611 }
877612
878613 if( count != strlen(text_to_draw)) {
@@ -885,16 +620,16 @@
885620
886621 // Pass the rest of it back in, recursively, on the next line
887622 memcpy(remaining_text_to_draw, text_to_draw + last_non_printing_character + 1, strlen(text_to_draw + last_non_printing_character + 1) + 1);
888-
623+
889624 new_destination = *destination;
890625 new_destination.top += InterfaceFonts[font_id].LineSpacing;
891626 _draw_screen_text(remaining_text_to_draw, &new_destination, flags, font_id, text_color);
892-
627+
893628 // Now truncate our text to draw
894629 text_to_draw[last_non_printing_character] = 0;
895630 }
896631 }
897-
632+
898633 // Truncate text if necessary
899634 int t_width = text_width(text_to_draw, font, style);
900635 if (t_width > RECTANGLE_WIDTH(destination)) {
@@ -901,7 +636,7 @@
901636 text_to_draw[trunc_text(text_to_draw, RECTANGLE_WIDTH(destination), font, style)] = 0;
902637 t_width = text_width(text_to_draw, font, style);
903638 }
904-
639+
905640 // Horizontal positioning
906641 if (flags & _center_horizontal)
907642 x = destination->left + (((destination->right - destination->left) - t_width) / 2);
@@ -909,7 +644,7 @@
909644 x = destination->right - t_width;
910645 else
911646 x = destination->left;
912-
647+
913648 // Vertical positioning
914649 int t_height = InterfaceFonts[font_id].Height;
915650 if (flags & _center_vertical) {
@@ -927,11 +662,11 @@
927662 y = destination->top + t_height;
928663 } else
929664 y = destination->bottom;
930-
665+
931666 // Now draw it
932667 draw_text(text_to_draw, x, y, SDL_MapRGB(draw_surface->format, color.r, color.g, color.b), font, style);
933668 }
934-#endif
669+
935670 static TextSpec NullSpec = {0, 0, 0};
936671
937672 TextSpec *_get_font_spec(short font_index)
@@ -941,11 +676,11 @@
941676
942677 // Sets current font to this index of interface font;
943678 // used in computer_interface.cpp
944-sdl_font_info *GetInterfaceFont(short font_index)
679+font_info *GetInterfaceFont(short font_index)
945680 {
946681 assert(font_index>=0 && font_index<NUMBER_OF_INTERFACE_FONTS);
947682
948- return InterfaceFonts[font_index].Info;
683+ return static_cast<font_info*>(InterfaceFonts[font_index].Info);
949684 }
950685
951686 // Gets the current font style;
@@ -968,7 +703,7 @@
968703 // Find font information
969704 assert(font_id >= 0 && font_id < NUMBER_OF_INTERFACE_FONTS);
970705 uint16 style = InterfaceFonts[font_id].Style;
971- const sdl_font_info *font = InterfaceFonts[font_id].Info;
706+ const font_info *font = InterfaceFonts[font_id].Info;
972707 if (font == NULL)
973708 return 0;
974709
--- marathon/trunk/AlephOneJP/RenderOther/sdl_fonts.cpp (revision 487)
+++ marathon/trunk/AlephOneJP/RenderOther/sdl_fonts.cpp (revision 488)
@@ -36,6 +36,9 @@
3636 #include <vector>
3737 #include <map>
3838
39+#include <boost/tokenizer.hpp>
40+#include <string>
41+
3942 #ifndef NO_STD_NAMESPACE
4043 using std::vector;
4144 using std::pair;
@@ -42,6 +45,11 @@
4245 using std::map;
4346 #endif
4447
48+#ifdef HAVE_SDL_TTF
49+#include <boost/tuple/tuple_comparison.hpp>
50+#include "preferences.h" // smooth_font
51+#include "AlephSansMono-Bold.h"
52+#endif
4553
4654 // Global variables
4755 typedef pair<int, int> id_and_size_t;
@@ -48,6 +56,12 @@
4856 typedef map<id_and_size_t, sdl_font_info *> font_list_t;
4957 static font_list_t font_list; // List of all loaded fonts
5058
59+#ifdef HAVE_SDL_TTF
60+typedef pair<TTF_Font *, int> ref_counted_ttf_font_t;
61+typedef map<ttf_font_key_t, ref_counted_ttf_font_t> ttf_font_list_t;
62+static ttf_font_list_t ttf_font_list;
63+#endif
64+
5165 // From shell_sdl.cpp
5266 extern vector<DirectorySpecifier> data_search_path;
5367
@@ -55,7 +69,12 @@
5569 /*
5670 * Initialize font management
5771 */
58-#if 0
72+
73+#ifdef HAVE_SDL_TTF
74+extern void fix_missing_overhead_map_fonts();
75+extern void fix_missing_interface_fonts();
76+#endif
77+
5978 void initialize_fonts(void)
6079 {
6180 logContext("initializing fonts");
@@ -67,9 +86,21 @@
6786
6887 if (open_res_file(fonts))
6988 found = true;
89+
90+ if (!found)
91+ {
92+ fonts = *i + "Fonts.fntA";
93+ if (open_res_file(fonts))
94+ found = true;
95+ }
7096 i++;
7197 }
7298 if (!found) {
99+#ifdef HAVE_SDL_TTF
100+ // use our own built-in font
101+ fix_missing_overhead_map_fonts();
102+ fix_missing_interface_fonts();
103+#else
73104 logFatal("Can't open font resource file");
74105 /*
75106 vector<DirectorySpecifier>::const_iterator i = data_search_path.begin(), end = data_search_path.end();
@@ -80,6 +111,7 @@
80111 }
81112 */
82113 exit(1);
114+#endif
83115 }
84116 }
85117
@@ -87,8 +119,8 @@
87119 /*
88120 * Load font from resources and allocate sdl_font_info
89121 */
90-
91-sdl_font_info *load_font(const TextSpec &spec)
122+/*
123+sdl_font_info *load_sdl_font(const TextSpec &spec)
92124 {
93125 sdl_font_info *info = NULL;
94126
@@ -193,23 +225,109 @@
193225 SDL_RWclose(p);
194226 return info;
195227 }
228+*/
229+#ifdef HAVE_SDL_TTF
230+static TTF_Font *load_ttf_font(const std::string& path, uint16 style, int16 size)
231+{
232+ // already loaded? increment reference counter and return pointer
233+ ttf_font_key_t search_key(path, style, size);
234+ ttf_font_list_t::iterator it = ttf_font_list.find(search_key);
235+ if (it != ttf_font_list.end())
236+ {
237+ TTF_Font *font = it->second.first;
238+ it->second.second++;
196239
240+ return font;
241+ }
197242
243+ TTF_Font *font;
244+ if (path == "mono")
245+ {
246+ font = TTF_OpenFontRW(SDL_RWFromConstMem(aleph_sans_mono_bold, sizeof(aleph_sans_mono_bold)), 0, size);
247+ }
248+ else
249+ {
250+ font = TTF_OpenFont(path.c_str(), size);
251+ }
252+
253+ if (font)
254+ {
255+ int ttf_style = TTF_STYLE_NORMAL;
256+ if (style & styleBold)
257+ ttf_style |= TTF_STYLE_BOLD;
258+ if (style & styleItalic)
259+ ttf_style |= TTF_STYLE_ITALIC;
260+
261+ TTF_SetFontStyle(font, ttf_style);
262+#ifdef TTF_HINTING_LIGHT
263+/* if (environment_preferences->smooth_text)
264+ TTF_SetFontHinting(font, TTF_HINTING_LIGHT);
265+ else
266+ TTF_SetFontHinting(font, TTF_HINTING_MONO);*/
267+#endif
268+
269+ ttf_font_key_t key(path, style, size);
270+ ref_counted_ttf_font_t value(font, 1);
271+
272+ ttf_font_list[key] = value;
273+ }
274+
275+ return font;
276+}
277+
278+static const char *locate_font(const std::string& path)
279+{
280+ if (path == "mono" || path == "")
281+ {
282+ return path.c_str();
283+ }
284+ else
285+ {
286+ static FileSpecifier file;
287+ if (file.SetNameWithPath(path.c_str()))
288+ return file.GetPath();
289+ else
290+ return "";
291+ }
292+}
293+#endif
294+int8 ttf_font_info::char_width(uint16 c, uint16 style) const
295+{
296+ int advance;
297+ TTF_GlyphMetrics(get_ttf(style), c, 0, 0, 0, 0, &advance);
298+
299+ return advance;
300+}
301+uint16 ttf_font_info::_text_width(const char *text, uint16 style, bool utf8) const
302+{
303+ return _text_width(text, strlen(text), style, utf8);
304+}
305+font_info *load_font(const TextSpec &spec) {
306+ ttf_font_info* info = new ttf_font_info();
307+ if(!TTF_WasInit() && TTF_Init()==-1) {
308+ printf("TTF_Init: %s\n", TTF_GetError());
309+ exit(1);
310+ }
311+ info->font = TTF_OpenFont("Fonts.ttf", spec.size);
312+ return info;
313+}
314+
315+
198316 /*
199317 * Unload font, free sdl_font_info
200318 */
201319
202-void unload_font(sdl_font_info *info)
320+void sdl_font_info::_unload()
203321 {
204322 // Look for font in list of loaded fonts
205323 font_list_t::const_iterator i = font_list.begin(), end = font_list.end();
206324 while (i != end) {
207- if (i->second == info) {
325+ if (i->second == this) {
208326
209327 // Found, decrement reference counter and delete
210- info->ref_count--;
211- if (info->ref_count <= 0) {
212- delete info;
328+ ref_count--;
329+ if (ref_count <= 0) {
330+ delete this; // !
213331 font_list.erase(i->first);
214332 return;
215333 }
@@ -217,146 +335,336 @@
217335 i++;
218336 }
219337 }
220-# else
221-void initialize_fonts(void)
338+
339+#ifdef HAVE_SDL_TTF
340+void ttf_font_info::_unload()
222341 {
223- logContext("initializing fonts");
342+ delete this;
224343 }
344+#endif
225345
226-#define FONTJ12 "shnmk12.bdf"
227-#define FONTE12 "shnm6x12a.bdf"
228-#define FONTJ10 "knj10.bdf"
229-#define FONTE10 "5x10a.bdf"
230-// 0001 1100 0000 0000
231-uint16 sjis2jis(char** data) {
232- char d1,d2;
233- uint8 c1,c2;
234- uint16 ret;
235- d1 = *((*data)++);
236- if( d1 > 0) { return d1; }
237- c1 = 256 + d1;
238- d2 = *((*data)++);
239- c2 = d2 < 0 ? 256 + d2 : d2;
240- if( c2 < 0x9f && c2 > 0x3f) {
241- if( c1 < 0xa0 && c1 > 0x80) {
242- c1 = (c1 - 0x70) * 2 - 1;
243- } else if( c1 < 0xf0 && c1 > 0xdf) {
244- c1 = (c1 - 0xb0) * 2 - 1;
245- } else { (*data)--; return c1; }
246- c2 -= c2 > 0x7f ? 0x20 : 0x1f;
247- } else {
248- if( c1 < 0xa0 && c1 > 0x80) {
249- c1 = (c1 - 0x70) * 2;
250- } else if( c1 < 0xf0 && c1 > 0xbf) {
251- c1 = (c1 - 0xb0) * 2;
252- } else { (*data)--; return c1; }
253- c2 -= 0x7e;
346+void unload_font(font_info *info)
347+{
348+ info->_unload();
349+}
350+
351+int8 sdl_font_info::char_width(uint8 c, uint16 style) const
352+{
353+ if (c < first_character || c > last_character)
354+ return 0;
355+ int8 width = width_table[(c - first_character) * 2 + 1] + ((style & styleBold) ? 1 : 0);
356+ if (width == -1) // non-existant character
357+ width = width_table[(last_character - first_character + 1) * 2 + 1] + ((style & styleBold) ? 1 : 0);
358+ return width;
359+}
360+
361+uint16 sdl_font_info::_text_width(const char *text, uint16 style, bool) const
362+{
363+ int width = 0;
364+ char c;
365+ while ((c = *text++) != 0)
366+ width += char_width(c, style);
367+ assert(0 <= width);
368+ assert(width == static_cast<int>(static_cast<uint16>(width)));
369+ return width;
370+}
371+
372+uint16 sdl_font_info::_text_width(const char *text, size_t length, uint16 style, bool) const
373+{
374+ int width = 0;
375+ while (length--)
376+ width += char_width(*text++, style);
377+ assert(0 <= width);
378+ assert(width == static_cast<int>(static_cast<uint16>(width)));
379+ return width;
380+}
381+
382+int sdl_font_info::_trunc_text(const char *text, int max_width, uint16 style) const
383+{
384+ int width = 0;
385+ int num = 0;
386+ char c;
387+ while ((c = *text++) != 0) {
388+ width += char_width(c, style);
389+ if (width > max_width)
390+ break;
391+ num++;
254392 }
255- ret = ((uint16)c1 << 8) | c2;
256- // fprintf(stderr,"%04x ",(*(uint16*)(data-2)));
257- return ret;
393+ return num;
258394 }
259-// 010000000000 0000
260-// 1
261-// 0-> 15 1->14
262395
263-static void ParseChar(uint8* pixs, FILE* fp, int xsize, int ysize) {
264- char buf[256];
265- int x,y;
266- int xs = xsize > 8 ? 15 : 7;
267- for (y = 0; y <= ysize; y++) {
268- fgets(buf, 256, fp);
269- if(strstr(buf,"BITMAP") ) { continue; }
270- if(strstr(buf,"ENDCHAR") ) { return; }
271- int txt = strtol(buf,0,16);
272- for(x = 0; x < xsize;x++) {
273- pixs[(y)*(ysize)+x] = (txt & ( 1 << (xs - x))) ? 1 : 0;
396+// sdl_font_info::_draw_text is in screen_drawing.cpp
397+
398+
399+
400+uint16 ttf_font_info::_text_width(const char *text, size_t length, uint16 style, bool utf8) const
401+{
402+ int width = 0;
403+ if (utf8)
404+ {
405+ char *temp = process_printable(text, length);
406+ TTF_SizeUTF8(get_ttf(style), temp, &width, 0);
407+ }
408+ else
409+ {
410+ uint16 *temp = process_macroman(text, length);
411+ TTF_SizeUNICODE(get_ttf(style), temp, &width, 0);
412+ }
413+
414+ return width;
415+}
416+
417+int ttf_font_info::_trunc_text(const char *text, int max_width, uint16 style) const
418+{
419+ int width;
420+ static uint16 temp[1024];
421+ mac_roman_to_unicode(text, temp, 1024);
422+ TTF_SizeUNICODE(get_ttf(style), temp, &width, 0);
423+ if (width < max_width) return strlen(text);
424+
425+ int num = strlen(text) - 1;
426+
427+ while (num > 0 && width > max_width)
428+ {
429+ num--;
430+ temp[num] = 0x0;
431+ TTF_SizeUNICODE(get_ttf(style), temp, &width, 0);
432+ }
433+
434+ return num;
435+}
436+
437+// ttf_font_info::_draw_text is in screen_drawing.cpp
438+
439+char *ttf_font_info::process_printable(const char *src, int len) const
440+{
441+ static char dst[1024];
442+ if (len > 1023) len = 1023;
443+ char *p = dst;
444+ while (*src && len-- > 0)
445+ {
446+ if ((unsigned char) *src >= ' ') *p++ = *src;
447+ *src++;
448+ }
449+
450+ *p = '\0';
451+ return dst;
452+}
453+
454+uint16 *ttf_font_info::process_macroman(const char *src, int len) const
455+{
456+ static uint16 dst[1024];
457+ if (len > 1023) len = 1023;
458+ uint16 *p = dst;
459+ while (*src && len-- > 0)
460+ {
461+ if ((unsigned char) *src >= ' ') *p++ = mac_roman_to_unicode(*src);
462+ else if ((unsigned char) *src == '\t')
463+ *p++ = ' ';
464+
465+ *src++;
466+ }
467+
468+ *p = 0x0;
469+ return dst;
470+}
471+
472+uint16 font_info::text_width(const char *text, uint16 style, bool utf8) const
473+{
474+ if (style & styleShadow)
475+ return _text_width(text, style, utf8) + 1;
476+ else
477+ return _text_width(text, style, utf8);
478+}
479+
480+uint16 font_info::text_width(const char *text, size_t length, uint16 style, bool utf8) const
481+{
482+ if (style & styleShadow)
483+ return _text_width(text, length, style, utf8) + 1;
484+ else
485+ return _text_width(text, length, style, utf8);
486+}
487+
488+static inline bool style_code(char c)
489+{
490+ switch(tolower(c)) {
491+ case 'p':
492+ case 'b':
493+ case 'i':
494+ case 'l':
495+ case 'r':
496+ case 'c':
497+ case 's':
498+ return true;
499+ break;
500+ default:
501+ return false;
502+ }
503+}
504+
505+class style_separator
506+{
507+public:
508+ bool operator() (std::string::const_iterator& next, std::string::const_iterator end, std::string& token)
509+ {
510+ if (next == end) return false;
511+
512+ token = std::string();
513+
514+ // if we start with a token, return it
515+ if (*next == '|' && next + 1 != end && style_code(*(next + 1)))
516+ {
517+ token += *next;
518+ ++next;
519+ token += *next;
520+ ++next;
521+ return true;
274522 }
523+
524+ token += *next;
525+ ++next;
526+
527+ // add characters until we hit a token
528+ for (;next != end && !(*next == '|' && next + 1 != end && style_code(*(next + 1))); ++next)
529+ {
530+ token += *next;
531+ }
532+
533+ return true;
275534 }
535+
536+ void reset() { }
537+
538+};
539+
540+static inline bool is_style_token(const std::string& token)
541+{
542+ return (token.size() == 2 && token[0] == '|' && style_code(token[1]));
276543 }
277544
278-sdl_font_info *load_font(const TextSpec &spec)
545+static void update_style(uint16& style, const std::string& token)
279546 {
280- const char* epath, *jpath;
281- FILE* ep, *jp;
282- sdl_font_info *ret = new sdl_font_info;
283- ret->location_table = new uint32 [65536];
284- ret->width_table = new int8 [65536];
285- memset(ret->location_table,0,65536*2);
286- memset(ret->width_table,0,65536);
287- if(spec.size < 10 ) {
288- epath = FONTE10;
289- jpath = FONTJ10;
290- ret->ascent = 8;
291- ret->descent = 2;
292- ret->leading = 0;
293- ret->bytes_per_row = 10;
294- } else {
295- epath = FONTE12;
296- jpath = FONTJ12;
297- ret->ascent = 10;
298- ret->descent = 2;
299- ret->leading = 0;
300- ret->bytes_per_row = 12;
547+ if (tolower(token[1]) == 'p')
548+ style &= ~(styleBold | styleItalic);
549+ else if (tolower(token[1]) == 'b')
550+ {
551+ style |= styleBold;
552+ style &= ~styleItalic;
301553 }
302- int ln = ret->bytes_per_row;
303- int dsize = ln * ln;
304- ep = fopen(epath,"r");
305- if( ! ep ) { fprintf(stderr,"Cannot open English Font file \n"); exit(1); }
306- jp = fopen(jpath,"r");
307- if( ! jp ) { fprintf(stderr,"Cannot open Japanese Font file \n"); exit(1); }
308- ret->pixmap = new uint8 [(96*96+256)*dsize];
309- const int BUF = 256;
310- char buf[BUF];
311- int index;
312- while(fgets(buf, BUF, ep)) {
313- if( buf[0] == 'E' && strstr(buf, "ENCODING") != NULL) {
314- char* p = strchr(buf, ' ');
315- index = strtol(p, 0, 10);
316- if( index < 0xa0 && index > 0x80 || index < 0xf0 && index > 0xbf || index < 32) {
317- continue;
554+ else if (tolower(token[1]) == 'i')
555+ {
556+ style |= styleItalic;
557+ style &= ~styleBold;
558+ }
559+}
560+
561+
562+int font_info::draw_styled_text(SDL_Surface *s, const std::string& text, size_t length, int x, int y, uint32 pixel, uint16 style, bool utf8) const
563+{
564+ int width = 0;
565+ boost::tokenizer<style_separator> tok(text.begin(), text.begin() + length);
566+ for (boost::tokenizer<style_separator>::iterator it = tok.begin(); it != tok.end(); ++it)
567+ {
568+ if (is_style_token(*it))
569+ {
570+ update_style(style, *it);
571+ }
572+ else
573+ {
574+ if (style & styleShadow)
575+ {
576+ _draw_text(s, it->c_str(), it->size(), x + width + 1, y + 1, SDL_MapRGB(s->format, 0x0, 0x0, 0x0), style, utf8);
318577 }
319- do {
320- fgets(buf, BUF, ep);
321- } while (strcmp(buf, "BITMAP\n"));
322- uint8* in = ret->pixmap + (dsize) * index;
323- ret->width_table[index] = ln / 2;
324- ret->location_table[index] = in - ret->pixmap;
325- ParseChar(in,ep,ln/2,ln);
326-
578+ width += _draw_text(s, it->c_str(), it->size(), x + width, y, pixel, style, utf8);
327579 }
328580 }
329- uint8* in = ret->pixmap + dsize * 256;
330- while(fgets(buf,BUF,jp)) {
331- if( buf[0] == 'E' && strstr(buf, "ENCODING") != NULL) {
332- char* p = strchr(buf, ' ');
333- index = strtol(p, 0, 10);
334- do {
335- fgets(buf, BUF, jp);
336- } while (strcmp(buf, "BITMAP\n"));
337- ret->width_table[index] = ln;
338- ret->location_table[index] = in - ret->pixmap;
339- ParseChar(in,jp,ln,ln);
340- in += dsize;
581+
582+ return width;
583+}
584+
585+int font_info::styled_text_width(const std::string& text, size_t length, uint16 style, bool utf8) const
586+{
587+ int width = 0;
588+ boost::tokenizer<style_separator> tok(text.begin(), text.begin() + length);
589+ for (boost::tokenizer<style_separator>::iterator it = tok.begin(); it != tok.end(); ++it)
590+ {
591+ if (is_style_token(*it))
592+ {
593+ update_style(style, *it);
341594 }
342-
595+ else
596+ {
597+ width += _text_width(it->c_str(), it->length(), style, utf8);
598+ }
343599 }
344- fclose(ep);
345- fclose(jp);
346- return ret;
600+
601+ if (style & styleShadow)
602+ return width + 1;
603+ else
604+ return width;
347605 }
348606
607+int font_info::trunc_styled_text(const std::string& text, int max_width, uint16 style) const
608+{
609+ if (style & styleShadow)
610+ {
611+ max_width -= 1;
612+ style &= (~styleShadow);
613+ }
349614
350-/*
351- * Unload font, free sdl_font_info
352- */
615+ int length = 0;
616+ boost::tokenizer<style_separator> tok(text);
617+ for (boost::tokenizer<style_separator>::iterator it = tok.begin(); it != tok.end(); ++it)
618+ {
619+ if (is_style_token(*it))
620+ {
621+ update_style(style, *it);
622+ length += 2;
623+ }
624+ else
625+ {
626+ int additional_length = _trunc_text(it->c_str(), max_width, style);
627+ max_width -= _text_width(it->c_str(), additional_length, style);
628+ length += additional_length;
629+ if (additional_length < it->size())
630+ return length;
631+ }
632+ }
353633
354-void unload_font(sdl_font_info *info)
634+ return length;
635+}
636+
637+std::string font_info::style_at(const std::string& text, std::string::const_iterator pos, uint16 style) const
355638 {
356- delete [] info->pixmap;
357- info->pixmap = NULL;
358- delete [] info->width_table;
359- delete [] info->location_table;
360- delete info;
639+ boost::tokenizer<style_separator> tok(text.begin(), pos);
640+ for (boost::tokenizer<style_separator>::iterator it = tok.begin(); it != tok.end(); ++it)
641+ {
642+ if (is_style_token(*it))
643+ update_style(style, *it);
644+ }
645+
646+ if (style & styleBold)
647+ return string("|b");
648+ else if (style & styleItalic)
649+ return string("|i");
650+ else
651+ return string();
361652 }
362-#endif
\ No newline at end of file
653+
654+int font_info::draw_text(SDL_Surface *s, const char *text, size_t length, int x, int y, uint32 pixel, uint16 style, bool utf8) const
655+{
656+ if (style & styleShadow)
657+ {
658+ _draw_text(s, text, length, x + 1, y + 1, SDL_MapRGB(s->format, 0x0, 0x0, 0x0), style, utf8);
659+ }
660+
661+ _draw_text(s, text, length, x, y, pixel, style, utf8);
662+}
663+
664+int font_info::trunc_text(const char *text, int max_width, uint16 style) const
665+{
666+ if (style & styleShadow)
667+ return _trunc_text(text, max_width - 1, style);
668+ else
669+ return _trunc_text(text, max_width, style);
670+}
--- marathon/trunk/AlephOneJP/Misc/interface.cpp (nonexistent)
+++ marathon/trunk/AlephOneJP/Misc/interface.cpp (revision 488)
@@ -0,0 +1,2822 @@
1+/*
2+ INTERFACE.C
3+
4+ Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
5+ and the "Aleph One" developers.
6+
7+ This program is free software; you can redistribute it and/or modify
8+ it under the terms of the GNU General Public License as published by
9+ the Free Software Foundation; either version 2 of the License, or
10+ (at your option) any later version.
11+
12+ This program is distributed in the hope that it will be useful,
13+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ GNU General Public License for more details.
16+
17+ This license is contained in the file "COPYING",
18+ which is included with this source code; it is available online at
19+ http://www.gnu.org/licenses/gpl.html
20+
21+ Thursday, December 30, 1993 6:56:22 PM
22+ Mac specific code.....
23+
24+ Friday, July 8, 1994 2:32:44 PM (alain)
25+ All old code in here is obsolete. This now has interface for the top-level
26+ interface (Begin Game, etcノ)
27+ Saturday, September 10, 1994 12:45:48 AM (alain)
28+ the interface gutted again. just the stuff that handles the menu though, the rest stayed
29+ the same.
30+ Thursday, June 8, 1995 2:56:16 PM (ryan)
31+ Pillaged, raped, & burned. (in that order)
32+
33+Jan 30, 2000 (Loren Petrich):
34+ Added some typecasts
35+ Removed some "static" declarations that conflict with "extern"
36+ Surrounded choose_saved_game_to_load with "extern "C""
37+
38+Feb. 4, 2000 (Loren Petrich):
39+ Changed halt() to assert(false) for better debugging
40+
41+Feb. 9, 2000 (Loren Petrich):
42+ Changed NUMBER_OF_INTRO_SCREENS to 3
43+ Changed NUMBER_OF_CREDIT_SCREENS to Hamish Sinclair's favorite number
44+
45+ Fixed multiple-clicks-necessary problem for too few screens.
46+ Was in next_game_state(); set game_state.phase (countdown value) to zero.
47+
48+Feb 19, 2000 (Loren Petrich):
49+ Set the single-player color to the player color set in the preferences,
50+ for the benefit of chase-cam users.
51+
52+Mar 5, 2000 (Loren Petrich):
53+ Added reset_screen() when starting a game, so that extravision
54+ will not be persistent.
55+
56+May 13, 2000 (Loren Petrich):
57+ Added Rhys Hill's fix for problems with quitting OpenGL
58+
59+Aug 12, 2000 (Loren Petrich):
60+ Using object-oriented file handler
61+
62+Aug 24, 2000 (Loren Petrich):
63+ Added source selector to calculate_picture_clut(), in order to better deal with
64+ object-oriented file handlers
65+
66+Nov 25, 2000 (Loren Petrich):
67+ Added support for movies that start at any level, including at the end of a game.
68+ Also added end-screen control.
69+
70+Jan 31, 2001 (Loren Petrich):
71+ In pause_game(), will stop the liquid faders that are active
72+
73+Jan 25, 2002 (Br'fin (Jeremy Parsons)):
74+ Disabled network and network microphone calls under Carbon
75+
76+Feb 27, 2002 (Br'fin (Jeremy Parsons)):
77+ Renabled network calls, but not microphone calls under Carbon
78+
79+May 16, 2002 (Woody Zenfell):
80+ Enforcing standard player behavior with regard to films and netplay
81+
82+Jun 5, 2002 (Loren Petrich):
83+ Added do-nothing "case _revert_game:" in portable_process_screen_click()
84+ at the request of Michael Adams.
85+
86+Feb 1, 2003 (Woody Zenfell):
87+ Reenabling network microphone support on all platforms, trying to share code
88+ and present consistent interfaces to the greatest degree practical.
89+
90+Feb 8-12, 2003 (Woody Zenfell):
91+ Introducing support for generalized game startup (will enable resumption of saved-games
92+ as netgames, among other things).
93+
94+Feb 13, 2003 (Woody Zenfell):
95+ We can now resume games as network games.
96+*/
97+
98+// NEED VISIBLE FEEDBACK WHEN APPLETALK IS NOT AVAILABLE!!!
99+
100+/* ZZZ: more on enforcing standard behavior...
101+ + Standard behavior forced when playing a network game.
102+ + Standard behavior forced when replaying a film.
103+ + Custom behavior allowed when starting or restoring a single-player game.
104+ + No film recorded in single-player if custom behavior != standard behavior.
105+
106+ Once films and netplay properly record each player's behavior prefs,
107+ and the relevant code uses per-player settings, this won't be necessary.
108+ Try a mass-search for "player_behavior" to find the areas affected.
109+*/
110+
111+#include "cseries.h" // sorry ryan, nov. 4
112+#include <string.h>
113+#include <stdlib.h>
114+#include <limits.h>
115+#include <algorithm>
116+
117+#ifdef PERFORMANCE
118+#include <perf.h>
119+
120+extern TP2PerfGlobals perf_globals;
121+#endif
122+
123+#include "map.h"
124+#include "shell.h"
125+#include "interface.h"
126+#include "player.h"
127+#include "network.h"
128+#include "screen_drawing.h"
129+#include "SoundManager.h"
130+#include "fades.h"
131+#include "game_window.h"
132+#include "game_errors.h"
133+#include "Mixer.h"
134+#include "Music.h"
135+#include "images.h"
136+#include "screen.h"
137+#include "network.h"
138+#include "vbl.h"
139+#include "shell.h"
140+#include "preferences.h"
141+#include "FileHandler.h"
142+#include "lua_script.h" // PostIdle
143+#include "interface_menus.h"
144+#include "XML_LevelScript.h"
145+#include "Music.h"
146+
147+#ifdef HAVE_SMPEG
148+#include <smpeg/smpeg.h>
149+#endif
150+
151+#include "sdl_dialogs.h"
152+#include "sdl_widgets.h"
153+#include "network_dialog_widgets_sdl.h"
154+
155+/* Change this when marathon changes & replays are no longer valid */
156+enum recording_version {
157+ aleph_recording_version= 0,
158+};
159+const short default_recording_version= aleph_recording_version;
160+const short max_handled_recording= aleph_recording_version;
161+
162+#include "screen_definitions.h"
163+#include "interface_menus.h"
164+
165+// LP addition: getting OpenGL rendering stuff
166+#include "render.h"
167+#include "OGL_Render.h"
168+#include "alephversion.h"
169+
170+// To tell it to stop playing,
171+// and also to run the end-game script
172+#include "XML_LevelScript.h"
173+
174+// Network microphone/speaker
175+#include "network_sound.h"
176+#include "network_distribution_types.h"
177+
178+// ZZZ: should the function that uses these (join_networked_resume_game()) go elsewhere?
179+#include "wad.h"
180+#include "game_wad.h"
181+
182+#include "motion_sensor.h" // for reset_motion_sensor()
183+
184+using alephone::Screen;
185+
186+#ifdef env68k
187+ #pragma segment shell
188+#endif
189+
190+/* ------------- enums */
191+
192+/* ------------- constants */
193+#define DISPLAY_PICT_RESOURCE_TYPE 'PICT'
194+#define CLOSE_WITHOUT_WARNING_DELAY (5*TICKS_PER_SECOND)
195+
196+#define NUMBER_OF_INTRO_SCREENS (3)
197+#define INTRO_SCREEN_DURATION (215 * MACHINE_TICKS_PER_SECOND / TICKS_PER_SECOND) // fudge to align with sound
198+
199+#ifdef DEMO
200+#define INTRO_SCREEN_TO_START_SONG_ON (1)
201+#else
202+#define INTRO_SCREEN_TO_START_SONG_ON (0)
203+#endif
204+
205+#define INTRO_SCREEN_BETWEEN_DEMO_BASE (INTRO_SCREEN_BASE+1) /* +1 to get past the powercomputing */
206+#define NUMBER_OF_INTRO_SCREENS_BETWEEN_DEMOS (1)
207+#define DEMO_INTRO_SCREEN_DURATION (10 * MACHINE_TICKS_PER_SECOND)
208+
209+#define TICKS_UNTIL_DEMO_STARTS (30 * MACHINE_TICKS_PER_SECOND)
210+
211+#define NUMBER_OF_PROLOGUE_SCREENS 0
212+#define PROLOGUE_DURATION (10 * MACHINE_TICKS_PER_SECOND)
213+
214+#define NUMBER_OF_EPILOGUE_SCREENS 1
215+#define EPILOGUE_DURATION (INDEFINATE_TIME_DELAY)
216+
217+#define NUMBER_OF_CREDIT_SCREENS 7
218+#define CREDIT_SCREEN_DURATION (15 * 60 * MACHINE_TICKS_PER_SECOND)
219+
220+#define NUMBER_OF_CHAPTER_HEADINGS 0
221+#define CHAPTER_HEADING_DURATION (7*MACHINE_TICKS_PER_SECOND)
222+
223+// For exiting the Marathon app
224+// #if defined(DEBUG) || !defined(DEMO)
225+#define NUMBER_OF_FINAL_SCREENS 0
226+// #else
227+// #define NUMBER_OF_FINAL_SCREENS 1
228+// #endif
229+#define FINAL_SCREEN_DURATION (INDEFINATE_TIME_DELAY)
230+
231+/* For teleportation, end movie, etc. */
232+#define EPILOGUE_LEVEL_NUMBER 256
233+
234+/* ------------- structures */
235+struct game_state {
236+ short state;
237+ short flags;
238+ short user;
239+ long phase;
240+ long last_ticks_on_idle;
241+ short current_screen;
242+ bool suppress_background_tasks;
243+ bool current_netgame_allows_microphone;
244+ short main_menu_display_count; // how many times have we shown the main menu?
245+};
246+
247+struct screen_data {
248+ short screen_base;
249+ short screen_count;
250+ long duration;
251+};
252+
253+/* -------------- constants */
254+struct screen_data display_screens[]= {
255+ { INTRO_SCREEN_BASE, NUMBER_OF_INTRO_SCREENS, INTRO_SCREEN_DURATION },
256+ { MAIN_MENU_BASE, 1, 0 },
257+ { CHAPTER_SCREEN_BASE, NUMBER_OF_CHAPTER_HEADINGS, CHAPTER_HEADING_DURATION },
258+ { PROLOGUE_SCREEN_BASE, NUMBER_OF_PROLOGUE_SCREENS, PROLOGUE_DURATION },
259+ { EPILOGUE_SCREEN_BASE, NUMBER_OF_EPILOGUE_SCREENS, EPILOGUE_DURATION },
260+ { CREDIT_SCREEN_BASE, NUMBER_OF_CREDIT_SCREENS, CREDIT_SCREEN_DURATION},
261+ { INTRO_SCREEN_BETWEEN_DEMO_BASE, NUMBER_OF_INTRO_SCREENS_BETWEEN_DEMOS, DEMO_INTRO_SCREEN_DURATION },
262+ { FINAL_SCREEN_BASE, NUMBER_OF_FINAL_SCREENS, FINAL_SCREEN_DURATION }
263+};
264+
265+#if 0
266+struct chapter_screen_sound_data {
267+ short level;
268+ short sound_code;
269+};
270+
271+struct chapter_screen_sound_data chapter_screen_sounds[]=
272+{
273+ {0, _snd_chapter1},
274+ {1, _snd_chapter2},
275+ {2, _snd_chapter3}
276+};
277+#define NUMBER_OF_CHAPTER_SCREEN_SOUNDS (sizeof(chapter_screen_sounds)/sizeof(chapter_screen_sounds[1]))
278+#endif
279+
280+/* -------------- local globals */
281+static struct game_state game_state;
282+static FileSpecifier DraggedReplayFile;
283+static bool interface_fade_in_progress= false;
284+static short interface_fade_type;
285+static short current_picture_clut_depth;
286+static struct color_table *animated_color_table= NULL;
287+static struct color_table *current_picture_clut= NULL;
288+
289+/* -------------- externs */
290+extern short interface_bit_depth;
291+extern short bit_depth;
292+
293+/* ----------- prototypes/PREPROCESS_MAP_MAC.C */
294+extern bool load_game_from_file(FileSpecifier& File);
295+extern bool choose_saved_game_to_load(FileSpecifier& File);
296+
297+/* ---------------------- prototypes */
298+static void display_credits(void);
299+static void draw_button(short index, bool pressed);
300+static void handle_replay(bool last_replay);
301+static bool begin_game(short user, bool cheat);
302+static void start_game(short user, bool changing_level);
303+// LP: "static" removed
304+void handle_load_game(void);
305+static void handle_save_film(void);
306+static void finish_game(bool return_to_main_menu);
307+static void clean_up_after_failed_game(bool inNetgame, bool inRecording, bool inFullCleanup);
308+static void handle_network_game(bool gatherer);
309+static void next_game_screen(void);
310+static void handle_interface_menu_screen_click(short x, short y, bool cheatkeys_down);
311+
312+static void display_introduction(void);
313+static void display_loading_map_error(void);
314+static void display_quit_screens(void);
315+static void display_screen(short base_pict_id);
316+static void display_introduction_screen_for_demo(void);
317+static void display_epilogue(void);
318+
319+static void force_system_colors(void);
320+static bool point_in_rectangle(short x, short y, screen_rectangle *rect);
321+
322+static void start_interface_fade(short type, struct color_table *original_color_table);
323+static void update_interface_fades(void);
324+static void interface_fade_out(short pict_resource_number, bool fade_music);
325+static bool can_interface_fade_out(void);
326+static void transfer_to_new_level(short level_number);
327+static void try_and_display_chapter_screen(short level, bool interface_table_is_valid, bool text_block);
328+
329+static screen_data *get_screen_data(
330+ short index);
331+
332+/* ---------------------- code begins */
333+
334+screen_data *get_screen_data(
335+ short index)
336+{
337+ assert(index>=0 && index<NUMBER_OF_SCREENS);
338+ return display_screens+index;
339+}
340+
341+void initialize_game_state(
342+ void)
343+{
344+ game_state.state= _display_intro_screens;
345+ game_state.user= _single_player;
346+ game_state.flags= 0;
347+ game_state.current_screen= 0;
348+ game_state.suppress_background_tasks= true;
349+ game_state.main_menu_display_count= 0;
350+
351+ toggle_menus(false);
352+
353+ display_introduction();
354+}
355+
356+void force_game_state_change(
357+ void)
358+{
359+ game_state.phase= 0;
360+}
361+
362+bool player_controlling_game(
363+ void)
364+{
365+ bool player_in_control= false;
366+
367+ assert(game_state.state==_game_in_progress || game_state.state==_switch_demo);
368+
369+ if(game_state.user==_single_player || game_state.user==_network_player)
370+ {
371+ player_in_control= true;
372+ }
373+
374+ return player_in_control;
375+}
376+
377+void toggle_suppression_of_background_tasks(
378+ void)
379+{
380+ game_state.suppress_background_tasks= !game_state.suppress_background_tasks;
381+}
382+
383+void set_game_state(
384+ short new_state)
385+{
386+ short old_state= game_state.state;
387+
388+ switch(old_state)
389+ {
390+ case _game_in_progress:
391+ switch(new_state)
392+ {
393+ case _display_epilogue:
394+ game_state.state= _begin_display_of_epilogue;
395+ game_state.phase= 0;
396+ break;
397+
398+ case _close_game:
399+ finish_game(true);
400+ break;
401+
402+ case _quit_game:
403+ finish_game(false);
404+ display_quit_screens();
405+ break;
406+
407+ case _switch_demo:
408+ /* Because Alain's code calls us at interrupt level 1, */
409+ /* we must defer processing of this message until idle */
410+ game_state.state= _switch_demo;
411+ game_state.phase= 0;
412+ break;
413+
414+ case _revert_game:
415+ /* Because reverting a game in the middle of the update_world loop sounds */
416+ /* sketchy, this is not done until idle time.. */
417+ game_state.state= new_state;
418+ game_state.phase= 0;
419+ break;
420+
421+ case _change_level:
422+ game_state.state= new_state;
423+ game_state.phase= 0;
424+ break;
425+
426+ default:
427+ assert(false);
428+ break;
429+ }
430+ break;
431+
432+ default:
433+ game_state.state= new_state;
434+ break;
435+ }
436+}
437+
438+short get_game_state(
439+ void)
440+{
441+ return game_state.state;
442+}
443+
444+bool current_netgame_allows_microphone()
445+{
446+ return game_state.current_netgame_allows_microphone;
447+}
448+
449+bool suppress_background_events(
450+ void)
451+{
452+ return game_state.suppress_background_tasks;
453+}
454+
455+short get_game_controller(
456+ void)
457+{
458+ return game_state.user;
459+}
460+
461+void set_change_level_destination(
462+ short level_number)
463+{
464+ assert(game_state.state== _change_level);
465+ game_state.current_screen= level_number;
466+}
467+
468+static short get_difficulty_level(void)
469+{
470+ return player_preferences->difficulty_level;
471+}
472+
473+
474+// ----- ZZZ start support for generalized game startup -----
475+// (should this be split out (with some other game startup code) to a new file?)
476+
477+// In this scheme, a "start" corresponds to a player available at the moment, who will
478+// participate in the game we're starting up. In general when this code talks about a
479+// 'player', it is referring to an already-existing player in the game world (which should
480+// already be restored or initialized fairly well before these routines are used).
481+// Generalized game startup will match starts to existing players, set leftover players
482+// as "zombies" so they exist but just stand there, and create new players to correspond
483+// with any remaining starts. As such it can handle both resume-game (some players already
484+// exist) and new-game (dynamic_world->player_count == 0) operations.
485+
486+static void construct_single_player_start(player_start_data* outStartArray, short* outStartCount)
487+{
488+ if(outStartCount != NULL)
489+ {
490+ *outStartCount = 1;
491+ }
492+
493+ outStartArray[0].team = player_preferences->color;
494+ outStartArray[0].color = player_preferences->color;
495+ outStartArray[0].identifier = 0;
496+ memcpy(outStartArray[0].name, &(player_preferences->name[1]), player_preferences->name[0]);
497+ outStartArray[0].name[player_preferences->name[0]] = '\0';
498+
499+ set_player_start_doesnt_auto_recenter_status(&outStartArray[0], dont_auto_recenter());
500+ set_player_start_doesnt_auto_switch_weapons_status(&outStartArray[0], dont_switch_to_new_weapon());
501+}
502+
503+#if !defined(DISABLE_NETWORKING)
504+static void construct_multiplayer_starts(player_start_data* outStartArray, short* outStartCount)
505+{
506+ int number_of_players = NetGetNumberOfPlayers();
507+
508+ if(outStartCount != NULL)
509+ {
510+ *outStartCount = number_of_players;
511+ }
512+
513+ for(int player_index= 0; player_index<number_of_players; ++player_index)
514+ {
515+ player_info *player_information = (player_info *)NetGetPlayerData(player_index);
516+ outStartArray[player_index].team = player_information->team;
517+ outStartArray[player_index].color= player_information->color;
518+ outStartArray[player_index].identifier = NetGetPlayerIdentifier(player_index);
519+
520+ /* Copy and translate from pascal string to cstring */
521+ memcpy(outStartArray[player_index].name, &player_information->name[1],
522+ player_information->name[0]);
523+ outStartArray[player_index].name[player_information->name[0]]= 0;
524+ }
525+}
526+#endif // !defined(DISABLE_NETWORKING)
527+
528+// This should be safe to use whether starting or resuming and whether single-player or multiplayer.
529+void match_starts_with_existing_players(player_start_data* ioStartArray, short* ioStartCount)
530+{
531+ // This code could be smarter, but it doesn't run very often, doesn't get big data sets, etc.
532+ // so I'm not going to worry about it.
533+
534+ bool startAssigned[MAXIMUM_NUMBER_OF_PLAYERS];
535+ int8 startAssignedToPlayer[MAXIMUM_NUMBER_OF_PLAYERS];
536+ for(int i = 0; i < MAXIMUM_NUMBER_OF_PLAYERS; i++)
537+ {
538+ startAssigned[i] = false;
539+ startAssignedToPlayer[i] = NONE;
540+ }
541+
542+ // First, match starts to players by name.
543+ for(int s = 0; s < *ioStartCount; s++)
544+ {
545+ for(int p = 0; p < dynamic_world->player_count; p++)
546+ {
547+ if(startAssignedToPlayer[p] == NONE)
548+ {
549+ if(strcmp(ioStartArray[s].name, get_player_data(p)->name) == 0)
550+ {
551+ startAssignedToPlayer[p] = s;
552+ startAssigned[s] = true;
553+ break;
554+ }
555+ }
556+ }
557+ }
558+
559+ // Match remaining starts to remaining players arbitrarily.
560+ for(int s = 0; s < *ioStartCount; s++)
561+ {
562+ if(!startAssigned[s])
563+ {
564+ for(int p = 0; p < dynamic_world->player_count; p++)
565+ {
566+ if(startAssignedToPlayer[p] == NONE)
567+ {
568+ startAssignedToPlayer[p] = s;
569+ startAssigned[s] = true;
570+ break;
571+ }
572+ }
573+ }
574+ }
575+
576+ // Create new starts for any players not covered.
577+ int p = 0;
578+ while(*ioStartCount < dynamic_world->player_count)
579+ {
580+ if(startAssignedToPlayer[p] == NONE)
581+ {
582+ player_data* thePlayer = get_player_data(p);
583+ ioStartArray[*ioStartCount].team = thePlayer->team;
584+ ioStartArray[*ioStartCount].color = thePlayer->color;
585+ ioStartArray[*ioStartCount].identifier = NONE;
586+ strcpy(ioStartArray[*ioStartCount].name, thePlayer->name);
587+ startAssignedToPlayer[p] = *ioStartCount;
588+ startAssigned[*ioStartCount] = true;
589+ (*ioStartCount)++;
590+ }
591+
592+ p++;
593+ }
594+
595+ // Assign remaining starts to players that don't exist yet
596+ p = dynamic_world->player_count;
597+ for(int s = 0; s < *ioStartCount; s++)
598+ {
599+ if(!startAssigned[s])
600+ {
601+ startAssignedToPlayer[p] = s;
602+ startAssigned[s] = true;
603+ p++;
604+ }
605+ }
606+
607+ // Reorder starts to match players - this is particularly unclever
608+ player_start_data theOriginalStarts[MAXIMUM_NUMBER_OF_PLAYERS];
609+ memcpy(theOriginalStarts, ioStartArray, sizeof(theOriginalStarts));
610+ for(p = 0; p < *ioStartCount; p++)
611+ {
612+ ioStartArray[p] = theOriginalStarts[startAssignedToPlayer[p]];
613+ }
614+}
615+
616+// This should be safe to use whether starting or resuming, and whether single- or multiplayer.
617+static void synchronize_players_with_starts(const player_start_data* inStartArray, short inStartCount)
618+{
619+ // s will walk through all the starts
620+ int s = 0;
621+
622+ // First we process existing players
623+ for( ; s < dynamic_world->player_count; s++)
624+ {
625+ player_data* thePlayer = get_player_data(s);
626+
627+ if(inStartArray[s].identifier == NONE)
628+ {
629+ // No start (live player) to go with this player (stored player)
630+ SET_PLAYER_ZOMBIE_STATUS(thePlayer, true);
631+ }
632+ else
633+ {
634+ // Update player's appearance to match the start
635+ thePlayer->team = inStartArray[s].team;
636+ thePlayer->color = inStartArray[s].color;
637+ thePlayer->identifier = player_identifier_value(inStartArray[s].identifier);
638+ strcpy(thePlayer->name, inStartArray[s].name);
639+
640+ SET_PLAYER_DOESNT_AUTO_RECENTER_STATUS(thePlayer,
641+ player_identifier_doesnt_auto_recenter(inStartArray[s].identifier));
642+ SET_PLAYER_DOESNT_AUTO_SWITCH_WEAPONS_STATUS(thePlayer,
643+ player_identifier_doesnt_auto_switch_weapons(inStartArray[s].identifier));
644+
645+ // Make sure if player was saved as zombie, they're not now.
646+ SET_PLAYER_ZOMBIE_STATUS(thePlayer, false);
647+ }
648+ }
649+
650+ // If there are any starts left, we need new players for them
651+ for( ; s < inStartCount; s++)
652+ {
653+ int theIndex = new_player(inStartArray[s].team, inStartArray[s].color, inStartArray[s].identifier);
654+ assert(theIndex == s);
655+ player_data* thePlayer = get_player_data(theIndex);
656+ strcpy(thePlayer->name, inStartArray[s].name);
657+ }
658+}
659+
660+static short find_start_for_identifier(const player_start_data* inStartArray, short inStartCount, short _inIdentifier)
661+{
662+ short inIdentifier= player_identifier_value(_inIdentifier);
663+ short s;
664+ for(s = 0; s < inStartCount; s++)
665+ {
666+ if(player_identifier_value(inStartArray[s].identifier) == inIdentifier)
667+ {
668+ break;
669+ }
670+ }
671+
672+ return (s == inStartCount) ? NONE : s;
673+}
674+
675+// The single-player machine, gatherer, and joiners all will use this routine. It should take most of its
676+// cues from the "extras" that load_game_from_file() does.
677+static bool make_restored_game_relevant(bool inNetgame, const player_start_data* inStartArray, short inStartCount)
678+{
679+ game_is_networked = inNetgame;
680+
681+ // set_random_seed() needs to happen before synchronize_players_with_starts()
682+ // since the latter calls new_player() which almost certainly uses global_random().
683+ // Note we always take the random seed directly from the dynamic_world, no need to screw around
684+ // with copying it from game_information or the like.
685+ set_random_seed(dynamic_world->random_seed);
686+
687+ synchronize_players_with_starts(inStartArray, inStartCount);
688+
689+ short theLocalPlayerIndex;
690+
691+#if !defined(DISABLE_NETWORKING)
692+ // Much of the code in this if()...else is very similar to code in begin_game(), should probably try to share.
693+ if(inNetgame)
694+ {
695+ game_info *network_game_info= (game_info *)NetGetGameData();
696+
697+ dynamic_world->game_information.game_time_remaining= network_game_info->time_limit;
698+ dynamic_world->game_information.kill_limit= network_game_info->kill_limit;
699+ dynamic_world->game_information.game_type= network_game_info->net_game_type;
700+ dynamic_world->game_information.game_options= network_game_info->game_options;
701+ dynamic_world->game_information.initial_random_seed= network_game_info->initial_random_seed;
702+ dynamic_world->game_information.difficulty_level= network_game_info->difficulty_level;
703+
704+ if (network_game_info->allow_mic)
705+ {
706+ install_network_microphone();
707+ game_state.current_netgame_allows_microphone= true;
708+ } else {
709+ game_state.current_netgame_allows_microphone= false;
710+ }
711+
712+ // ZZZ: until players specify their behavior modifiers over the network,
713+ // to avoid out-of-sync we must force them all the same.
714+ standardize_player_behavior_modifiers();
715+
716+ theLocalPlayerIndex = NetGetLocalPlayerIndex();
717+ }
718+ else
719+#endif // !defined(DISABLE_NETWORKING)
720+ {
721+ dynamic_world->game_information.difficulty_level= get_difficulty_level();
722+ restore_custom_player_behavior_modifiers();
723+ theLocalPlayerIndex = find_start_for_identifier(inStartArray, inStartCount, 0);
724+ }
725+
726+ assert(theLocalPlayerIndex != NONE);
727+ set_local_player_index(theLocalPlayerIndex);
728+ set_current_player_index(theLocalPlayerIndex);
729+
730+ bool success = entering_map(true /*restoring game*/);
731+
732+ reset_motion_sensor(theLocalPlayerIndex);
733+
734+ if(!success) clean_up_after_failed_game(inNetgame, false /*recording*/, true /*full cleanup*/);
735+
736+ return success;
737+}
738+
739+// ZZZ end generalized game startup support -----
740+
741+#if !defined(DISABLE_NETWORKING)
742+// ZZZ: this will get called (eventually) shortly after NetUpdateJoinState() returns netStartingResumeGame
743+bool join_networked_resume_game()
744+{
745+ bool success = true;
746+
747+ // Get the saved-game data
748+ byte* theSavedGameFlatData = NetReceiveGameData(false /* do_physics */);
749+ if(theSavedGameFlatData == NULL)
750+ {
751+ success = false;
752+ }
753+
754+ if(success)
755+ {
756+ // Use the saved-game data
757+ wad_header theWadHeader;
758+ wad_data* theWad;
759+
760+ theWad = inflate_flat_data(theSavedGameFlatData, &theWadHeader);
761+ if(theWad == NULL)
762+ {
763+ success = false;
764+ free(theSavedGameFlatData);
765+ }
766+
767+ if(success)
768+ {
769+ success = process_map_wad(theWad, true /* resuming */, theWadHeader.data_version);
770+ free_wad(theWad); /* Note that the flat data points into the wad. */
771+ // ZZZ: maybe this is what the Bungie comment meant, but apparently
772+ // free_wad() somehow (voodoo) frees theSavedGameFlatData as well.
773+ }
774+
775+ if(success)
776+ {
777+ // try to locate the Map file for the saved-game, so that (1) we have a crack
778+ // at continuing the game if the original gatherer disappears, and (2) we can
779+ // save the game on our own machine and continue it properly (as part of a bigger scenario) later.
780+ uint32 theParentChecksum = theWadHeader.parent_checksum;
781+ if(use_map_file(theParentChecksum))
782+ {
783+ // LP: getting the level scripting off of the map file
784+ // Being careful to carry over errors so that Pfhortran errors can be ignored
785+ short SavedType, SavedError = get_game_error(&SavedType);
786+ RunLevelScript(dynamic_world->current_level_number);
787+ RunScriptChunks();
788+ set_game_error(SavedType,SavedError);
789+ }
790+ else
791+ {
792+ /* Tell the user theyユre screwed when they try to leave this level. */
793+ // ZZZ: should really issue a different warning since the ramifications are different
794+ alert_user(infoError, strERRORS, cantFindMap, 0);
795+
796+ // LP addition: makes the game look normal
797+ hide_cursor();
798+
799+ /* Set to the default map. */
800+ set_to_default_map();
801+
802+ ResetLevelScript();
803+ RunScriptChunks();
804+ }
805+
806+ // set the revert-game info to defaults (for full-auto saving on the local machine)
807+ set_saved_game_name_to_default();
808+
809+ player_start_data theStarts[MAXIMUM_NUMBER_OF_PLAYERS];
810+ short theStartCount;
811+ construct_multiplayer_starts(theStarts, &theStartCount);
812+
813+ success = make_restored_game_relevant(true /* multiplayer */, theStarts, theStartCount);
814+ }
815+ }
816+
817+ if(success)
818+ {
819+ Music::instance()->PreloadLevelMusic();
820+ start_game(_network_player, false /*changing level?*/);
821+ }
822+
823+ return success;
824+}
825+#endif // !defined(DISABLE_NETWORKING)
826+
827+extern bool load_and_start_game(FileSpecifier& File);
828+
829+// ZZZ: changes to use generalized game startup support
830+// This will be used only on the machine that picked "Continue Saved Game".
831+bool load_and_start_game(FileSpecifier& File)
832+{
833+ bool success;
834+
835+ hide_cursor();
836+ if (can_interface_fade_out())
837+ {
838+ interface_fade_out(MAIN_MENU_BASE, true);
839+ }
840+
841+ success= load_game_from_file(File);
842+
843+ if (!success)
844+ {
845+ /* Reset the system colors, since the screen clut is all black.. */
846+ force_system_colors();
847+ show_cursor(); // JTP: Was hidden by force system colors
848+ display_loading_map_error();
849+ }
850+
851+ bool userWantsMultiplayer;
852+ size_t theResult = UNONE;
853+
854+ if (success)
855+ {
856+#ifdef __MACOS__
857+ theResult = 0;
858+#else
859+ theResult = should_restore_game_networked();
860+#endif
861+ }
862+
863+ if (theResult == UNONE)
864+ {
865+ // cancelled
866+ success = false;
867+ }
868+ else
869+ {
870+ userWantsMultiplayer = (theResult != 0);
871+ }
872+
873+ if (success)
874+ {
875+#if !defined(DISABLE_NETWORKING)
876+ if (userWantsMultiplayer)
877+ {
878+ set_game_state(_displaying_network_game_dialogs);
879+ success = network_gather(true /*resuming*/);
880+ }
881+#endif // !defined(DISABLE_NETWORKING)
882+
883+ if (success)
884+ {
885+ player_start_data theStarts[MAXIMUM_NUMBER_OF_PLAYERS];
886+ short theNumberOfStarts;
887+
888+#if !defined(DISABLE_NETWORKING)
889+ if (userWantsMultiplayer)
890+ {
891+ construct_multiplayer_starts(theStarts, &theNumberOfStarts);
892+ }
893+ else
894+#endif // !defined(DISABLE_NETWORKING)
895+ {
896+ construct_single_player_start(theStarts, &theNumberOfStarts);
897+ }
898+
899+ match_starts_with_existing_players(theStarts, &theNumberOfStarts);
900+
901+#if !defined(DISABLE_NETWORKING)
902+ if (userWantsMultiplayer)
903+ {
904+ NetSetupTopologyFromStarts(theStarts, theNumberOfStarts);
905+ success = NetStart();
906+ if (success)
907+ {
908+ byte* theSavedGameFlatData = (byte*)get_flat_data(File, false /* union wad? */, 0 /* level # */);
909+ if (theSavedGameFlatData == NULL)
910+ {
911+ success = false;
912+ }
913+
914+ if (success)
915+ {
916+ long theSavedGameFlatDataLength = get_flat_data_length(theSavedGameFlatData);
917+ OSErr theError = NetDistributeGameDataToAllPlayers(theSavedGameFlatData, theSavedGameFlatDataLength, false /* do_physics? */);
918+ if (theError != noErr)
919+ {
920+ success = false;
921+ }
922+ free(theSavedGameFlatData);
923+ }
924+ }
925+ }
926+#endif // !defined(DISABLE_NETWORKING)
927+
928+ if (success)
929+ {
930+ success = make_restored_game_relevant(userWantsMultiplayer, theStarts, theNumberOfStarts);
931+ if (success)
932+ {
933+ Music::instance()->PreloadLevelMusic();
934+ start_game(userWantsMultiplayer ? _network_player : _single_player, false);
935+ }
936+ }
937+ }
938+ }
939+
940+ if (!success) {
941+ /* We failed. Balance the cursor */
942+ /* Should this also force the system colors or something? */
943+ show_cursor();
944+ }
945+
946+ return success;
947+}
948+
949+extern bool handle_open_replay(FileSpecifier& File);
950+
951+bool handle_open_replay(FileSpecifier& File)
952+{
953+ DraggedReplayFile = File;
954+
955+ bool success;
956+
957+ force_system_colors();
958+ success= begin_game(_replay_from_file, false);
959+ if(!success) display_main_menu();
960+ return success;
961+}
962+
963+// Called from within update_world..
964+bool check_level_change(
965+ void)
966+{
967+ bool level_changed= false;
968+
969+ if(game_state.state==_change_level)
970+ {
971+ transfer_to_new_level(game_state.current_screen);
972+ level_changed= true;
973+ }
974+
975+ return level_changed;
976+}
977+
978+void pause_game(
979+ void)
980+{
981+ stop_fade();
982+ if (!OGL_IsActive() || !(TEST_FLAG(Get_OGL_ConfigureData().Flags,OGL_Flag_Fader)))
983+ set_fade_effect(NONE);
984+ darken_world_window();
985+ set_keyboard_controller_status(false);
986+#ifdef SDL
987+ show_cursor();
988+#endif
989+}
990+
991+void resume_game(
992+ void)
993+{
994+#ifdef SDL
995+ hide_cursor();
996+#endif
997+ if (!OGL_IsActive() || !(TEST_FLAG(Get_OGL_ConfigureData().Flags,OGL_Flag_Fader)))
998+ SetFadeEffectDelay(TICKS_PER_SECOND/2);
999+ validate_world_window();
1000+ set_keyboard_controller_status(true);
1001+}
1002+
1003+void draw_menu_button_for_command(
1004+ short index)
1005+{
1006+ short rectangle_index= index-1+_new_game_button_rect;
1007+
1008+ assert(get_game_state()==_display_main_menu);
1009+
1010+ /* Draw it initially depressed.. */
1011+ draw_button(rectangle_index, true);
1012+#ifdef SDL
1013+ SDL_Delay(1000 / 12);
1014+#else
1015+ uint32 initial_tick= machine_tick_count();
1016+ while(machine_tick_count()-initial_tick<5) /* 1/12 second */
1017+ ;
1018+#endif
1019+ draw_button(rectangle_index, false);
1020+}
1021+
1022+void update_interface_display(
1023+ void)
1024+{
1025+ struct screen_data *data;
1026+
1027+ data= get_screen_data(game_state.state);
1028+
1029+ /* Use this to avoid the fade.. */
1030+ draw_full_screen_pict_resource_from_images(data->screen_base+game_state.current_screen);
1031+}
1032+
1033+bool idle_game_state(uint32 time)
1034+{
1035+ int machine_ticks_elapsed = time - game_state.last_ticks_on_idle;
1036+
1037+ if(machine_ticks_elapsed || game_state.phase==0)
1038+ {
1039+ if(game_state.phase != INDEFINATE_TIME_DELAY)
1040+ {
1041+ game_state.phase-= machine_ticks_elapsed;
1042+ }
1043+
1044+ /* Note that we still go through this if we have an indefinate phase.. */
1045+ if(game_state.phase<=0)
1046+ {
1047+ switch(get_game_state())
1048+ {
1049+ case _display_quit_screens:
1050+ case _display_intro_screens:
1051+ case _display_prologue:
1052+ case _display_epilogue:
1053+ case _display_credits:
1054+ next_game_screen();
1055+ break;
1056+
1057+ case _display_intro_screens_for_demo:
1058+ case _display_main_menu:
1059+ /* Start the demo.. */
1060+ if(!begin_game(_demo, false))
1061+ {
1062+ /* This means that there was not a valid demo to play */
1063+ game_state.phase= TICKS_UNTIL_DEMO_STARTS;
1064+ }
1065+ break;
1066+
1067+ case _close_game:
1068+ display_main_menu();
1069+ break;
1070+
1071+ case _switch_demo:
1072+ /* This is deferred to the idle task because it */
1073+ /* occurs at interrupt time.. */
1074+ switch(game_state.user)
1075+ {
1076+ case _replay:
1077+ finish_game(true);
1078+ break;
1079+
1080+ case _demo:
1081+ finish_game(false);
1082+ display_introduction_screen_for_demo();
1083+ break;
1084+
1085+ default:
1086+ assert(false);
1087+ break;
1088+ }
1089+ break;
1090+
1091+ case _display_chapter_heading:
1092+ dprintf("Chapter heading...");
1093+ break;
1094+
1095+ case _quit_game:
1096+ /* About to quit, but can still hit this through order of ops.. */
1097+ break;
1098+
1099+ case _revert_game:
1100+ /* Reverting while in the update loop sounds sketchy.. */
1101+ if(revert_game())
1102+ {
1103+ game_state.state= _game_in_progress;
1104+ game_state.phase = 15 * MACHINE_TICKS_PER_SECOND;
1105+ game_state.last_ticks_on_idle= machine_tick_count();
1106+ update_interface(NONE);
1107+ } else {
1108+ /* Give them the error... */
1109+ display_loading_map_error();
1110+
1111+ /* And finish their current game.. */
1112+ finish_game(true);
1113+ }
1114+ break;
1115+
1116+ case _begin_display_of_epilogue:
1117+ finish_game(false);
1118+ display_epilogue();
1119+ break;
1120+
1121+ case _game_in_progress:
1122+ game_state.phase = 15 * MACHINE_TICKS_PER_SECOND;
1123+ //game_state.last_ticks_on_idle= machine_tick_count();
1124+ break;
1125+
1126+ case _change_level:
1127+ case _displaying_network_game_dialogs:
1128+ break;
1129+
1130+ default:
1131+ assert(false);
1132+ break;
1133+ }
1134+ }
1135+ game_state.last_ticks_on_idle= machine_tick_count();
1136+ }
1137+
1138+ /* if weユre not paused and thereユs something to draw (i.e., anything different from
1139+ last time), render a frame */
1140+ if(game_state.state==_game_in_progress)
1141+ {
1142+ // ZZZ change: update_world() whether or not get_keyboard_controller_status() is true
1143+ // This way we won't fill up queues and stall netgames if one player switches out for a bit.
1144+ std::pair<bool, int16> theUpdateResult= update_world();
1145+ short ticks_elapsed= theUpdateResult.second;
1146+
1147+ if (get_keyboard_controller_status())
1148+ {
1149+ // ZZZ: I don't know for sure that render_screen works best with the number of _real_
1150+ // ticks elapsed rather than the number of (potentially predictive) ticks elapsed.
1151+ // This is a guess.
1152+ if (theUpdateResult.first)
1153+ render_screen(ticks_elapsed);
1154+ }
1155+
1156+ return theUpdateResult.first;
1157+ } else {
1158+ /* Update the fade ins, etc.. */
1159+ update_interface_fades();
1160+ return false;
1161+ }
1162+}
1163+
1164+void display_main_menu(
1165+ void)
1166+{
1167+ game_state.state= _display_main_menu;
1168+ game_state.current_screen= 0;
1169+ game_state.phase= TICKS_UNTIL_DEMO_STARTS;
1170+ game_state.last_ticks_on_idle= machine_tick_count();
1171+ game_state.user= _single_player;
1172+ game_state.flags= 0;
1173+
1174+ display_screen(MAIN_MENU_BASE);
1175+
1176+ /* Start up the song! */
1177+ if(!Music::instance()->Playing() && game_state.main_menu_display_count==0)
1178+ {
1179+ Music::instance()->RestartIntroMusic();
1180+ }
1181+
1182+ // Draw AlephOne Version to screen
1183+ FontSpecifier& Font = GetOnScreenFont();
1184+
1185+#if defined(mac)
1186+ extern WindowPtr screen_window;
1187+ WindowPtr window= screen_window;
1188+ GrafPtr old_port, port;
1189+ port = GetWindowPort(window);
1190+ GetPort(&old_port);
1191+ SetPort(port);
1192+
1193+ Font.Use();
1194+ Rect portRect;
1195+ GetPortBounds(port, &portRect);
1196+ short X0 = portRect.right;
1197+ short Y0 = portRect.bottom;
1198+#elif defined(SDL)
1199+ // JTP: This works, but I don't know correctness
1200+ SDL_Surface *world_pixels = SDL_GetVideoSurface();
1201+ short X0 = world_pixels->w;
1202+ short Y0 = world_pixels->h;
1203+#endif
1204+ // The line spacing is a generalization of "5" for larger fonts
1205+ short Offset = Font.LineSpacing / 3;
1206+ short RightJustOffset = Font.TextWidth(A1_VERSION_STRING);
1207+ short X = X0 - RightJustOffset;
1208+ short Y = Y0 - Offset;
1209+#if defined(mac)
1210+ MoveTo(X, Y);
1211+ RGBForeColor(&(RGBColor){0x4000, 0x4000, 0x4000});
1212+ DrawString("\p"A1_VERSION_STRING);
1213+ RGBForeColor(&rgb_black);
1214+ SetPort(old_port);
1215+#elif defined(SDL)
1216+ // ZZZ: this still won't work for fullscreen (drawing to wrong surface as usual), but at
1217+ // least in windowed mode it will draw.
1218+ _set_port_to_screen_window();
1219+ draw_text(world_pixels, A1_VERSION_STRING, X, Y, SDL_MapRGB(world_pixels->format, 0x40, 0x40,
1220+ 0x40), Font.Info, Font.Style);
1221+ _restore_port();
1222+#endif
1223+ game_state.main_menu_display_count++;
1224+}
1225+
1226+
1227+// Kludge for Carbon/Classic -- when exiting a main-menu dialog box, redisplay
1228+static void ForceRepaintMenuDisplay()
1229+{
1230+#ifdef mac
1231+ if (!system_information->has_ten || system_information->machine_is_bluebox)
1232+ {
1233+ if (get_game_state() == _display_main_menu)
1234+ display_screen(MAIN_MENU_BASE);
1235+ }
1236+#endif
1237+}
1238+
1239+
1240+void do_menu_item_command(
1241+ short menu_id,
1242+ short menu_item,
1243+ bool cheat)
1244+{
1245+ switch(menu_id)
1246+ {
1247+
1248+ case mGame:
1249+ switch(menu_item)
1250+ {
1251+ case iPause:
1252+ switch(game_state.user)
1253+ {
1254+ case _single_player:
1255+ case _replay:
1256+ if (get_keyboard_controller_status())
1257+ {
1258+ pause_game();
1259+ }
1260+ else
1261+ {
1262+ resume_game();
1263+ }
1264+ break;
1265+
1266+ case _demo:
1267+ finish_game(true);
1268+ break;
1269+
1270+ case _network_player:
1271+ break;
1272+
1273+ default:
1274+ assert(false);
1275+ break;
1276+ }
1277+ break;
1278+
1279+ case iSave:
1280+ switch(game_state.user)
1281+ {
1282+ case _single_player:
1283+#if 0
1284+ save_game();
1285+ validate_world_window();
1286+#endif
1287+ break;
1288+
1289+ case _demo:
1290+ case _replay:
1291+ finish_game(true);
1292+ break;
1293+
1294+ case _network_player:
1295+ break;
1296+
1297+ default:
1298+ assert(false);
1299+ break;
1300+ }
1301+ break;
1302+
1303+ case iRevert:
1304+ /* Not implemented.. */
1305+ break;
1306+
1307+ case iCloseGame:
1308+ case iQuitGame:
1309+ {
1310+ bool really_wants_to_quit= false;
1311+
1312+ switch(game_state.user)
1313+ {
1314+ case _single_player:
1315+ if(PLAYER_IS_DEAD(local_player) ||
1316+ dynamic_world->tick_count-local_player->ticks_at_last_successful_save<CLOSE_WITHOUT_WARNING_DELAY)
1317+ {
1318+ really_wants_to_quit= true;
1319+ } else {
1320+ pause_game();
1321+ show_cursor();
1322+ really_wants_to_quit= quit_without_saving();
1323+ hide_cursor();
1324+ resume_game();
1325+ }
1326+ break;
1327+
1328+ case _demo:
1329+ case _replay:
1330+ case _network_player:
1331+ really_wants_to_quit= true;
1332+ break;
1333+
1334+ default:
1335+ assert(false);
1336+ break;
1337+ }
1338+
1339+ if(really_wants_to_quit)
1340+ {
1341+ // Rhys Hill fix for crash when quitting OpenGL
1342+ if (!OGL_IsActive())
1343+ render_screen(0); /* Get rid of hole.. */
1344+/* If you want to quit on command-q while in the game.. */
1345+#if 0
1346+ if(menu_item==iQuitGame)
1347+ {
1348+ set_game_state(_quit_game);
1349+ } else
1350+#endif
1351+ set_game_state(_close_game);
1352+ }
1353+ }
1354+ break;
1355+
1356+ default:
1357+ assert(false);
1358+ break;
1359+ }
1360+ break;
1361+
1362+ case mInterface:
1363+ switch(menu_item)
1364+ {
1365+ case iNewGame:
1366+ begin_game(_single_player, cheat);
1367+ ForceRepaintMenuDisplay();
1368+ break;
1369+ case iPlaySingletonLevel:
1370+ begin_game(_single_player,2);
1371+ break;
1372+
1373+ case iJoinGame:
1374+#ifdef mac
1375+ if (system_information->machine_has_network_memory)
1376+ {
1377+ handle_network_game(false);
1378+ }
1379+ else
1380+ {
1381+ alert_user(infoError, strERRORS, notEnoughNetworkMemory, 0);
1382+ }
1383+#elif defined(__MACOS__)
1384+ break;
1385+#else
1386+ handle_network_game(false);
1387+#endif
1388+ break;
1389+
1390+ case iGatherGame:
1391+#ifdef mac
1392+ if (system_information->machine_has_network_memory)
1393+ {
1394+ handle_network_game(true);
1395+ }
1396+ else
1397+ {
1398+ alert_user(infoError, strERRORS, notEnoughNetworkMemory, 0);
1399+ }
1400+#elif defined(__MACOS__)
1401+ break;
1402+#else
1403+ handle_network_game(true);
1404+#endif
1405+ break;
1406+
1407+ case iLoadGame:
1408+ handle_load_game();
1409+ break;
1410+
1411+ case iReplayLastFilm:
1412+ case iReplaySavedFilm:
1413+ handle_replay(menu_item==iReplayLastFilm);
1414+ break;
1415+
1416+ case iCredits:
1417+ display_credits();
1418+ break;
1419+
1420+ case iPreferences:
1421+ do_preferences();
1422+ game_state.phase= TICKS_UNTIL_DEMO_STARTS;
1423+ game_state.last_ticks_on_idle= machine_tick_count();
1424+ ForceRepaintMenuDisplay();
1425+ break;
1426+
1427+ case iCenterButton:
1428+ break;
1429+
1430+ case iSaveLastFilm:
1431+ handle_save_film();
1432+ break;
1433+
1434+ case iQuit:
1435+ display_quit_screens();
1436+ break;
1437+
1438+ default:
1439+ assert(false);
1440+ break;
1441+ }
1442+ break;
1443+
1444+ default:
1445+ assert(false);
1446+ break;
1447+ }
1448+}
1449+
1450+void portable_process_screen_click(
1451+ short x,
1452+ short y,
1453+ bool cheatkeys_down)
1454+{
1455+ switch(get_game_state())
1456+ {
1457+ case _game_in_progress:
1458+ case _begin_display_of_epilogue:
1459+ case _change_level:
1460+ case _displaying_network_game_dialogs:
1461+ case _quit_game:
1462+ case _close_game:
1463+ case _switch_demo:
1464+ case _revert_game:
1465+ break;
1466+
1467+ case _display_intro_screens_for_demo:
1468+ /* Get out of user mode. */
1469+ display_main_menu();
1470+ break;
1471+
1472+ case _display_quit_screens:
1473+ case _display_intro_screens:
1474+ case _display_chapter_heading:
1475+ case _display_prologue:
1476+ case _display_epilogue:
1477+ case _display_credits:
1478+ /* Force the state change next time through.. */
1479+ force_game_state_change();
1480+ break;
1481+
1482+ case _display_main_menu:
1483+ handle_interface_menu_screen_click(x, y, cheatkeys_down);
1484+ break;
1485+
1486+ default:
1487+ assert(false);
1488+ break;
1489+ }
1490+}
1491+
1492+bool enabled_item(
1493+ short item)
1494+{
1495+ bool enabled= true;
1496+
1497+ switch(item)
1498+ {
1499+ case iNewGame:
1500+ case iLoadGame:
1501+ case iPlaySingletonLevel:
1502+ case iPreferences:
1503+ case iReplaySavedFilm:
1504+ case iCredits:
1505+ case iQuit:
1506+ break;
1507+
1508+ case iCenterButton:
1509+ enabled= false;
1510+ break;
1511+
1512+ case iReplayLastFilm:
1513+ case iSaveLastFilm:
1514+ enabled= has_recording_file();
1515+ break;
1516+
1517+ case iGatherGame:
1518+ case iJoinGame:
1519+ enabled= networking_available();
1520+ break;
1521+
1522+ default:
1523+ assert(false);
1524+ break;
1525+ }
1526+
1527+ return enabled;
1528+}
1529+
1530+void paint_window_black(
1531+ void)
1532+{
1533+ _set_port_to_screen_window();
1534+ _erase_screen(_black_color);
1535+ _restore_port();
1536+}
1537+
1538+/* --------------------- static code */
1539+
1540+static void display_introduction(
1541+ void)
1542+{
1543+ struct screen_data *screen_data= get_screen_data(_display_intro_screens);
1544+
1545+ paint_window_black();
1546+ game_state.state= _display_intro_screens;
1547+ game_state.current_screen= 0;
1548+ if (screen_data->screen_count)
1549+ {
1550+ if (game_state.state==_display_intro_screens && game_state.current_screen==INTRO_SCREEN_TO_START_SONG_ON)
1551+ {
1552+ Music::instance()->RestartIntroMusic();
1553+ }
1554+
1555+ game_state.phase= screen_data->duration;
1556+ game_state.last_ticks_on_idle= machine_tick_count();
1557+ display_screen(screen_data->screen_base);
1558+ }
1559+ else
1560+ {
1561+ display_main_menu();
1562+ }
1563+}
1564+
1565+static void display_introduction_screen_for_demo(
1566+ void)
1567+{
1568+ struct screen_data *screen_data= get_screen_data(_display_intro_screens_for_demo);
1569+
1570+ game_state.state= _display_intro_screens_for_demo;
1571+ game_state.current_screen= 0;
1572+ if(screen_data->screen_count)
1573+ {
1574+ game_state.phase= screen_data->duration;
1575+ game_state.last_ticks_on_idle= machine_tick_count();
1576+ display_screen(screen_data->screen_base);
1577+ } else {
1578+ display_main_menu();
1579+ }
1580+}
1581+
1582+static void display_epilogue(
1583+ void)
1584+{
1585+ Music::instance()->RestartIntroMusic();
1586+
1587+ {
1588+ long ticks= machine_tick_count();
1589+
1590+ do
1591+ {
1592+ Music::instance()->Idle();
1593+ }
1594+ while (machine_tick_count()-ticks<10);
1595+ }
1596+
1597+ game_state.state= _display_epilogue;
1598+ game_state.phase= 0;
1599+ game_state.current_screen= 0;
1600+ game_state.last_ticks_on_idle= machine_tick_count();
1601+
1602+ hide_cursor();
1603+ // Setting of the end-screen parameters has been moved to XML_LevelScript.cpp
1604+ for (int i=0; i<NumEndScreens; i++)
1605+ try_and_display_chapter_screen(CHAPTER_SCREEN_BASE+EndScreenIndex+i, true, true);
1606+ show_cursor();
1607+}
1608+
1609+static void display_credits(
1610+ void)
1611+{
1612+ if (NUMBER_OF_CREDIT_SCREENS)
1613+ {
1614+ struct screen_data *screen_data= get_screen_data(_display_credits);
1615+
1616+ game_state.state= _display_credits;
1617+ game_state.current_screen= 0;
1618+ game_state.user= _single_player;
1619+ game_state.flags= 0;
1620+
1621+ game_state.phase= screen_data->duration;
1622+ game_state.last_ticks_on_idle= machine_tick_count();
1623+ display_screen(screen_data->screen_base);
1624+ }
1625+}
1626+
1627+static void display_quit_screens(
1628+ void)
1629+{
1630+ struct screen_data *screen_data= get_screen_data(_display_quit_screens);
1631+
1632+ if(screen_data->screen_count)
1633+ {
1634+ game_state.state= _display_quit_screens;
1635+ game_state.current_screen= 0;
1636+ game_state.user= _single_player;
1637+ game_state.flags= 0;
1638+ game_state.phase= screen_data->duration;
1639+ game_state.last_ticks_on_idle= machine_tick_count();
1640+
1641+ display_screen(screen_data->screen_base);
1642+ } else {
1643+ /* No screens. */
1644+ game_state.state= _quit_game;
1645+ game_state.phase= 0;
1646+ }
1647+}
1648+
1649+static void transfer_to_new_level(
1650+ short level_number)
1651+{
1652+ struct entry_point entry;
1653+ bool success= true;
1654+
1655+ entry.level_number= level_number;
1656+
1657+#if !defined(DISABLE_NETWORKING)
1658+ /* Only can transfer if NetUnSync returns true */
1659+ if(game_is_networked)
1660+ {
1661+ if(NetUnSync())
1662+ {
1663+ success= true;
1664+ } else {
1665+ set_game_error(gameError, errUnsyncOnLevelChange);
1666+ success= false;
1667+ }
1668+ }
1669+#endif // !defined(DISABLE_NETWORKING)
1670+
1671+ if(success)
1672+ {
1673+ stop_fade();
1674+ set_fade_effect(NONE);
1675+ Music::instance()->StopLevelMusic();
1676+//#ifdef mac
1677+// if(OGL_IsActive())
1678+ {
1679+ exit_screen();
1680+ // Enter_screen will be called again in start_game
1681+ }
1682+//#endif
1683+ set_keyboard_controller_status(false);
1684+ FindLevelMovie(entry.level_number);
1685+ show_movie(entry.level_number);
1686+
1687+ // if this is the EPILOGUE_LEVEL_NUMBER, then it is time to get
1688+ // out of here already (as we've just played the epilogue movie,
1689+ // we can move on to the _display_epilogue game state)
1690+ if (level_number == EPILOGUE_LEVEL_NUMBER) {
1691+ finish_game(false);
1692+ show_cursor(); // for some reason, cursor stays hidden otherwise
1693+ set_game_state(_begin_display_of_epilogue);
1694+ force_game_state_change();
1695+ return;
1696+ }
1697+
1698+ if (!game_is_networked) try_and_display_chapter_screen(CHAPTER_SCREEN_BASE + level_number, true, false);
1699+ success= goto_level(&entry, false);
1700+ set_keyboard_controller_status(true);
1701+ }
1702+
1703+ if(success)
1704+ {
1705+ start_game(game_state.user, true);
1706+ } else {
1707+ display_loading_map_error();
1708+ finish_game(true);
1709+ }
1710+}
1711+
1712+/* The port is set.. */
1713+static void draw_button(
1714+ short index,
1715+ bool pressed)
1716+{
1717+ screen_rectangle *screen_rect= get_interface_rectangle(index);
1718+ short pict_resource_number= MAIN_MENU_BASE + pressed;
1719+
1720+ set_drawing_clip_rectangle(screen_rect->top, screen_rect->left, screen_rect->bottom, screen_rect->right);
1721+
1722+ /* Use this to avoid the fade.. */
1723+ draw_full_screen_pict_resource_from_images(pict_resource_number);
1724+
1725+ set_drawing_clip_rectangle(SHRT_MIN, SHRT_MIN, SHRT_MAX, SHRT_MAX);
1726+}
1727+
1728+static void handle_replay( /* This is gross. */
1729+ bool last_replay)
1730+{
1731+ bool success;
1732+
1733+ if(!last_replay) force_system_colors();
1734+ success= begin_game(_replay, !last_replay);
1735+ if(!success) display_main_menu();
1736+}
1737+
1738+// ZZZ: some modifications to use generalized game-startup
1739+static bool begin_game(
1740+ short user,
1741+ bool cheat)
1742+{
1743+ struct entry_point entry;
1744+ struct player_start_data starts[MAXIMUM_NUMBER_OF_PLAYERS];
1745+ struct game_data game_information;
1746+ short number_of_players;
1747+ bool success= true;
1748+ bool is_networked= false;
1749+ bool clean_up_on_failure= true;
1750+ bool record_game= false;
1751+ uint32 parent_checksum = 0;
1752+
1753+ objlist_clear(starts, MAXIMUM_NUMBER_OF_PLAYERS);
1754+
1755+ switch(user)
1756+ {
1757+ case _network_player:
1758+#if !defined(DISABLE_NETWORKING)
1759+ {
1760+ game_info *network_game_info= (game_info *)NetGetGameData();
1761+
1762+ construct_multiplayer_starts(starts, &number_of_players);
1763+
1764+ game_information.game_time_remaining= network_game_info->time_limit;
1765+ game_information.kill_limit= network_game_info->kill_limit;
1766+ game_information.game_type= network_game_info->net_game_type;
1767+ game_information.game_options= network_game_info->game_options;
1768+ game_information.initial_random_seed= network_game_info->initial_random_seed;
1769+ game_information.difficulty_level= network_game_info->difficulty_level;
1770+ parent_checksum = network_game_info->parent_checksum;
1771+ entry.level_number = network_game_info->level_number;
1772+ entry.level_name[0] = 0;
1773+
1774+ if (network_game_info->allow_mic)
1775+ {
1776+ install_network_microphone();
1777+ game_state.current_netgame_allows_microphone= true;
1778+ } else {
1779+ game_state.current_netgame_allows_microphone= false;
1780+ }
1781+ game_information.cheat_flags = network_game_info->cheat_flags;
1782+ std::fill_n(game_information.parameters, 2, 0);
1783+
1784+ is_networked= true;
1785+ record_game= true;
1786+ // ZZZ: until players specify their behavior modifiers over the network,
1787+ // to avoid out-of-sync we must force them all the same.
1788+ standardize_player_behavior_modifiers();
1789+ }
1790+#endif // !defined(DISABLE_NETWORKING)
1791+ break;
1792+
1793+ case _replay_from_file:
1794+ case _replay:
1795+ case _demo:
1796+ switch(user)
1797+ {
1798+ case _replay:
1799+ {
1800+ FileSpecifier ReplayFile;
1801+ show_cursor(); // JTP: Hidden one way or another :p
1802+
1803+ success= find_replay_to_use(cheat, ReplayFile);
1804+ if(success)
1805+ {
1806+ success= setup_for_replay_from_file(ReplayFile, get_current_map_checksum());
1807+ hide_cursor();
1808+ }
1809+ }
1810+ break;
1811+
1812+ case _demo:
1813+ success= setup_replay_from_random_resource(get_current_map_checksum());
1814+ break;
1815+
1816+ case _replay_from_file:
1817+ success= setup_for_replay_from_file(DraggedReplayFile, get_current_map_checksum());
1818+ user= _replay;
1819+ break;
1820+
1821+ default:
1822+ assert(false);
1823+ break;
1824+ }
1825+
1826+ if(success)
1827+ {
1828+ uint32 unused1;
1829+ short recording_version;
1830+
1831+ get_recording_header_data(&number_of_players,
1832+ &entry.level_number, &unused1, &recording_version,
1833+ starts, &game_information);
1834+
1835+ if(recording_version > max_handled_recording)
1836+ {
1837+ stop_replay();
1838+ alert_user(infoError, strERRORS, replayVersionTooNew, 0);
1839+ success= false;
1840+ }
1841+ else
1842+ {
1843+ entry.level_name[0] = 0;
1844+ game_information.game_options |= _overhead_map_is_omniscient;
1845+ record_game= false;
1846+ // ZZZ: until films store behavior modifiers, we must require
1847+ // that they record and playback only with standard modifiers.
1848+ standardize_player_behavior_modifiers();
1849+ }
1850+ }
1851+ break;
1852+
1853+ case _single_player:
1854+ if(cheat)
1855+ {
1856+ entry.level_number= get_level_number_from_user();
1857+ if(entry.level_number==NONE) success= false; /* Cancelled */
1858+ } else {
1859+ entry.level_number= 0;
1860+ }
1861+
1862+ // ZZZ: let the user use his behavior modifiers in single-player.
1863+ restore_custom_player_behavior_modifiers();
1864+
1865+ entry.level_name[0] = 0;
1866+ starts[0].identifier = 0;
1867+ //AS: make things clearer
1868+ memset(entry.level_name,0,66);
1869+
1870+ construct_single_player_start(starts, &number_of_players);
1871+
1872+ game_information.game_time_remaining= INT32_MAX;
1873+ game_information.kill_limit = 0;
1874+ game_information.game_type= _game_of_kill_monsters;
1875+ game_information.game_options= _burn_items_on_death|_ammo_replenishes|_weapons_replenish|_monsters_replenish;
1876+ game_information.initial_random_seed= machine_tick_count();
1877+ game_information.difficulty_level= get_difficulty_level();
1878+ std::fill_n(game_information.parameters, 2, 0);
1879+
1880+ // ZZZ: until film files store player behavior flags, we must require
1881+ // that all films recorded be made with standard behavior.
1882+ record_game= is_player_behavior_standard();
1883+
1884+ break;
1885+
1886+ default:
1887+ assert(false);
1888+ break;
1889+ }
1890+
1891+ if(success)
1892+ {
1893+ if(record_game)
1894+ {
1895+ set_recording_header_data(number_of_players, entry.level_number, (user == _network_player) ? parent_checksum : get_current_map_checksum(),
1896+ default_recording_version, starts, &game_information);
1897+ start_recording();
1898+ }
1899+
1900+ hide_cursor();
1901+ /* This has already been done to get to gather/join */
1902+ if(can_interface_fade_out())
1903+ {
1904+ interface_fade_out(MAIN_MENU_BASE, true);
1905+ }
1906+
1907+ /* Try to display the first chapter screen.. */
1908+ if (user != _network_player && user != _demo)
1909+ {
1910+ FindLevelMovie(entry.level_number);
1911+ show_movie(entry.level_number);
1912+ try_and_display_chapter_screen(CHAPTER_SCREEN_BASE + entry.level_number, false, false);
1913+ }
1914+
1915+ /* Begin the game! */
1916+ success= new_game(number_of_players, is_networked, &game_information, starts, &entry);
1917+ if(success)
1918+ {
1919+ start_game(user, false);
1920+ } else {
1921+ clean_up_after_failed_game(user == _network_player, record_game, clean_up_on_failure);
1922+ }
1923+ } else {
1924+ /* This means that some weird replay problem happened: */
1925+ /* 1) User cancelled */
1926+ /* 2) Demos not present */
1927+ /* 3) Error... */
1928+ /* Either way, we eat them.. */
1929+ }
1930+
1931+ return success;
1932+}
1933+
1934+static void start_game(
1935+ short user,
1936+ bool changing_level)
1937+{
1938+ /* Change our menus.. */
1939+ toggle_menus(true);
1940+
1941+ // LP change: reset screen so that extravision will not be persistent
1942+ reset_screen();
1943+
1944+ enter_screen();
1945+
1946+ // LP: this is in case we are starting underneath a liquid
1947+ if (!OGL_IsActive() || !(TEST_FLAG(Get_OGL_ConfigureData().Flags,OGL_Flag_Fader)))
1948+ {
1949+ set_fade_effect(NONE);
1950+ SetFadeEffectDelay(TICKS_PER_SECOND/2);
1951+ }
1952+
1953+ // Screen should already be black!
1954+ validate_world_window();
1955+
1956+ draw_interface();
1957+
1958+#ifdef PERFORMANCE
1959+ PerfControl(perf_globals, true);
1960+#endif
1961+
1962+ // ZZZ: If it's a netgame, we want prediction; else no.
1963+ set_prediction_wanted(user == _network_player);
1964+
1965+ game_state.state= _game_in_progress;
1966+ game_state.current_screen= 0;
1967+ game_state.phase = MACHINE_TICKS_PER_SECOND;
1968+ game_state.last_ticks_on_idle= machine_tick_count();
1969+ game_state.user= user;
1970+ game_state.flags= 0;
1971+
1972+ assert((!changing_level&&!get_keyboard_controller_status()) || (changing_level && get_keyboard_controller_status()));
1973+ if(!changing_level)
1974+ {
1975+ set_keyboard_controller_status(true);
1976+ }
1977+}
1978+
1979+// LP: "static" removed
1980+void handle_load_game(
1981+ void)
1982+{
1983+ FileSpecifier FileToLoad;
1984+ bool success= false;
1985+
1986+ force_system_colors();
1987+ show_cursor(); // JTP: Was hidden by force system colors
1988+ if(choose_saved_game_to_load(FileToLoad))
1989+ {
1990+ if(load_and_start_game(FileToLoad))
1991+ {
1992+ success= true;
1993+ }
1994+ }
1995+
1996+ if(!success)
1997+ {
1998+ hide_cursor(); // JTP: Will be shown when fade stops
1999+ display_main_menu();
2000+ }
2001+}
2002+
2003+extern bool current_net_game_has_scores();
2004+
2005+static void finish_game(
2006+ bool return_to_main_menu)
2007+{
2008+ set_keyboard_controller_status(false);
2009+
2010+#ifdef PERFORMANCE
2011+ PerfControl(perf_globals, false);
2012+#endif
2013+ /* Note that we have to deal with the switch demo state later because */
2014+ /* Alain's code calls us at interrupt level 1. (so we defer it) */
2015+ assert(game_state.state==_game_in_progress || game_state.state==_switch_demo || game_state.state==_revert_game || game_state.state==_change_level || game_state.state==_begin_display_of_epilogue);
2016+ toggle_menus(false);
2017+
2018+ stop_fade();
2019+ set_fade_effect(NONE);
2020+ exit_screen();
2021+
2022+ /* Stop the replay */
2023+ switch(game_state.user)
2024+ {
2025+ case _single_player:
2026+ case _network_player:
2027+ stop_recording();
2028+ break;
2029+
2030+ case _demo:
2031+ case _replay:
2032+ stop_replay();
2033+ break;
2034+
2035+ default:
2036+ vhalt(csprintf(temporary, "What is user %d?", game_state.user));
2037+ break;
2038+ }
2039+
2040+ /* Fade out! (Pray) */ // should be interface_color_table for valkyrie, but doesn't work.
2041+ Music::instance()->ClearLevelMusic();
2042+ Music::instance()->FadeOut(MACHINE_TICKS_PER_SECOND / 2);
2043+ full_fade(_cinematic_fade_out, interface_color_table);
2044+ paint_window_black();
2045+ full_fade(_end_cinematic_fade_out, interface_color_table);
2046+
2047+ show_cursor();
2048+
2049+ leaving_map();
2050+
2051+ // LP: stop playing the background music if it was present
2052+ Music::instance()->StopLevelMusic();
2053+
2054+ /* Get as much memory back as we can. */
2055+ free_and_unlock_memory(); // this could call free_map..
2056+ unload_all_collections();
2057+ SoundManager::instance()->UnloadAllSounds();
2058+
2059+#if !defined(DISABLE_NETWORKING)
2060+ if (game_state.user==_network_player)
2061+ {
2062+ if(game_state.current_netgame_allows_microphone)
2063+ {
2064+ remove_network_microphone();
2065+ }
2066+ NetUnSync(); // gracefully exit from the game
2067+
2068+ /* Don't update the screen, etc.. */
2069+ game_state.state= _displaying_network_game_dialogs;
2070+
2071+ force_system_colors();
2072+ display_net_game_stats();
2073+ exit_networking();
2074+ load_environment_from_preferences();
2075+ }
2076+ else
2077+#endif // !defined(DISABLE_NETWORKING)
2078+ if (game_state.user == _replay && !(dynamic_world->game_information.game_type == _game_of_kill_monsters && dynamic_world->player_count == 1))
2079+ {
2080+ game_state.state = _displaying_network_game_dialogs;
2081+
2082+ force_system_colors();
2083+ display_net_game_stats();
2084+ }
2085+
2086+ if(return_to_main_menu) display_main_menu();
2087+}
2088+
2089+static void clean_up_after_failed_game(bool inNetgame, bool inRecording, bool inFullCleanup)
2090+{
2091+ /* Stop recording.. */
2092+ if(inRecording)
2093+ {
2094+ stop_recording();
2095+ }
2096+
2097+ /* Show the cursor here on failure. */
2098+ show_cursor();
2099+
2100+ /* The only time we don't clean up is on the replays.. */
2101+ if(inFullCleanup)
2102+ {
2103+ if (inNetgame)
2104+ {
2105+#if !defined(DISABLE_NETWORKING)
2106+ if(game_state.current_netgame_allows_microphone)
2107+ {
2108+ remove_network_microphone();
2109+ }
2110+ exit_networking();
2111+#endif // !defined(DISABLE_NETWORKING)
2112+ } else {
2113+/* NOTE: The network code is now responsible for displaying its own errors!!!! */
2114+ /* Give them the error... */
2115+ display_loading_map_error();
2116+ }
2117+
2118+ /* Display the main menu on failure.... */
2119+ display_main_menu();
2120+ }
2121+ set_game_error(systemError, errNone);
2122+}
2123+
2124+static void handle_network_game(
2125+ bool gatherer)
2126+{
2127+#if !defined(DISABLE_NETWORKING)
2128+ bool successful_gather = false;
2129+ bool joined_resume_game = false;
2130+
2131+ force_system_colors();
2132+
2133+ /* Don't update the screen, etc.. */
2134+ game_state.state= _displaying_network_game_dialogs;
2135+ if(gatherer)
2136+ {
2137+ successful_gather= network_gather(false);
2138+ if (successful_gather) successful_gather= NetStart();
2139+ } else {
2140+ int theNetworkJoinResult= network_join();
2141+ if (theNetworkJoinResult == kNetworkJoinedNewGame || theNetworkJoinResult == kNetworkJoinedResumeGame) successful_gather= true;
2142+ if (theNetworkJoinResult == kNetworkJoinedResumeGame) joined_resume_game= true;
2143+ }
2144+
2145+ if (successful_gather)
2146+ {
2147+ if (joined_resume_game)
2148+ {
2149+ if (join_networked_resume_game() == false) clean_up_after_failed_game(true /*netgame*/, false /*recording*/, true /*full cleanup*/);
2150+ }
2151+ else
2152+ {
2153+ begin_game(_network_player, false);
2154+ }
2155+ } else {
2156+ /* We must restore the colors on cancel. */
2157+ display_main_menu();
2158+ }
2159+#else // !defined(DISABLE_NETWORKING)
2160+ alert_user(infoError, strERRORS, networkNotSupportedForDemo, 0);
2161+#endif // !defined(DISABLE_NETWORKING)
2162+}
2163+
2164+static void handle_save_film(
2165+ void)
2166+{
2167+ force_system_colors();
2168+ show_cursor(); // JTP: Hidden by force_system_colors
2169+ move_replay();
2170+ hide_cursor(); // JTP: Will be shown by display_main_menu
2171+ display_main_menu();
2172+}
2173+
2174+static void next_game_screen(
2175+ void)
2176+{
2177+ struct screen_data *data= get_screen_data(game_state.state);
2178+
2179+ stop_interface_fade();
2180+ if(++game_state.current_screen>=data->screen_count)
2181+ {
2182+ switch(game_state.state)
2183+ {
2184+ case _display_main_menu:
2185+ /* Whoops. didn't get it. */
2186+ alert_user(fatalError, strERRORS, outOfMemory, 0);
2187+ break;
2188+
2189+ case _display_quit_screens:
2190+ /* Fade out.. */
2191+ interface_fade_out(data->screen_base+game_state.current_screen, true);
2192+ game_state.state= _quit_game;
2193+ break;
2194+
2195+ default:
2196+ display_main_menu();
2197+ break;
2198+ }
2199+ } else {
2200+ if(game_state.state==_display_intro_screens &&
2201+ game_state.current_screen==INTRO_SCREEN_TO_START_SONG_ON)
2202+ {
2203+ Music::instance()->RestartIntroMusic();
2204+ }
2205+ // LP addition: check to see if a picture exists before drawing it.
2206+ // Otherwise, set the countdown value to zero.
2207+ short pict_resource_number= data->screen_base+game_state.current_screen;
2208+ if (images_picture_exists(pict_resource_number))
2209+ {
2210+ game_state.phase= data->duration;
2211+ game_state.last_ticks_on_idle= machine_tick_count();
2212+ display_screen(data->screen_base);
2213+ }
2214+ else
2215+ {
2216+ game_state.phase= 0;
2217+ game_state.last_ticks_on_idle= machine_tick_count();
2218+ }
2219+ }
2220+}
2221+
2222+static void display_loading_map_error(
2223+ void)
2224+{
2225+ short error, type;
2226+
2227+ /* Give them the error... */
2228+ error= get_game_error(&type);
2229+ if(type==gameError)
2230+ {
2231+ short string_id;
2232+
2233+ switch(error)
2234+ {
2235+ case errServerDied:
2236+ string_id= serverQuitInCooperativeNetGame;
2237+ break;
2238+ case errUnsyncOnLevelChange:
2239+ string_id= unableToGracefullyChangeLevelsNet;
2240+ break;
2241+
2242+ case errMapFileNotSet:
2243+ case errIndexOutOfRange:
2244+ case errTooManyOpenFiles:
2245+ case errUnknownWadVersion:
2246+ case errWadIndexOutOfRange:
2247+ default:
2248+ string_id= badReadMapGameError;
2249+ break;
2250+ }
2251+ alert_user(infoError, strERRORS, string_id, error);
2252+ } else {
2253+ alert_user(infoError, strERRORS, badReadMapSystemError, error);
2254+ }
2255+ set_game_error(systemError, errNone);
2256+}
2257+
2258+static void force_system_colors(
2259+ void)
2260+{
2261+ if(can_interface_fade_out())
2262+ {
2263+ interface_fade_out(MAIN_MENU_BASE, true);
2264+ }
2265+
2266+ if(interface_bit_depth==8)
2267+ {
2268+ struct color_table *system_colors= build_8bit_system_color_table();
2269+
2270+ assert_world_color_table(system_colors, (struct color_table *) NULL);
2271+
2272+ delete system_colors;
2273+ }
2274+}
2275+
2276+static void display_screen(
2277+ short base_pict_id)
2278+{
2279+ short pict_resource_number= base_pict_id+game_state.current_screen;
2280+ static bool picture_drawn= false;
2281+
2282+ if (images_picture_exists(pict_resource_number))
2283+ {
2284+ stop_interface_fade();
2285+
2286+ if(current_picture_clut)
2287+ {
2288+ interface_fade_out(pict_resource_number, false);
2289+ }
2290+
2291+ assert(!current_picture_clut);
2292+ current_picture_clut= calculate_picture_clut(CLUTSource_Images,pict_resource_number);
2293+ current_picture_clut_depth= interface_bit_depth;
2294+
2295+ if(current_picture_clut)
2296+ {
2297+ /* slam the entire clut to black, now. */
2298+ if (interface_bit_depth==8)
2299+ {
2300+ assert_world_color_table(current_picture_clut, (struct color_table *) NULL);
2301+ }
2302+
2303+ full_fade(_start_cinematic_fade_in, current_picture_clut);
2304+
2305+ draw_full_screen_pict_resource_from_images(pict_resource_number);
2306+ picture_drawn= true;
2307+
2308+ assert(current_picture_clut);
2309+ start_interface_fade(_long_cinematic_fade_in, current_picture_clut);
2310+ }
2311+ }
2312+
2313+ if(!picture_drawn)
2314+ {
2315+dprintf("Didn't draw: %d;g", pict_resource_number);
2316+ /* Go for the next one.. */
2317+ next_game_screen();
2318+ }
2319+}
2320+
2321+static bool point_in_rectangle(
2322+ short x,
2323+ short y,
2324+ screen_rectangle *rect)
2325+{
2326+ bool in_rectangle= false;
2327+
2328+ if(x>=rect->left && x<rect->right && y>=rect->top && y<rect->bottom)
2329+ {
2330+ in_rectangle= true;
2331+ }
2332+
2333+ return in_rectangle;
2334+}
2335+
2336+#ifndef SDL
2337+extern bool MoviePlaying; // interface_macintosh.cpp
2338+#endif
2339+static void handle_interface_menu_screen_click(
2340+ short x,
2341+ short y,
2342+ bool cheatkeys_down)
2343+{
2344+ short index;
2345+ screen_rectangle *screen_rect;
2346+ short xoffset = 0, yoffset = 0;
2347+
2348+#ifndef SDL
2349+ if (MoviePlaying)
2350+ return; // fixes click-through movie bug
2351+#endif
2352+
2353+#ifdef SDL
2354+ xoffset = (SDL_GetVideoSurface()->w - 640) / 2;
2355+ yoffset = (SDL_GetVideoSurface()->h - 480) / 2;
2356+#endif
2357+
2358+ /* find it.. */
2359+ for(index= _new_game_button_rect; index<NUMBER_OF_INTERFACE_RECTANGLES; ++index)
2360+ {
2361+ screen_rect= get_interface_rectangle(index);
2362+ if (point_in_rectangle(x - xoffset, y - yoffset, screen_rect))
2363+ break;
2364+ }
2365+
2366+ /* we found one.. */
2367+ if(index!=NUMBER_OF_INTERFACE_RECTANGLES)
2368+ {
2369+ if(enabled_item(index-_new_game_button_rect+1))
2370+ {
2371+ bool last_state= true;
2372+
2373+ stop_interface_fade();
2374+
2375+ screen_rect= get_interface_rectangle(index);
2376+
2377+ /* Draw it initially depressed.. */
2378+ draw_button(index, last_state);
2379+
2380+ while(mouse_still_down())
2381+ {
2382+ bool state;
2383+
2384+ get_mouse_position(&x, &y);
2385+ state= point_in_rectangle(x - xoffset, y - yoffset, screen_rect);
2386+ if(state != last_state)
2387+ {
2388+ draw_button(index, state);
2389+ last_state= state;
2390+ }
2391+ }
2392+
2393+ /* Draw it unpressed.. */
2394+ draw_button(index, false);
2395+
2396+ if(last_state)
2397+ {
2398+ do_menu_item_command(mInterface, index-_new_game_button_rect+1, cheatkeys_down);
2399+ }
2400+ }
2401+ }
2402+}
2403+
2404+/* Note that this is modal. This sucks... */
2405+static void try_and_display_chapter_screen(
2406+ short pict_resource_number,
2407+ bool interface_table_is_valid,
2408+ bool text_block)
2409+{
2410+ /* If the picture exists... */
2411+ if (scenario_picture_exists(pict_resource_number))
2412+ {
2413+ short existing_state= game_state.state;
2414+ game_state.state= _display_chapter_heading;
2415+ free_and_unlock_memory();
2416+
2417+ /* This will NOT work if the initial level entered has a chapter screen, which is why */
2418+ /* we perform this check. (The interface_color_table is not valid...) */
2419+ if(interface_table_is_valid)
2420+ {
2421+ full_fade(_cinematic_fade_out, interface_color_table);
2422+ paint_window_black();
2423+ }
2424+
2425+ /* Fade the screen to black.. */
2426+ assert(!current_picture_clut);
2427+ current_picture_clut= calculate_picture_clut(CLUTSource_Scenario,pict_resource_number);
2428+ current_picture_clut_depth= interface_bit_depth;
2429+
2430+ if (current_picture_clut)
2431+ {
2432+ LoadedResource SoundRsrc;
2433+#if defined(mac)
2434+ SndChannelPtr channel= NULL;
2435+ SndListHandle sound= NULL;
2436+#endif
2437+
2438+ /* slam the entire clut to black, now. */
2439+ if (interface_bit_depth==8)
2440+ {
2441+ assert_world_color_table(current_picture_clut, (struct color_table *) NULL);
2442+ }
2443+ full_fade(_start_cinematic_fade_in, current_picture_clut);
2444+
2445+ /* Draw the picture */
2446+ draw_full_screen_pict_resource_from_scenario(pict_resource_number);
2447+
2448+ if (get_sound_resource_from_scenario(pict_resource_number,SoundRsrc))
2449+ {
2450+#if defined(mac)
2451+ sound = SndListHandle(SoundRsrc.GetHandle());
2452+
2453+ OSErr sound_error= SndNewChannel(&channel, 0, 0, (SndCallBackUPP) NULL);
2454+
2455+ if (sound_error==noErr)
2456+ {
2457+ HLockHi((Handle)sound);
2458+ SndPlay(channel, sound, true);
2459+ }
2460+#elif defined(SDL)
2461+ Mixer::instance()->PlaySoundResource(SoundRsrc);
2462+#endif
2463+ }
2464+
2465+ /* Fade in.... */
2466+ assert(current_picture_clut);
2467+ full_fade(_long_cinematic_fade_in, current_picture_clut);
2468+
2469+ scroll_full_screen_pict_resource_from_scenario(pict_resource_number, text_block);
2470+
2471+ wait_for_click_or_keypress(text_block ? -1 : 10*MACHINE_TICKS_PER_SECOND);
2472+
2473+ /* Fade out! (Pray) */
2474+ interface_fade_out(pict_resource_number, false);
2475+
2476+#if defined(mac)
2477+ if (channel)
2478+ SndDisposeChannel(channel, true);
2479+#elif defined(SDL)
2480+ Mixer::instance()->StopSoundResource();
2481+#endif
2482+ }
2483+ game_state.state= existing_state;
2484+ }
2485+}
2486+
2487+
2488+#if !defined(DISABLE_NETWORKING)
2489+/*
2490+ * Network microphone handling
2491+ */
2492+
2493+void install_network_microphone(
2494+ void)
2495+{
2496+ open_network_speaker();
2497+ NetAddDistributionFunction(kNewNetworkAudioDistributionTypeID, received_network_audio_proc, true);
2498+ open_network_microphone();
2499+}
2500+
2501+void remove_network_microphone(
2502+ void)
2503+{
2504+ close_network_microphone();
2505+ NetRemoveDistributionFunction(kNewNetworkAudioDistributionTypeID);
2506+ close_network_speaker();
2507+}
2508+#endif // !defined(DISABLE_NETWORKING)
2509+
2510+
2511+/* ------------ interface fade code */
2512+/* Be aware that we could try to change bit depths before a fade is completed. */
2513+static void start_interface_fade(
2514+ short type,
2515+ struct color_table *original_color_table)
2516+{
2517+ hide_cursor();
2518+ assert(!interface_fade_in_progress);
2519+ animated_color_table= new color_table;
2520+ obj_copy(*animated_color_table, *original_color_table);
2521+
2522+ if(animated_color_table)
2523+ {
2524+ interface_fade_in_progress= true;
2525+ interface_fade_type= type;
2526+
2527+ explicit_start_fade(type, original_color_table, animated_color_table);
2528+ }
2529+}
2530+
2531+static void update_interface_fades(
2532+ void)
2533+{
2534+ bool still_fading= false;
2535+
2536+ if(interface_fade_in_progress)
2537+ {
2538+ still_fading= update_fades();
2539+ if(!still_fading)
2540+ {
2541+ stop_interface_fade();
2542+ }
2543+ }
2544+}
2545+
2546+void stop_interface_fade(
2547+ void)
2548+{
2549+ if(interface_fade_in_progress)
2550+ {
2551+ stop_fade();
2552+ interface_fade_in_progress= false;
2553+
2554+ assert(animated_color_table);
2555+ delete animated_color_table;
2556+
2557+ if (interface_bit_depth==8)
2558+ {
2559+ assert_world_color_table(current_picture_clut, (struct color_table *) NULL);
2560+ }
2561+
2562+ if(game_state.state==_display_main_menu)
2563+ {
2564+ /* This isn't a showcursor because of balancing problems (first time through..) */
2565+ show_cursor();
2566+ }
2567+ }
2568+}
2569+
2570+/* Called right before we start a game.. */
2571+void interface_fade_out(
2572+ short pict_resource_number,
2573+ bool fade_music)
2574+{
2575+ assert(current_picture_clut);
2576+ if(current_picture_clut)
2577+ {
2578+ struct color_table *fadeout_animated_color_table;
2579+
2580+ /* We have to check this because they could go into preferences and change on us, */
2581+ /* the evil swine. */
2582+ if(current_picture_clut_depth != interface_bit_depth)
2583+ {
2584+ delete current_picture_clut;
2585+ current_picture_clut= calculate_picture_clut(CLUTSource_Images,pict_resource_number);
2586+ current_picture_clut_depth= interface_bit_depth;
2587+ }
2588+
2589+ hide_cursor();
2590+
2591+ fadeout_animated_color_table= new color_table;
2592+ obj_copy(*fadeout_animated_color_table, *current_picture_clut);
2593+
2594+ if(fade_music)
2595+ Music::instance()->FadeOut(MACHINE_TICKS_PER_SECOND/2);
2596+ if (fadeout_animated_color_table)
2597+ {
2598+ explicit_start_fade(_cinematic_fade_out, current_picture_clut, fadeout_animated_color_table);
2599+ while (update_fades())
2600+ Music::instance()->Idle();
2601+
2602+ /* Oops. Founda memory leak.. */
2603+ delete fadeout_animated_color_table;
2604+ }
2605+
2606+ if(fade_music)
2607+ {
2608+ while(Music::instance()->Playing())
2609+ Music::instance()->Idle();
2610+
2611+ /* and give up the memory */
2612+ Music::instance()->Pause();
2613+ }
2614+
2615+ paint_window_black();
2616+ full_fade(_end_cinematic_fade_out, current_picture_clut);
2617+
2618+ /* Hopefully we can do this here.. */
2619+ delete current_picture_clut;
2620+ current_picture_clut= NULL;
2621+ }
2622+}
2623+
2624+static bool can_interface_fade_out(
2625+ void)
2626+{
2627+ return (current_picture_clut==NULL) ? false : true;
2628+}
2629+
2630+bool interface_fade_finished(
2631+ void)
2632+{
2633+ return fade_finished();
2634+}
2635+
2636+
2637+void do_preferences(void)
2638+{
2639+ struct screen_mode_data mode = graphics_preferences->screen_mode;
2640+
2641+ handle_preferences();
2642+
2643+ if (mode.bit_depth != graphics_preferences->screen_mode.bit_depth) {
2644+ paint_window_black();
2645+ Screen::instance()->Initialize(&graphics_preferences->screen_mode);
2646+
2647+ /* Re fade in, so that we get the proper colortable loaded.. */
2648+ display_main_menu();
2649+ } else if (memcmp(&mode, &graphics_preferences->screen_mode, sizeof(struct screen_mode_data)))
2650+ change_screen_mode(&graphics_preferences->screen_mode, false);
2651+}
2652+
2653+
2654+/*
2655+ * Toggle system hotkeys
2656+ */
2657+
2658+void toggle_menus(bool game_started)
2659+{
2660+ // nothing to do
2661+}
2662+
2663+
2664+/*
2665+ * Update game window
2666+ */
2667+
2668+void update_game_window(void)
2669+{
2670+ switch(get_game_state()) {
2671+ case _game_in_progress:
2672+ update_screen_window();
2673+ break;
2674+
2675+ case _display_quit_screens:
2676+ case _display_intro_screens_for_demo:
2677+ case _display_intro_screens:
2678+ case _display_chapter_heading:
2679+ case _display_prologue:
2680+ case _display_epilogue:
2681+ case _display_credits:
2682+ case _display_main_menu:
2683+ update_interface_display();
2684+ break;
2685+
2686+ default:
2687+ break;
2688+ }
2689+}
2690+
2691+
2692+/*
2693+ * Exit networking
2694+ */
2695+
2696+void exit_networking(void)
2697+{
2698+#if !defined(DISABLE_NETWORKING)
2699+ NetExit();
2700+#endif // !defined(DISABLE_NETWORKING)
2701+}
2702+
2703+
2704+/*
2705+ * Show movie
2706+ */
2707+
2708+extern bool option_nosound;
2709+
2710+void show_movie(short index)
2711+{
2712+#ifdef HAVE_SMPEG
2713+ float PlaybackSize = 2;
2714+
2715+ FileSpecifier IntroMovie;
2716+ FileSpecifier *File = GetLevelMovie(PlaybackSize);
2717+
2718+ if (!File && index == 0)
2719+ {
2720+ if (IntroMovie.SetNameWithPath(getcstr(temporary, strFILENAMES, filenameMOVIE)))
2721+ File = &IntroMovie;
2722+ }
2723+
2724+ if (!File) return;
2725+
2726+ SDL_Surface *s = SDL_GetVideoSurface();
2727+
2728+#if defined(__APPLE__) && defined(__MACH__)
2729+ if (!(s->flags & SDL_FULLSCREEN))
2730+ SDL_putenv("SDL_VIDEO_YUV_HWACCEL=0");
2731+ else
2732+ SDL_putenv("SDL_VIDEO_YUV_HWACCEL=");
2733+
2734+#endif
2735+ {
2736+ SoundManager::Pause pauseSoundManager;
2737+
2738+ SMPEG_Info info;
2739+ SMPEG *movie;
2740+
2741+ movie = SMPEG_new(File->GetPath(), &info, option_nosound ? 0 : 1);
2742+ if (!movie) return;
2743+ if (!info.has_video) {
2744+ SMPEG_delete(movie);
2745+ return;
2746+ }
2747+ int width = (int) (info.width * PlaybackSize);
2748+ int height = (int) (info.height * PlaybackSize);
2749+
2750+ SMPEG_setdisplay(movie, SDL_GetVideoSurface(), NULL, NULL);
2751+ if (width <= s->w && height <= s->h)
2752+ {
2753+ SMPEG_scaleXY(movie, width, height);
2754+ SMPEG_move(movie, (s->w - width) / 2, (s->h - height) / 2);
2755+ }
2756+
2757+ bool done = false;
2758+ SMPEG_play(movie);
2759+ while (!done && SMPEG_status(movie) == SMPEG_PLAYING)
2760+ {
2761+ SDL_Event event;
2762+ while (SDL_PollEvent(&event) )
2763+ {
2764+ switch (event.type) {
2765+ case SDL_KEYDOWN:
2766+ case SDL_MOUSEBUTTONDOWN:
2767+ done = true;
2768+ break;
2769+ default:
2770+ break;
2771+ }
2772+ }
2773+
2774+ SDL_Delay(100);
2775+ }
2776+ SMPEG_delete(movie);
2777+ }
2778+#endif // HAVE_SMPEG
2779+}
2780+
2781+
2782+size_t should_restore_game_networked()
2783+{
2784+ dialog d;
2785+
2786+ vertical_placer *placer = new vertical_placer;
2787+ placer->dual_add(new w_title("RESUME GAME"), d);
2788+ placer->add(new w_spacer, true);
2789+
2790+ horizontal_placer *resume_as_placer = new horizontal_placer;
2791+ w_toggle* theRestoreAsNetgameToggle = new w_toggle(dynamic_world->player_count > 1, 0);
2792+ theRestoreAsNetgameToggle->set_labels_stringset(kSingleOrNetworkStringSetID);
2793+ resume_as_placer->dual_add(theRestoreAsNetgameToggle->label("Resume as"), d);
2794+ resume_as_placer->dual_add(theRestoreAsNetgameToggle, d);
2795+
2796+ placer->add(resume_as_placer, true);
2797+
2798+ placer->add(new w_spacer(), true);
2799+ placer->add(new w_spacer(), true);
2800+
2801+ horizontal_placer *button_placer = new horizontal_placer;
2802+ button_placer->dual_add(new w_button("RESUME", dialog_ok, &d), d);
2803+ button_placer->dual_add(new w_button("CANCEL", dialog_cancel, &d), d);
2804+
2805+ placer->add(button_placer, true);
2806+
2807+ // We return -1 (NONE) for "cancel", 0 for "not networked", and 1 for "networked".
2808+ size_t theResult;
2809+
2810+ d.set_widget_placer(placer);
2811+
2812+ if(d.run() == 0)
2813+ {
2814+ theResult = theRestoreAsNetgameToggle->get_selection();
2815+ }
2816+ else
2817+ {
2818+ theResult = UNONE;
2819+ }
2820+
2821+ return theResult;
2822+}
--- marathon/trunk/AlephOneJP/Misc/sdl_widgets.cpp (nonexistent)
+++ marathon/trunk/AlephOneJP/Misc/sdl_widgets.cpp (revision 488)
@@ -0,0 +1,2485 @@
1+/*
2+
3+ Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4+ and the "Aleph One" developers.
5+
6+ This program is free software; you can redistribute it and/or modify
7+ it under the terms of the GNU General Public License as published by
8+ the Free Software Foundation; either version 2 of the License, or
9+ (at your option) any later version.
10+
11+ This program is distributed in the hope that it will be useful,
12+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ GNU General Public License for more details.
15+
16+ This license is contained in the file "COPYING",
17+ which is included with this source code; it is available online at
18+ http://www.gnu.org/licenses/gpl.html
19+
20+*/
21+
22+/*
23+ * sdl_widgets.cpp - Widgets for SDL dialogs
24+ *
25+ * Written in 2000 by Christian Bauer
26+ *
27+ * Sept-Nov. 2001 (Woody Zenfell):
28+ * Significant extensions to support more dynamic behavior and more useful layouts
29+ *
30+ * Mar 1, 2002 (Woody Zenfell):
31+ * Moved w_levels here from shell_sdl; am using it in Setup Network Game box.
32+ *
33+ * May 16, 2002 (Woody Zenfell):
34+ * changes to w_key to support assignment of mouse buttons as well as keys;
35+ * also fixed mouse-movement-while-binding behavior.
36+ *
37+ * August 27, 2003 (Woody Zenfell):
38+ * new w_enabling_toggle can enable/disable a bank of other widgets according to its state
39+ * new w_file_chooser displays filename; allows selection of file (via FileSpecifier::ReadDialog())
40+ */
41+
42+#include "cseries.h"
43+#include "sdl_dialogs.h"
44+#include "network_dialog_widgets_sdl.h"
45+#include "sdl_fonts.h"
46+#include "sdl_widgets.h"
47+#include "resource_manager.h"
48+
49+#include "shape_descriptors.h"
50+#include "screen_drawing.h"
51+#include "images.h"
52+#include "shell.h"
53+#include "world.h"
54+#include "SoundManager.h"
55+#include "interface.h"
56+#include "player.h"
57+
58+#include "screen.h"
59+
60+// ZZZ: for stringset business for modified w_select
61+#include "TextStrings.h"
62+
63+#include "mouse.h" // (ZZZ) NUM_SDL_MOUSE_BUTTONS, SDLK_BASE_MOUSE_BUTTON
64+
65+#include <sstream>
66+
67+/*
68+ * Widget base class
69+ */
70+
71+// ZZZ: initialize my additional storage elements
72+// (thought: I guess "widget" could be simplified, and have a subclass "useful_widget" to handle most things
73+// other than spacers. Spacers are common and I guess we're starting to eat a fair amount of storage for a
74+// widget that does nothing and draws nothing... oh well, at least RAM is cheap. ;) )
75+widget::widget() : active(false), dirty(false), enabled(true), font(NULL), identifier(NONE), owning_dialog(NULL), saved_min_width(0), saved_min_height(0), associated_label(0)
76+{
77+ rect.x = 0;
78+ rect.y = 0;
79+ rect.w = 0;
80+ rect.h = 0;
81+}
82+
83+widget::widget(int theme_widget) : active(false), dirty(false), enabled(true), font(get_theme_font(theme_widget, style)), identifier(NONE), owning_dialog(NULL), saved_min_width(0), saved_min_height(0), associated_label(0)
84+{
85+ rect.x = 0;
86+ rect.y = 0;
87+ rect.w = 0;
88+ rect.h = 0;
89+}
90+
91+void widget::associate_label(w_label *label)
92+{
93+ associated_label = label;
94+}
95+
96+w_label *widget::label(const char *text)
97+{
98+ if (!associated_label)
99+ {
100+ associated_label = new w_label(text);
101+ associated_label->associate_widget(this);
102+ }
103+
104+ return associated_label;
105+}
106+
107+// ZZZ: enable/disable
108+void widget::set_enabled(bool inEnabled)
109+{
110+ if(enabled != inEnabled) {
111+ enabled = inEnabled;
112+
113+ // If we had the focus when we were disabled, we should not have the focus afterward.
114+ if(active && !enabled)
115+ owning_dialog->activate_next_widget();
116+
117+ // Assume we need a redraw to reflect new state
118+ dirty = true;
119+
120+ if (associated_label)
121+ associated_label->set_enabled(inEnabled);
122+ }
123+}
124+
125+void widget::place(const SDL_Rect &r, placement_flags flags)
126+{
127+ rect.h = r.h;
128+ rect.y = r.y;
129+
130+ if (flags & placeable::kFill)
131+ {
132+ rect.x = r.x;
133+ rect.w = r.w;
134+ }
135+ else
136+ {
137+ rect.w = saved_min_width;
138+ if (flags & placeable::kAlignLeft)
139+ {
140+ rect.x = r.x;
141+ }
142+ else if (flags & placeable::kAlignRight)
143+ {
144+ rect.x = r.x + r.w - saved_min_width;
145+ }
146+ else
147+ {
148+ rect.x = r.x + (r.w - saved_min_width) / 2;
149+ }
150+ }
151+}
152+
153+
154+/*
155+ * Static text
156+ */
157+
158+// ZZZ change: copy the given string instead of just pointing to it. Much easier for messages that change.
159+w_static_text::w_static_text(const char *t, int _theme_type) : widget(_theme_type), theme_type(_theme_type)
160+{
161+ text = strdup(t);
162+ rect.w = text_width(text, font, style);
163+ rect.h = font->get_line_height();
164+ saved_min_height = rect.h;
165+ saved_min_width = rect.w;
166+}
167+
168+void w_static_text::draw(SDL_Surface *s) const
169+{
170+ uint32 pixel;
171+ pixel = get_theme_color(theme_type, DEFAULT_STATE, 0);
172+
173+ draw_text(s, text, rect.x, rect.y + font->get_ascent(), pixel, font, style);
174+}
175+
176+void w_label::click(int x, int y)
177+{
178+ if (associated_widget)
179+ associated_widget->click(x, y);
180+
181+}
182+
183+void w_label::draw(SDL_Surface *s) const
184+{
185+ int state = enabled ? (active ? ACTIVE_STATE : DEFAULT_STATE) : DISABLED_STATE;
186+ uint16 style = 0;
187+ draw_text(s, text, rect.x, rect.y + font->get_ascent() + (rect.h - font->get_line_height()) / 2, get_theme_color(LABEL_WIDGET, state, FOREGROUND_COLOR), font, style);
188+}
189+
190+// ZZZ addition: change text.
191+void
192+w_static_text::set_text(const char* t) {
193+ free(text);
194+ text = strdup(t);
195+ dirty = true;
196+}
197+
198+// ZZZ addition: free text buffer.
199+w_static_text::~w_static_text() {
200+ free(text);
201+}
202+
203+/*
204+ * Picture (PICT resource)
205+ */
206+
207+w_pict::w_pict(int id)
208+{
209+ LoadedResource rsrc;
210+ get_resource(FOUR_CHARS_TO_INT('P', 'I', 'C', 'T'), id, rsrc);
211+ picture = picture_to_surface(rsrc);
212+ if (picture) {
213+ rect.w = static_cast<uint16>(picture->w);
214+ rect.h = static_cast<uint16>(picture->h);
215+ SDL_SetColorKey(picture, SDL_SRCCOLORKEY, SDL_MapRGB(picture->format, 0xff, 0xff, 0xff));
216+ } else
217+ rect.w = rect.h = 0;
218+}
219+
220+w_pict::~w_pict()
221+{
222+ if (picture)
223+ SDL_FreeSurface(picture);
224+}
225+
226+void w_pict::draw(SDL_Surface *s) const
227+{
228+ if (picture)
229+ SDL_BlitSurface(picture, NULL, s, const_cast<SDL_Rect *>(&rect));
230+}
231+
232+
233+/*
234+ * Button
235+ */
236+
237+w_button_base::w_button_base(const char *t, action_proc p, void *a, int _type) : widget(_type), text(t), proc(p), arg(a), down(false), pressed(false), type(_type)
238+{
239+ rect.w = text_width(text.c_str(), font, style) + get_theme_space(_type, BUTTON_L_SPACE) + get_theme_space(_type, BUTTON_R_SPACE);
240+ button_c_default = get_theme_image(_type, DEFAULT_STATE, BUTTON_C_IMAGE, rect.w - get_theme_image(_type, DEFAULT_STATE, BUTTON_L_IMAGE)->w - get_theme_image(_type, DEFAULT_STATE, BUTTON_R_IMAGE)->w);
241+ button_c_active = get_theme_image(_type, ACTIVE_STATE, BUTTON_C_IMAGE, rect.w - get_theme_image(_type, ACTIVE_STATE, BUTTON_L_IMAGE)->w - get_theme_image(_type, ACTIVE_STATE, BUTTON_R_IMAGE)->w);
242+ button_c_disabled = get_theme_image(_type, DISABLED_STATE, BUTTON_C_IMAGE, rect.w - get_theme_image(_type, DISABLED_STATE, BUTTON_L_IMAGE)->w - get_theme_image(_type, DISABLED_STATE, BUTTON_R_IMAGE)->w);
243+ button_c_pressed = get_theme_image(_type, PRESSED_STATE, BUTTON_C_IMAGE, rect.w - get_theme_image(_type, PRESSED_STATE, BUTTON_L_IMAGE)->w - get_theme_image(_type, PRESSED_STATE, BUTTON_R_IMAGE)->w);
244+
245+ rect.h = static_cast<uint16>(get_theme_space(_type, BUTTON_HEIGHT));
246+
247+ saved_min_width = rect.w;
248+ saved_min_height = rect.h;
249+}
250+
251+w_button_base::~w_button_base()
252+{
253+ if (button_c_default) SDL_FreeSurface(button_c_default);
254+ if (button_c_active) SDL_FreeSurface(button_c_active);
255+ if (button_c_disabled) SDL_FreeSurface(button_c_disabled);
256+ if (button_c_pressed) SDL_FreeSurface(button_c_pressed);
257+}
258+
259+void w_button_base::set_callback(action_proc p, void *a)
260+{
261+ proc = p;
262+ arg = a;
263+}
264+
265+void w_button_base::draw(SDL_Surface *s) const
266+{
267+ // Label (ZZZ: different color for disabled)
268+ int state = DEFAULT_STATE;
269+ if (pressed)
270+ state = PRESSED_STATE;
271+ else if (!enabled)
272+ state = DISABLED_STATE;
273+ else if (active)
274+ state = ACTIVE_STATE;
275+
276+ if (use_theme_images(type))
277+ {
278+ SDL_Surface *button_l = get_theme_image(type, state, BUTTON_L_IMAGE);
279+ SDL_Surface *button_r = get_theme_image(type, state, BUTTON_R_IMAGE);
280+ SDL_Surface *button_c = button_c_default;
281+ if (pressed)
282+ button_c = button_c_pressed;
283+ else if (!enabled)
284+ button_c = button_c_disabled;
285+ else if (active)
286+ button_c = button_c_active;
287+
288+ // Button image
289+ SDL_Rect r = {rect.x, rect.y,
290+ static_cast<Uint16>(button_l->w),
291+ static_cast<Uint16>(button_l->h)};
292+ SDL_BlitSurface(button_l, NULL, s, &r);
293+ r.x = r.x + static_cast<Sint16>(button_l->w); // MDA: MSVC throws warnings if we use +=
294+ r.w = static_cast<Uint16>(button_c->w);
295+ r.h = static_cast<Uint16>(button_c->h);
296+ SDL_BlitSurface(button_c, NULL, s, &r);
297+ r.x = r.x + static_cast<Sint16>(button_c->w);
298+ r.w = static_cast<Uint16>(button_r->w);
299+ r.h = static_cast<Uint16>(button_r->h);
300+ SDL_BlitSurface(button_r, NULL, s, &r);
301+ }
302+ else
303+ {
304+ uint32 pixel;
305+ if (use_theme_color(type, BACKGROUND_COLOR))
306+ {
307+ uint32 pixel = get_theme_color(type, state, BACKGROUND_COLOR);
308+ SDL_Rect r = {rect.x + 1, rect.y + 1, rect.w - 2, rect.h - 2};
309+ SDL_FillRect(s, &r, pixel);
310+ }
311+
312+ pixel = get_theme_color(type, state, FRAME_COLOR);
313+ draw_rectangle(s, &rect, pixel);
314+ }
315+
316+ draw_text(s, text.c_str(), rect.x + get_theme_space(type, BUTTON_L_SPACE),
317+ rect.y + get_theme_space(type, BUTTON_T_SPACE) + font->get_ascent(),
318+ get_theme_color(type, state), font, style);
319+}
320+
321+void w_button_base::mouse_move(int x, int y)
322+{
323+ if (down)
324+ {
325+ if (x >= 0 && x <= rect.w && y >= 0 && y <= rect.h)
326+ {
327+ if (!pressed)
328+ dirty = true;
329+ pressed = true;
330+ }
331+ else
332+ {
333+ if (pressed)
334+ dirty = true;
335+ pressed = false;
336+ }
337+ get_owning_dialog()->draw_dirty_widgets();
338+ }
339+}
340+
341+void w_button_base::mouse_down(int, int)
342+{
343+ if (!enabled) return;
344+ down = true;
345+ pressed = true;
346+ dirty = true;
347+ get_owning_dialog()->draw_dirty_widgets();
348+}
349+
350+void w_button_base::mouse_up(int x, int y)
351+{
352+ if (!enabled) return;
353+
354+ down = false;
355+ pressed = false;
356+ dirty = true;
357+ get_owning_dialog()->draw_dirty_widgets();
358+
359+ if (proc && x >= 0 && x <= rect.w && y >= 0 && y <= rect.h)
360+ proc(arg);
361+}
362+
363+void w_button_base::click(int /*x*/, int /*y*/)
364+{
365+ // simulate a mouse press
366+ mouse_down(0, 0);
367+ SDL_Delay(1000 / 12);
368+ mouse_up(0, 0);
369+}
370+
371+/*
372+ * Tabs
373+ */
374+
375+w_tab::w_tab(const vector<string>& _labels, tab_placer *_placer) : widget(TAB_WIDGET), labels(_labels), placer(_placer), active_tab(1), pressed_tab(0)
376+{
377+ saved_min_height = get_theme_space(TAB_WIDGET, BUTTON_HEIGHT);
378+ for (vector<string>::iterator it = labels.begin(); it != labels.end(); ++it)
379+ {
380+ int l_space = (it == labels.begin()) ? get_theme_space(TAB_WIDGET, BUTTON_L_SPACE) : get_theme_space(TAB_WIDGET, TAB_LC_SPACE);
381+ int r_space = (it == labels.end() - 1) ? get_theme_space(TAB_WIDGET, BUTTON_R_SPACE) : get_theme_space(TAB_WIDGET, TAB_RC_SPACE);
382+ int width = l_space + r_space + font->text_width(it->c_str(), style);
383+ widths.push_back(width);
384+ saved_min_width += width;
385+
386+ // load center images
387+ const int states[] = { DEFAULT_STATE, PRESSED_STATE, ACTIVE_STATE, DISABLED_STATE };
388+ images.resize(PRESSED_STATE + 1);
389+ for (int i = 0; i < 4; ++i)
390+ {
391+ int li_space = (it == labels.begin()) ? get_theme_image(TAB_WIDGET, states[i], TAB_L_IMAGE)->w : get_theme_image(TAB_WIDGET, states[i], TAB_LC_IMAGE)->w;
392+ int ri_space = (it == labels.end() - 1) ? get_theme_image(TAB_WIDGET, states[i], TAB_R_IMAGE)->w : get_theme_image(TAB_WIDGET, states[i], TAB_RC_IMAGE)->w;
393+ int c_space = width - li_space - ri_space;
394+ images[states[i]].push_back(get_theme_image(TAB_WIDGET, states[i], TAB_C_IMAGE, c_space));
395+ }
396+ }
397+
398+}
399+
400+w_tab::~w_tab()
401+{
402+ for (std::vector<std::vector<SDL_Surface *> >::iterator it = images.begin(); it != images.end(); ++it)
403+ {
404+ for (std::vector<SDL_Surface *>::iterator it2 = it->begin(); it2 != it->end(); ++it2)
405+ {
406+ SDL_FreeSurface(*it2);
407+ }
408+ }
409+}
410+
411+void w_tab::draw(SDL_Surface *s) const
412+{
413+ int x = rect.x;
414+ for (int i = 0; i < labels.size(); ++i)
415+ {
416+ int state;
417+ if (!enabled)
418+ state = DISABLED_STATE;
419+ else if (i == pressed_tab)
420+ state = PRESSED_STATE;
421+ else if (active && i == active_tab)
422+ state = ACTIVE_STATE;
423+ else
424+ state = DEFAULT_STATE;
425+
426+ int l_space;
427+ int l_offset = 0;
428+
429+ SDL_Surface *l_image;
430+ if (i == 0)
431+ {
432+ l_space = get_theme_space(TAB_WIDGET, BUTTON_L_SPACE);
433+ l_offset = 1;
434+ l_image = get_theme_image(TAB_WIDGET, state, TAB_L_IMAGE);
435+ }
436+ else
437+ {
438+ l_space = get_theme_space(TAB_WIDGET, TAB_LC_SPACE);
439+ l_image = get_theme_image(TAB_WIDGET, state, TAB_LC_IMAGE);
440+ }
441+
442+ int r_space;
443+ int r_offset = 0;
444+ SDL_Surface *r_image;
445+ if (i == labels.size() - 1)
446+ {
447+ r_space = get_theme_space(TAB_WIDGET, BUTTON_R_SPACE);
448+ r_offset = 1;
449+ r_image = get_theme_image(TAB_WIDGET, state, TAB_R_IMAGE);
450+ }
451+ else
452+ {
453+ r_space = get_theme_space(TAB_WIDGET, TAB_RC_SPACE);
454+ r_image = get_theme_image(TAB_WIDGET, state, TAB_RC_IMAGE);
455+ }
456+
457+ int c_space;
458+ SDL_Surface *c_image = images[state][i];
459+ c_space = font->text_width(labels[i].c_str(), style);
460+
461+ if (use_theme_images(TAB_WIDGET))
462+ {
463+ SDL_Rect r = { x, rect.y, static_cast<Uint16>(l_image->w), static_cast<Uint16>(l_image->h) };
464+ SDL_BlitSurface(l_image, NULL, s, &r);
465+ r.x = r.x + static_cast<Sint16>(l_image->w);
466+ r.w = static_cast<Uint16>(c_image->w);
467+ r.h = static_cast<Uint16>(c_image->h);
468+ SDL_BlitSurface(c_image, NULL, s, &r);
469+ r.x = r.x + static_cast<Sint16>(c_image->w);
470+ r.w = static_cast<Uint16>(r_image->w);
471+ r.h = static_cast<Uint16>(r_image->h);
472+ SDL_BlitSurface(r_image, NULL, s, &r);
473+ }
474+ else
475+ {
476+ if (use_theme_color(TAB_WIDGET, BACKGROUND_COLOR))
477+ {
478+ SDL_Rect r = { x + l_offset, rect.y + 1, l_space + c_space + r_space - l_offset - r_offset, get_theme_space(TAB_WIDGET, BUTTON_HEIGHT) - 2 };
479+ uint32 pixel = get_theme_color(TAB_WIDGET, state, BACKGROUND_COLOR);
480+ SDL_FillRect(s, &r, pixel);
481+ }
482+ }
483+
484+ font->draw_text(s, labels[i].c_str(), labels[i].size(), x + l_space, rect.y + get_theme_space(TAB_WIDGET, BUTTON_T_SPACE) + font->get_ascent(), get_theme_color(TAB_WIDGET, state, FOREGROUND_COLOR), style);
485+
486+ x += l_space + c_space + r_space;
487+ }
488+
489+ if (!use_theme_images(TAB_WIDGET))
490+ {
491+ uint32 pixel = get_theme_color(TAB_WIDGET, DEFAULT_STATE, FRAME_COLOR);
492+ // draw the frame
493+ SDL_Rect frame = { rect.x, rect.y, x - rect.x, get_theme_space(TAB_WIDGET, BUTTON_HEIGHT) };
494+ draw_rectangle(s, &frame, pixel);
495+ }
496+}
497+
498+void w_tab::choose_tab(int i)
499+{
500+ pressed_tab = i;
501+ active_tab = (i + 1) % labels.size();
502+ placer->choose_tab(i);
503+ get_owning_dialog()->draw();
504+}
505+
506+void w_tab::click(int x, int y)
507+{
508+ if (enabled)
509+ {
510+ if (!x && !y)
511+ {
512+ choose_tab(active_tab);
513+ }
514+ else
515+ {
516+ int width = 0;
517+ for (int i = 0; i < labels.size(); ++i)
518+ {
519+ if (x > width && x < width + widths[i])
520+ {
521+ choose_tab(i);
522+ return;
523+ }
524+
525+ width += widths[i];
526+ }
527+ }
528+ }
529+}
530+
531+void w_tab::event(SDL_Event& e)
532+{
533+ if (e.type == SDL_KEYDOWN)
534+ {
535+ switch (e.key.keysym.sym) {
536+ case SDLK_LEFT:
537+ if (active_tab > 0)
538+ {
539+ if (active_tab - 1== pressed_tab)
540+ {
541+ if (pressed_tab > 0)
542+ active_tab -= 2;
543+ }
544+ else
545+ active_tab--;
546+
547+ }
548+ dirty = true;
549+ e.type = SDL_NOEVENT;
550+ break;
551+
552+ case SDLK_RIGHT:
553+ if (active_tab < labels.size() - 1)
554+ {
555+ if (active_tab + 1 == pressed_tab)
556+ {
557+ if (pressed_tab < labels.size() - 1)
558+ active_tab += 2;
559+ }
560+ else
561+ active_tab++;
562+ }
563+ dirty = true;
564+ e.type = SDL_NOEVENT;
565+ break;
566+
567+ default:
568+ break;
569+
570+ }
571+ }
572+}
573+
574+/*
575+ * Selection button
576+ */
577+
578+const uint16 MAX_TEXT_WIDTH = 200;
579+
580+// ZZZ: how come we have to do this? because of that "&" in the typedef for action_proc?
581+// Anyway, this fixes the "crash when clicking in the Environment menu" bug we've seen
582+// in the Windows version all this time.
583+w_select_button::w_select_button(const char *s, action_proc p, void *a, bool u)
584+ : widget(LABEL_WIDGET), selection(s), proc(p), arg(a), utf8(u), p_flags(placeable::kDefault)
585+{
586+ uint16 max_selection_width = MAX_TEXT_WIDTH;
587+
588+ saved_min_width = max_selection_width;
589+ saved_min_height = font->get_line_height();
590+}
591+
592+
593+
594+void w_select_button::draw(SDL_Surface *s) const
595+{
596+ int y = rect.y + font->get_ascent();
597+
598+ // Selection (ZZZ: different color for disabled)
599+ set_drawing_clip_rectangle(0, rect.x + selection_x, static_cast<uint16>(s->h), rect.x + rect.w);
600+
601+ int state = enabled ? (active ? ACTIVE_STATE : DEFAULT_STATE) : DISABLED_STATE;
602+
603+ draw_text(s, selection, rect.x + selection_x, y, get_theme_color(ITEM_WIDGET, state), font, style, utf8);
604+ set_drawing_clip_rectangle(SHRT_MIN, SHRT_MIN, SHRT_MAX, SHRT_MAX);
605+
606+ // Cursor
607+ if (active) {
608+ //!!
609+ }
610+}
611+
612+void w_select_button::click(int /*x*/, int /*y*/)
613+{
614+ if(enabled)
615+ proc(arg);
616+}
617+
618+void w_select_button::set_selection(const char *s)
619+{
620+ selection = s;
621+ if (p_flags & placeable::kAlignRight)
622+ {
623+ selection_x = rect.w - text_width(selection, font, style);
624+ }
625+ dirty = true;
626+}
627+
628+void w_select_button::place(const SDL_Rect &r, placement_flags flags)
629+{
630+ rect.h = r.h;
631+ rect.y = r.y;
632+
633+ rect.x = r.x;
634+ rect.w = r.w;
635+ p_flags = flags;
636+
637+ if (flags & placeable::kAlignRight)
638+ {
639+ selection_x = rect.w - text_width(selection, font, style);
640+ }
641+ else
642+ {
643+ selection_x = 0;
644+ }
645+
646+}
647+
648+/*
649+ * Selection widget (base class)
650+ */
651+
652+// ZZZ: change of behavior/semantics:
653+// if passed an invalid selection, reset to a VALID one (i.e. 0)
654+// if no valid labels, returns -1 when asked for selection
655+// draw(), get_selection() check num_labels directly instead of trying to keep selection set at -1
656+
657+static const char* sNoValidOptionsString = "(no valid options)"; // XXX should be moved outside compiled code e.g. to MML
658+
659+w_select::w_select(size_t s, const char **l) : widget(LABEL_WIDGET), labels(l), we_own_labels(false), selection(s), selection_changed_callback(NULL), utf8(false)
660+{
661+ num_labels = 0;
662+ if(labels) {
663+ while (labels[num_labels])
664+ num_labels++;
665+ if (selection >= num_labels || selection < 0)
666+ selection = 0;
667+ }
668+
669+ saved_min_height = font->get_line_height();
670+}
671+
672+
673+w_select::~w_select() {
674+ if(we_own_labels && labels)
675+ free(labels);
676+}
677+
678+void w_select::place(const SDL_Rect& r, placement_flags flags)
679+{
680+ rect.h = r.h;
681+ rect.y = r.y;
682+ rect.x = r.x;
683+ rect.w = r.w;
684+
685+}
686+
687+int w_select::min_width()
688+{
689+ return get_largest_label_width();
690+}
691+
692+void w_select::draw(SDL_Surface *s) const
693+{
694+ int y = rect.y + font->get_ascent();
695+
696+ // Selection (ZZZ: different color for disabled)
697+ const char *str = (num_labels > 0 ? labels[selection] : sNoValidOptionsString);
698+
699+ int state = enabled ? (active ? ACTIVE_STATE : DEFAULT_STATE) : DISABLED_STATE;
700+
701+ draw_text(s, str, rect.x, y, get_theme_color(ITEM_WIDGET, state), font, style, utf8);
702+
703+ // Cursor
704+ if (active) {
705+ //!!
706+ }
707+}
708+
709+void w_select::click(int /*x*/, int /*y*/)
710+{
711+ if(enabled) {
712+ selection++;
713+ if (selection >= num_labels)
714+ selection = 0;
715+
716+ selection_changed();
717+ }
718+}
719+
720+void w_select::event(SDL_Event &e)
721+{
722+ if (e.type == SDL_KEYDOWN) {
723+ if (e.key.keysym.sym == SDLK_LEFT) {
724+ if (num_labels == 0)
725+ selection = 0;
726+ else if (selection == 0)
727+ selection = num_labels - 1;
728+ else
729+ selection--;
730+ selection_changed();
731+ e.type = SDL_NOEVENT; // Swallow event
732+ } else if (e.key.keysym.sym == SDLK_RIGHT) {
733+ if (selection >= num_labels - 1)
734+ selection = 0;
735+ else
736+ selection++;
737+ selection_changed();
738+ e.type = SDL_NOEVENT; // Swallow event
739+ }
740+ }
741+}
742+
743+void w_select::set_selection(size_t s, bool simulate_user_input /* default: false */)
744+{
745+ //if (s >= num_labels || s < 0)
746+ // s = 0;
747+ assert(s == PIN(s, 0, num_labels - 1));
748+
749+ if(selection != s) {
750+ selection = s;
751+
752+ if(simulate_user_input)
753+ selection_changed();
754+ }
755+
756+ dirty = true;
757+}
758+
759+// ZZZ: change labels after creation
760+void w_select::set_labels(const char** inLabels) {
761+ if(we_own_labels && labels != NULL)
762+ free(labels);
763+
764+ labels = inLabels;
765+ num_labels = 0;
766+ if(labels) {
767+ while(inLabels[num_labels])
768+ num_labels++;
769+ }
770+ we_own_labels = false;
771+ if (selection >= num_labels)
772+ set_selection(0);
773+ else
774+ set_selection(selection);
775+
776+ // Hope new labels have same max width as old, or that user called set_full_width() on us.
777+}
778+
779+
780+// ZZZ: set labels according to stringset
781+void w_select::set_labels_stringset(short inStringSetID) {
782+ // free old label pointers, if appropriate
783+ if(we_own_labels && labels != NULL)
784+ free(labels);
785+
786+ // see if we need space for label pointers. if so, allocate and fill them in.
787+ if(TS_IsPresent(inStringSetID) && TS_CountStrings(inStringSetID) > 0) {
788+ num_labels = TS_CountStrings(inStringSetID);
789+
790+ // Allocate one extra pointer slot to fill with NULL
791+ // Note: this works right if the string count is 0... but we check anyway, can save a malloc/free.
792+ labels = (const char**)malloc(sizeof(const char*) * (num_labels + 1));
793+ labels[num_labels] = NULL;
794+
795+ for(size_t i = 0; i < num_labels; i++) {
796+ // shared references should be OK, stringsets ought to be pretty stable. No need to copy...
797+ labels[i] = TS_GetCString(inStringSetID, i);
798+ }
799+
800+ // we allocated; we free.
801+ we_own_labels = true;
802+ }
803+ else { // no stringset or no strings in stringset
804+ labels = NULL;
805+ num_labels = 0;
806+ we_own_labels = false;
807+ }
808+
809+ if (selection >= num_labels)
810+ set_selection(0);
811+ else
812+ set_selection(selection);
813+
814+ // Hope new labels have same max width as old, or that user called set_full_width() on us.
815+}
816+
817+void w_select::selection_changed(void)
818+{
819+ play_dialog_sound(DIALOG_CLICK_SOUND);
820+ dirty = true;
821+
822+ // ZZZ: call user-specified callback
823+ if(selection_changed_callback != NULL)
824+ selection_changed_callback(this);
825+}
826+
827+
828+// ZZZ addition
829+uint16 w_select::get_largest_label_width() {
830+ uint16 max_label_width = 0;
831+ for (size_t i=0; i<num_labels; i++) {
832+ uint16 width = text_width(labels[i], font, style, utf8);
833+ if (width > max_label_width)
834+ max_label_width = width;
835+ }
836+
837+ // ZZZ: account for "no valid options" string
838+ if(num_labels <= 0)
839+ max_label_width = text_width(sNoValidOptionsString, font, style, utf8);
840+
841+ return max_label_width;
842+}
843+
844+
845+/*
846+ * On-off toggle
847+ */
848+
849+#ifdef HAVE_SDL_TTF
850+const char *w_toggle::onoff_labels[] = {"-", "x", NULL };
851+#else
852+const char *w_toggle::onoff_labels[] = {"Off", "On", NULL};
853+#endif
854+
855+w_toggle::w_toggle(bool selection, const char **labels) : w_select(selection, labels) {
856+ if (labels == onoff_labels && use_theme_images(CHECKBOX))
857+ {
858+ saved_min_height = get_theme_space(CHECKBOX, BUTTON_HEIGHT);
859+ }
860+#ifdef HAVE_SDL_TTF
861+ else if (labels == onoff_labels)
862+ {
863+ labels_are_utf8(true);
864+ font = get_theme_font(CHECKBOX, style);
865+ saved_min_height = get_theme_space(CHECKBOX, BUTTON_HEIGHT);
866+ }
867+#endif
868+}
869+
870+void w_toggle::draw(SDL_Surface *s) const
871+{
872+ int y = rect.y + font->get_ascent();
873+
874+ // Selection (ZZZ: different color for disabled)
875+ const char *str = (num_labels > 0 ? labels[selection] : sNoValidOptionsString);
876+
877+ int state = enabled ? (active ? ACTIVE_STATE : DEFAULT_STATE) : DISABLED_STATE;
878+
879+ if (labels == onoff_labels && use_theme_images(CHECKBOX))
880+ {
881+ SDL_Surface *image = get_theme_image(CHECKBOX, state, selection);
882+ SDL_Rect r = { rect.x, rect.y + (rect.h - saved_min_height) / 2 + get_theme_space(CHECKBOX, BUTTON_T_SPACE), image->w, image->h };
883+ SDL_BlitSurface(image, 0, s, &r);
884+ }
885+#ifdef HAVE_SDL_TTF
886+ else if (labels == onoff_labels)
887+ {
888+ draw_text(s, str, rect.x, rect.y + (rect.h - saved_min_height) / 2 + get_theme_space(CHECKBOX, BUTTON_T_SPACE), get_theme_color(LABEL_WIDGET, state, FOREGROUND_COLOR), font, style, utf8);
889+ }
890+#endif
891+ else
892+ {
893+ draw_text(s, str, rect.x, rect.y + font->get_ascent(), get_theme_color(ITEM_WIDGET, state), font, style, utf8);
894+ }
895+
896+ // Cursor
897+ if (active) {
898+ //!!
899+ }
900+}
901+
902+int w_toggle::min_width()
903+{
904+ if (labels == onoff_labels && use_theme_images(CHECKBOX))
905+ {
906+ return get_theme_image(CHECKBOX, DEFAULT_STATE, 0)->w;
907+ }
908+ else
909+ {
910+ return w_select::min_width();
911+ }
912+}
913+
914+
915+/*
916+ * Enabling toggle (ZZZ)
917+ *
918+ * Can enable/disable a bank of other widgets according to its state
919+ */
920+
921+
922+w_enabling_toggle::w_enabling_toggle(bool inSelection, bool inEnablesWhenOn, const char** inLabels)
923+ : w_toggle(inSelection, inLabels), enables_when_on(inEnablesWhenOn)
924+{
925+}
926+
927+
928+void
929+w_enabling_toggle::selection_changed()
930+{
931+ w_toggle::selection_changed();
932+
933+ for(DependentCollection::iterator i = dependents.begin(); i != dependents.end(); i++)
934+ update_widget_enabled(*i);
935+}
936+
937+
938+/*
939+ * Player color selection
940+ */
941+
942+w_player_color::w_player_color(int selection) : w_select(selection, NULL)
943+{
944+ set_labels_stringset(kTeamColorsStringSetID);
945+}
946+
947+void w_player_color::draw(SDL_Surface *s) const
948+{
949+ int y = rect.y + font->get_ascent();
950+
951+ // Selection
952+ if (selection >= 0) {
953+ uint32 pixel = get_dialog_player_color(selection);
954+ SDL_Rect r = {rect.x, rect.y + 1, 48, rect.h - 2};
955+ SDL_FillRect(s, &r, pixel);
956+ } else {
957+ int state = enabled ? (active ? ACTIVE_STATE : DEFAULT_STATE) : DISABLED_STATE;
958+
959+ draw_text(s, "<unknown>", rect.x, y, get_theme_color(ITEM_WIDGET, state), font, style);
960+ }
961+
962+ // Cursor
963+ if (active) {
964+ //!!
965+ }
966+}
967+
968+class w_color_block : public widget {
969+public:
970+ w_color_block(const rgb_color *color) : m_color(color) {
971+ saved_min_height = 64;
972+ saved_min_width = 64;
973+ }
974+
975+ void draw(SDL_Surface *s) const {
976+ uint32 pixel = SDL_MapRGB(s->format, m_color->red >> 8, m_color->green >> 8, m_color->blue >> 8);
977+ SDL_Rect r = { rect.x, rect.y, 64, 64 };
978+ SDL_FillRect(s, &r, pixel);
979+ }
980+
981+ bool is_dirty() { return true; }
982+private:
983+ const rgb_color *m_color;
984+};
985+
986+
987+void w_color_picker::click(int, int)
988+{
989+ if (!enabled) return;
990+ dialog d;
991+
992+ vertical_placer *placer = new vertical_placer;
993+ placer->dual_add(new w_title("CHOOSE A COLOR"), d);
994+ placer->add(new w_spacer(), true);
995+
996+ w_color_block *color_block = new w_color_block(&m_color);
997+ placer->dual_add(color_block, d);
998+
999+ table_placer *table = new table_placer(2, get_theme_space(ITEM_WIDGET));
1000+ table->col_flags(0, placeable::kAlignRight);
1001+
1002+ w_slider *red_w = new w_slider(16, m_color.red >> 12);
1003+ table->dual_add(red_w->label("Red"), d);
1004+ table->dual_add(red_w, d);
1005+
1006+ w_slider *green_w = new w_slider(16, m_color.green >> 12);
1007+ table->dual_add(green_w->label("Green"), d);
1008+ table->dual_add(green_w, d);
1009+
1010+ w_slider *blue_w = new w_slider(16, m_color.blue >> 12);
1011+ table->dual_add(blue_w->label("Blue"), d);
1012+ table->dual_add(blue_w, d);
1013+
1014+ placer->add(table, true);
1015+ placer->add(new w_spacer(), true);
1016+
1017+ horizontal_placer *button_placer = new horizontal_placer;
1018+ button_placer->dual_add(new w_button("CANCEL", dialog_cancel, &d), d);
1019+ button_placer->dual_add(new w_button("OK", dialog_ok, &d), d);
1020+
1021+ placer->add(button_placer, true);
1022+
1023+ rgb_color old_color = m_color;
1024+
1025+ d.set_widget_placer(placer);
1026+ d.set_processing_function(w_color_picker::update_color(red_w, green_w, blue_w, &m_color.red, &m_color.green, &m_color.blue));
1027+
1028+ if (d.run() == 0)
1029+ {
1030+ m_color.red = red_w->get_selection() << 12;
1031+ m_color.green = green_w->get_selection() << 12;
1032+ m_color.blue = blue_w->get_selection() << 12;
1033+
1034+ dirty = true;
1035+ get_owning_dialog()->draw_dirty_widgets();
1036+ }
1037+ else
1038+ {
1039+ m_color = old_color;
1040+ }
1041+}
1042+
1043+void w_color_picker::draw(SDL_Surface *s) const
1044+{
1045+ uint32 pixel = SDL_MapRGB(s->format, m_color.red >> 8, m_color.green >> 8, m_color.blue >> 8);
1046+ SDL_Rect r = {rect.x, rect.y + 1, 48, rect.h - 2 };
1047+ SDL_FillRect(s, &r, pixel);
1048+}
1049+
1050+/*
1051+ * Text entry widget
1052+ */
1053+
1054+w_text_entry::w_text_entry(size_t max_c, const char *initial_text)
1055+ : widget(TEXT_ENTRY_WIDGET), enter_pressed_callback(NULL), value_changed_callback(NULL), max_chars(max_c), enable_mac_roman(false)
1056+{
1057+ // Initialize buffer
1058+ buf = new char[max_chars + 1];
1059+ set_text(initial_text);
1060+
1061+ saved_min_width = MAX_TEXT_WIDTH;
1062+
1063+ saved_min_height = (int16) font->get_ascent() + font->get_descent() + font->get_leading();
1064+}
1065+
1066+w_text_entry::~w_text_entry()
1067+{
1068+ delete[] buf;
1069+}
1070+
1071+void w_text_entry::place(const SDL_Rect& r, placement_flags flags)
1072+{
1073+ rect.h = (int16) font->get_ascent() + (int16) font->get_descent() + font->get_leading();
1074+
1075+ rect.y = r.y + (r.h - rect.h) / 2;
1076+
1077+ text_x = 0;
1078+ rect.x = r.x;
1079+ rect.w = r.w;
1080+ max_text_width = rect.w;
1081+}
1082+
1083+void w_text_entry::draw(SDL_Surface *s) const
1084+{
1085+ int y = rect.y + font->get_ascent();
1086+
1087+ int16 theRectX = rect.x;
1088+ uint16 theRectW = rect.w;
1089+ int16 theTextX = text_x;
1090+
1091+ // Text
1092+ int16 x = theRectX + theTextX;
1093+ uint16 width = text_width(buf, font, style);
1094+ if (width > max_text_width)
1095+ x -= width - max_text_width;
1096+ set_drawing_clip_rectangle(0, theRectX + theTextX, static_cast<uint16>(s->h), theRectX + theRectW);
1097+
1098+ int state = enabled ? (active ? ACTIVE_STATE : DEFAULT_STATE) : DISABLED_STATE;
1099+
1100+ draw_text(s, buf, x, y, get_theme_color(TEXT_ENTRY_WIDGET, state), font, style);
1101+ set_drawing_clip_rectangle(SHRT_MIN, SHRT_MIN, SHRT_MAX, SHRT_MAX);
1102+
1103+ // Cursor
1104+ if (active) {
1105+ SDL_Rect r = {x + width - (width ? 1 : 0), rect.y, 1, rect.h};
1106+ SDL_FillRect(s, &r, get_theme_color(TEXT_ENTRY_WIDGET, CURSOR_STATE));
1107+ }
1108+}
1109+
1110+void w_text_entry::event(SDL_Event &e)
1111+{
1112+ if (e.type == SDL_KEYDOWN) {
1113+ switch (e.key.keysym.sym) {
1114+ case SDLK_LEFT: // Left/right does nothing (for now)
1115+ case SDLK_RIGHT:
1116+ e.type = SDL_NOEVENT; // Swallow event
1117+ break;
1118+
1119+ case SDLK_RETURN:
1120+ case SDLK_KP_ENTER:
1121+ if(enter_pressed_callback)
1122+ enter_pressed_callback(this);
1123+
1124+ e.type = SDL_NOEVENT; // Swallow event (shouldn't typing do this also??)
1125+ break;
1126+
1127+ case SDLK_BACKSPACE: // Backspace deletes last character
1128+backspace: if (num_chars) {
1129+ buf[--num_chars] = 0;
1130+ modified_text();
1131+ play_dialog_sound(DIALOG_DELETE_SOUND);
1132+ }
1133+ break;
1134+ case SDLK_UP:
1135+ case SDLK_DOWN:
1136+ break;
1137+
1138+ default: { // Printable characters are entered into the buffer
1139+ uint16 uc = e.key.keysym.unicode;
1140+ if (uc >= ' ' && (uc < 0x80 || enable_mac_roman) && (num_chars + 1) < max_chars)
1141+ {
1142+ char c;
1143+ if (uc < 0x80)
1144+ {
1145+ c = static_cast<char>(uc);
1146+ }
1147+ else if (enable_mac_roman)
1148+ {
1149+ c = unicode_to_mac_roman(uc);
1150+ }
1151+ buf[num_chars++] = c;
1152+ buf[num_chars] = 0;
1153+ modified_text();
1154+ play_dialog_sound(DIALOG_TYPE_SOUND);
1155+ } else if (uc == 21) { // Ctrl-U: erase text
1156+ buf[0] = 0;
1157+ num_chars = 0;
1158+ modified_text();
1159+ play_dialog_sound(DIALOG_ERASE_SOUND);
1160+ } else if (uc == 4 || uc == 8) // Ctrl-D/H: backspace
1161+ goto backspace;
1162+ break;
1163+ }
1164+ }
1165+ }
1166+}
1167+
1168+void w_text_entry::set_text(const char *text)
1169+{
1170+ memset(buf, 0, max_chars + 1);
1171+ if (text)
1172+ strncpy(buf, text, max_chars);
1173+ num_chars = strlen(buf);
1174+ modified_text();
1175+}
1176+
1177+void w_text_entry::modified_text(void)
1178+{
1179+ dirty = true;
1180+
1181+ // ZZZ: callback if desired
1182+ if(value_changed_callback)
1183+ value_changed_callback(this);
1184+}
1185+
1186+/*
1187+ * Number entry widget
1188+ */
1189+
1190+w_number_entry::w_number_entry(int initial_number) : w_text_entry(/*16*/4, NULL)
1191+{
1192+ set_number(initial_number);
1193+ saved_min_width = MAX_TEXT_WIDTH / 2;
1194+}
1195+
1196+void w_number_entry::event(SDL_Event &e)
1197+{
1198+ if (e.type == SDL_KEYDOWN) {
1199+ // ZZZ fix: under Mac OS X, Christian's code was filtering out backspace in numeric entry fields
1200+ // Maybe it would be better to have w_text_entry call a virtual method "typed_printable" or something
1201+ // which w_number_entry could override to filter out non-numeric characters.
1202+ // Anyway, here I just ignore the keysym.sym's that show up in w_text_entry::event(), to avoid
1203+ // filtering them out incorrectly.
1204+ switch(e.key.keysym.sym) {
1205+ case SDLK_LEFT:
1206+ case SDLK_RIGHT:
1207+ case SDLK_RETURN:
1208+ case SDLK_KP_ENTER:
1209+ case SDLK_BACKSPACE:
1210+ break;
1211+
1212+ default:
1213+ {
1214+ uint16 uc = e.key.keysym.unicode;
1215+ if (uc >= ' ' && uc < 0x80) {
1216+ if (uc < '0' || uc > '9') {
1217+ // Swallow all non-numbers
1218+ e.type = SDL_NOEVENT;
1219+ return;
1220+ }
1221+ }
1222+ } // default
1223+ break;
1224+ } // switch
1225+ } // if key down
1226+
1227+ w_text_entry::event(e);
1228+}
1229+
1230+void w_number_entry::set_number(int number)
1231+{
1232+ char str[16];
1233+ sprintf(str, "%d", number);
1234+ set_text(str);
1235+}
1236+
1237+w_password_entry::w_password_entry(size_t max_chars, const char *initial_text) : w_text_entry(max_chars, initial_text)
1238+{
1239+
1240+}
1241+
1242+void w_password_entry::draw(SDL_Surface *s) const
1243+{
1244+ string real_text = buf;
1245+ memset(buf, '*', strlen(buf));
1246+ w_text_entry::draw(s);
1247+ strcpy(buf, real_text.c_str());
1248+}
1249+
1250+/*
1251+ * Key name widget
1252+ */
1253+
1254+static const char *WAITING_TEXT = "waiting for new key";
1255+
1256+w_key::w_key(SDLKey key) : widget(LABEL_WIDGET), binding(false)
1257+{
1258+ set_key(key);
1259+
1260+ saved_min_width = text_width(WAITING_TEXT, font, style);
1261+ saved_min_height = font->get_line_height();
1262+}
1263+
1264+void w_key::place(const SDL_Rect& r, placement_flags flags)
1265+{
1266+ rect.h = r.h;
1267+ rect.y = r.y;
1268+
1269+ key_x = 0;
1270+ rect.x = r.x;
1271+ rect.w = r.w;
1272+}
1273+
1274+// ZZZ: we provide phony key names for the phony keys used for mouse buttons.
1275+static const char* sMouseButtonKeyName[NUM_SDL_MOUSE_BUTTONS] = {
1276+ "mouse 1", // things like "Middle Mouse Button" are too long to draw properly
1277+ "mouse 3",
1278+ "mouse 2",
1279+ "mouse 4",
1280+ "mouse 5",
1281+ "mouse 6",
1282+ "mouse 7",
1283+ "mouse 8"
1284+};
1285+
1286+// ZZZ: this injects our phony key names but passes along the rest.
1287+static const char*
1288+GetSDLKeyName(SDLKey inKey) {
1289+ if(inKey >= SDLK_BASE_MOUSE_BUTTON && inKey < SDLK_BASE_MOUSE_BUTTON + NUM_SDL_MOUSE_BUTTONS)
1290+ return sMouseButtonKeyName[inKey - SDLK_BASE_MOUSE_BUTTON];
1291+ else
1292+ return SDL_GetKeyName(inKey);
1293+}
1294+
1295+void w_key::draw(SDL_Surface *s) const
1296+{
1297+ int y = rect.y + font->get_ascent();
1298+
1299+ // Key
1300+ int16 x = rect.x + key_x;
1301+ if (binding) {
1302+ SDL_Rect r = {x, rect.y, text_width(WAITING_TEXT, font, style), rect.h};
1303+// SDL_FillRect(s, &r, get_dialog_color(KEY_BINDING_COLOR));
1304+ draw_text(s, WAITING_TEXT, x, y, get_theme_color(ITEM_WIDGET, ACTIVE_STATE), font, style);
1305+ } else {
1306+ int state = enabled ? (active ? ACTIVE_STATE : DEFAULT_STATE) : DISABLED_STATE;
1307+
1308+ // ZZZ: potential to use the phony (i.e. mouse-button) key names
1309+ draw_text(s, GetSDLKeyName(key), x, y, get_theme_color(ITEM_WIDGET, state), font, style);
1310+ }
1311+}
1312+
1313+void w_key::click(int /*x*/, int /*y*/)
1314+{
1315+ get_owning_dialog()->activate_widget(this);
1316+ if(enabled) {
1317+ if (!binding) {
1318+ binding = true;
1319+ dirty = true;
1320+ }
1321+ }
1322+}
1323+
1324+void w_key::event(SDL_Event &e)
1325+{
1326+ if(binding) {
1327+ // ZZZ: let mouse buttons assign like (unused) keys
1328+ if(e.type == SDL_MOUSEBUTTONDOWN) {
1329+ e.type = SDL_KEYDOWN;
1330+ e.key.keysym.sym = (SDLKey)(SDLK_BASE_MOUSE_BUTTON + e.button.button - 1);
1331+ }
1332+
1333+ if (e.type == SDL_KEYDOWN) {
1334+ if (e.key.keysym.sym != SDLK_ESCAPE)
1335+ set_key(e.key.keysym.sym);
1336+ dirty = true;
1337+ binding = false;
1338+ e.key.keysym.sym = SDLK_DOWN; // Activate next widget
1339+ }
1340+
1341+ // ZZZ: suppress mouse motion while assigning
1342+ // (it's annoying otherwise, trust me)
1343+ if(e.type == SDL_MOUSEMOTION)
1344+ e.type = SDL_NOEVENT;
1345+ }
1346+}
1347+
1348+void w_key::set_key(SDLKey k)
1349+{
1350+ key = k;
1351+}
1352+
1353+/*
1354+ * Progress
1355+ */
1356+void w_progress_bar::draw(SDL_Surface* s) const
1357+{
1358+ int filled_width = (rect.w - 2) * value / max_value;
1359+ SDL_Rect dst_rect = rect;
1360+ dst_rect.h -= 2;
1361+ dst_rect.y += 2;
1362+ SDL_FillRect(s, &dst_rect, get_theme_color(MESSAGE_WIDGET, DEFAULT_STATE, FOREGROUND_COLOR));
1363+ dst_rect.x += filled_width + 1;
1364+ dst_rect.y++;
1365+ dst_rect.h -= 2;
1366+ dst_rect.w = dst_rect.w - filled_width - 2;
1367+ if (use_theme_color(DIALOG_FRAME, BACKGROUND_COLOR))
1368+ SDL_FillRect(s, &dst_rect, get_theme_color(DIALOG_FRAME, DEFAULT_STATE, BACKGROUND_COLOR));
1369+}
1370+
1371+void w_progress_bar::set_progress(int inValue, int inMaxValue)
1372+{
1373+ value = inValue;
1374+ max_value = inMaxValue;
1375+ dirty = true;
1376+}
1377+
1378+
1379+
1380+/*
1381+ * Slider
1382+ */
1383+
1384+const int SLIDER_WIDTH = 160;
1385+const int SLIDER_THUMB_HEIGHT = 14;
1386+const int SLIDER_THUMB_WIDTH = 8;
1387+const int SLIDER_TROUGH_HEIGHT = 8;
1388+
1389+w_slider::w_slider(int num, int s) : widget(LABEL_WIDGET), selection(s), num_items(num), thumb_dragging(false)
1390+{
1391+ slider_l = get_theme_image(SLIDER_WIDGET, DEFAULT_STATE, SLIDER_L_IMAGE);
1392+ slider_r = get_theme_image(SLIDER_WIDGET, DEFAULT_STATE, SLIDER_R_IMAGE);
1393+ slider_c = get_theme_image(SLIDER_WIDGET, DEFAULT_STATE, SLIDER_C_IMAGE, SLIDER_WIDTH - slider_l->w - slider_r->w);
1394+ thumb = get_theme_image(SLIDER_THUMB, DEFAULT_STATE, 0);
1395+
1396+ trough_width = SLIDER_WIDTH - get_theme_space(SLIDER_WIDGET, SLIDER_L_SPACE) - get_theme_space(SLIDER_WIDGET, SLIDER_R_SPACE);
1397+
1398+ saved_min_width = SLIDER_WIDTH;
1399+ if (use_theme_images(SLIDER_WIDGET))
1400+ saved_min_height = std::max(static_cast<uint16>(slider_c->h), static_cast<uint16>(thumb->h));
1401+ else
1402+ saved_min_height = SLIDER_THUMB_HEIGHT + 2;
1403+}
1404+
1405+w_slider::~w_slider()
1406+{
1407+ if (slider_c) SDL_FreeSurface(slider_c);
1408+}
1409+
1410+void w_slider::place(const SDL_Rect& r, placement_flags flags)
1411+{
1412+ rect.h = r.h;
1413+ rect.y = r.y + (r.h - saved_min_height) / 2;
1414+ rect.x = r.x;
1415+ slider_x = 0;
1416+ rect.w = r.w;
1417+
1418+ set_selection(selection);
1419+}
1420+
1421+void w_slider::draw(SDL_Surface *s) const
1422+{
1423+ if (use_theme_images(SLIDER_WIDGET))
1424+ {
1425+ // Slider trough
1426+ SDL_Rect r = {rect.x + slider_x, rect.y + (saved_min_height - slider_l->h) / 2,
1427+ static_cast<Uint16>(slider_l->w),
1428+ static_cast<Uint16>(slider_l->h)};
1429+ SDL_BlitSurface(slider_l, NULL, s, &r);
1430+ r.x = r.x + static_cast<Sint16>(slider_l->w);
1431+ r.w = static_cast<Uint16>(slider_c->w);
1432+ r.h = static_cast<Uint16>(slider_c->h);
1433+ SDL_BlitSurface(slider_c, NULL, s, &r);
1434+ r.x = r.x + static_cast<Sint16>(slider_c->w);
1435+ r.w = static_cast<Uint16>(slider_r->w);
1436+ r.h = static_cast<Uint16>(slider_r->h);
1437+ SDL_BlitSurface(slider_r, NULL, s, &r);
1438+
1439+ // Slider thumb
1440+ r.x = rect.x + static_cast<Sint16>(thumb_x);
1441+ r.y = rect.y + (saved_min_height - thumb->h) / 2 + get_theme_space(SLIDER_WIDGET, SLIDER_T_SPACE);
1442+ r.w = static_cast<Uint16>(thumb->w);
1443+ r.h = static_cast<Uint16>(thumb->h);
1444+ SDL_BlitSurface(thumb, NULL, s, &r);
1445+ }
1446+ else
1447+ {
1448+ SDL_Rect r = {rect.x, rect.y + (saved_min_height - SLIDER_TROUGH_HEIGHT) / 2 + get_theme_space(SLIDER_WIDGET, SLIDER_T_SPACE), SLIDER_WIDTH, SLIDER_TROUGH_HEIGHT};
1449+ uint32 pixel = get_theme_color(SLIDER_WIDGET, DEFAULT_STATE, FRAME_COLOR);
1450+ draw_rectangle(s, &r, pixel);
1451+
1452+ pixel = get_theme_color(SLIDER_WIDGET, DEFAULT_STATE, FOREGROUND_COLOR);
1453+ r.x = r.x + 1;
1454+ r.y = r.y + 1;
1455+ r.w = r.w - 2;
1456+ r.h = r.h - 2;
1457+ SDL_FillRect(s, &r, pixel);
1458+
1459+ pixel = get_theme_color(SLIDER_THUMB, DEFAULT_STATE, FRAME_COLOR);
1460+ r.x = rect.x + static_cast<Sint16>(thumb_x);
1461+ r.y = rect.y + (saved_min_height - SLIDER_THUMB_HEIGHT) / 2;
1462+ r.w = thumb_width();
1463+ r.h = SLIDER_THUMB_HEIGHT;
1464+ draw_rectangle(s, &r, pixel);
1465+
1466+ pixel = get_theme_color(SLIDER_THUMB, DEFAULT_STATE, FOREGROUND_COLOR);
1467+ r.x = r.x + 1;
1468+ r.y = r.y + 1;
1469+ r.w = r.w - 2;
1470+ r.h = r.h - 2;
1471+ SDL_FillRect(s, &r, pixel);
1472+
1473+ }
1474+}
1475+
1476+void w_slider::mouse_move(int x, int /*y*/)
1477+{
1478+ if (thumb_dragging) {
1479+ int delta_x = (x - slider_x - get_theme_space(SLIDER_WIDGET, SLIDER_L_SPACE)) - thumb_drag_x;
1480+ set_selection(delta_x * num_items / (trough_width - thumb_width()));
1481+ }
1482+}
1483+
1484+void w_slider::click(int x, int /*y*/)
1485+{
1486+ if(enabled) {
1487+ if (x >= thumb_x && x < thumb_x + thumb_width()) {
1488+ thumb_dragging = dirty = true;
1489+ thumb_drag_x = x - thumb_x;
1490+ }
1491+ }
1492+}
1493+
1494+void w_slider::event(SDL_Event &e)
1495+{
1496+ if (e.type == SDL_KEYDOWN) {
1497+ if (e.key.keysym.sym == SDLK_LEFT) {
1498+ set_selection(selection - 1);
1499+ item_selected();
1500+ e.type = SDL_NOEVENT; // Swallow event
1501+ } else if (e.key.keysym.sym == SDLK_RIGHT) {
1502+ set_selection(selection + 1);
1503+ item_selected();
1504+ e.type = SDL_NOEVENT; // Swallow event
1505+ }
1506+ } else if (e.type == SDL_MOUSEBUTTONUP) {
1507+ if (thumb_dragging) {
1508+ thumb_dragging = false;
1509+ dirty = true;
1510+ item_selected();
1511+ }
1512+ }
1513+}
1514+
1515+void w_slider::set_selection(int s)
1516+{
1517+ if (s >= num_items)
1518+ s = num_items - 1;
1519+ else if (s < 0)
1520+ s = 0;
1521+ selection = s;
1522+ thumb_x = int(float(selection * (trough_width - thumb_width())) / (num_items - 1) + 0.5);
1523+ thumb_x += get_theme_space(SLIDER_WIDGET, SLIDER_L_SPACE) + slider_x;
1524+ dirty = true;
1525+}
1526+
1527+int w_slider::thumb_width() const
1528+{
1529+ if (use_theme_images(SLIDER_WIDGET))
1530+ return thumb->w;
1531+ else
1532+ return SLIDER_THUMB_WIDTH;
1533+}
1534+
1535+
1536+/*
1537+ * List selection
1538+ */
1539+
1540+w_list_base::w_list_base(uint16 width, size_t lines, size_t /*sel*/) : widget(ITEM_WIDGET), num_items(0), shown_items(lines), thumb_dragging(false)
1541+{
1542+ rect.w = width;
1543+ rect.h = item_height() * static_cast<uint16>(shown_items) + get_theme_space(LIST_WIDGET, T_SPACE) + get_theme_space(LIST_WIDGET, B_SPACE);
1544+
1545+ frame_tl = get_theme_image(LIST_WIDGET, DEFAULT_STATE, TL_IMAGE);
1546+ frame_tr = get_theme_image(LIST_WIDGET, DEFAULT_STATE, TR_IMAGE);
1547+ frame_bl = get_theme_image(LIST_WIDGET, DEFAULT_STATE, BL_IMAGE);
1548+ frame_br = get_theme_image(LIST_WIDGET, DEFAULT_STATE, BR_IMAGE);
1549+ frame_t = get_theme_image(LIST_WIDGET, DEFAULT_STATE, T_IMAGE, rect.w - frame_tl->w - frame_tr->w, 0);
1550+ frame_l = get_theme_image(LIST_WIDGET, DEFAULT_STATE, L_IMAGE, 0, rect.h - frame_tl->h - frame_bl->h);
1551+ frame_r = get_theme_image(LIST_WIDGET, DEFAULT_STATE, R_IMAGE, 0, rect.h - frame_tr->h - frame_br->h);
1552+ frame_b = get_theme_image(LIST_WIDGET, DEFAULT_STATE, B_IMAGE, rect.w - frame_bl->w - frame_br->w, 0);
1553+
1554+ thumb_t = get_theme_image(LIST_THUMB, DEFAULT_STATE, THUMB_T_IMAGE);
1555+ thumb_tc = NULL;
1556+ SDL_Surface *thumb_tc_unscaled = get_theme_image(LIST_THUMB, DEFAULT_STATE, THUMB_TC_IMAGE);
1557+ thumb_c = get_theme_image(LIST_THUMB, DEFAULT_STATE, THUMB_C_IMAGE);
1558+ SDL_Surface *thumb_bc_unscaled = get_theme_image(LIST_THUMB, DEFAULT_STATE, THUMB_BC_IMAGE);
1559+ thumb_bc = NULL;
1560+ thumb_b = get_theme_image(LIST_THUMB, DEFAULT_STATE, THUMB_B_IMAGE);
1561+
1562+ min_thumb_height = static_cast<uint16>(thumb_t->h + thumb_tc_unscaled->h + thumb_c->h + thumb_bc_unscaled->h + thumb_b->h);
1563+
1564+ trough_rect.x = rect.w - get_theme_space(LIST_WIDGET, TROUGH_R_SPACE);
1565+ trough_rect.y = get_theme_space(LIST_WIDGET, TROUGH_T_SPACE);
1566+ trough_rect.w = get_theme_space(LIST_WIDGET, TROUGH_WIDTH);
1567+ trough_rect.h = rect.h - get_theme_space(LIST_WIDGET, TROUGH_T_SPACE) - get_theme_space(LIST_WIDGET, TROUGH_B_SPACE);
1568+
1569+ saved_min_width = rect.w;
1570+ saved_min_height = rect.h;
1571+}
1572+
1573+w_list_base::~w_list_base()
1574+{
1575+ if (frame_t) SDL_FreeSurface(frame_t);
1576+ if (frame_l) SDL_FreeSurface(frame_l);
1577+ if (frame_r) SDL_FreeSurface(frame_r);
1578+ if (frame_b) SDL_FreeSurface(frame_b);
1579+ if (thumb_tc) SDL_FreeSurface(thumb_tc);
1580+ if (thumb_bc) SDL_FreeSurface(thumb_bc);
1581+}
1582+
1583+void w_list_base::draw_image(SDL_Surface *dst, SDL_Surface *s, int16 x, int16 y) const
1584+{
1585+ SDL_Rect r = {x, y, static_cast<Uint16>(s->w), static_cast<Uint16>(s->h)};
1586+ SDL_BlitSurface(s, NULL, dst, &r);
1587+}
1588+
1589+void w_list_base::draw(SDL_Surface *s) const
1590+{
1591+ if (use_theme_images(LIST_WIDGET))
1592+ {
1593+ // Draw frame
1594+ int16 x = rect.x;
1595+ int16 y = rect.y;
1596+ draw_image(s, frame_tl, x, y);
1597+ draw_image(s, frame_t, x + static_cast<int16>(frame_tl->w), y);
1598+ draw_image(s, frame_tr, x + static_cast<int16>(frame_tl->w) + static_cast<int16>(frame_t->w), y);
1599+ draw_image(s, frame_l, x, y + static_cast<int16>(frame_tl->h));
1600+ draw_image(s, frame_r, x + rect.w - static_cast<int16>(frame_r->w), y + static_cast<int16>(frame_tr->h));
1601+ draw_image(s, frame_bl, x, y + static_cast<int16>(frame_tl->h) + static_cast<int16>(frame_l->h));
1602+ draw_image(s, frame_b, x + static_cast<int16>(frame_bl->w), y + rect.h - static_cast<int16>(frame_b->h));
1603+ draw_image(s, frame_br, x + static_cast<int16>(frame_bl->w) + static_cast<int16>(frame_b->w), y + static_cast<int16>(frame_tr->h) + static_cast<int16>(frame_r->h));
1604+
1605+ // Draw thumb
1606+ x = rect.x + trough_rect.x;
1607+ y = rect.y + thumb_y;
1608+ draw_image(s, thumb_t, x, y);
1609+ draw_image(s, thumb_tc, x, y = y + static_cast<int16>(thumb_t->h));
1610+ draw_image(s, thumb_c, x, y = y + static_cast<int16>(thumb_tc->h));
1611+ draw_image(s, thumb_bc, x, y = y + static_cast<int16>(thumb_c->h));
1612+ draw_image(s, thumb_b, x, y = y + static_cast<int16>(thumb_bc->h));
1613+
1614+ }
1615+ else
1616+ {
1617+ uint32 pixel = get_theme_color(LIST_WIDGET, DEFAULT_STATE, FRAME_COLOR);
1618+ draw_rectangle(s, &rect, pixel);
1619+
1620+ SDL_Rect real_trough = { rect.x + trough_rect.x, rect.y + trough_rect.y, trough_rect.w, trough_rect.h };
1621+ draw_rectangle(s, &real_trough, pixel);
1622+ real_trough.x = real_trough.x + 1;
1623+ real_trough.y = real_trough.y + 1;
1624+ real_trough.w = real_trough.w - 2;
1625+ real_trough.h = real_trough.h - 2;
1626+ if (use_theme_color(LIST_THUMB, BACKGROUND_COLOR))
1627+ {
1628+ pixel = get_theme_color(LIST_THUMB, DEFAULT_STATE, BACKGROUND_COLOR);
1629+ SDL_FillRect(s, &real_trough, pixel);
1630+ }
1631+
1632+ pixel = get_theme_color(LIST_THUMB, DEFAULT_STATE, FRAME_COLOR);
1633+ SDL_Rect thumb_rect = { rect.x + trough_rect.x, rect.y + thumb_y, trough_rect.w, thumb_t->h + thumb_tc->h + thumb_c->h + thumb_bc->h + thumb_b->h};
1634+ draw_rectangle(s, &thumb_rect, pixel);
1635+
1636+ pixel = get_theme_color(LIST_THUMB, DEFAULT_STATE, FOREGROUND_COLOR);
1637+ thumb_rect.x = thumb_rect.x + 1;
1638+ thumb_rect.y = thumb_rect.y + 1;
1639+ thumb_rect.w = thumb_rect.w - 2;
1640+ thumb_rect.h = thumb_rect.h - 2;
1641+ SDL_FillRect(s, &thumb_rect, pixel);
1642+
1643+ }
1644+
1645+ // Draw items
1646+ draw_items(s);
1647+}
1648+
1649+void w_list_base::mouse_move(int x, int y)
1650+{
1651+ if (thumb_dragging) {
1652+ int delta_y = y - thumb_drag_y;
1653+ if (delta_y > 0 && num_items > shown_items && trough_rect.h > thumb_height) {
1654+ set_top_item(delta_y * (num_items - shown_items) / (trough_rect.h - thumb_height));
1655+ } else {
1656+ set_top_item(0);
1657+ }
1658+ } else {
1659+ if (x < get_theme_space(LIST_WIDGET, L_SPACE) || x >= rect.w - get_theme_space(LIST_WIDGET, R_SPACE)
1660+ || y < get_theme_space(LIST_WIDGET, T_SPACE) || y >= rect.h - get_theme_space(LIST_WIDGET, B_SPACE))
1661+ return;
1662+
1663+ if ((y - get_theme_space(LIST_WIDGET, T_SPACE)) / item_height() + top_item < min(num_items, top_item + shown_items))
1664+ { set_selection((y - get_theme_space(LIST_WIDGET, T_SPACE)) / item_height() + top_item); }
1665+// else
1666+// { set_selection(num_items - 1); }
1667+ }
1668+}
1669+
1670+void w_list_base::place(const SDL_Rect& r, placement_flags flags)
1671+{
1672+ widget::place(r, flags);
1673+
1674+ trough_rect.x = rect.w - get_theme_space(LIST_WIDGET, TROUGH_R_SPACE);
1675+ trough_rect.y = get_theme_space(LIST_WIDGET, TROUGH_T_SPACE);
1676+ trough_rect.w = get_theme_space(LIST_WIDGET, TROUGH_WIDTH);
1677+ trough_rect.h = rect.h - get_theme_space(LIST_WIDGET, TROUGH_T_SPACE) - get_theme_space(LIST_WIDGET, TROUGH_B_SPACE);
1678+
1679+ frame_t = get_theme_image(LIST_WIDGET, DEFAULT_STATE, T_IMAGE, rect.w - frame_tl->w - frame_tr->w, 0);
1680+ frame_l = get_theme_image(LIST_WIDGET, DEFAULT_STATE, L_IMAGE, 0, rect.h - frame_tl->h - frame_bl->h);
1681+ frame_r = get_theme_image(LIST_WIDGET, DEFAULT_STATE, R_IMAGE, 0, rect.h - frame_tr->h - frame_br->h);
1682+ frame_b = get_theme_image(LIST_WIDGET, DEFAULT_STATE, B_IMAGE, rect.w - frame_bl->w - frame_br->w, 0);
1683+}
1684+
1685+void w_list_base::click(int x, int y)
1686+{
1687+ if (x >= trough_rect.x && x < trough_rect.x + trough_rect.w
1688+// && y >= trough_rect.y && y < trough_rect.y + trough_rect.h) {
1689+ && y >= thumb_y && y <= thumb_y + thumb_height) {
1690+ thumb_dragging = dirty = true;
1691+ thumb_drag_y = y - thumb_y;
1692+ } else {
1693+ if (num_items > 0 && is_item_selectable(selection))
1694+ item_selected();
1695+ }
1696+}
1697+
1698+void w_list_base::event(SDL_Event &e)
1699+{
1700+ if (e.type == SDL_KEYDOWN) {
1701+ switch (e.key.keysym.sym) {
1702+ case SDLK_UP:
1703+ if (selection != 0)
1704+ { set_selection(selection - 1); }
1705+ e.type = SDL_NOEVENT; // Prevent selection of previous widget
1706+ break;
1707+ case SDLK_DOWN:
1708+ if (selection < num_items - 1)
1709+ { set_selection(selection + 1); }
1710+ e.type = SDL_NOEVENT; // Prevent selection of next widget
1711+ break;
1712+ case SDLK_PAGEUP:
1713+ if (selection > shown_items)
1714+ { set_selection(selection - shown_items); }
1715+ else
1716+ { set_selection(0); }
1717+ break;
1718+ case SDLK_PAGEDOWN:
1719+ if (selection + shown_items < num_items - 1)
1720+ { set_selection(selection + shown_items); }
1721+ else
1722+ { set_selection(num_items - 1); }
1723+ break;
1724+ case SDLK_HOME:
1725+ set_selection(0);
1726+ break;
1727+ case SDLK_END:
1728+ set_selection(num_items - 1);
1729+ break;
1730+ default:
1731+ break;
1732+ }
1733+ } else if (e.type == SDL_MOUSEBUTTONUP) {
1734+ if (thumb_dragging) {
1735+ thumb_dragging = false;
1736+ dirty = true;
1737+ }
1738+ } else if (e.type == SDL_MOUSEBUTTONDOWN) {
1739+ switch (e.button.button) {
1740+ case SDL_BUTTON_WHEELUP:
1741+ if (top_item > 3)
1742+ set_top_item(top_item - 3);
1743+ else
1744+ set_top_item(0);
1745+ break;
1746+ case SDL_BUTTON_WHEELDOWN:
1747+ if (top_item < num_items - shown_items - 3)
1748+ set_top_item(top_item + 3);
1749+ else
1750+ set_top_item(num_items - shown_items);
1751+ break;
1752+ default:
1753+ break;
1754+ }
1755+ }
1756+}
1757+
1758+void w_list_base::set_selection(size_t s)
1759+{
1760+ // Set selection, check for bounds
1761+ assert(s == PIN(s, 0, num_items - 1));
1762+ if (s != selection)
1763+ dirty = true;
1764+ selection = s;
1765+
1766+ // Make selection visible
1767+ if (s < top_item)
1768+ set_top_item(s);
1769+ else if (s >= top_item + shown_items)
1770+ set_top_item(s - shown_items + 1);
1771+}
1772+
1773+void w_list_base::new_items(void)
1774+{
1775+ // Reset top item and selection
1776+
1777+ // ghs: actually, remember top item and selection
1778+ size_t saved_top_item = top_item;
1779+ size_t saved_selection = get_selection();
1780+ top_item = selection = 0;
1781+ dirty = true;
1782+
1783+ // Calculate thumb height
1784+ if (num_items <= shown_items)
1785+ thumb_height = trough_rect.h;
1786+ else if (num_items == 0)
1787+ thumb_height = static_cast<uint16>(shown_items) * trough_rect.h;
1788+ else
1789+ thumb_height = uint16(float(shown_items * trough_rect.h) / num_items + 0.5);
1790+ if (thumb_height < min_thumb_height)
1791+ thumb_height = min_thumb_height;
1792+ else if (thumb_height > trough_rect.h)
1793+ thumb_height = trough_rect.h;
1794+
1795+ // Create dynamic thumb images
1796+ if (thumb_tc)
1797+ SDL_FreeSurface(thumb_tc);
1798+ if (thumb_bc)
1799+ SDL_FreeSurface(thumb_bc);
1800+ int rem_height = thumb_height - thumb_t->h - thumb_c->h - thumb_b->h;
1801+ int dyn_height = rem_height / 2;
1802+ thumb_tc = get_theme_image(LIST_THUMB, DEFAULT_STATE, THUMB_TC_IMAGE, 0, dyn_height);
1803+ thumb_bc = get_theme_image(LIST_THUMB, DEFAULT_STATE, THUMB_BC_IMAGE, 0, (rem_height & 1) ? dyn_height + 1 : dyn_height);
1804+
1805+ thumb_y = 0;
1806+ if (thumb_y > trough_rect.h - thumb_height)
1807+ thumb_y = trough_rect.h - thumb_height;
1808+ thumb_y = thumb_y + trough_rect.y;
1809+
1810+ if (saved_selection < num_items)
1811+ set_selection(saved_selection);
1812+ if (saved_top_item)
1813+ set_top_item(saved_top_item);
1814+}
1815+
1816+void w_list_base::center_item(size_t i)
1817+{
1818+ set_top_item((i > shown_items / 2) ? i - shown_items / 2 : 0);
1819+}
1820+
1821+void w_list_base::set_top_item(size_t i)
1822+{
1823+ // Set top item (check for bounds)
1824+ if (num_items > shown_items)
1825+ i = PIN(i, 0, num_items - shown_items);
1826+ else
1827+ i = 0;
1828+ if (i != top_item)
1829+ dirty = true;
1830+ top_item = i;
1831+
1832+ // Calculate thumb y position
1833+ if (num_items <= shown_items)
1834+ thumb_y = 0;
1835+ else
1836+ thumb_y = int16(float(top_item * (trough_rect.h - thumb_height)) / (num_items - shown_items) + 0.5);
1837+ if (thumb_y > trough_rect.h - thumb_height)
1838+ thumb_y = trough_rect.h - thumb_height;
1839+ thumb_y = thumb_y + trough_rect.y;
1840+}
1841+
1842+
1843+
1844+// ZZZ: maybe this belongs in a new file or something - it's definitely A1-related
1845+// whereas most (but not all) of the other widgets here are sort of 'general-purpose'.
1846+// Anyway, moved here from shell_sdl.h and enhanced ever so slightly, will now be
1847+// using it in the setup network game dialog as well.
1848+
1849+/*
1850+ * Level number dialog
1851+ */
1852+
1853+w_levels::w_levels(const vector<entry_point> &items, dialog *d)
1854+ : w_list<entry_point>(items, 400, 8, 0), parent(d), show_level_numbers(true) {}
1855+
1856+// ZZZ: new constructor gives more control over widget's appearance.
1857+w_levels::w_levels(const vector<entry_point>& items, dialog* d, uint16 inWidth,
1858+ size_t inNumLines, size_t inSelectedItem, bool in_show_level_numbers)
1859+ : w_list<entry_point>(items, inWidth, inNumLines, inSelectedItem), parent(d), show_level_numbers(in_show_level_numbers) {}
1860+
1861+void
1862+w_levels::item_selected(void)
1863+{
1864+ parent->quit(0);
1865+}
1866+
1867+void
1868+w_levels::draw_item(vector<entry_point>::const_iterator i, SDL_Surface *s, int16 x, int16 y, uint16 width, bool selected) const
1869+{
1870+ y = y + font->get_ascent();
1871+ char str[256];
1872+
1873+ if(show_level_numbers)
1874+ sprintf(str, "%d - %s", i->level_number + 1, i->level_name);
1875+ else
1876+ sprintf(str, "%s", i->level_name);
1877+
1878+ set_drawing_clip_rectangle(0, x, static_cast<short>(s->h), x + width);
1879+ draw_text(s, str, x, y, get_theme_color(ITEM_WIDGET, selected ? ACTIVE_STATE : DEFAULT_STATE), font, style);
1880+ set_drawing_clip_rectangle(SHRT_MIN, SHRT_MIN, SHRT_MAX, SHRT_MAX);
1881+}
1882+
1883+
1884+/*
1885+ * String List
1886+ */
1887+
1888+w_string_list::w_string_list(const vector<string> &items, dialog *d, int sel)
1889+ : w_list<string>(items, 400, 8, sel), parent(d) {}
1890+
1891+void w_string_list::item_selected(void)
1892+{
1893+ parent->quit(0);
1894+}
1895+
1896+void w_string_list::draw_item(vector<string>::const_iterator i, SDL_Surface *s, int16 x, int16 y, uint16 width, bool selected) const
1897+{
1898+ y = y + font->get_ascent();
1899+ char str[256];
1900+
1901+ sprintf(str, "%s", i->c_str ());
1902+
1903+ set_drawing_clip_rectangle(0, x, static_cast<short>(s->h), x + width);
1904+ draw_text(s, str, x, y, get_theme_color(ITEM_WIDGET, selected ? ACTIVE_STATE : DEFAULT_STATE), font, style);
1905+ set_drawing_clip_rectangle(SHRT_MIN, SHRT_MIN, SHRT_MAX, SHRT_MAX);
1906+}
1907+
1908+
1909+/*
1910+ * Selection Popup
1911+ */
1912+
1913+w_select_popup::w_select_popup (action_proc p, void *a) : w_select_button ("", gotSelectedCallback, NULL)
1914+{
1915+ set_arg(this);
1916+ selection = -1;
1917+
1918+ action = p;
1919+ arg = a;
1920+}
1921+
1922+void w_select_popup::set_labels(const vector<string>& inLabels)
1923+{
1924+ labels = inLabels;
1925+
1926+ // recalculate min width
1927+ saved_min_width = 0;
1928+ for (vector<string>::iterator it = labels.begin(); it != labels.end(); ++it)
1929+ {
1930+ uint16 width = text_width(it->c_str(), font, style);
1931+ if (width > saved_min_width)
1932+ saved_min_width = width;
1933+ }
1934+}
1935+
1936+void w_select_popup::set_selection (int value)
1937+{
1938+ if (value < labels.size () && value >= 0)
1939+ selection = value;
1940+ else
1941+ selection = -1;
1942+
1943+ if (selection == -1)
1944+ w_select_button::set_selection ("");
1945+ else
1946+ w_select_button::set_selection (labels[selection].c_str ());
1947+}
1948+
1949+void w_select_popup::gotSelected ()
1950+{
1951+ if (labels.size () > 1) {
1952+ dialog theDialog;
1953+ vertical_placer *placer = new vertical_placer;
1954+
1955+ w_string_list* string_list_w = new w_string_list (labels, &theDialog, selection >= 0 ? selection : 0);
1956+ placer->dual_add (string_list_w, theDialog);
1957+ theDialog.activate_widget(string_list_w);
1958+
1959+ theDialog.set_widget_placer(placer);
1960+ if (theDialog.run () == 0)
1961+ set_selection (string_list_w->get_selection ());
1962+ }
1963+
1964+ if (action)
1965+ action (arg);
1966+}
1967+
1968+
1969+static const char* const sFileChooserInvalidFileString = "(no valid selection)";
1970+
1971+w_file_chooser::w_file_chooser(const char* inDialogPrompt, Typecode inTypecode)
1972+ : w_select_button("", NULL, NULL, true), typecode(inTypecode)
1973+{
1974+ strncpy(dialog_prompt, inDialogPrompt, sizeof(dialog_prompt));
1975+ set_selection(sFileChooserInvalidFileString);
1976+}
1977+
1978+
1979+
1980+void
1981+w_file_chooser::set_file(const FileSpecifier& inFile)
1982+{
1983+ file = inFile;
1984+ update_filename();
1985+}
1986+
1987+
1988+
1989+void
1990+w_file_chooser::click(int, int)
1991+{
1992+ if(enabled)
1993+ {
1994+ if(file.ReadDialog(typecode, dialog_prompt))
1995+ {
1996+ update_filename();
1997+ if (m_callback)
1998+ m_callback ();
1999+ }
2000+ }
2001+}
2002+
2003+
2004+
2005+void
2006+w_file_chooser::update_filename()
2007+{
2008+ if(file.Exists())
2009+ {
2010+ file.GetName(filename);
2011+ set_selection(filename);
2012+ }
2013+ else
2014+ set_selection(sFileChooserInvalidFileString);
2015+}
2016+
2017+
2018+const string w_items_in_room_get_name_of_item (GameListMessage::GameListEntry item)
2019+{
2020+ return item.name ();
2021+}
2022+
2023+const string w_items_in_room_get_name_of_item (prospective_joiner_info item)
2024+{
2025+ return pstring_to_string (item.name);
2026+}
2027+
2028+const string w_items_in_room_get_name_of_item (MetaserverPlayerInfo item)
2029+{
2030+ return item.name ();
2031+}
2032+
2033+void w_games_in_room::draw_item(const GameListMessage::GameListEntry& item, SDL_Surface* s, int16 x, int16 y, uint16 width, bool selected) const
2034+{
2035+ int game_style = style;
2036+
2037+ int state;
2038+ if (!item.compatible())
2039+ {
2040+ state = item.target() ? SELECTED_INCOMPATIBLE_GAME : INCOMPATIBLE_GAME;
2041+ }
2042+ else if (item.running())
2043+ {
2044+ state = item.target() ? SELECTED_RUNNING_GAME : RUNNING_GAME;
2045+ }
2046+ else
2047+ {
2048+ state = item.target() ? SELECTED_GAME : GAME;
2049+ }
2050+
2051+ uint32 fg;
2052+ if (selected)
2053+ {
2054+ fg = get_theme_color(ITEM_WIDGET, ACTIVE_STATE);
2055+ }
2056+ else
2057+ {
2058+ fg = get_theme_color(METASERVER_GAMES, state, FOREGROUND_COLOR);
2059+ }
2060+
2061+ uint32 bg = get_theme_color(METASERVER_GAMES, state, BACKGROUND_COLOR);
2062+ SDL_Rect r = { x, y, width, 3 * font->get_line_height() + 2};
2063+ SDL_FillRect(s, &r, bg);
2064+
2065+ if (use_theme_color(METASERVER_GAMES, FRAME_COLOR))
2066+ {
2067+ uint32 frame = get_theme_color(METASERVER_GAMES, state, FRAME_COLOR);
2068+ draw_rectangle(s, &r, frame);
2069+ }
2070+
2071+
2072+ x += 1;
2073+ width -= 2;
2074+ y += font->get_ascent() + 1;
2075+
2076+ ostringstream time_or_ping;
2077+ int right_text_width = 0;
2078+
2079+ // first line, game name, ping or time remaining
2080+ if (item.running())
2081+ {
2082+ if (item.m_description.m_timeLimit && !(item.m_description.m_timeLimit == INT32_MAX || item.m_description.m_timeLimit == -1))
2083+ {
2084+ if (item.minutes_remaining() == 1)
2085+ {
2086+ time_or_ping << "~1 Minute";
2087+ }
2088+ else
2089+ {
2090+ time_or_ping << item.minutes_remaining() << " Minutes";
2091+ }
2092+ }
2093+ else
2094+ {
2095+ time_or_ping << "Untimed";
2096+ }
2097+ }
2098+ else
2099+ {
2100+ // draw ping
2101+ }
2102+
2103+ right_text_width = text_width(time_or_ping.str().c_str(), font, game_style);
2104+
2105+ // draw game name
2106+ set_drawing_clip_rectangle(0, x, static_cast<short>(s->h), x + width - right_text_width);
2107+ font->draw_styled_text(s, item.name(), item.name().size(), x, y, fg, game_style);
2108+
2109+ // draw remaining or ping
2110+ set_drawing_clip_rectangle(0, x, static_cast<short>(s->h), x + width);
2111+ draw_text(s, time_or_ping.str().c_str(), x + width - right_text_width, y, fg, font, game_style);
2112+
2113+ y += font->get_line_height();
2114+
2115+ ostringstream game_and_map;
2116+
2117+ if (!item.compatible())
2118+ {
2119+ game_and_map << "|i" << item.m_description.m_scenarioName;
2120+ if (item.m_description.m_scenarioVersion != "")
2121+ {
2122+ game_and_map << ", Version " << item.m_description.m_scenarioVersion;
2123+ }
2124+ }
2125+ else
2126+ {
2127+ game_and_map << item.game_string()
2128+ << " on |i"
2129+ << item.m_description.m_mapName;
2130+ }
2131+
2132+ font->draw_styled_text(s, game_and_map.str().c_str(), game_and_map.str().size(), x, y, fg, game_style);
2133+
2134+ y += font->get_line_height();
2135+
2136+ right_text_width = font->styled_text_width(item.m_hostPlayerName, item.m_hostPlayerName.size(), game_style);
2137+ set_drawing_clip_rectangle(0, x, static_cast<short>(s->h), x + width - right_text_width);
2138+
2139+ ostringstream game_settings;
2140+ if (item.running())
2141+ {
2142+ if (item.m_description.m_numPlayers == 1)
2143+ {
2144+ game_settings << "1 Player";
2145+ }
2146+ else
2147+ {
2148+ game_settings << static_cast<uint16>(item.m_description.m_numPlayers) << " Players";
2149+ }
2150+ }
2151+ else
2152+ {
2153+ game_settings << static_cast<uint16>(item.m_description.m_numPlayers)
2154+ << "/"
2155+ << item.m_description.m_maxPlayers
2156+ << " Players";
2157+ }
2158+
2159+ if (item.m_description.m_timeLimit && !(item.m_description.m_timeLimit == INT32_MAX || item.m_description.m_timeLimit == -1))
2160+ {
2161+ game_settings << ", "
2162+ << item.m_description.m_timeLimit / 60 / TICKS_PER_SECOND
2163+ << " Minutes";
2164+ }
2165+
2166+ if (item.m_description.m_teamsAllowed)
2167+ {
2168+ game_settings << ", Teams";
2169+ }
2170+
2171+ draw_text(s, game_settings.str().c_str(), x, y, fg, font, game_style);
2172+
2173+ set_drawing_clip_rectangle(0, x, static_cast<short>(s->h), x + width);
2174+ font->draw_styled_text(s, item.m_hostPlayerName, item.m_hostPlayerName.size(), x + width - right_text_width, y, fg, game_style);
2175+
2176+ set_drawing_clip_rectangle(SHRT_MIN, SHRT_MIN, SHRT_MAX, SHRT_MAX);
2177+}
2178+
2179+
2180+static inline uint8 darken(uint8 component, uint8 amount)
2181+{
2182+ return PIN((uint16) component * (255 - amount) / 255, 0, 255);
2183+}
2184+
2185+static inline uint8 lighten(uint8 component, uint8 amount)
2186+{
2187+ return PIN((uint16) component + (255 - component) * amount / 255, 0, 255);
2188+}
2189+
2190+void w_players_in_room::draw_item(const MetaserverPlayerInfo& item, SDL_Surface* s,
2191+ int16 x, int16 y, uint16 width, bool selected) const
2192+{
2193+ set_drawing_clip_rectangle(0, x, static_cast<short>(s->h), x + width);
2194+
2195+ SDL_Rect r = {x, y, width, font->get_line_height() + 4};
2196+
2197+ if (item.target())
2198+ {
2199+ SDL_FillRect(s, &r, SDL_MapRGB(s->format, 0xff, 0xff, 0xff));
2200+ }
2201+
2202+ // background is player color
2203+ uint32 pixel;
2204+ if (item.target())
2205+ {
2206+ int amount = 0x7f;
2207+ pixel = SDL_MapRGB(s->format,
2208+ lighten(item.color()[0] >> 8, amount),
2209+ lighten(item.color()[1] >> 8, amount),
2210+ lighten(item.color()[2] >> 8, amount));
2211+ }
2212+ else
2213+ {
2214+ int amount;
2215+ if (item.away())
2216+ amount = 0xbf;
2217+ else
2218+ amount = 0;
2219+
2220+ pixel = SDL_MapRGB(s->format,
2221+ darken(item.color()[0] >> 8, amount),
2222+ darken(item.color()[1] >> 8, amount),
2223+ darken(item.color()[2] >> 8, amount));
2224+ }
2225+
2226+ r.x = x + kPlayerColorSwatchWidth + kSwatchGutter + 1;
2227+ r.y = y + 1;
2228+ r.w = width - kPlayerColorSwatchWidth - kSwatchGutter - 2;
2229+ r.h = font->get_line_height() + 2;
2230+ SDL_FillRect(s, &r, pixel);
2231+
2232+ // team swatch
2233+ r.x = x + 1;
2234+ r.y = y + 1;
2235+ r.w = kPlayerColorSwatchWidth;
2236+ r.h = font->get_line_height() + 2;
2237+
2238+ if (item.target())
2239+ {
2240+ int amount = 0x7f;
2241+ pixel = SDL_MapRGB(s->format,
2242+ lighten(item.team_color()[0] >> 8, amount),
2243+ lighten(item.team_color()[1] >> 8, amount),
2244+ lighten(item.team_color()[2] >> 8, amount));
2245+ }
2246+ else
2247+ {
2248+ int amount;
2249+ if (item.away())
2250+ amount = 0x7f;
2251+ else
2252+ amount = 0;
2253+
2254+ pixel = SDL_MapRGB(s->format,
2255+ darken(item.team_color()[0] >> 8, amount),
2256+ darken(item.team_color()[1] >> 8, amount),
2257+ darken(item.team_color()[2] >> 8, amount));
2258+ }
2259+
2260+ SDL_FillRect(s, &r, pixel);
2261+
2262+ y += font->get_ascent();
2263+ uint32 color;
2264+ if (selected)
2265+ {
2266+ color = get_theme_color(ITEM_WIDGET, ACTIVE_STATE);
2267+ }
2268+ else if (item.away())
2269+ {
2270+ color = SDL_MapRGB(s->format, 0x7f, 0x7f, 0x7f);
2271+ }
2272+ else
2273+ {
2274+ color = SDL_MapRGB(s->format, 0xff, 0xff, 0xff);
2275+ }
2276+
2277+ font->draw_styled_text(s, item.name(), item.name().size(), x + kPlayerColorSwatchWidth + kSwatchGutter + 2, y + 1, color, item.away() ? style : style | styleShadow);
2278+
2279+ set_drawing_clip_rectangle(SHRT_MIN, SHRT_MIN, SHRT_MAX, SHRT_MAX);
2280+}
2281+
2282+
2283+void w_colorful_chat::append_entry(const ColoredChatEntry& e)
2284+{
2285+ if (e.message.empty())
2286+ {
2287+ get_owning_dialog()->draw_dirty_widgets();
2288+ return;
2289+ }
2290+
2291+ string name;
2292+ if (font->styled_text_width(e.sender, e.sender.size(), style | styleShadow) > kNameWidth)
2293+ name = string(e.sender, 0, font->trunc_styled_text(e.sender, kNameWidth, style | styleShadow));
2294+ else
2295+ name = e.sender;
2296+
2297+ int message_style = style;
2298+ int available_width = rect.w - get_theme_space(LIST_WIDGET, L_SPACE) - get_theme_space(LIST_WIDGET, R_SPACE);
2299+ if (e.type == ColoredChatEntry::ChatMessage)
2300+ {
2301+ available_width -= kNameWidth + taper_width() + 2;
2302+ }
2303+ else if (e.type == ColoredChatEntry::PrivateMessage)
2304+ {
2305+ message_style |= styleShadow;
2306+ available_width -= kNameWidth + taper_width() + 4;
2307+ }
2308+ else
2309+ {
2310+ message_style |= styleShadow;
2311+ available_width -= 2;
2312+ }
2313+
2314+ size_t usable_characters = font->trunc_styled_text(e.message, available_width, message_style);
2315+ string::const_iterator middle;
2316+ string::const_iterator rest;
2317+ if (usable_characters != e.message.size()) {
2318+ size_t last_space = e.message.find_last_of(' ', usable_characters);
2319+ if (last_space != 0 && last_space <= usable_characters)
2320+ {
2321+ middle = e.message.begin() + last_space;
2322+ rest = middle + 1;
2323+ }
2324+ else
2325+ {
2326+ middle = e.message.begin() + usable_characters;
2327+ rest = middle;
2328+ }
2329+ } else {
2330+ middle = e.message.begin() + usable_characters;
2331+ rest = middle;
2332+ }
2333+
2334+ ColoredChatEntry e_begin = e;
2335+ e_begin.message = string(e.message.begin(), middle);
2336+ e_begin.sender = name;
2337+
2338+ ColoredChatEntry e_rest = e;
2339+ if (rest != e.message.end())
2340+ {
2341+ e_rest.message = font->style_at(e.message, middle, message_style) + string(rest, e.message.end());
2342+ }
2343+ else
2344+ e_rest.message = string(rest, e.message.end());
2345+ e_rest.sender = name;
2346+
2347+ bool save_top_item = top_item < num_items - shown_items;
2348+ size_t saved_top_item = top_item;
2349+ entries.push_back(e_begin);
2350+
2351+ num_items = entries.size();
2352+ new_items();
2353+ if (save_top_item) {
2354+ set_top_item(saved_top_item);
2355+ } else if (num_items > shown_items) {
2356+ set_top_item(num_items - shown_items);
2357+ }
2358+
2359+ append_entry(e_rest);
2360+}
2361+
2362+void w_colorful_chat::draw_item(vector<ColoredChatEntry>::const_iterator it, SDL_Surface *s, int16 x, int16 y, uint16 width, bool selected) const
2363+{
2364+ int computed_y = y + font->get_ascent();
2365+
2366+ uint16 message_x = x;
2367+ uint16 message_width = width;
2368+
2369+ if ((*it).type == ColoredChatEntry::ChatMessage || (*it).type == ColoredChatEntry::PrivateMessage)
2370+ {
2371+ // draw the name
2372+ SDL_Rect r = { x, y, kNameWidth, font->get_line_height() + 1};
2373+ uint32 pixel = SDL_MapRGB(s->format,
2374+ (*it).color.red >> 8,
2375+ (*it).color.green >> 8,
2376+ (*it).color.blue >> 8);
2377+ SDL_FillRect(s, &r, pixel);
2378+
2379+ // draw taper
2380+ r.x += kNameWidth ;
2381+ if (it->type == ColoredChatEntry::PrivateMessage)
2382+ {
2383+ // red bar under taper
2384+ r.w = taper_width() + 2;
2385+ SDL_FillRect(s, &r, SDL_MapRGB(s->format, 0x7f, 0x0, 0x0));
2386+ }
2387+
2388+ r.w = 1;
2389+ for (int i = 0; i < taper_width(); ++i)
2390+ {
2391+ r.y++;
2392+ r.h -= 2;
2393+ SDL_FillRect(s, &r, pixel);
2394+ r.x++;
2395+ }
2396+
2397+ set_drawing_clip_rectangle(0, x, static_cast<uint16>(s->h), x + kNameWidth);
2398+ font->draw_styled_text(s, it->sender, it->sender.size(), x + 1, computed_y, SDL_MapRGB(s->format, 0xff, 0xff, 0xff), style | styleShadow);
2399+
2400+ message_x += kNameWidth + taper_width() + 2;
2401+ message_width -= kNameWidth + taper_width() + 2;
2402+ }
2403+
2404+ uint32 message_color = SDL_MapRGB(s->format, 0xff, 0xff, 0xff);
2405+ if (it->type == ColoredChatEntry::ChatMessage)
2406+ {
2407+ message_color = get_theme_color(CHAT_ENTRY, DEFAULT_STATE, FOREGROUND_COLOR);
2408+ }
2409+
2410+ int message_style = style;
2411+ if ((*it).type != ColoredChatEntry::ChatMessage) message_style |= styleShadow;
2412+
2413+ if ((*it).type == ColoredChatEntry::ServerMessage)
2414+ {
2415+ // draw the blue bar
2416+ uint32 pixel = SDL_MapRGB(s->format, 0x0, 0x0, 0x7f);
2417+ SDL_Rect r = { message_x, y, message_width, font->get_line_height() + 1 };
2418+ SDL_FillRect(s, &r, pixel);
2419+ message_x += 1;
2420+ message_width -= 2;
2421+ }
2422+ else if ((*it).type == ColoredChatEntry::PrivateMessage)
2423+ {
2424+ // draw a red bar
2425+ uint32 pixel = SDL_MapRGB(s->format, 0x7f, 0x0, 0x0);
2426+ SDL_Rect r = { message_x, y, message_width, font->get_line_height() + 1 };
2427+ SDL_FillRect(s, &r, pixel);
2428+ message_x += 1;
2429+ message_width -= 2;
2430+ }
2431+ else if ((*it).type == ColoredChatEntry::LocalMessage)
2432+ {
2433+ // draw a gray bar
2434+ uint32 pixel = SDL_MapRGB(s->format, 0x3f, 0x3f, 0x3f);
2435+ SDL_Rect r = { message_x, y, message_width, font->get_line_height() + 1 };
2436+ SDL_FillRect(s, &r, pixel);
2437+ message_x += 1;
2438+ message_width -= 2;
2439+ }
2440+
2441+ set_drawing_clip_rectangle(0, message_x, static_cast<uint16>(s->h), message_x + message_width);
2442+ font->draw_styled_text(s, it->message, it->message.size(), message_x, computed_y, message_color, message_style);
2443+
2444+ set_drawing_clip_rectangle(SHRT_MIN, SHRT_MIN, SHRT_MAX, SHRT_MAX);
2445+}
2446+
2447+void SDLWidgetWidget::hide ()
2448+{
2449+ hidden = true;
2450+ m_widget->set_enabled (false);
2451+}
2452+
2453+void SDLWidgetWidget::show ()
2454+{
2455+ hidden = false;
2456+ m_widget->set_enabled (!inactive);
2457+}
2458+
2459+void SDLWidgetWidget::deactivate ()
2460+{
2461+ inactive = true;
2462+ m_widget->set_enabled (false);
2463+}
2464+
2465+void SDLWidgetWidget::activate ()
2466+{
2467+ inactive = false;
2468+ m_widget->set_enabled (!hidden);
2469+}
2470+
2471+
2472+PlayersInGameWidget::PlayersInGameWidget (w_players_in_game2* pig)
2473+ : SDLWidgetWidget (pig)
2474+ , m_pig (pig)
2475+ {}
2476+
2477+void PlayersInGameWidget::redraw ()
2478+{
2479+ m_pig->start_displaying_actual_information ();
2480+ m_pig->update_display ();
2481+ m_pig->get_owning_dialog ()->draw_dirty_widgets ();
2482+}
2483+
2484+
2485+
旧リポジトリブラウザで表示