58 static NewsItem _news_items[MAX_NEWS]; ///< The news FIFO queue |
58 static NewsItem _news_items[MAX_NEWS]; ///< The news FIFO queue |
59 static NewsID _current_news = INVALID_NEWS; ///< points to news item that should be shown next |
59 static NewsID _current_news = INVALID_NEWS; ///< points to news item that should be shown next |
60 static NewsID _oldest_news = 0; ///< points to first item in fifo queue |
60 static NewsID _oldest_news = 0; ///< points to first item in fifo queue |
61 static NewsID _latest_news = INVALID_NEWS; ///< points to last item in fifo queue |
61 static NewsID _latest_news = INVALID_NEWS; ///< points to last item in fifo queue |
62 |
62 |
|
63 struct news_d { |
|
64 uint16 follow_vehicle; |
|
65 int32 scrollpos_x; |
|
66 int32 scrollpos_y; |
|
67 int32 dest_scrollpos_x; |
|
68 int32 dest_scrollpos_y; |
|
69 NewsItem *ni; |
|
70 }; |
|
71 assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(news_d)); |
|
72 |
63 /** Forced news item. |
73 /** Forced news item. |
64 * Users can force an item by accessing the history or "last message". |
74 * Users can force an item by accessing the history or "last message". |
65 * If the message being shown was forced by the user, its index is stored in |
75 * If the message being shown was forced by the user, its index is stored in |
66 * _forced_news. Otherwise, \a _forced_news variable is INVALID_NEWS. */ |
76 * _forced_news. Otherwise, \a _forced_news variable is INVALID_NEWS. */ |
67 static NewsID _forced_news = INVALID_NEWS; |
77 static NewsID _forced_news = INVALID_NEWS; |
68 |
78 |
69 static byte _total_news = 0; ///< Number of news items in FIFO queue @see _news_items |
79 static byte _total_news = 0; ///< Number of news items in FIFO queue @see _news_items |
70 |
80 |
71 void DrawNewsNewVehicleAvail(Window *w); |
81 void DrawNewsNewVehicleAvail(Window *w, const NewsItem *ni); |
72 void DrawNewsBankrupcy(Window *w); |
82 void DrawNewsBankrupcy(Window *w, const NewsItem *ni); |
73 static void MoveToNextItem(); |
83 static void MoveToNextItem(); |
74 |
84 |
75 StringID GetNewsStringNewVehicleAvail(const NewsItem *ni); |
85 StringID GetNewsStringNewVehicleAvail(const NewsItem *ni); |
76 StringID GetNewsStringBankrupcy(const NewsItem *ni); |
86 StringID GetNewsStringBankrupcy(const NewsItem *ni); |
77 |
87 |
115 } |
125 } |
116 |
126 |
117 static void NewsWindowProc(Window *w, WindowEvent *e) |
127 static void NewsWindowProc(Window *w, WindowEvent *e) |
118 { |
128 { |
119 switch (e->event) { |
129 switch (e->event) { |
120 case WE_CREATE: { // If chatbar is open at creation time, we need to go above it |
130 case WE_CREATE: { // If chatbar is open at creation time, we need to go above it |
121 const Window *w1 = FindWindowById(WC_SEND_NETWORK_MSG, 0); |
131 const Window *w1 = FindWindowById(WC_SEND_NETWORK_MSG, 0); |
122 w->message.msg = (w1 != NULL) ? w1->height : 0; |
132 w->message.msg = (w1 != NULL) ? w1->height : 0; |
123 } break; |
133 } break; |
124 |
134 |
125 case WE_PAINT: { |
135 case WE_PAINT: { |
126 const NewsItem *ni = WP(w, news_d).ni; |
136 const NewsItem *ni = WP(w, news_d).ni; |
127 ViewPort *vp; |
137 ViewPort *vp; |
128 |
138 |
129 switch (ni->display_mode) { |
139 switch (ni->display_mode) { |
130 case NM_NORMAL: |
140 case NM_NORMAL: |
131 case NM_THIN: { |
141 case NM_THIN: { |
132 DrawNewsBorder(w); |
142 DrawNewsBorder(w); |
133 |
143 |
134 DrawString(2, 1, STR_00C6, TC_FROMSTRING); |
144 DrawString(2, 1, STR_00C6, TC_FROMSTRING); |
135 |
145 |
136 SetDParam(0, ni->date); |
146 SetDParam(0, ni->date); |
137 DrawStringRightAligned(428, 1, STR_01FF, TC_FROMSTRING); |
147 DrawStringRightAligned(428, 1, STR_01FF, TC_FROMSTRING); |
138 |
148 |
139 if (!(ni->flags & NF_VIEWPORT)) { |
149 if (!(ni->flags & NF_VIEWPORT)) { |
140 CopyInDParam(0, ni->params, lengthof(ni->params)); |
150 CopyInDParam(0, ni->params, lengthof(ni->params)); |
141 DrawStringMultiCenter(215, ni->display_mode == NM_NORMAL ? 76 : 56, |
151 DrawStringMultiCenter(215, ni->display_mode == NM_NORMAL ? 76 : 56, |
142 ni->string_id, w->width - 4); |
152 ni->string_id, w->width - 4); |
143 } else { |
153 } else { |
144 /* Back up transparency options to draw news view */ |
154 /* Back up transparency options to draw news view */ |
145 TransparencyOptionBits to_backup = _transparency_opt; |
155 TransparencyOptionBits to_backup = _transparency_opt; |
146 _transparency_opt = 0; |
156 _transparency_opt = 0; |
147 DrawWindowViewport(w); |
157 DrawWindowViewport(w); |
148 _transparency_opt = to_backup; |
158 _transparency_opt = to_backup; |
149 |
159 |
150 /* Shade the viewport into gray, or color*/ |
160 /* Shade the viewport into gray, or color*/ |
151 vp = w->viewport; |
161 vp = w->viewport; |
152 GfxFillRect(vp->left - w->left, vp->top - w->top, |
162 GfxFillRect(vp->left - w->left, vp->top - w->top, |
153 vp->left - w->left + vp->width - 1, vp->top - w->top + vp->height - 1, |
163 vp->left - w->left + vp->width - 1, vp->top - w->top + vp->height - 1, |
154 (ni->flags & NF_INCOLOR ? PALETTE_TO_TRANSPARENT : PALETTE_TO_STRUCT_GREY) | (1 << USE_COLORTABLE) |
164 (ni->flags & NF_INCOLOR ? PALETTE_TO_TRANSPARENT : PALETTE_TO_STRUCT_GREY) | (1 << USE_COLORTABLE) |
155 ); |
165 ); |
156 |
166 |
157 CopyInDParam(0, ni->params, lengthof(ni->params)); |
167 CopyInDParam(0, ni->params, lengthof(ni->params)); |
158 DrawStringMultiCenter(w->width / 2, 20, ni->string_id, w->width - 4); |
168 DrawStringMultiCenter(w->width / 2, 20, ni->string_id, w->width - 4); |
|
169 } |
|
170 break; |
159 } |
171 } |
160 break; |
172 |
161 } |
173 case NM_CALLBACK: { |
162 |
174 _draw_news_callback[ni->callback](w, ni); |
163 case NM_CALLBACK: { |
175 break; |
164 _draw_news_callback[ni->callback](w); |
|
165 break; |
|
166 } |
|
167 |
|
168 default: { |
|
169 DrawWindowWidgets(w); |
|
170 if (!(ni->flags & NF_VIEWPORT)) { |
|
171 CopyInDParam(0, ni->params, lengthof(ni->params)); |
|
172 DrawStringMultiCenter(140, 38, ni->string_id, 276); |
|
173 } else { |
|
174 DrawWindowViewport(w); |
|
175 CopyInDParam(0, ni->params, lengthof(ni->params)); |
|
176 DrawStringMultiCenter(w->width / 2, w->height - 16, ni->string_id, w->width - 4); |
|
177 } |
176 } |
178 break; |
177 |
179 } |
178 default: { |
180 } |
179 DrawWindowWidgets(w); |
181 } break; |
180 if (!(ni->flags & NF_VIEWPORT)) { |
182 |
181 CopyInDParam(0, ni->params, lengthof(ni->params)); |
183 case WE_CLICK: { |
182 DrawStringMultiCenter(140, 38, ni->string_id, 276); |
184 switch (e->we.click.widget) { |
183 } else { |
185 case 1: { |
184 DrawWindowViewport(w); |
186 NewsItem *ni = WP(w, news_d).ni; |
185 CopyInDParam(0, ni->params, lengthof(ni->params)); |
187 DeleteWindow(w); |
186 DrawStringMultiCenter(w->width / 2, w->height - 16, ni->string_id, w->width - 4); |
188 ni->duration = 0; |
187 } |
189 _forced_news = INVALID_NEWS; |
188 break; |
190 } break; |
189 } |
191 case 0: { |
|
192 NewsItem *ni = WP(w, news_d).ni; |
|
193 if (ni->flags & NF_VEHICLE) { |
|
194 Vehicle *v = GetVehicle(ni->data_a); |
|
195 ScrollMainWindowTo(v->x_pos, v->y_pos); |
|
196 } else if (ni->flags & NF_TILE) { |
|
197 if (!ScrollMainWindowToTile(ni->data_a) && ni->data_b != 0) |
|
198 ScrollMainWindowToTile(ni->data_b); |
|
199 } |
190 } |
200 } break; |
191 } break; |
201 } |
192 |
202 } break; |
193 case WE_CLICK: { |
203 |
194 switch (e->we.click.widget) { |
204 case WE_KEYPRESS: |
195 case 1: { |
205 if (e->we.keypress.keycode == WKC_SPACE) { |
196 NewsItem *ni = WP(w, news_d).ni; |
206 /* Don't continue. */ |
197 DeleteWindow(w); |
207 e->we.keypress.cont = false; |
198 ni->duration = 0; |
208 DeleteWindow(w); |
199 _forced_news = INVALID_NEWS; |
209 } |
200 } break; |
210 break; |
201 case 0: { |
211 |
202 NewsItem *ni = WP(w, news_d).ni; |
212 case WE_MESSAGE: // The chatbar has notified us that is was either created or closed |
203 if (ni->flags & NF_VEHICLE) { |
213 switch (e->we.message.msg) { |
204 Vehicle *v = GetVehicle(ni->data_a); |
214 case WE_CREATE: w->message.msg = e->we.message.wparam; break; |
205 ScrollMainWindowTo(v->x_pos, v->y_pos); |
215 case WE_DESTROY: w->message.msg = 0; break; |
206 } else if (ni->flags & NF_TILE) { |
216 } |
207 if (!ScrollMainWindowToTile(ni->data_a) && ni->data_b != 0) |
217 break; |
208 ScrollMainWindowToTile(ni->data_b); |
218 |
209 } |
219 case WE_TICK: { // Scroll up newsmessages from the bottom in steps of 4 pixels |
210 } break; |
220 int diff; |
211 } |
221 int y = max(w->top - 4, _screen.height - w->height - 12 - w->message.msg); |
212 } break; |
222 if (y == w->top) return; |
213 |
223 |
214 case WE_KEYPRESS: |
224 if (w->viewport != NULL) |
215 if (e->we.keypress.keycode == WKC_SPACE) { |
225 w->viewport->top += y - w->top; |
216 /* Don't continue. */ |
226 |
217 e->we.keypress.cont = false; |
227 diff = Delta(w->top, y); |
218 DeleteWindow(w); |
228 w->top = y; |
219 } |
229 |
220 break; |
230 SetDirtyBlocks(w->left, w->top - diff, w->left + w->width, w->top + w->height); |
221 |
231 } break; |
222 case WE_MESSAGE: // The chatbar has notified us that is was either created or closed |
|
223 switch (e->we.message.msg) { |
|
224 case WE_CREATE: w->message.msg = e->we.message.wparam; break; |
|
225 case WE_DESTROY: w->message.msg = 0; break; |
|
226 } |
|
227 break; |
|
228 |
|
229 case WE_TICK: { // Scroll up newsmessages from the bottom in steps of 4 pixels |
|
230 int diff; |
|
231 int y = max(w->top - 4, _screen.height - w->height - 12 - w->message.msg); |
|
232 if (y == w->top) return; |
|
233 |
|
234 if (w->viewport != NULL) |
|
235 w->viewport->top += y - w->top; |
|
236 |
|
237 diff = Delta(w->top, y); |
|
238 w->top = y; |
|
239 |
|
240 SetDirtyBlocks(w->left, w->top - diff, w->left + w->width, w->top + w->height); |
|
241 } break; |
232 } |
242 } |
233 } |
243 } |
234 |
244 |
235 /** |
245 /** |
236 * Return the correct index in the pseudo-fifo |
246 * Return the correct index in the pseudo-fifo |
252 return (i + MAX_NEWS - 1) % MAX_NEWS; |
262 return (i + MAX_NEWS - 1) % MAX_NEWS; |
253 } |
263 } |
254 |
264 |
255 /** |
265 /** |
256 * Add a new newsitem to be shown. |
266 * Add a new newsitem to be shown. |
257 * @param string String to display, can have special values based on parameter \a flags |
267 * @param string String to display, can have special values based on parameter \a display_mode |
258 * @param flags various control bits that will show various news-types. See macro NEWS_FLAGS() |
268 * @param display_mode, any of the NewsMode enums (NM_) |
|
269 * @param flags any of the NewsFlag enums (NF_) |
|
270 * @param type news category, any of the NewsType enums (NT_) |
|
271 * @param callback news callback function, any of the NewsCallback enums (DNC_) |
259 * @param data_a news-specific value based on news type |
272 * @param data_a news-specific value based on news type |
260 * @param data_b news-specific value based on news type |
273 * @param data_b news-specific value based on news type |
261 * @note flags exists of 4 byte-sized extra parameters. |
|
262 * -# Bits 0 - 7 display_mode, any of the NewsMode enums (NM_) |
|
263 * -# Bits 8 - 15 news flags, any of the NewsFlags enums (NF_) |
|
264 * -# Bits 16 - 23 news category, any of the NewsType enums (NT_) |
|
265 * -# Bits 24 - 31 news callback function, any of the NewsCallback enums (DNC_) |
|
266 * |
274 * |
267 * If the display mode is NM_CALLBACK, special news is shown and parameter |
275 * @note If the display mode is NM_CALLBACK, special news is shown and parameter |
268 * \a string has a special meaning. |
276 * \a string has a special meaning. |
269 * - For DNC_TRAINAVAIL, DNC_ROADAVAIL, DNC_SHIPAVAIL, DNC_AIRCRAFTAVAIL messages: StringID is |
277 * - For DNC_TRAINAVAIL, DNC_ROADAVAIL, DNC_SHIPAVAIL, DNC_AIRCRAFTAVAIL messages: StringID is |
270 * the index of the engine that is shown |
278 * the index of the engine that is shown |
271 * |
279 * |
272 * - For DNC_BANKRUPCY: bytes 0-3 of StringID contains the player that is in trouble, |
280 * - For DNC_BANKRUPCY: bytes 0-3 of StringID contains the player that is in trouble, |
273 * and 4-7 contains what kind of bankrupcy message is shown. |
281 * and 4-7 contains what kind of bankrupcy message is shown. |
274 * @see NewsBankrupcy |
282 * @see NewsBankrupcy |
275 * |
283 * |
276 * @see NewsMode |
284 * @see NewsMode |
277 * @see NewsFlags |
285 * @see NewsFlag |
278 * @see NewsType |
286 * @see NewsType |
279 * @see NewsCallback |
287 * @see NewsCallback |
280 */ |
288 */ |
281 void AddNewsItem(StringID string, uint32 flags, uint data_a, uint data_b) |
289 void AddNewsItem(StringID string, NewsMode display_mode, NewsFlag flags, NewsType type, NewsCallback callback, uint data_a, uint data_b) |
282 { |
290 { |
283 NewsID l_news; |
291 NewsID l_news; |
284 |
292 |
285 if (_game_mode == GM_MENU) return; |
293 if (_game_mode == GM_MENU) return; |
286 |
294 |
304 |
312 |
305 /*DEBUG(misc, 0, "+cur %3d, old %2d, lat %3d, for %3d, tot %2d", |
313 /*DEBUG(misc, 0, "+cur %3d, old %2d, lat %3d, for %3d, tot %2d", |
306 _current_news, _oldest_news, _latest_news, _forced_news, _total_news);*/ |
314 _current_news, _oldest_news, _latest_news, _forced_news, _total_news);*/ |
307 |
315 |
308 /* Add news to _latest_news */ |
316 /* Add news to _latest_news */ |
309 { |
317 NewsItem *ni = &_news_items[_latest_news]; |
310 Window *w; |
318 memset(ni, 0, sizeof(*ni)); |
311 NewsItem *ni = &_news_items[_latest_news]; |
319 |
312 memset(ni, 0, sizeof(*ni)); |
320 ni->string_id = string; |
313 |
321 ni->display_mode = display_mode; |
314 ni->string_id = string; |
322 ni->flags = flags; |
315 ni->display_mode = (byte)flags; |
323 |
316 ni->flags = (byte)(flags >> 8); |
324 /* show this news message in color? */ |
317 |
325 if (_cur_year >= _patches.colored_news_year) ni->flags |= NF_INCOLOR; |
318 /* show this news message in color? */ |
326 |
319 if (_cur_year >= _patches.colored_news_year) ni->flags |= NF_INCOLOR; |
327 ni->type = type; |
320 |
328 ni->callback = callback; |
321 ni->type = (byte)(flags >> 16); |
329 ni->data_a = data_a; |
322 ni->callback = (byte)(flags >> 24); |
330 ni->data_b = data_b; |
323 ni->data_a = data_a; |
331 ni->date = _date; |
324 ni->data_b = data_b; |
332 CopyOutDParam(ni->params, 0, lengthof(ni->params)); |
325 ni->date = _date; |
333 |
326 CopyOutDParam(ni->params, 0, lengthof(ni->params)); |
334 Window *w = FindWindowById(WC_MESSAGE_HISTORY, 0); |
327 |
335 if (w == NULL) return; |
328 w = FindWindowById(WC_MESSAGE_HISTORY, 0); |
336 SetWindowDirty(w); |
329 if (w == NULL) return; |
337 w->vscroll.count = _total_news; |
330 SetWindowDirty(w); |
|
331 w->vscroll.count = _total_news; |
|
332 } |
|
333 } |
338 } |
334 |
339 |
335 |
340 |
336 /** |
341 /** |
337 * Maximum age of news items. |
342 * Maximum age of news items. |
338 * Don't show item if it's older than x days, corresponds with NewsType in news.h |
343 * Don't show item if it's older than x days, corresponds with NewsType in news_type.h |
339 * @see NewsType |
344 * @see NewsType |
340 */ |
345 */ |
341 static const byte _news_items_age[NT_END] = { |
346 static const byte _news_items_age[NT_END] = { |
342 60, ///< NT_ARRIVAL_PLAYER |
347 60, ///< NT_ARRIVAL_PLAYER |
343 60, ///< NT_ARRIVAL_OTHER |
348 60, ///< NT_ARRIVAL_OTHER |