1 /* pbar.c: handles rate-of-change indicators
2  * This file is part of lincity.
3  * Lincity is copyright (c) I J Peters 1995-1997, (c) Greg Sharp 1997-2001.
4  * Portions copyright (c) 2001 Corey Keasling.
5  * ---------------------------------------------------------------------- */
6 #include "lcconfig.h"
7 #include <math.h>
8 #include "cliglobs.h"
9 #include "lchelp.h"
10 #include "mouse.h"
11 #include "shrglobs.h"
12 #include "pbar.h"
13 #include "lin-city.h"
14 #include "lclib.h"
15 #include "stats.h"
16 #include "engglobs.h"
17 
18 struct pbar_st pbars[NUM_PBARS];
19 
20 void
init_pbars(void)21 init_pbars (void)
22 {
23     int i, p;
24     for (p = 0; p < NUM_PBARS; p++) {
25 	pbars[p].data_size = 0;
26 	pbars[p].oldtot = 0;
27 	pbars[p].tot = 0;
28 	pbars[p].diff = 0;
29 	for (i = 0; i < PBAR_DATA_SIZE; i++)
30 	    pbars[p].data[i] = 0;
31     }
32 
33 }
34 
35 void
pbars_full_refresh(void)36 pbars_full_refresh (void)
37 {
38     Rect* pba = &scr.pbar_area;
39     draw_small_bezel (pba->x+4, pba->y+4, pba->w-8, pba->h-8, yellow(0));
40     init_pbar_text ();
41     draw_pbars ();
42 }
43 
44 void
init_pbar_text(void)45 init_pbar_text (void)
46 {
47     clear_pbar_text (&scr.pbar_pop);
48     clear_pbar_text (&scr.pbar_tech);
49     clear_pbar_text (&scr.pbar_food);
50     clear_pbar_text (&scr.pbar_jobs);
51     clear_pbar_text (&scr.pbar_money);
52     clear_pbar_text (&scr.pbar_coal);
53     clear_pbar_text (&scr.pbar_goods);
54     clear_pbar_text (&scr.pbar_ore);
55     clear_pbar_text (&scr.pbar_steel);
56 }
57 
58 /* ---------------------------------------------------------------------- *
59  * Pbar drawing function
60  * ---------------------------------------------------------------------- */
61 
62 void
draw_pbar(Rect * b,char * graphic)63 draw_pbar (Rect* b, char* graphic)
64 /* XXX: WCK: why not just make the graphic include the black? */
65 /* GCS: Good idea, but xpicedit is painful to use! */
66 {
67     Fgl_fillbox (b->x, b->y, b->w, b->h, 0);
68     Fgl_putbox (b->x + (b->w / 2) - 8, b->y, 16, 16, graphic);
69 }
70 
71 void
draw_pbars(void)72 draw_pbars (void)
73 {
74     draw_pbar (&scr.pbar_pop, pop_pbar_graphic);
75     draw_pbar (&scr.pbar_tech, tech_pbar_graphic);
76     draw_pbar (&scr.pbar_food, food_pbar_graphic);
77     draw_pbar (&scr.pbar_jobs, jobs_pbar_graphic);
78     draw_pbar (&scr.pbar_money, money_pbar_graphic);
79     draw_pbar (&scr.pbar_coal, coal_pbar_graphic);
80     draw_pbar (&scr.pbar_goods, goods_pbar_graphic);
81     draw_pbar (&scr.pbar_ore, ore_pbar_graphic);
82     draw_pbar (&scr.pbar_steel, steel_pbar_graphic);
83 }
84 
85 /* Text functions */
86 
87 void
clear_pbar_text(Rect * pbar)88 clear_pbar_text (Rect* pbar)
89 {
90     Fgl_fillbox (pbar->x + pbar->w + 1, pbar->y, PBAR_TEXT_W, pbar->h, 0);
91 }
92 
93 void
write_pbar_int(Rect * b,int val)94 write_pbar_int (Rect* b, int val)
95 {
96     char s[16];
97     format_number5 (s, pbars[val].data[pbars[val].data_size-1]);
98     Fgl_setfontcolors (0, 255);
99     Fgl_write (b->x + b->w + 25, b->y + 4, s);
100     Fgl_setfontcolors (TEXT_BG_COLOUR, TEXT_FG_COLOUR);
101 }
102 
103 void
write_pbar_text(Rect * b,char * s)104 write_pbar_text (Rect* b, char * s)
105 {
106     Fgl_setfontcolors (0, 255);
107     Fgl_write (b->x + b->w + 25, b->y + 4, s);
108     Fgl_setfontcolors (TEXT_BG_COLOUR, TEXT_FG_COLOUR);
109 }
110 
111 /* XXX: WCK: Macros anyone? */
112 /* GCS: I thought I might like to change the "sensitivity" of the pbars
113    on a case-by-case basis, but never got around to it. */
114 /* WCK: sure, but the preprocessor can still do some of the work, so: */
115 /* GCS: The new macros look good.  I killed the old code. */
116 
117 #define pbar_adjust_pop(diff) 2 * diff
118 #define pbar_adjust_tech(diff) diff > 0 ? diff / 4 + 1 : -((-diff+1)/ 2)
119 #define pbar_adjust_food(diff) diff > 0 ? diff / 2 + 1 : diff
120 #define pbar_adjust_jobs(diff) diff > 0 ? diff / 2 + 1 : diff
121 #define pbar_adjust_coal(diff) diff > 0 ? diff / 2 + 1 : diff
122 #define pbar_adjust_goods(diff) diff > 0 ? diff / 2 + 1 : diff
123 #define pbar_adjust_ore(diff) diff > 0 ? diff / 2 + 1 : diff
124 #define pbar_adjust_steel(diff) diff > 0 ? diff / 2 + 1 : diff
125 #define pbar_adjust_money(diff) diff  > 0 ? diff / 800 + 1 : diff / 400
126 
127 
128 /* XXX: wck: write_pbar_* changes font colours every time its called; only
129    need to do this once.  Maybe it should be folded in.*/
130 
131 void
refresh_population_text(void)132 refresh_population_text (void)
133 {
134   /* GCS: This function is kind of a hack, but I need the population
135      to be refreshed immediately after the rocket is launched.
136      Therefore, this function! */
137     Rect * b;
138     update_pbar (PPOP, housed_population + people_pool, 0);
139     b = &scr.pbar_pop;
140     write_pbar_int (b, PPOP);
141 }
142 
143 void
refresh_pbars(void)144 refresh_pbars (void)
145 {
146     Rect * b;
147     char s[10];
148 
149     /* Population */
150     b = &scr.pbar_pop;
151     draw_pbar_new (b, pbar_adjust_pop(pbars[PPOP].diff));
152     write_pbar_int (b, PPOP);
153 
154     /* Technology */
155     b = &scr.pbar_tech;
156     draw_pbar_new (b, pbar_adjust_tech(pbars[PTECH].diff));
157 
158     snprintf (s, 10, "%5.1f",
159 	      (float) pbars[PTECH].data[pbars[PTECH].data_size - 1] *
160 	      100.0 / MAX_TECH_LEVEL);
161 
162     write_pbar_text (b, s);
163 
164     /* Food */
165     b = &scr.pbar_food;
166     draw_pbar_new (b, pbar_adjust_food(pbars[PFOOD].diff));
167     write_pbar_int (b, PFOOD);
168 
169     /* Jobs */
170     b = &scr.pbar_jobs;
171     draw_pbar_new (b, pbar_adjust_jobs(pbars[PFOOD].diff));
172     write_pbar_int (b, PJOBS);
173 
174     /* Coal */
175     b = &scr.pbar_coal;
176     draw_pbar_new (b, pbar_adjust_coal(pbars[PCOAL].diff));
177     write_pbar_int (b, PCOAL);
178 
179     /* Goods */
180     b = &scr.pbar_goods;
181     draw_pbar_new (b, pbar_adjust_goods(pbars[PGOODS].diff));
182     write_pbar_int (b, PGOODS);
183 
184     /* Ore */
185     b = &scr.pbar_ore;
186     draw_pbar_new (b, pbar_adjust_ore(pbars[PORE].diff));
187     write_pbar_int (b, PORE);
188 
189     /* Steel */
190     b = &scr.pbar_steel;
191     draw_pbar_new (b, pbar_adjust_steel(pbars[PSTEEL].diff));
192     write_pbar_int (b, PSTEEL);
193 
194     /* Money */
195     b = &scr.pbar_money;
196     draw_pbar_new (b, pbar_adjust_money(pbars[PMONEY].diff));
197     write_pbar_int (b, PMONEY);
198 }
199 
200 
201 /*
202    update_pbar: add a new value to the array used to calculate the
203    pbar display.  If month_flag is 1, the oldtotal is updated, all
204    values are shifted up (dropping the first one), and the new value
205    is added at the end. If 0, the new value replaces the most recently
206    updated value.  The data is summed and the result compared to the
207    old total.
208 
209 */
210 
211 void
update_pbar(int pbar_num,int value,int month_flag)212 update_pbar (int pbar_num, int value, int month_flag)
213 {
214 
215     int i;
216 
217     struct pbar_st * pbar = &pbars[pbar_num];
218 
219     if (month_flag) {
220 	pbar->oldtot = pbar->tot;
221 
222 	/* If the dataset isn't full, just add it and forget month_flag */
223 	if (pbar->data_size < PBAR_DATA_SIZE) {
224 	    pbar->data_size++;
225 	    month_flag = 0;
226 	}
227     }
228 
229     pbar->tot = 0;
230 
231     for (i = 0; i < (pbar->data_size - 1); i++) {
232 	if (month_flag)
233 	    pbar->tot += (pbar->data[i] = pbar->data[i+1]);
234 	else
235 	    pbar->tot += pbar->data[i];
236     }
237 
238 
239     pbar->tot += pbar->data[i] = value;
240     pbar->diff = pbar->tot - pbar->oldtot;
241 }
242 
243 void
update_pbars_daily()244 update_pbars_daily()
245 {
246     update_pbar (PPOP, housed_population + people_pool, 0);
247     update_pbar (PTECH, tech_level, 0);
248     update_pbar (PFOOD, food_in_markets / 1000, 0);
249     update_pbar (PJOBS, jobs_in_markets / 1000, 0);
250     update_pbar (PCOAL, coal_in_markets / 250, 0);
251     update_pbar (PGOODS, goods_in_markets / 500, 0);
252     update_pbar (PORE, ore_in_markets / 500, 0);
253     update_pbar (PSTEEL, steel_in_markets / 25, 0);
254     update_pbar (PMONEY, total_money, 0);
255 }
256 
257 void
update_pbars_monthly()258 update_pbars_monthly()
259 {
260     update_pbar (PPOP, housed_population + people_pool, 1);
261     update_pbar (PTECH, tech_level, 1);
262     update_pbar (PFOOD, tfood_in_markets / data_last_month, 1);
263     update_pbar (PJOBS, tjobs_in_markets / data_last_month, 1);
264     update_pbar (PCOAL, tcoal_in_markets / data_last_month, 1);
265     update_pbar (PGOODS, tgoods_in_markets / data_last_month, 1);
266     update_pbar (PORE, tore_in_markets / data_last_month, 1);
267     update_pbar (PSTEEL, tsteel_in_markets / data_last_month, 1);
268     update_pbar (PMONEY, total_money, 1);
269 }
270 
271 int
compute_pbar_offset(Rect * b,int val)272 compute_pbar_offset (Rect* b, int val)
273 {
274     int offset;
275     int val_abs = val > 0 ? val : -val;
276 
277     if (!val)
278 	return 0;
279 
280     offset = (int) log (val_abs);
281     if (offset > (b->w / 2) - 8) {
282 	offset = (b->w / 2) - 8;
283     }
284     offset = val > 0 ? offset : -offset;
285 
286     return offset;
287 }
288 
289 void
draw_pbar_new(Rect * b,int val)290 draw_pbar_new (Rect* b, int val)
291 {
292 
293     int offset;
294     int spike_start, spike_end;
295 
296     /* offset, oldoffset are the size of spike in pixels */
297     offset = compute_pbar_offset (b, val);
298 
299     /* Clear both sides of the pbar */
300     Fgl_fillbox (b->x, b->y, b->w / 2 - 8, b->h, 0);
301     Fgl_fillbox (b->x + (b->w / 2) + 8, b->y, b->w / 2 - 8, b->h, 0);
302 
303 
304     /* Figure out pos/neg and length and draw */
305     if (offset > 0) {
306 /* Right/Positive */
307       spike_start = b->x + (b->w / 2) + 8;
308       spike_end = spike_start + offset;
309       Fgl_fillbox (spike_start, b->y+2, spike_end - spike_start, b->h-4,
310 		   (green(12)));
311     } else if (offset < 0) {
312 /* Left/Negative */
313       spike_end = b->x + (b->w / 2) - 8;
314       spike_start = spike_end + offset;
315       Fgl_fillbox (spike_start, b->y+2, spike_end - spike_start, b->h-4,
316 		   (red(12)));
317     }
318 }
319 
320 
321 
322 void
pbar_mouse(int rawx,int rawy,int button)323 pbar_mouse(int rawx, int rawy, int button)
324 {
325   if (button != LC_MOUSE_RIGHTBUTTON)
326     return;
327 
328   /* check for help with pbars */
329   activate_help ("pbar.hlp");
330 
331 #if defined (commentout)
332   if (mouse_in_rect (&scr.pbar_pop,x,y)) {
333     activate_help ("pbar-pop.hlp");
334     return;
335   }
336   else if (mouse_in_rect (&scr.pbar_tech,x,y)) {
337     activate_help ("pbar-tech.hlp");
338     return;
339   }
340   else if (mouse_in_rect (&scr.pbar_food,x,y)) {
341     activate_help ("pbar-food.hlp");
342     return;
343   }
344   else if (mouse_in_rect (&scr.pbar_jobs,x,y)) {
345     activate_help ("pbar-jobs.hlp");
346     return;
347   }
348   else if (mouse_in_rect (&scr.pbar_money,x,y)) {
349     activate_help ("pbar-money.hlp");
350     return;
351   }
352   else if (mouse_in_rect (&scr.pbar_coal,x,y)) {
353     activate_help ("pbar-coal.hlp");
354     return;
355   }
356   else if (mouse_in_rect (&scr.pbar_goods,x,y)) {
357     activate_help ("pbar-goods.hlp");
358     return;
359   }
360   else if (mouse_in_rect (&scr.pbar_ore,x,y)) {
361     activate_help ("pbar-ore.hlp");
362     return;
363   }
364   else if (mouse_in_rect (&scr.pbar_steel,x,y)) {
365     activate_help ("pbar-steel.hlp");
366     return;
367   }
368 #endif
369 }
370