DayZ 1.27
DayZ Explorer by KGB
 
Загрузка...
Поиск...
Не найдено
RadialMenu.c
См. документацию.
6
8{
9 protected Widget m_Parent;
12 protected ImageWidget m_RadialSelectorImage;
13 protected ImageWidget m_RadialSelectorPointerImage;
18
19 protected float m_AngleRadOffset;
20 protected ref Timer m_UpdateTimer;
21
22 //widget
23 static const string RADIAL_SELECTOR = "RadialSelector";
24 static const string RADIAL_SELECTOR_IMAGE = "SelectorImage";
25 static const string RADIAL_SELECTOR_POINTER = "SelectorPointer";
26 static const string RADIAL_DELIMITER_CONTAINER = "RadialDelimiterContainer";
27 static const string RADIAL_ITEM_CARD_CONTAINER = "RadialItemCardContainer";
28
29 //controls
33 protected float m_ControllerAngle;
34 protected float m_ControllerTilt;
35
36 //controller
37 protected int m_ControllerTimout;
38 protected bool m_IsControllerTimoutEnabled = true; //enables/disables controller deselect timeout reset
39 protected const float CONTROLLER_DESELECT_TIMEOUT = 1000; //timeout [ms] after which selection is automatically deselect when controller is not active
40 protected const float CONTROLLER_TILT_TRESHOLD_SELECT = 0.8; //tilt value (0.0-1.0) for controller sticks after which the selection will be selected
41 protected const float CONTROLLER_TILT_TRESHOLD_EXECUTE = 1.0; //tilt value (0.0-1.0) for controller sticks after which the selection will be executed
42
43 //mouse
44 protected bool m_WidgetInitialized;
45 protected const float MOUSE_SAFE_ZONE_RADIUS = 120; //Radius [px] of safe zone where every previous selection is deselected
46
47 //References
48 protected float m_RadiusOffset; //Radius [% of the main container size]
49 protected float m_ExecuteDistanceOffset; //Distance offset [% of the main container size] after which the selection will be automatically executed
50 protected float m_OffsetFromTop; //first item in the menu won't be directly on top but offset by a rad angle value (clock-wise)
51 protected float m_ItemCardRadiusOffset; //Radius [% of the main container size] for item cards
52 protected string m_DelimiterLayout; //layout file name with path
53
56
57 /*
58
59 RADIAL MENU EVENTS
60
61 Mouse:
62 OnMouseSelect
63 OnMouseDeselect
64 OnMouseExecute - unused, press events used instead
65 OnMousePressLeft
66 OnMousePressRight
67
68 Controller:
69 OnControllerSelect
70 OnControllerDeselect
71 OnControllerExecute - unused, press events used instead
72 OnControllerPressSelect
73 OnControllerPressBack
74
75 Common:
76 OnControlsChanged - controls has been changed (mouse<->controller)
77
78 */
79
80
81 //============================================
82 // RadialMenu
83 //============================================
85 {
86 m_Instance = this;
87
88 //set default control type
89#ifdef PLATFORM_CONSOLE
90 Input inp = GetGame().GetInput();
92 {
94 }
95 else
96 {
98 }
99#endif
100#ifdef PLATFORM_WINDOWS
102#endif
103
106
107 //radial cards
109 m_UpdateTimer = new Timer();
110 m_UpdateTimer.Run(0.01, this, "Update", NULL, true);
111 }
112
114 {
115 }
116
118 {
119 return m_Instance;
120 }
121
122 //Set handler
124 {
126 m_RadialSelector = w.FindAnyWidget(RADIAL_SELECTOR);
127 m_RadialSelectorImage = ImageWidget.Cast(m_RadialSelector.FindAnyWidget(RADIAL_SELECTOR_IMAGE));
129
131 m_RadialSelectorDisabledColor = ARGB(255,150,150,150);
132
133 //parent
134 m_Parent = w;
135 m_Parent.SetHandler(this);
136 }
137
138 //controls
140 {
141 if (m_ControlType != type)
142 {
143 m_ControlType = type;
144 GetGame().GameScript.CallFunction(m_RegisteredClass, "OnControlsChanged", NULL, type);
145 }
146 }
147
149 {
151 {
152 return true;
153 }
154
155 return false;
156 }
157
159 {
160 if (m_ControlType == RadialMenuControlType.CONTROLLER)
161 {
162 return true;
163 }
164
165 return false;
166 }
167
168 void SetWidgetInitialized(bool state)
169 {
170 m_WidgetInitialized = state;
171 }
172
174 {
175 return m_WidgetInitialized;
176 }
177
178 //============================================
179 // Setup
180 //============================================
182 {
184 if (m_UpdateTimer && !m_UpdateTimer.IsRunning())
185 m_UpdateTimer.Run(0.01, this, "Update", NULL, true);
186 }
187
188 //Set radial menu parameters
189 //Radius offset [% of the main container size]
190 void SetRadiusOffset(float radius_offset)
191 {
192 m_RadiusOffset = radius_offset;
193 }
194
195 //Distance offset [% of the main container size] after which the selection will be automatically executed
196 void SetExecuteDistOffset(float execute_dist_offset)
197 {
198 m_ExecuteDistanceOffset = execute_dist_offset;
199 }
200
201 //First item in the menu won't be directly on top but offset by a rad angle value (clock-wise)
202 void SetOffsetFromTop(float offset_from_top)
203 {
204 m_OffsetFromTop = offset_from_top;
205 }
206
207 //Radius [% of the main container size] for item cards
208 void SetItemCardRadiusOffset(float item_card_radius_offset)
209 {
210 m_ItemCardRadiusOffset = item_card_radius_offset;
211 }
212
213 //Enable/Disable controller timeout
215 {
217 }
218
219 void SetWidgetProperties(string delimiter_layout)
220 {
221 m_DelimiterLayout = delimiter_layout;
222 }
223
224 //============================================
225 // Visual
226 //============================================
227 //hide_selector => shows/hides radial selector when refreshing radial menu
228 void Refresh(bool hide_selector = true)
229 {
230 int item_cards_count = GetItemCardsCount();
231 if (item_cards_count > 0)
232 m_AngleRadOffset = 2 * Math.PI / item_cards_count;
233 float angle_rad = -Math.PI / 2;
234
235 //--PARAM top offset--
236 if (m_OffsetFromTop != 0)
237 {
238 angle_rad = angle_rad + m_OffsetFromTop;
239 }
240 //--------------------
241
242 //delete all delimiters
243 Widget delimiters_panel = m_Parent.FindAnyWidget(RADIAL_DELIMITER_CONTAINER);
244 if (delimiters_panel)
245 {
246 Widget del_child = delimiters_panel.GetChildren();
247 while (del_child)
248 {
249 Widget child_to_destroy1 = del_child;
250 del_child = del_child.GetSibling();
251
252 delete child_to_destroy1;
253 }
254 }
255
256 //Position item cards, crate radial delimiters
257 Widget item_cards_panel = m_Parent.FindAnyWidget(RADIAL_ITEM_CARD_CONTAINER);
258 Widget item_card = item_cards_panel.GetChildren();
259
260 //get radius
261 float original_r = GetRadius();
262 float item_cards_r = original_r;
263
264 //--PARAM top offset--....
265 if (m_ItemCardRadiusOffset != 0)
266 {
267 item_cards_r = item_cards_r * m_ItemCardRadiusOffset;
268 if (item_cards_r < 0) item_cards_r = 0; //min radius is 0
269 }
270
271 m_RadialItemCards.Clear();
272 for (int i = 0; i < item_cards_count; ++i)
273 {
274 //position item cards
275 if (item_card)
276 {
277 //creates circle from simple widget items
278 float pos_x = item_cards_r * Math.Cos(angle_rad);
279 float pos_y = item_cards_r * Math.Sin(angle_rad);
280
281 pos_x = pos_x / original_r;
282 pos_y = pos_y / original_r;
283
284 item_card.SetPos(pos_x, pos_y);
285
286 //store item card
287 m_RadialItemCards.Insert(item_card, angle_rad);
288
289 //get next child
290 item_card = item_card.GetSibling();
291 }
292 //-------------------------
293
294 //create delimiter
295 if (item_cards_count > 1 && delimiters_panel && m_DelimiterLayout)
296 {
297 Widget delimiter_widget = GetGame().GetWorkspace().CreateWidgets(m_DelimiterLayout, delimiters_panel);
298 float delim_angle_rad = angle_rad + (m_AngleRadOffset / 2);
299 delimiter_widget.SetPos(0, 0);
300 delimiter_widget.SetRotation(0, 0, GetAngleInDegrees(delim_angle_rad) + 90);
301 }
302
303 //calculate next angle
304 angle_rad += m_AngleRadOffset;
305 }
306
307 //hide selector on refresh
308 if (hide_selector)
309 {
311 }
312 }
313
314 //Radial selector
315 protected void ShowRadialSelector(Widget selected_item)
316 {
317 if (m_RadialSelector && selected_item)
318 {
319 int item_count = m_RadialItemCards.Count();
320 if (item_count > 1)
321 {
322 int angle_deg = GetAngleInDegrees(m_RadialItemCards.Get(selected_item));
323 m_RadialSelector.SetRotation(0, 0, angle_deg + 90); //rotate widget according to its desired rotation
324
325 //set radial selector size
326 float progress = (1 / item_count) * 2;
327 m_RadialSelectorImage.SetMaskProgress(progress);
328
329 m_RadialSelector.Show(true);
330
331 bool grey_selector = selected_item.GetFlags() & WidgetFlags.DISABLED;
332 if (!grey_selector)
333 {
336 }
337 else
338 {
341 }
342 }
343 }
344 }
345
346 protected void HideRadialSelector()
347 {
349 {
350 m_RadialSelector.Show(false);
351 }
352 }
353
354 //============================================
355 // Widget size calculations
356 //============================================
357 protected int GetItemCardsCount()
358 {
359 Widget child = m_ItemCardsContainer.GetChildren();
360 int count = 0;
361
362 while (child)
363 {
364 ++count;
365
366 child = child.GetSibling();
367 }
368
369 return count;
370 }
371
372 protected float GetRadius()
373 {
374 float radius = Math.AbsFloat(GetParentMinSize() * 0.5);
375
376 //PARAM --radius--
377 if (m_RadiusOffset > 0)
378 {
379 return radius * m_RadiusOffset;
380 }
381 //----------------
382
383 return radius;
384 }
385
386 protected void GetParentCenter(out float center_x, out float center_y)
387 {
388 if (m_Parent)
389 {
390 float wx;
391 float wy;
392 m_Parent.GetScreenPos(wx, wy);
393
394 float ww;
395 float wh;
396 m_Parent.GetScreenSize(ww, wh);
397
398 center_x = wx + ww / 2; //center
399 center_y = wy + wh / 2;
400 }
401 }
402
403 protected float GetParentMinSize()
404 {
405 if (m_Parent)
406 {
407 float size_x;
408 float size_y;
409 m_Parent.GetScreenSize(size_x, size_y);
410
411 return Math.Min(size_x, size_y);
412 }
413
414 return 0;
415 }
416
417 //============================================
418 // Angle calculations
419 //============================================
420 //get object by angle (degrees)
421 protected Widget GetObjectByDegAngle(float deg_angle)
422 {
423 for (int i = 0; i < m_RadialItemCards.Count(); ++i)
424 {
425 Widget w = m_RadialItemCards.GetKey(i);
426 float w_angle = GetAngleInDegrees(m_RadialItemCards.Get(w));
427 float offset = GetAngleInDegrees(m_AngleRadOffset) / 2;
428 float min_angle = w_angle - offset;
429 float max_angle = w_angle + offset;
430
431 if (min_angle < 0) min_angle += 360; //clamp 0-360
432 if (max_angle > 360) max_angle -= 360;
433
434 if (min_angle > max_angle) //angle radius is in the cycling point 360->
435 {
436 if (min_angle <= deg_angle) //is cursor position also before this point
437 {
438 if (deg_angle > max_angle)
439 {
440 return w;
441 }
442 }
443 else //is cursor position after this point
444 {
445 if (deg_angle < max_angle)
446 {
447 return w;
448 }
449 }
450 }
451 else
452 {
453 if (deg_angle >= min_angle && deg_angle < max_angle) //min, max angles are within 0-360 radius
454 {
455 return w;
456 }
457 }
458 }
459
460 return NULL;
461 }
462
463 //returns GUI compatible mouse-to-parent angle
464 protected float GetMousePointerAngle()
465 {
466 int mouse_x;
467 int mouse_y;
468 GetMousePos(mouse_x, mouse_y);
469
470 float center_x;
471 float center_y;
472 GetParentCenter(center_x, center_y);
473
474 float tan_x = mouse_x - center_x;
475 float tan_y = mouse_y - center_y;
476 float angle = Math.Atan2(tan_y, tan_x);
477
478 return angle;
479 }
480
481 //returns distance from parent center
482 protected float GetMouseDistance()
483 {
484 int mouse_x;
485 int mouse_y;
486 GetMousePos(mouse_x, mouse_y);
487
488 float center_x;
489 float center_y;
490 GetParentCenter(center_x, center_y);
491
492 float distance = vector.Distance(Vector(mouse_x, mouse_y, 0), Vector(center_x, center_y, 0));
493
494 return distance;
495 }
496
497 //return angle 0-360 deg
498 protected float GetAngleInDegrees(float rad_angle)
499 {
500 float rad_deg = rad_angle * Math.RAD2DEG;
501
502 int angle_mp = rad_deg / 360;
503
504 if (rad_deg < 0)
505 {
506 rad_deg = rad_deg - (360 * angle_mp);
507 rad_deg += 360;
508 }
509
510 return rad_deg;
511 }
512
513 //============================================
514 // Update
515 //============================================
516 //mouse
517 int last_time = -1;
518 protected void Update()
519 {
520 if (this && !m_RegisteredClass)
521 {
522 m_UpdateTimer.Stop();
523 return;
524 }
525
526 //get delta time
527 if (last_time < 0)
528 {
530 }
531 int delta_time = GetGame().GetTime() - last_time;
533
534 //controls
535 if (this && m_RegisteredClass && m_RegisteredClass.IsVisible())
536 {
537 //mouse controls
539 {
540 float mouse_angle = GetMousePointerAngle();
541 float mouse_distance = GetMouseDistance();
542
543 //--PARAM --safe zone radius--
544 if (mouse_distance <= MOUSE_SAFE_ZONE_RADIUS)
545 {
546 //Deselect
548 m_SelectedObject = NULL;
549 //hide selector
551 }
552 else
553 {
554 //Deselect
556
557 //Select
560 //show selector
562 }
563 }
564 //controller controls
565 else if (IsUsingController())
566 {
568
569 //Controller tilt
570 if (m_ControllerAngle > -1 && m_ControllerTilt > -1)
571 {
572 //Right analogue stick
574 //Select
575 if (w_selected)
576 {
577 if (w_selected != m_SelectedObject)
578 {
580 {
581 //Deselect
582 GetGame().GameScript.CallFunction(m_RegisteredClass, "OnControllerDeselect", NULL, m_SelectedObject);
583
584 //Select new object
585 m_SelectedObject = w_selected;
586 GetGame().GameScript.CallFunction(m_RegisteredClass, "OnControllerSelect", NULL, m_SelectedObject);
587 //show selector
589 }
590 }
591 }
592 else
593 {
594 GetGame().GameScript.CallFunction(m_RegisteredClass, "OnControllerDeselect", NULL, m_SelectedObject);
595 m_SelectedObject = NULL;
596 //hide selector
598 }
599 }
600 //if controller is giving no feedback
601 else
602 {
604 {
605 m_ControllerTimout += delta_time;
606
608 {
609 GetGame().GameScript.CallFunction(m_RegisteredClass, "OnControllerDeselect", NULL, m_SelectedObject);
610 m_SelectedObject = NULL;
611 //hide selector
613
614 m_ControllerTimout = 0; //reset controller timeout
615 }
616 }
617 }
618
619 m_ControllerAngle = -1; //reset angle and tilt
620 m_ControllerTilt = -1;
621 }
622
623 m_WidgetInitialized = true;
624 }
625 }
626
627 float NormalizeInvertAngle(float angle)
628 {
629 float new_angle = 360 - angle;
630 int angle_mp = new_angle / 360;
631
632 new_angle = new_angle - (360 * angle_mp);
633
634 return new_angle;
635 }
636
637 //============================================
638 // Controls
639 //============================================
641 {
642 Input input = GetGame().GetInput();
643
644 //Controller radial
645 float angle;
646 float tilt;
647 input.GetGamepadThumbDirection(GamepadButton.THUMB_RIGHT, angle, tilt);
648 angle = NormalizeInvertAngle(angle * Math.RAD2DEG);
649
650 m_ControllerAngle = angle;
651 m_ControllerTilt = tilt;
652 m_ControllerTimout = 0; //reset controller timeout
653
654 //Controller buttons
655 //Select (A,cross)
656 if (m_SelectInputWrapper.InputP().LocalPress())
657 {
658 GetGame().GameScript.CallFunction(m_RegisteredClass, "OnControllerPressSelect", NULL, m_SelectedObject);
659 }
660
661 //Back (B,circle)
662 if (m_BackInputWrapper.InputP().LocalPress())
663 {
664 GetGame().GameScript.CallFunction(m_RegisteredClass, "OnControllerPressBack", NULL, m_SelectedObject);
665 }
666 }
667
668 override bool OnMouseButtonUp(Widget w, int x, int y, int button)
669 {
670 if (button == MouseState.LEFT && m_SelectedObject/* && w == m_SelectedObject*/)
671 {
672 //Execute
674
675 return true;
676 }
677
678 if (button == MouseState.RIGHT)
679 {
680 //Back one level
681 GetGame().GameScript.CallFunction(m_RegisteredClass, "OnMousePressRight", NULL, NULL);
682
683 return true;
684 }
685
686 return false;
687 }
688}
map
Определения ControlsXboxNew.c:4
static AnimSoundObjectBuilderBank GetInstance()
Определения DayZAnimEventMaps.c:186
Icon x
Icon y
class OptionSelectorMultistate extends OptionSelector class_name
ref map< Widget, float > m_RadialItemCards
Определения RadialMenu.c:17
static const string RADIAL_ITEM_CARD_CONTAINER
Определения RadialMenu.c:27
float m_AngleRadOffset
Определения RadialMenu.c:19
UAIDWrapper m_SelectInputWrapper
Определения RadialMenu.c:31
UAIDWrapper m_BackInputWrapper
Определения RadialMenu.c:32
float GetMousePointerAngle()
Определения RadialMenu.c:464
bool IsUsingController()
Определения RadialMenu.c:158
float m_RadiusOffset
Определения RadialMenu.c:48
void RegisterClass(UIScriptedMenu class_name)
Определения RadialMenu.c:181
bool m_WidgetInitialized
Определения RadialMenu.c:44
const float CONTROLLER_DESELECT_TIMEOUT
Определения RadialMenu.c:39
static const string RADIAL_SELECTOR
Определения RadialMenu.c:23
void HideRadialSelector()
Определения RadialMenu.c:346
Widget m_SelectedObject
Определения RadialMenu.c:16
Widget m_ItemCardsContainer
Определения RadialMenu.c:10
void UpdataControllerInput()
Определения RadialMenu.c:640
ImageWidget m_RadialSelectorImage
Определения RadialMenu.c:12
void SetExecuteDistOffset(float execute_dist_offset)
Определения RadialMenu.c:196
string m_DelimiterLayout
Определения RadialMenu.c:52
float m_ItemCardRadiusOffset
Определения RadialMenu.c:51
RadialMenuControlType m_ControlType
Определения RadialMenu.c:30
bool m_IsControllerTimoutEnabled
Определения RadialMenu.c:38
void SetWidgetInitialized(bool state)
Определения RadialMenu.c:168
float NormalizeInvertAngle(float angle)
Определения RadialMenu.c:627
void SetOffsetFromTop(float offset_from_top)
Определения RadialMenu.c:202
void SetRadiusOffset(float radius_offset)
Определения RadialMenu.c:190
void GetParentCenter(out float center_x, out float center_y)
Определения RadialMenu.c:386
void ShowRadialSelector(Widget selected_item)
Определения RadialMenu.c:315
static const string RADIAL_SELECTOR_POINTER
Определения RadialMenu.c:25
float GetMouseDistance()
Определения RadialMenu.c:482
Widget m_RadialSelector
Определения RadialMenu.c:11
ref UIScriptedMenu m_RegisteredClass
Определения RadialMenu.c:54
RadialMenuControlType
Определения RadialMenu.c:2
@ CONTROLLER
Определения RadialMenu.c:4
@ MOUSE
Определения RadialMenu.c:3
float m_OffsetFromTop
Определения RadialMenu.c:50
const float MOUSE_SAFE_ZONE_RADIUS
Определения RadialMenu.c:45
ImageWidget m_RadialSelectorPointerImage
Определения RadialMenu.c:13
int m_RadialSelectorDisabledColor
Определения RadialMenu.c:15
float m_ControllerTilt
Определения RadialMenu.c:34
int GetItemCardsCount()
Определения RadialMenu.c:357
static ref RadialMenu m_Instance
Определения RadialMenu.c:55
bool IsUsingMouse()
Определения RadialMenu.c:148
void SetControlType(RadialMenuControlType type)
Определения RadialMenu.c:139
float GetRadius()
Определения RadialMenu.c:372
const float CONTROLLER_TILT_TRESHOLD_SELECT
Определения RadialMenu.c:40
bool IsWidgetInitialized()
Определения RadialMenu.c:173
const float CONTROLLER_TILT_TRESHOLD_EXECUTE
Определения RadialMenu.c:41
static const string RADIAL_SELECTOR_IMAGE
Определения RadialMenu.c:24
int last_time
Определения RadialMenu.c:517
float m_ControllerAngle
Определения RadialMenu.c:33
int m_RadialSelectorOriginalColor
Определения RadialMenu.c:14
void SetWidgetProperties(string delimiter_layout)
Определения RadialMenu.c:219
static const string RADIAL_DELIMITER_CONTAINER
Определения RadialMenu.c:26
int m_ControllerTimout
Определения RadialMenu.c:37
void ActivateControllerTimeout(bool state)
Определения RadialMenu.c:214
void SetItemCardRadiusOffset(float item_card_radius_offset)
Определения RadialMenu.c:208
float GetParentMinSize()
Определения RadialMenu.c:403
void ~RadialMenu()
Определения RadialMenu.c:113
void RadialMenu()
Определения RadialMenu.c:84
float GetAngleInDegrees(float rad_angle)
Определения RadialMenu.c:498
Widget GetObjectByDegAngle(float deg_angle)
Определения RadialMenu.c:421
ref Timer m_UpdateTimer
Определения RadialMenu.c:20
float m_ExecuteDistanceOffset
Определения RadialMenu.c:49
Widget m_Parent
Определения SizeToChild.c:92
proto native UAInputAPI GetUApi()
proto native WorkspaceWidget GetWorkspace()
proto int GetTime()
returns mission time in milliseconds
ScriptModule GameScript
Определения Game.c:12
proto native Input GetInput()
proto bool GetGamepadThumbDirection(GamepadButton thumbButton, out float angle, out float value)
return true if was deflected button.
proto native bool IsEnabledMouseAndKeyboardEvenOnServer()
Определения input.c:11
Определения EnMath.c:7
void OnWidgetScriptInit(Widget w)
Определения SizeToChild.c:14
void Refresh()
Определения LayoutHolder.c:202
map: item x vector(index, width, height)
Определения EnWidgets.c:651
Определения DayZPlayerImplement.c:63
Определения UAInput.c:15
proto native UAIDWrapper GetPersistentWrapper()
proto native UAInput GetInputByID(int iID)
returns list of all bindable (i.e. visible) inputs from the active group ('core' by default)
Определения DayZGame.c:64
Определения EnWidgets.c:190
static proto native float Distance(vector v1, vector v2)
Returns the distance between tips of two 3D vectors.
Определения EnConvert.c:106
proto native CGame GetGame()
proto volatile int CallFunction(Class inst, string function, out void returnVal, void parm)
GamepadButton
Определения EnSystem.c:341
proto native vector Vector(float x, float y, float z)
Vector constructor from components.
static proto float Min(float x, float y)
Returns smaller of two given values.
static proto float Cos(float angle)
Returns cosinus of angle in radians.
static proto float Atan2(float y, float x)
Returns angle in radians from tangent.
static proto float Sin(float angle)
Returns sinus of angle in radians.
static const float RAD2DEG
Определения EnMath.c:16
static proto float AbsFloat(float f)
Returns absolute value.
static const float PI
Определения EnMath.c:12
MouseState
Определения EnSystem.c:311
proto void GetMousePos(out int x, out int y)
bool OnMouseButtonUp(Widget w, int x, int y, int button)
WidgetFlags
Определения EnWidgets.c:58
proto native external Widget CreateWidgets(string layout, Widget parentWidget=NULL, bool immedUpdate=true)
Create widgets from *.layout file.
int ARGB(int a, int r, int g, int b)
Определения proto.c:322