]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/menu/draw.qc
bugfix target_spawn, and change docs to match it
[divverent/nexuiz.git] / data / qcsrc / menu / draw.qc
1 string draw_mousepointer;
2 vector draw_mousepointer_offset;
3 vector draw_mousepointer_size;
4
5 string draw_UseSkinFor(string pic)
6 {
7         if(substring(pic, 0, 1) == "/")
8                 return substring(pic, 1, strlen(pic)-1);
9         else
10                 return strcat(draw_currentSkin, "/", pic);
11 }
12
13 void draw_setMousePointer(string pic, vector theSize, vector theOffset)
14 {
15         draw_mousepointer = strzone(draw_UseSkinFor(pic));
16         draw_mousepointer_size = theSize;
17         draw_mousepointer_offset = eX * (theOffset_x * theSize_x) + eY * (theOffset_y * theSize_y);
18 }
19
20 void draw_drawMousePointer(vector where)
21 {
22         drawpic(boxToGlobal(where, draw_shift, draw_scale) - draw_mousepointer_offset, draw_mousepointer, draw_mousepointer_size, '1 1 1', draw_alpha, 0);
23 }
24
25 void draw_reset()
26 {
27         drawfont = FONT_USER+0;
28         draw_shift = '0 0 0';
29         draw_scale = '1 0 0' * cvar("vid_conwidth") + '0 1 0' * cvar("vid_conheight");
30         draw_alpha = 1;
31 }
32
33 vector globalToBox(vector v, vector theOrigin, vector theScale)
34 {
35         v -= theOrigin;
36         v_x /= theScale_x;
37         v_y /= theScale_y;
38         return v;
39 }
40
41 vector globalToBoxSize(vector v, vector theScale)
42 {
43         v_x /= theScale_x;
44         v_y /= theScale_y;
45         return v;
46 }
47
48 vector boxToGlobal(vector v, vector theOrigin, vector theScale)
49 {
50         v_x *= theScale_x;
51         v_y *= theScale_y;
52         v += theOrigin;
53         return v;
54 }
55
56 vector boxToGlobalSize(vector v, vector theScale)
57 {
58         v_x *= theScale_x;
59         v_y *= theScale_y;
60         return v;
61 }
62
63 void draw_PreloadPicture(string pic)
64 {
65         pic = draw_UseSkinFor(pic);
66         precache_pic(pic);
67 }
68
69 void draw_Picture(vector theOrigin, string pic, vector theSize, vector theColor, float theAlpha)
70 {
71         pic = draw_UseSkinFor(pic);
72         drawpic(boxToGlobal(theOrigin, draw_shift, draw_scale), pic, boxToGlobalSize(theSize, draw_scale), theColor, theAlpha * draw_alpha, 0);
73 }
74
75 vector draw_PictureSize(string pic)
76 {
77         pic = draw_UseSkinFor(pic);
78         return drawgetimagesize(pic);
79 }
80
81 void draw_Fill(vector theOrigin, vector theSize, vector theColor, float theAlpha)
82 {
83         drawfill(boxToGlobal(theOrigin, draw_shift, draw_scale), boxToGlobalSize(theSize, draw_scale), theColor, theAlpha * draw_alpha, 0);
84 }
85
86 // a button picture is a texture containing three parts:
87 //   1/4 width: left part
88 //   1/2 width: middle part (stretched)
89 //   1/4 width: right part
90 // it is assumed to be 4x as wide as high for aspect ratio purposes, which
91 // means, the parts are a square, two squares and a square.
92 void draw_ButtonPicture(vector theOrigin, string pic, vector theSize, vector theColor, float theAlpha)
93 {
94         vector square;
95         vector width, height;
96         vector bW;
97         pic = draw_UseSkinFor(pic);
98         theOrigin = boxToGlobal(theOrigin, draw_shift, draw_scale);
99         theSize = boxToGlobalSize(theSize, draw_scale);
100         theAlpha *= draw_alpha;
101         width = eX * theSize_x;
102         height = eY * theSize_y;
103         if(theSize_x <= theSize_y * 2)
104         {
105                 // button not wide enough
106                 // draw just left and right part then
107                 square = eX * theSize_x * 0.5;
108                 bW = eX * (0.25 * theSize_x / (theSize_y * 2));
109                 drawsubpic(theOrigin,          square + height, pic, '0 0 0', eY + bW, theColor, theAlpha, 0);
110                 drawsubpic(theOrigin + square, square + height, pic, eX - bW, eY + bW, theColor, theAlpha, 0);
111         }
112         else
113         {
114                 square = eX * theSize_y;
115                 drawsubpic(theOrigin,                  height  +     square, pic, '0    0 0', '0.25 1 0', theColor, theAlpha, 0);
116                 drawsubpic(theOrigin +         square, theSize - 2 * square, pic, '0.25 0 0', '0.5  1 0', theColor, theAlpha, 0);
117                 drawsubpic(theOrigin + width - square, height  +     square, pic, '0.75 0 0', '0.25 1 0', theColor, theAlpha, 0);
118         }
119 }
120
121 // a vertical button picture is a texture containing three parts:
122 //   1/4 height: left part
123 //   1/2 height: middle part (stretched)
124 //   1/4 height: right part
125 // it is assumed to be 4x as high as wide for aspect ratio purposes, which
126 // means, the parts are a square, two squares and a square.
127 void draw_VertButtonPicture(vector theOrigin, string pic, vector theSize, vector theColor, float theAlpha)
128 {
129         vector square;
130         vector width, height;
131         vector bH;
132         pic = draw_UseSkinFor(pic);
133         theOrigin = boxToGlobal(theOrigin, draw_shift, draw_scale);
134         theSize = boxToGlobalSize(theSize, draw_scale);
135         theAlpha *= draw_alpha;
136         width = eX * theSize_x;
137         height = eY * theSize_y;
138         if(theSize_y <= theSize_x * 2)
139         {
140                 // button not high enough
141                 // draw just upper and lower part then
142                 square = eY * theSize_y * 0.5;
143                 bH = eY * (0.25 * theSize_y / (theSize_x * 2));
144                 drawsubpic(theOrigin,          square + width, pic, '0 0 0', eX + bH, theColor, theAlpha, 0);
145                 drawsubpic(theOrigin + square, square + width, pic, eY - bH, eX + bH, theColor, theAlpha, 0);
146         }
147         else
148         {
149                 square = eY * theSize_x;
150                 drawsubpic(theOrigin,                   width   +     square, pic, '0 0    0', '1 0.25 0', theColor, theAlpha, 0);
151                 drawsubpic(theOrigin +          square, theSize - 2 * square, pic, '0 0.25 0', '1 0.5  0', theColor, theAlpha, 0);
152                 drawsubpic(theOrigin + height - square, width   +     square, pic, '0 0.75 0', '1 0.25 0', theColor, theAlpha, 0);
153         }
154 }
155
156 // a border picture is a texture containing nine parts:
157 //   1/4 width: left part
158 //   1/2 width: middle part (stretched)
159 //   1/4 width: right part
160 // divided into
161 //   1/4 height: top part
162 //   1/2 height: middle part (stretched)
163 //   1/4 height: bottom part
164 void draw_BorderPicture(vector theOrigin, string pic, vector theSize, vector theColor, float theAlpha, vector theBorderSize)
165 {
166         vector dX, dY;
167         vector width, height;
168         vector bW, bH;
169         pic = draw_UseSkinFor(pic);
170         theOrigin = boxToGlobal(theOrigin, draw_shift, draw_scale);
171         theSize = boxToGlobalSize(theSize, draw_scale);
172         theBorderSize = boxToGlobalSize(theBorderSize, draw_scale);
173         theAlpha *= draw_alpha;
174         width = eX * theSize_x;
175         height = eY * theSize_y;
176         if(theSize_x <= theBorderSize_x * 2)
177         {
178                 // not wide enough... draw just left and right then
179                 bW = eX * (0.25 * theSize_x / (theBorderSize_x * 2));
180                 if(theSize_y <= theBorderSize_y * 2)
181                 {
182                         // not high enough... draw just corners
183                         bH = eY * (0.25 * theSize_y / (theBorderSize_y * 2));
184                         drawsubpic(theOrigin,                 width * 0.5 + height * 0.5, pic, '0 0 0',           bW + bH, theColor, theAlpha, 0);
185                         drawsubpic(theOrigin + width   * 0.5, width * 0.5 + height * 0.5, pic, eX - bW,           bW + bH, theColor, theAlpha, 0);
186                         drawsubpic(theOrigin + height  * 0.5, width * 0.5 + height * 0.5, pic, eY - bH,           bW + bH, theColor, theAlpha, 0);
187                         drawsubpic(theOrigin + theSize * 0.5, width * 0.5 + height * 0.5, pic, eX + eY - bW - bH, bW + bH, theColor, theAlpha, 0);
188                 }
189                 else
190                 {
191                         dY = theBorderSize_x * eY;
192                         drawsubpic(theOrigin,                             width * 0.5          +     dY, pic, '0 0    0',           '0 0.25 0' + bW, theColor, theAlpha, 0);
193                         drawsubpic(theOrigin + width * 0.5,               width * 0.5          +     dY, pic, '0 0    0' + eX - bW, '0 0.25 0' + bW, theColor, theAlpha, 0);
194                         drawsubpic(theOrigin                        + dY, width * 0.5 + height - 2 * dY, pic, '0 0.25 0',           '0 0.5  0' + bW, theColor, theAlpha, 0);
195                         drawsubpic(theOrigin + width * 0.5          + dY, width * 0.5 + height - 2 * dY, pic, '0 0.25 0' + eX - bW, '0 0.5  0' + bW, theColor, theAlpha, 0);
196                         drawsubpic(theOrigin               + height - dY, width * 0.5          +     dY, pic, '0 0.75 0',           '0 0.25 0' + bW, theColor, theAlpha, 0);
197                         drawsubpic(theOrigin + width * 0.5 + height - dY, width * 0.5          +     dY, pic, '0 0.75 0' + eX - bW, '0 0.25 0' + bW, theColor, theAlpha, 0);
198                 }
199         }
200         else
201         {
202                 if(theSize_y <= theBorderSize_y * 2)
203                 {
204                         // not high enough... draw just top and bottom then
205                         bH = eY * (0.25 * theSize_y / (theBorderSize_y * 2));
206                         dX = theBorderSize_x * eX;
207                         drawsubpic(theOrigin,                                         dX + height * 0.5, pic, '0    0 0',           '0.25 0 0' + bH, theColor, theAlpha, 0);
208                         drawsubpic(theOrigin + dX,                        width - 2 * dX + height * 0.5, pic, '0.25 0 0',           '0.5  0 0' + bH, theColor, theAlpha, 0);
209                         drawsubpic(theOrigin + width - dX,                            dX + height * 0.5, pic, '0.75 0 0',           '0.25 0 0' + bH, theColor, theAlpha, 0);
210                         drawsubpic(theOrigin              + height * 0.5,             dX + height * 0.5, pic, '0    0 0' + eY - bH, '0.25 0 0' + bH, theColor, theAlpha, 0);
211                         drawsubpic(theOrigin + dX         + height * 0.5, width - 2 * dX + height * 0.5, pic, '0.25 0 0' + eY - bH, '0.5  0 0' + bH, theColor, theAlpha, 0);
212                         drawsubpic(theOrigin + width - dX + height * 0.5,             dX + height * 0.5, pic, '0.75 0 0' + eY - bH, '0.25 0 0' + bH, theColor, theAlpha, 0);
213                 }
214                 else
215                 {
216                         dX = theBorderSize_x * eX;
217                         dY = theBorderSize_x * eY;
218                         drawsubpic(theOrigin,                                        dX          +     dY, pic, '0    0    0', '0.25 0.25 0', theColor, theAlpha, 0);
219                         drawsubpic(theOrigin                  + dX,      width - 2 * dX          +     dY, pic, '0.25 0    0', '0.5  0.25 0', theColor, theAlpha, 0);
220                         drawsubpic(theOrigin          + width - dX,                  dX          +     dY, pic, '0.75 0    0', '0.25 0.25 0', theColor, theAlpha, 0);
221                         drawsubpic(theOrigin          + dY,                          dX + height - 2 * dY, pic, '0    0.25 0', '0.25 0.5  0', theColor, theAlpha, 0);
222                         drawsubpic(theOrigin          + dY         + dX, width - 2 * dX + height - 2 * dY, pic, '0.25 0.25 0', '0.5  0.5  0', theColor, theAlpha, 0);
223                         drawsubpic(theOrigin          + dY + width - dX,             dX + height - 2 * dY, pic, '0.75 0.25 0', '0.25 0.5  0', theColor, theAlpha, 0);
224                         drawsubpic(theOrigin + height - dY,                          dX          +     dY, pic, '0    0.75 0', '0.25 0.25 0', theColor, theAlpha, 0);
225                         drawsubpic(theOrigin + height - dY         + dX, width - 2 * dX          +     dY, pic, '0.25 0.75 0', '0.5  0.25 0', theColor, theAlpha, 0);
226                         drawsubpic(theOrigin + height - dY + width - dX,             dX          +     dY, pic, '0.75 0.75 0', '0.25 0.25 0', theColor, theAlpha, 0);
227                 }
228         }
229 }
230 void draw_Text(vector theOrigin, string theText, vector theSize, vector theColor, float theAlpha, float ICanHasKallerz)
231 {
232         if(theSize_x <= 0 || theSize_y <= 0)
233                 error("Drawing zero size text?\n");
234         if(ICanHasKallerz)
235                 drawcolorcodedstring(boxToGlobal(theOrigin, draw_shift, draw_scale), theText, boxToGlobalSize(theSize, draw_scale), theAlpha * draw_alpha, 0);
236         else
237                 drawstring(boxToGlobal(theOrigin, draw_shift, draw_scale), theText, boxToGlobalSize(theSize, draw_scale), theColor, theAlpha * draw_alpha, 0);
238 }
239 void draw_CenterText(vector theOrigin, string theText, vector theSize, vector theColor, float theAlpha, float ICanHasKallerz)
240 {
241         draw_Text(theOrigin - eX * theSize_x * 0.5 * draw_TextWidth(theText, ICanHasKallerz), theText, theSize, theColor, theAlpha, ICanHasKallerz);
242 }
243
244 float draw_TextWidth(string theText, float ICanHasKallerz)
245 {
246         //return strlen(theText);
247         //print("draw_TextWidth \"", theText, "\"\n");
248         return stringwidth(theText, ICanHasKallerz);
249 }
250
251 float draw_clipSet;
252 void draw_SetClip()
253 {
254         if(draw_clipSet)
255                 error("Already clipping, no stack implemented here, sorry");
256         drawsetcliparea(draw_shift_x, draw_shift_y, draw_scale_x, draw_scale_y);
257         draw_clipSet = 1;
258 }
259
260 void draw_SetClipRect(vector theOrigin, vector theScale)
261 {
262         vector o, s;
263         if(draw_clipSet)
264                 error("Already clipping, no stack implemented here, sorry");
265         o = boxToGlobal(theOrigin, draw_shift, draw_scale);
266         s = boxToGlobalSize(theScale, draw_scale);
267         drawsetcliparea(o_x, o_y, s_x, s_y);
268         draw_clipSet = 1;
269 }
270
271 void draw_ClearClip()
272 {
273         if(!draw_clipSet)
274                 error("Not clipping, can't clear it then");
275         drawresetcliparea();
276         draw_clipSet = 0;
277 }
278
279 string draw_TextShortenToWidth(string theText, float maxWidth, float ICanHasKallerz)
280 {
281         if(draw_TextWidth(theText, ICanHasKallerz) <= maxWidth)
282                 return theText;
283         else
284                 return strcat(substring(theText, 0, draw_TextLengthUpToWidth(theText, maxWidth - draw_TextWidth("...", ICanHasKallerz), ICanHasKallerz)), "...");
285 }
286
287 float draw_TextLengthUpToWidth(string theText, float maxWidth, float ICanHasKallerz)
288 {
289         // STOP.
290         // The following function is SLOW.
291         // For your safety and for the protection of those around you...
292         // DO NOT CALL THIS AT HOME.
293         // No really, don't.
294         if(draw_TextWidth(theText, ICanHasKallerz) <= maxWidth)
295                 return strlen(theText); // yeah!
296
297         // binary search for right place to cut string
298         float left, right, middle; // this always works
299         left = 0;
300         right = strlen(theText); // this always fails
301         do
302         {
303                 middle = floor((left + right) / 2);
304                 if(draw_TextWidth(substring(theText, 0, middle), ICanHasKallerz) <= maxWidth)
305                         left = middle;
306                 else
307                         right = middle;
308         }
309         while(left < right - 1);
310
311         // NOTE: when color codes are involved, this binary search is,
312         // mathematically, BROKEN. However, it is obviously guaranteed to
313         // terminate, as the range still halves each time - but nevertheless, it is
314         // guaranteed that it finds ONE valid cutoff place (where "left" is in
315         // range, and "right" is outside).
316
317         return left;
318 }