From 31aae994850ea9965157508baa43cf97c47ea373 Mon Sep 17 00:00:00 2001 From: dominik Date: Wed, 18 Aug 2004 23:38:53 +0000 Subject: [PATCH] (svn r80) New internal news system (implemented with fifo queue) Message history works now --- news_gui.c | 420 +++++++++++++++++++++++++++++------------------------ 1 file changed, 232 insertions(+), 188 deletions(-) diff --git a/news_gui.c b/news_gui.c index 2b25b5c0c7..d08185333b 100644 --- a/news_gui.c +++ b/news_gui.c @@ -8,10 +8,29 @@ #include "news.h" #include "vehicle.h" -static NewsItem _news_items[10]; -static NewsItem _active_news_items[20]; - -void ExpireNewsItem(); +/* News system +News system is realized as a FIFO queue (in an array) +The positions in the queue can't be rearranged, we only access +the array elements through pointers to the elements. Once the +array is full, the oldest entry (_oldest_news) is being overwritten +by the newest (_latest news). + +oldest current lastest + | | | +[O------------F-------------C---------L ] + | + forced +*/ + +# define MAX_NEWS 30 + +static NewsItem _news_items[MAX_NEWS]; +static byte _current_news = 255; // points to news item that should be shown next +static byte _oldest_news = 0; // points to first item in fifo queue +static byte _latest_news = 255; // points to last item in fifo queue +static byte _forced_news = 255; // points to a forced-to-be-shown item (255 for none) + +static byte _total_news = 0; // total news count void DrawNewsNewTrainAvail(Window *w); void DrawNewsNewRoadVehAvail(Window *w); @@ -41,6 +60,10 @@ GetNewsStringCallbackProc * const _get_news_string_callback[] = { GetNewsStringBankrupcy, /* DNC_BANKRUPCY */ }; +void InitNewsItemStructs() +{ + memset(_news_items, 0, sizeof(_news_items)); +} void DrawNewsBorder(Window *w) { @@ -68,7 +91,7 @@ static void NewsWindowProc(Window *w, WindowEvent *e) if (ni->display_mode == NM_NORMAL || ni->display_mode == NM_THIN) { DrawNewsBorder(w); - + DrawString(2, 1, STR_00C6, 0); SET_DPARAM16(0, ni->date); @@ -88,7 +111,7 @@ static void NewsWindowProc(Window *w, WindowEvent *e) GfxFillRect(vp->left - w->left, vp->top - w->top, vp->left - w->left + vp->width - 1, vp->top - w->top + vp->height - 1, ni->flags & NF_INCOLOR ? 0x4322:0x4323 ); - + COPY_IN_DPARAM(0, ni->params, lengthof(ni->params)); DrawStringMultiCenter((w->width>>1), 20, ni->string_id, 428); } @@ -109,7 +132,12 @@ static void NewsWindowProc(Window *w, WindowEvent *e) case WE_CLICK: { switch(e->click.widget) { - case 1:DeleteWindow(w); ExpireNewsItem(); break; + case 1: { + DeleteWindow(w); + NewsItem *ni = WP(w,news_d).ni; + ni->duration = 0; + if(_forced_news!=255) _forced_news = 255; + } break; case 0: { NewsItem *ni = WP(w,news_d).ni; if (ni->flags & NF_VEHICLE) { @@ -128,7 +156,6 @@ static void NewsWindowProc(Window *w, WindowEvent *e) // Don't continue. e->keypress.cont = false; DeleteWindow(w); - ExpireNewsItem(); } break; @@ -147,102 +174,66 @@ static void NewsWindowProc(Window *w, WindowEvent *e) } } +// returns the correct index in the array +// (to deal with overflows) +byte getIndex(byte i) +{ + if(i==255) { + if(_oldest_news <= _latest_news) + return _latest_news; + else + return MAX_NEWS; + } + if(i >= MAX_NEWS) i %= MAX_NEWS; + return i; +} + void AddNewsItem(StringID string, uint32 flags, uint data_a, uint data_b) { NewsItem *ni; + Window *w; if (_game_mode == GM_MENU) return; - // Find a free place and add it there. - for(ni=_news_items; ni!=endof(_news_items); ni++) { - if (ni->string_id==0) { - ni->string_id = string; - ni->display_mode = (byte)flags; - ni->flags = (byte)(flags >> 8) | NF_NOEXPIRE; - - // show this news message in color? - if (_date >= ConvertIntDate(_patches.colored_news_date)) - ni->flags |= NF_INCOLOR; - - ni->type = (byte)(flags >> 16); - ni->callback = (byte)(flags >> 24); - ni->duration = 555; - ni->data_a = data_a; - ni->data_b = data_b; - ni->date = _date; - COPY_OUT_DPARAM(ni->params, 0, lengthof(ni->params)); - break; - } - } -} + _forced_news = 255; + if(_total_news < MAX_NEWS) _total_news++; -// _active_news_items 0..9 are the ones that have already been shown -// _active_news_items 10..19 are the ones that are to be shown next + // make sure our pointer isn't overflowing + _latest_news = getIndex(++_latest_news); -static void MoveNewsItems() -{ - Window *w; - NewsItem *ni; + // overwrite oldest news entry + if( _oldest_news == _latest_news && _news_items[_oldest_news].string_id != 0) + _oldest_news = getIndex(++_oldest_news); // but make sure we're not overflowing here - // No new news item? - if (_news_items[0].string_id == 0) - return; + // add news to _latest_news + ni = &_news_items[_latest_news]; - // Check if the status bar message is still being displayed? - w = FindWindowById(WC_STATUS_BAR, 0); - if (w != NULL && WP(w,def_d).data_1 > -1280) - return; + ni->string_id = string; + ni->display_mode = (byte)flags; + ni->flags = (byte)(flags >> 8) | NF_NOEXPIRE; - // Add the news items to the list of pending ones. - for(ni=_active_news_items + 10; ni != _active_news_items + 20; ni++) { - if (ni->string_id == 0) { - *ni = _news_items[0]; - memcpy_overlapping(_news_items, _news_items+1, sizeof(_news_items) - sizeof(_news_items[0]) * 1); - endof(_news_items)[-1].string_id = 0; - break; - } - } -} + // show this news message in color? + if (_date >= ConvertIntDate(_patches.colored_news_date)) + ni->flags |= NF_INCOLOR; -void ExpireNewsItem() -{ - memcpy_overlapping(_active_news_items, _active_news_items + 1, sizeof(_active_news_items) - sizeof(_active_news_items[0])); - endof(_active_news_items)[-1].string_id = 0; + ni->type = (byte)(flags >> 16); + ni->callback = (byte)(flags >> 24); + ni->data_a = data_a; + ni->data_b = data_b; + ni->date = _date; + COPY_OUT_DPARAM(ni->params, 0, lengthof(ni->params)); + + w = FindWindowById(WC_MESSAGE_HISTORY, 0); + if(w==NULL) return; + SetWindowDirty(w); + w->vscroll.count = _total_news; } +// don't show item if it's older than x days static const byte _news_items_age[] = {60, 60, 90, 60, 90, 30, 150, 30, 90, 180}; -static void RemoveOldNewsItems() -{ - NewsItem *ni, *nit; - - ni = _active_news_items; - do { - if (ni->string_id != 0 && - ni != _active_news_items + 10 && - _date - _news_items_age[ni->type] > ni->date) { - - if (ni >= _active_news_items + 10) { - nit = ni; - while (nit != _active_news_items + 19) { - nit[0] = nit[1]; - nit++; - } - nit->string_id = 0; - } else { - nit = ni + 1; - while (nit != _active_news_items + 1) { - nit--; - nit[0] = nit[-1]; - } - _active_news_items[0].string_id = 0; - } - } - } while (++ni != endof(_active_news_items) ); -} - static const Widget _news_type13_widgets[] = { { WWT_PANEL, 15, 0, 429, 0, 169, 0x0}, { WWT_PANEL, 15, 0, 10, 0, 11, 0x0}, @@ -289,106 +280,145 @@ static WindowDesc _news_type0_desc = { static byte _news_sounds[] = { 27, 27, 0, 0, 0, 0, 28, 0, 0, 0 }; -static void ProcessNewsItem(NewsItem *ni) +// open up an own newspaper window for the news item +static void ShowNewspaper(NewsItem *ni) { Window *w; int sound; + int top; + ni->flags &= ~(NF_NOEXPIRE|NF_FORCE_BIG); + ni->duration = 555; + + sound = _news_sounds[ni->type]; + if (sound != 0) + SndPlayFx(sound); + + top = _screen.height - 4; + if (ni->display_mode == NM_NORMAL || ni->display_mode == NM_CALLBACK) { + _news_type13_desc.top = top; + w = AllocateWindowDesc(&_news_type13_desc); + if (ni->flags & NF_VIEWPORT) { + AssignWindowViewport(w, 2, 58, 0x1AA, 0x6E, ni->data_a | ((ni->flags&NF_VEHICLE) ? 0x80000000 : 0), 0); + } + } else if (ni->display_mode == NM_THIN) { + _news_type2_desc.top = top; + w = AllocateWindowDesc(&_news_type2_desc); + if (ni->flags & NF_VIEWPORT) { + AssignWindowViewport(w, 2, 58, 0x1AA, 0x46, ni->data_a | ((ni->flags&NF_VEHICLE) ? 0x80000000 : 0), 0); + } + } else { + _news_type0_desc.top = top; + w = AllocateWindowDesc(&_news_type0_desc); + if (ni->flags & NF_VIEWPORT) { + AssignWindowViewport(w, 3, 17, 0x112, 0x2F, ni->data_a | ((ni->flags&NF_VEHICLE) ? 0x80000000 : 0), 0); + } + } + WP(w,news_d).ni = &_news_items[(_forced_news==255)?_current_news:_forced_news]; + w->flags4 |= WF_DISABLE_VP_SCROLL; +} - // No news item, quit - if (ni->string_id == 0) - return; +// show news item in the ticker +static void ShowTicker(NewsItem *ni) +{ + Window *w; - // Delete the item once the duration reaches 0 - if (ni->duration == 0) { - DeleteWindowById(WC_NEWS_WINDOW, 0); - ExpireNewsItem(); - return; + SndPlayFx(20); + _statusbar_news_item = *ni; + w = FindWindowById(WC_STATUS_BAR, 0); + if (w != 0) + WP(w,def_d).data_1 = 360; +} + + +// Are we ready to show another news item? +// Only if nothing is in the newsticker and no newspaper is displayed +static bool ReadyForNextItem() +{ + Window *w; + NewsItem *ni = &_news_items[(_forced_news==255)?_current_news:_forced_news]; + + // Ticker message + // Check if the status bar message is still being displayed? + w = FindWindowById(WC_STATUS_BAR, 0); + if (w != NULL && WP(w,def_d).data_1 > -1280) + { + return false; } - ni->duration--; - - // As long as the window still is shown, don't go further - w = FindWindowById(WC_NEWS_WINDOW, 0); - if (w != NULL) - return; - // Expire the item if NF_NOEXPIRE was removed - if (!(ni->flags & NF_NOEXPIRE)) { - ExpireNewsItem(); - return; + // Newspaper message + // Wait until duration reaches 0 + if (ni->duration != 0) { + ni->duration--; + return false; } - if (!HASBIT(_news_display_opt, ni->type) && !(ni->flags&NF_FORCE_BIG)) { - SndPlayFx(20); - _statusbar_news_item = *ni; - w = FindWindowById(WC_STATUS_BAR, 0); - if (w != 0) - WP(w,def_d).data_1 = 360; - ExpireNewsItem(); - } else { - int top; + // neither newsticker nor newspaper are running + return true; +} + +static void MoveToNexItem() +{ + DeleteWindowById(WC_NEWS_WINDOW, 0); - ni->flags &= ~(NF_NOEXPIRE|NF_FORCE_BIG); + // if we're not at the last item, than move on + if(_current_news != _latest_news) + { + NewsItem *ni; - sound = _news_sounds[ni->type]; - if (sound != 0) - SndPlayFx(sound); + _current_news = getIndex(++_current_news); + ni = &_news_items[_current_news]; - top = _screen.height - 4; - if (ni->display_mode == NM_NORMAL || ni->display_mode == NM_CALLBACK) { - _news_type13_desc.top = top; - w = AllocateWindowDesc(&_news_type13_desc); - if (ni->flags & NF_VIEWPORT) { - AssignWindowViewport(w, 2, 58, 0x1AA, 0x6E, ni->data_a | ((ni->flags&NF_VEHICLE) ? 0x80000000 : 0), 0); - } - } else if (ni->display_mode == NM_THIN) { - _news_type2_desc.top = top; - w = AllocateWindowDesc(&_news_type2_desc); - if (ni->flags & NF_VIEWPORT) { - AssignWindowViewport(w, 2, 58, 0x1AA, 0x46, ni->data_a | ((ni->flags&NF_VEHICLE) ? 0x80000000 : 0), 0); - } - } else { - _news_type0_desc.top = top; - w = AllocateWindowDesc(&_news_type0_desc); - if (ni->flags & NF_VIEWPORT) { - AssignWindowViewport(w, 3, 17, 0x112, 0x2F, ni->data_a | ((ni->flags&NF_VEHICLE) ? 0x80000000 : 0), 0); - } - } - WP(w,news_d).ni = _active_news_items + 10; - w->flags4 |= WF_DISABLE_VP_SCROLL; + // check the date, don't show too old items + if(_date - _news_items_age[ni->type] > ni->date) + return; + + // show newspaper or send to ticker? + if(!HASBIT(_news_display_opt, ni->type) && !(ni->flags&NF_FORCE_BIG)) + ShowTicker(ni); + else + ShowNewspaper(ni); } -} +} void NewsLoop() { - RemoveOldNewsItems(); - MoveNewsItems(); - ProcessNewsItem(_active_news_items + 10); + // no news item yet + if(_total_news==0) + return; + + if( ReadyForNextItem() ) + MoveToNexItem(); } -void ShowLastNewsMessage() +/* Do a forced show of a specific message */ +void ShowNewsMessage(byte i) { - // No news item immediately before 10? - if (_active_news_items[9].string_id == 0) - return; - // Delete the news window DeleteWindowById(WC_NEWS_WINDOW, 0); - - // Move all items one step - memmove(_active_news_items+1, _active_news_items, sizeof(NewsItem)*19); - _active_news_items[0].string_id = 0; - // Default duration and flags for re-shown items - _active_news_items[10].duration = 555; - _active_news_items[10].flags |= NF_NOEXPIRE | NF_FORCE_BIG; + // setup forced news item + _forced_news = i; + + if(_forced_news!=255) + { + NewsItem *ni = &_news_items[_forced_news]; + ni->duration = 555; + ni->flags |= NF_NOEXPIRE | NF_FORCE_BIG; + DeleteWindowById(WC_NEWS_WINDOW, 0); + ShowNewspaper(ni); + } } -void InitNewsItemStructs() +void ShowLastNewsMessage() { - memset(_news_items, 0, sizeof(_news_items)); - memset(_active_news_items, 0, sizeof(_active_news_items)); + if(_forced_news==255) + ShowNewsMessage(_current_news); + else + ShowNewsMessage( getIndex(_forced_news-1) ); } + + static void MessageOptionsWndProc(Window *w, WindowEvent *e) { switch(e->event) { @@ -407,7 +437,7 @@ static void MessageOptionsWndProc(Window *w, WindowEvent *e) DrawWindowWidgets(w); DrawStringCentered(185, 15, STR_0205_MESSAGE_TYPES, 0); - + y = 27; for(i=STR_0206_ARRIVAL_OF_FIRST_VEHICLE; i <= STR_020F_GENERAL_INFORMATION; i++) { DrawString(124, y, i, 0); @@ -426,7 +456,7 @@ static void MessageOptionsWndProc(Window *w, WindowEvent *e) _news_display_opt |= (1 << (wid>>1)); } SetWindowDirty(w); - /* XXX: write settings */ + // XXX: write settings } if( e->click.widget == 23) { _news_display_opt = 0; @@ -481,9 +511,6 @@ static const WindowDesc _message_options_desc = { MessageOptionsWndProc }; - - - void ShowMessageOptions() { DeleteWindowById(WC_GAME_OPTIONS, 0); @@ -491,6 +518,24 @@ void ShowMessageOptions() } + +/* return news by number, with 0 being the most +recent news. Returns false if end of queue reached. */ +static byte getNews(byte i) +{ + if(i>=MAX_NEWS) + { + return -1; + } + + i = _latest_news - i; + i = i % MAX_NEWS; + + if(_news_items[i].string_id == 0) return -1; + + return i; +} + static void GetNewsString(NewsItem *ni, byte *buffer) { StringID str; @@ -535,23 +580,20 @@ static void MessageHistoryWndProc(Window *w, WindowEvent *e) { switch(e->event) { case WE_PAINT: { - uint n, y, i; - char buffer[256]; + byte buffer[256]; + int y=19; + byte p, show; NewsItem *ni; - for(n=10; n!=0; n--) - if (!_active_news_items[n - 1].string_id) - break; - n = 10 - n; - - SetVScrollCount(w, n); DrawWindowWidgets(w); - y = 18; - for(i=w->vscroll.pos; i!=n; i++) { - ni = &_active_news_items[i + (10 - n)]; + if(_total_news==0) break; + show = min(_total_news, 10); - assert(ni->string_id); + for(p=w->vscroll.pos; pvscroll.pos+show; p++) + { + // get news in correct order + ni = &_news_items[ getNews(p) ]; SET_DPARAM16(0, ni->date); DrawString(4, y, STR_00AF, 16); @@ -560,22 +602,23 @@ static void MessageHistoryWndProc(Window *w, WindowEvent *e) DoDrawString(buffer, 85, y, 16); y += 12; } + break; } case WE_CLICK: switch(e->click.widget) { case 2: { - uint y = (e->click.pt.y - 18) / 12; - NewsItem *ni; + int y = (e->click.pt.y - 19) / 12; + byte p, q; - if (y >= (uint)w->vscroll.count) - return; + p = y + w->vscroll.pos; + if( p > _total_news-1 ) break; - ni = &_active_news_items[w->vscroll.pos + y + (10 - w->vscroll.count)]; + if(_latest_news >= p) q=_latest_news - p; + else q=_latest_news + MAX_NEWS - p; -// NOT YET... -// ShowNewsMessage(y); + ShowNewsMessage(q); break; } @@ -587,13 +630,13 @@ static void MessageHistoryWndProc(Window *w, WindowEvent *e) static const Widget _message_history_widgets[] = { { WWT_CLOSEBOX, 13, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, { WWT_CAPTION, 13, 11, 399, 0, 13, STR_MESSAGE_HISTORY, STR_018C_WINDOW_TITLE_DRAG_THIS}, -{ WWT_IMGBTN, 13, 0, 388, 14, 147, 0x0, STR_MESSAGE_HISTORY_TIP}, -{ WWT_SCROLLBAR, 13, 389, 399, 14, 147, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, +{ WWT_IMGBTN, 13, 0, 388, 14, 139, 0x0, STR_MESSAGE_HISTORY_TIP}, +{ WWT_SCROLLBAR, 13, 389, 399, 14, 139, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, { WWT_LAST}, }; static const WindowDesc _message_history_desc = { - 240, 22, 400, 148, + 240, 22, 400, 140, WC_MESSAGE_HISTORY,0, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, _message_history_widgets, @@ -603,12 +646,13 @@ static const WindowDesc _message_history_desc = { void ShowMessageHistory() { Window *w; - + DeleteWindowById(WC_MESSAGE_HISTORY, 0); w = AllocateWindowDesc(&_message_history_desc); if (w) { - w->vscroll.cap = 11; + w->vscroll.cap = 10; + w->vscroll.count = _total_news; SetWindowDirty(w); } }