rewrote RecursiveHullCheck, no longer gets stuck on angle changes, and is generally...
[divverent/darkplaces.git] / ui.c
1
2 #include "quakedef.h"
3
4 cvar_t ui_showname = {0, "ui_showname", "0"};
5
6 #define ITEM_CLICKABLE 1
7 #define ITEM_DRAWABLE 2
8
9 #define UIKEY_LEFT 1
10 #define UIKEY_RIGHT 2
11 #define UIKEY_UP 3
12 #define UIKEY_DOWN 4
13 #define UIKEY_ENTER 5
14
15 #define UI_MOUSEBUTTONS 3
16
17 static int                      ui_alive, ui_active;
18 static float            ui_mouse_x, ui_mouse_y;
19 static int                      ui_mousebutton[UI_MOUSEBUTTONS], ui_mouseclick;
20 static int                      ui_keyui, ui_keyitem;
21 static ui_item_t        *ui_keyrealitem;
22
23 static ui_t *ui_list[MAX_UI_COUNT];
24
25 //static qpic_t *ui_mousepointer;
26 static rtexture_t *ui_mousepointertexture;
27
28 static byte pointerimage[256] =
29 {
30         0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
31         0x02, 0x06, 0x07, 0x07, 0x07, 0x07, 0x06, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
32         0x02, 0x06, 0x05, 0x05, 0x05, 0x04, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
33         0x02, 0x06, 0x05, 0x05, 0x04, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
34         0x02, 0x06, 0x05, 0x04, 0x05, 0x06, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
35         0x02, 0x06, 0x04, 0x01, 0x04, 0x05, 0x06, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
36         0x02, 0x05, 0x01, 0xFF, 0x01, 0x04, 0x05, 0x06, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
37         0x02, 0x01, 0xFF, 0xFF, 0xFF, 0x01, 0x04, 0x05, 0x06, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
38         0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x04, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
39         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
40         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
41         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
42         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
43         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
44         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
45         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
46 };
47
48 void ui_start(void)
49 {
50 //      ui_mousepointer = Draw_CachePic("ui/mousepointer.lmp");
51         ui_mousepointertexture = R_LoadTexture("mousepointer", 16, 16, pointerimage, TEXF_ALPHA | TEXF_PRECACHE);
52         ui_mouse_x = vid.conwidth * 0.5;
53         ui_mouse_y = vid.conheight * 0.5;
54         ui_alive = true;
55 }
56
57 void ui_shutdown(void)
58 {
59 //      ui_mousepointer = NULL;
60         ui_mousepointertexture = NULL;
61         ui_alive = false;
62 }
63
64 void ui_newmap(void)
65 {
66 }
67
68 void ui_init(void)
69 {
70         Cvar_RegisterVariable(&ui_showname);
71         R_RegisterModule("UI", ui_start, ui_shutdown, ui_newmap);
72 }
73
74 void ui_mouseupdate(float x, float y)
75 {
76         if (ui_alive)
77         {
78                 ui_mouse_x = bound(0, x, vid.conwidth);
79                 ui_mouse_y = bound(0, y, vid.conheight);
80         }
81 }
82
83 void ui_mouseupdaterelative(float x, float y)
84 {
85         if (ui_alive)
86         {
87                 ui_mouse_x += x;
88                 ui_mouse_y += y;
89                 ui_mouse_x = bound(0, ui_mouse_x, vid.conwidth);
90                 ui_mouse_y = bound(0, ui_mouse_y, vid.conheight);
91         }
92 }
93
94 ui_t *ui_create(void)
95 {
96         ui_t *ui;
97         ui = qmalloc(sizeof(*ui));
98         if (ui == NULL)
99                 Sys_Error("ui_create: unable to allocate memory for new ui\n");
100         memset(ui, 0, sizeof(*ui));
101         return ui;
102 }
103
104 void ui_free(ui_t *ui)
105 {
106         if (ui)
107                 qfree(ui);
108 }
109
110 void ui_clear(ui_t *ui)
111 {
112         ui->item_count = 0;
113 }
114
115 void ui_item
116 (
117         ui_t *ui, char *basename, int number,
118         float x, float y, qpic_t *pic, char *string,
119         float left, float top, float width, float height,
120         void(*leftkey)(void *nativedata1, void *nativedata2, float data1, float data2),
121         void(*rightkey)(void *nativedata1, void *nativedata2, float data1, float data2),
122         void(*enterkey)(void *nativedata1, void *nativedata2, float data1, float data2),
123         void(*mouseclick)(void *nativedata1, void *nativedata2, float data1, float data2, float xfrac, float yfrac),
124         void *nativedata1, void *nativedata2, float data1, float data2
125 )
126 {
127         int i;
128         ui_item_t *it;
129         char itemname[32];
130         snprintf(itemname, sizeof(itemname), "%s%04d", basename, number);
131         for (it = ui->items, i = 0;i < ui->item_count;it++, i++)
132                 if (it->name == NULL || !strncmp(itemname, it->name, 32))
133                         break;
134         if (i == ui->item_count)
135         {
136                 if (i == MAX_UI_ITEMS)
137                 {
138                         Con_Printf("ui_item: ran out of UI item slots\n");
139                         return;
140                 }
141                 ui->item_count++;
142         }
143         memset(it, 0, sizeof(ui_item_t));
144         strncpy(it->name, itemname, 32);
145         it->flags = 0;
146         if (pic || string)
147         {
148                 it->flags |= ITEM_DRAWABLE;
149                 it->draw_pic = pic;
150                 it->draw_string = string;
151                 it->draw_x = x;
152                 it->draw_y = y;
153         }
154         if (leftkey || rightkey || enterkey || mouseclick)
155         {
156                 it->flags |= ITEM_CLICKABLE;
157                 it->click_x = x + left;
158                 it->click_y = y + top;
159                 it->click_x2 = it->click_x + width;
160                 it->click_y2 = it->click_y + height;
161                 it->leftkey = leftkey;
162                 it->rightkey = rightkey;
163                 it->enterkey = enterkey;
164                 it->mouseclick = mouseclick;
165                 if (it->mouseclick == NULL)
166                         it->mouseclick = (void *)it->enterkey;
167                 if (it->leftkey == NULL)
168                         it->leftkey = it->enterkey;
169                 if (it->rightkey == NULL)
170                         it->rightkey = it->enterkey;
171                 it->nativedata1 = nativedata1;
172                 it->nativedata2 = nativedata2;
173         }
174 }
175
176 void ui_item_remove(ui_t *ui, char *basename, int number)
177 {
178         int i;
179         ui_item_t *it;
180         char itemname[32];
181         snprintf(itemname, sizeof(itemname), "%s%04d", basename, number);
182         for (it = ui->items, i = 0;i < ui->item_count;it++, i++)
183                 if (it->name && !strncmp(itemname, it->name, 32))
184                         break;
185         if (i < ui->item_count)
186                 it->name[0] = 0;
187 }
188
189 ui_item_t *ui_hititem(float x, float y)
190 {
191         int i, j;
192         ui_item_t *it;
193         ui_t *ui;
194         for (j = 0;j < MAX_UI_COUNT;j++)
195                 if ((ui = ui_list[j]))
196                         for (it = ui->items, i = 0;i < ui->item_count;it++, i++)
197                                 if (it->name[0] && (it->flags & ITEM_CLICKABLE))
198                                         if (x >= it->click_x && y >= it->click_y && x < it->click_x2 && y < it->click_y2)
199                                                 return it;
200         return NULL;
201 }
202
203 int ui_uiactive(ui_t *ui)
204 {
205         int i;
206         for (i = 0;i < MAX_UI_COUNT;i++)
207                 if (ui_list[i] == ui)
208                         return true;
209         return false;
210 }
211
212 void ui_activate(ui_t *ui, int yes)
213 {
214         int i;
215         if (yes)
216         {
217                 if (ui_uiactive(ui))
218                         return;
219
220                 for (i = 0;i < MAX_UI_COUNT;i++)
221                 {
222                         if (ui_list[i] == NULL)
223                         {
224                                 ui_list[i] = ui;
225                                 return;
226                         }
227                 }
228
229                 Con_Printf("ui_activate: ran out of active ui list items\n");
230         }
231         else
232         {
233                 for (i = 0;i < MAX_UI_COUNT;i++)
234                 {
235                         if (ui_list[i] == ui)
236                         {
237                                 ui_list[i] = NULL;
238                                 return;
239                         }
240                 }
241         }
242 }
243
244 int ui_isactive(void)
245 {
246         int j;
247         ui_t *ui;
248         if (ui_alive)
249         {
250                 for (j = 0;j < MAX_UI_COUNT;j++)
251                         if ((ui = ui_list[j]))
252                                 if (ui->item_count)
253                                         return true;
254         }
255         return false;
256 }
257
258 #define UI_QUEUE_SIZE 256
259 static byte ui_keyqueue[UI_QUEUE_SIZE];
260 static int ui_keyqueuepos = 0;
261
262 void ui_leftkeyupdate(int pressed)
263 {
264         static int key = false;
265         if (pressed && !key && ui_keyqueuepos < UI_QUEUE_SIZE)
266                 ui_keyqueue[ui_keyqueuepos++] = UIKEY_LEFT;
267         key = pressed;
268 }
269
270 void ui_rightkeyupdate(int pressed)
271 {
272         static int key = false;
273         if (pressed && !key && ui_keyqueuepos < UI_QUEUE_SIZE)
274                 ui_keyqueue[ui_keyqueuepos++] = UIKEY_RIGHT;
275         key = pressed;
276 }
277
278 void ui_upkeyupdate(int pressed)
279 {
280         static int key = false;
281         if (pressed && !key && ui_keyqueuepos < UI_QUEUE_SIZE)
282                 ui_keyqueue[ui_keyqueuepos++] = UIKEY_UP;
283         key = pressed;
284 }
285
286 void ui_downkeyupdate(int pressed)
287 {
288         static int key = false;
289         if (pressed && !key && ui_keyqueuepos < UI_QUEUE_SIZE)
290                 ui_keyqueue[ui_keyqueuepos++] = UIKEY_DOWN;
291         key = pressed;
292 }
293
294 void ui_mousebuttonupdate(int button, int pressed)
295 {
296         if (button < 0 || button >= UI_MOUSEBUTTONS)
297                 return;
298         if (button == 0 && ui_mousebutton[button] && !pressed)
299                 ui_mouseclick = true;
300         ui_mousebutton[button] = pressed;
301 }
302
303 void ui_update(void)
304 {
305         ui_item_t *startitem, *it;
306         if (ui_alive)
307         {
308                 ui_mouse_x = bound(0, ui_mouse_x, vid.conwidth);
309                 ui_mouse_y = bound(0, ui_mouse_y, vid.conheight);
310
311                 if ((ui_active = ui_isactive()))
312                 {
313                         // validate currently selected item
314                         if(ui_list[ui_keyui] == NULL)
315                         {
316                                 while (ui_list[ui_keyui] == NULL)
317                                         ui_keyui = (ui_keyui + 1) % MAX_UI_COUNT;
318                                 ui_keyitem = 0;
319                         }
320                         ui_keyitem = bound(0, ui_keyitem, ui_list[ui_keyui]->item_count - 1);
321                         startitem = ui_keyrealitem = &ui_list[ui_keyui]->items[ui_keyitem];
322                         if ((ui_keyrealitem->flags & ITEM_CLICKABLE) == 0)
323                         {
324                                 do
325                                 {
326                                         // FIXME: cycle through UIs as well as items in a UI
327                                         ui_keyitem = (ui_keyitem - 1) % ui_list[ui_keyui]->item_count - 1;
328                                         ui_keyrealitem = &ui_list[ui_keyui]->items[ui_keyitem];
329                                 }
330                                 while (ui_keyrealitem != startitem && (ui_keyrealitem->flags & ITEM_CLICKABLE) == 0);
331                         }
332
333                         if (ui_keyqueuepos)
334                         {
335                                 int i;
336                                 for (i = 0;i < ui_keyqueuepos;i++)
337                                 {
338                                         startitem = ui_keyrealitem;
339                                         switch(ui_keyqueue[i])
340                                         {
341                                         case UIKEY_UP:
342                                                 do
343                                                 {
344                                                         ui_keyitem--;
345                                                         if (ui_keyitem < 0)
346                                                         {
347                                                                 do
348                                                                         ui_keyui = (ui_keyui - 1) % MAX_UI_COUNT;
349                                                                 while(ui_list[ui_keyui] == NULL);
350                                                                 ui_keyitem = ui_list[ui_keyui]->item_count - 1;
351                                                         }
352                                                         ui_keyrealitem = &ui_list[ui_keyui]->items[ui_keyitem];
353                                                 }
354                                                 while (ui_keyrealitem != startitem && (ui_keyrealitem->flags & ITEM_CLICKABLE) == 0);
355                                                 break;
356                                         case UIKEY_DOWN:
357                                                 do
358                                                 {
359                                                         ui_keyitem++;
360                                                         if (ui_keyitem >= ui_list[ui_keyui]->item_count)
361                                                         {
362                                                                 do
363                                                                         ui_keyui = (ui_keyui + 1) % MAX_UI_COUNT;
364                                                                 while(ui_list[ui_keyui] == NULL);
365                                                                 ui_keyitem = 0;
366                                                         }
367                                                         ui_keyrealitem = &ui_list[ui_keyui]->items[ui_keyitem];
368                                                 }
369                                                 while (ui_keyrealitem != startitem && (ui_keyrealitem->flags & ITEM_CLICKABLE) == 0);
370                                                 break;
371                                         case UIKEY_LEFT:
372                                                 if (ui_keyrealitem->leftkey)
373                                                         ui_keyrealitem->leftkey(ui_keyrealitem->nativedata1, ui_keyrealitem->nativedata2, ui_keyrealitem->data1, ui_keyrealitem->data2);
374                                                 break;
375                                         case UIKEY_RIGHT:
376                                                 if (ui_keyrealitem->rightkey)
377                                                         ui_keyrealitem->rightkey(ui_keyrealitem->nativedata1, ui_keyrealitem->nativedata2, ui_keyrealitem->data1, ui_keyrealitem->data2);
378                                                 break;
379                                         case UIKEY_ENTER:
380                                                 if (ui_keyrealitem->enterkey)
381                                                         ui_keyrealitem->enterkey(ui_keyrealitem->nativedata1, ui_keyrealitem->nativedata2, ui_keyrealitem->data1, ui_keyrealitem->data2);
382                                                 break;
383                                         }
384                                 }
385                         }
386                         ui_keyqueuepos = 0;
387
388                         if (ui_mouseclick && (it = ui_hititem(ui_mouse_x, ui_mouse_y)) && it->mouseclick)
389                                 it->mouseclick(it->nativedata1, it->nativedata2, it->data1, it->data2, ui_mouse_x - it->click_x, ui_mouse_y - it->click_y);
390         }
391         }
392         ui_mouseclick = false;
393 }
394
395 void ui_draw(void)
396 {
397         int i, j;
398         ui_item_t *it;
399         ui_t *ui;
400         if (ui_alive && ui_active)
401         {
402                 for (j = 0;j < MAX_UI_COUNT;j++)
403                         if ((ui = ui_list[j]))
404                                 if (ui->item_count)
405                                         for (i = 0, it = ui->items;i < ui->item_count;i++, it++)
406                                                 if (it->flags & ITEM_DRAWABLE)
407                                                 {
408                                                         if (it->draw_pic)
409                                                                 Draw_Pic(it->draw_x, it->draw_y, it->draw_pic);
410                                                         if (it->draw_string)
411                                                                 Draw_String(it->draw_x, it->draw_y, it->draw_string, 9999);
412                                                 }
413
414                 if ((it = ui_hititem(ui_mouse_x, ui_mouse_y)))
415                 {
416                         if (it->draw_pic)
417                                 Draw_AdditivePic(it->draw_x, it->draw_y, it->draw_pic);
418                         if (it->draw_string)
419                                 Draw_AdditiveString(it->draw_x, it->draw_y, it->draw_string, 9999);
420                         if (ui_showname.value)
421                                 Draw_String(ui_mouse_x, ui_mouse_y + 16, it->name, 9999);
422         }
423
424                 it = ui_keyrealitem;
425                 if (it->draw_pic)
426                         Draw_AdditivePic(it->draw_x, it->draw_y, it->draw_pic);
427                 if (it->draw_string)
428                         Draw_AdditiveString(it->draw_x, it->draw_y, it->draw_string, 9999);
429
430 //              Draw_Pic(ui_mouse_x, ui_mouse_y, ui_mousepointer);
431                 Draw_GenericPic(ui_mousepointertexture, 1, 1, 1, 1, ui_mouse_x, ui_mouse_y, 16, 16);
432         }
433 }