├── dxGUI
├── colorpicker.png
├── Rectangle.lua
├── Image.lua
├── CursorManager.lua
├── ImageSection.lua
├── TextBordered.lua
├── shaders
│ ├── blur.fx
│ ├── gradient.fx
│ ├── circle.fx
│ ├── tex_matrix.fx
│ └── hud_mask.fx
├── Circle.lua
├── RenderTarget.lua
├── Text.lua
├── TextShadowed.lua
├── Bar.lua
├── Tabs.lua
├── Button.lua
├── Checkbox.lua
├── DxConstructionMasked.lua
├── Gradient.lua
├── dxInput.lua
├── Cursor.lua
├── Slider.lua
├── Blur.lua
├── baseClass.lua
├── TextLines.lua
├── Edit.lua
├── DxConstruction.lua
├── List.lua
├── Anim.lua
└── Map.lua
├── README.md
├── meta.xml
├── classutils.lua
├── utilits.lua
└── LICENSE
/dxGUI/colorpicker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheNormalnij/MTA-dxGUI/HEAD/dxGUI/colorpicker.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MTA-dxGUI
2 | Cool and powerfull dxGUI library for MTA
3 |
4 | See [wiki](https://github.com/TheNormalnij/MTA-dxGUI/wiki) for details
5 |
--------------------------------------------------------------------------------
/dxGUI/Rectangle.lua:
--------------------------------------------------------------------------------
1 | dxGUI.baseClass:subclass{
2 | type = 'rectangle';
3 | color = 0xFF000000;
4 | postGUI = false;
5 |
6 | draw = function( self )
7 | dxDrawRectangle( self.x, self.y, self.w, self.h, self.color, self.postGUI )
8 | end;
9 |
10 | setPostGUI = function( self, state )
11 | if state == true or state == false then
12 | self.postGUI = state
13 | end
14 | end;
15 | }
--------------------------------------------------------------------------------
/dxGUI/Image.lua:
--------------------------------------------------------------------------------
1 | dxGUI.baseClass:subclass{
2 | type = 'image';
3 | rotation = 0;
4 | rotationCenterOffsetX = 0;
5 | rotationCenterOffsetY = 0;
6 | color = 0xFFFFFFFF;
7 | postGUI = false;
8 |
9 | draw = function( self )
10 | dxDrawImage( self.x, self.y, self.w, self.h, self.image, self.rotation,
11 | self.rotationCenterOffsetX, self.rotationCenterOffsetY, self.color, self.postGUI )
12 | end;
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/dxGUI/CursorManager.lua:
--------------------------------------------------------------------------------
1 |
2 | local guiWithCursor = {}
3 |
4 | CursorManager = {
5 |
6 | addGui = function( guiObject )
7 | if not CursorManager.isGuiRequireCursor( guiObject ) then
8 | table.insert( guiWithCursor, guiObject )
9 | end
10 | showCursor( true )
11 | end;
12 |
13 | removeGui = function( guiObject )
14 | if table.removeValue( guiWithCursor, guiObject )
15 | and #guiWithCursor == 0
16 | and isCursorShowing()
17 | then
18 | showCursor( false )
19 | end
20 | end;
21 |
22 | isGuiRequireCursor = function( guiObject )
23 | return table.find( guiWithCursor, guiObject )
24 | end;
25 |
26 | }
--------------------------------------------------------------------------------
/dxGUI/ImageSection.lua:
--------------------------------------------------------------------------------
1 | dxGUI.baseClass:subclass{
2 | type = 'imageSection';
3 | u = 0;
4 | v = 0;
5 |
6 | rotation = 0;
7 | rotationCenterOffsetX = 0;
8 | rotationCenterOffsetY = 0;
9 | color = 0xFFFFFFFF;
10 | postGUI = false;
11 |
12 | create = function( self )
13 | self.us = self.us or self.w
14 | self.vs = self.vs or self.h
15 | return self
16 | end;
17 |
18 | draw = function( self )
19 | dxDrawImageSection( self.x, self.y, self.w, self.h, self.u, self.v, self.us, self.vs, self.image, self.rotation,
20 | self.rotationCenterOffsetX, self.rotationCenterOffsetY, self.color, self.postGUI )
21 | end;
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/dxGUI/TextBordered.lua:
--------------------------------------------------------------------------------
1 | -- from https://wiki.multitheftauto.com/wiki/DxDrawBorderedText
2 | dxGUI.text:subclass{
3 | type = 'textBordered';
4 | outline = 2;
5 |
6 | draw = function( self )
7 | for oX = (self.outline * -1), self.outline do
8 | for oY = (self.outline * -1), self.outline do
9 | dxDrawText(self.text, self.x + oX, self.y + oY, self.x + self.w + oX, self.y + self.h + oY, 0xFF000000, self.scale, self.font, self.alignX, self.alignY, self.clip, self.wordBreak, self.postGUI, self.colorCoded)
10 | end
11 | end
12 | dxDrawText (self.text, self.x, self.y, self.x + self.w, self.y + self.h, self.color, self.scale, self.font, self.alignX, self.alignY, self.clip, self.wordBreak, self.postGUI, self.colorCoded)
13 | end;
14 | }
15 |
--------------------------------------------------------------------------------
/dxGUI/shaders/blur.fx:
--------------------------------------------------------------------------------
1 | texture texture0;
2 | float factor;
3 |
4 | sampler Sampler0 = sampler_state
5 | {
6 | Texture = (texture0);
7 | AddressU = MIRROR;
8 | AddressV = MIRROR;
9 | };
10 |
11 | struct PSInput
12 | {
13 | float2 TexCoord : TEXCOORD0;
14 | };
15 |
16 | float4 PixelShader_Background(PSInput PS) : COLOR0
17 | {
18 | float4 sum = tex2D(Sampler0, PS.TexCoord);
19 | for (float i = 1; i < 3; i++) {
20 | sum += tex2D(Sampler0, float2(PS.TexCoord.x, PS.TexCoord.y + (i * factor)));
21 | sum += tex2D(Sampler0, float2(PS.TexCoord.x, PS.TexCoord.y - (i * factor)));
22 | sum += tex2D(Sampler0, float2(PS.TexCoord.x - (i * factor), PS.TexCoord.y));
23 | sum += tex2D(Sampler0, float2(PS.TexCoord.x + (i * factor), PS.TexCoord.y));
24 | }
25 | sum /= 9;
26 | sum.a = 1.0;
27 | return sum;
28 | }
29 |
30 | technique complercated
31 | {
32 | pass P0
33 | {
34 | PixelShader = compile ps_2_0 PixelShader_Background();
35 | }
36 | }
37 |
38 | technique simple
39 | {
40 | pass P0
41 | {
42 | Texture[0] = texture0;
43 | }
44 | }
--------------------------------------------------------------------------------
/dxGUI/shaders/gradient.fx:
--------------------------------------------------------------------------------
1 | //
2 | // Gradient shader - gradient.fx
3 | //
4 |
5 | float2 gradientVector = float2( 1, 1 );
6 | float4 sGradientFromColor = float4( 0, 0, 0, 0 );
7 | float4 sGradientToColor = float4( 1, 1, 1, 1 );
8 |
9 | //------------------------------------------------------------------------------------------
10 | // PixelShaderFunction
11 | // 1. Read from PS structure
12 | // 2. Process
13 | // 3. Return pixel color
14 | //------------------------------------------------------------------------------------------
15 | float4 PixelShaderFunction( float4 Diffuse : COLOR0, float2 TexCoord : TEXCOORD0) : COLOR0
16 | {
17 | return ( sGradientFromColor + ( sGradientToColor - sGradientFromColor ) * length( TexCoord * gradientVector ) ) * Diffuse;
18 | }
19 |
20 | //------------------------------------------------------------------------------------------
21 | // Techniques
22 | //------------------------------------------------------------------------------------------
23 | technique tec0
24 | {
25 | pass P0
26 | {
27 | PixelShader = compile ps_2_0 PixelShaderFunction();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/meta.xml:
--------------------------------------------------------------------------------
1 |
2 | true
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/dxGUI/Circle.lua:
--------------------------------------------------------------------------------
1 | dxGUI.baseClass:subclass{
2 | type = 'circle';
3 | color = 0xFF000000;
4 | angleStart = 0;
5 | borderWidth = 1e9;
6 | postGUI = false;
7 |
8 | create = function( self )
9 | local angleSweep = self.angleSweep or math.pi * 2 - self.angleStart
10 | if ( angleSweep < math.pi * 2 ) then
11 | self.angleEnd = math.fmod( self.angleStart + angleSweep, math.pi * 2 )
12 | else
13 | self.angleStart = 0
14 | self.angleEnd = math.pi * 2
15 | end
16 |
17 | if not dxGUI.circle.circleShader then
18 | dxGUI.circle.circleShader = dxCreateShader( 'dxGUI/shaders/circle.fx' )
19 | end
20 | return self
21 | end;
22 |
23 | setAngleSweep = function( self, angleSweep )
24 | if ( angleSweep < math.pi * 2 ) then
25 | self.angleEnd = math.fmod( self.angleStart + angleSweep, math.pi * 2 )
26 | else
27 | self.angleStart = 0
28 | self.angleEnd = math.pi * 2
29 | end
30 | end;
31 |
32 | draw = function( self )
33 | local circleShader = self.circleShader
34 | dxSetShaderValue( circleShader, 'sCircleWidthInPixel', self.w );
35 | dxSetShaderValue( circleShader, 'sCircleHeightInPixel', self.h );
36 | dxSetShaderValue( circleShader, 'sBorderWidthInPixel', self.borderWidth );
37 | dxSetShaderValue( circleShader, 'sAngleStart', self.angleStart - math.pi );
38 | dxSetShaderValue( circleShader, 'sAngleEnd', self.angleEnd - math.pi );
39 | dxDrawImage( self.x, self.y, self.w, self.h, self.circleShader, 0, 0, 0, self.color, self.postGUI )
40 | end;
41 |
42 | setPostGUI = function( self, state )
43 | if state == true or state == false then
44 | self.postGUI = state
45 | end
46 | end;
47 | }
--------------------------------------------------------------------------------
/dxGUI/RenderTarget.lua:
--------------------------------------------------------------------------------
1 |
2 | local currentRT
3 |
4 | local drawingOffsetsEnabled = false
5 |
6 | local _dxSetRenderTarget = dxSetRenderTarget
7 |
8 | local _dxDrawImage = dxDrawImage
9 | local _dxDrawText = dxDrawText
10 | local _dxDrawImageSection = dxDrawImageSection
11 | local _dxDrawRectangle = dxDrawRectangle
12 |
13 | local function setDrawingOffset( offX, offY )
14 | if offX and offY then
15 | dxDrawImage = function( x, y, ... )
16 | _dxDrawImage( x - offX, y - offY, ... )
17 | end
18 | dxDrawText = function( text, x1, y1, x2, y2, ... )
19 | _dxDrawText( text, x1 - offX, y1 - offY, x2 - offX, y2 - offY, ... )
20 | end
21 | dxDrawImageSection = function( x, y, ... )
22 | _dxDrawImageSection( x - offX, y - offY, ... )
23 | end
24 | dxDrawRectangle = function( x, y, ... )
25 | _dxDrawRectangle( x - offX, y - offY, ... )
26 | end
27 | drawingOffsetsEnabled = true
28 | else
29 | dxDrawImage = _dxDrawImage
30 | dxDrawText = _dxDrawText
31 | dxDrawImageSection = _dxDrawImageSection
32 | dxDrawRectangle = _dxDrawRectangle
33 | drawingOffsetsEnabled = false
34 | end
35 | end
36 |
37 | function DxRenderTarget:setAsTarget( clean, offX, offY )
38 | _dxSetRenderTarget( self, clean )
39 | currentRT = self
40 | setDrawingOffset( offX, offY )
41 | end
42 |
43 | function DxRenderTarget.setDrawingOffset( offX, offY )
44 | return setDrawingOffset( offX, offY )
45 | end
46 |
47 | function DxRenderTarget.getCurrentTarget( )
48 | return currentRT
49 | end
50 |
51 | function dxSetRenderTarget( target, clean )
52 | _dxSetRenderTarget( target, clean )
53 | currentRT = target
54 | setDrawingOffset( )
55 | end
--------------------------------------------------------------------------------
/dxGUI/Text.lua:
--------------------------------------------------------------------------------
1 | dxGUI.baseClass:subclass{
2 | type = 'text';
3 | text = '';
4 | w = 0;
5 | h = 0;
6 | color = 0xFF000000;
7 | scale = 1;
8 | font = 'default';
9 | alignX = 'left'; -- Can be "left", "center" or "right"
10 | alignY = 'top'; -- Can be "top", "center" or "bottom"
11 | clip = false;
12 | wordBreak = false;
13 | postGUI = false;
14 | colorCoded = false;
15 | subPixelPositioning = false;
16 |
17 | draw = function( self )
18 | dxDrawText( self.text, self.x, self.y, self.x + self.w, self.y + self.h, self.color, self.scale, self.font, self.alignX, self.alignY,
19 | self.clip, self.wordBreak, self.postGUI, self.colorCoded, self.subPixelPositioning )
20 | end;
21 |
22 | getText = function( self )
23 | return self.text
24 | end;
25 |
26 | setText = function( self, text )
27 | if type( text ) == 'number' then
28 | self.text = tostring( text )
29 | return true
30 | end
31 | if type( text ) == 'string' then
32 | self.text = text
33 | return true
34 | end
35 | return false
36 | end;
37 |
38 | getRealSize = function( self )
39 | return dxGetTextWidth( self.text, self.scale, self.font, self.colorCoded ),
40 | dxGetFontHeight( self.scale, self.font )
41 | end;
42 |
43 | setRealSize = function( self )
44 | self.w, self.h = self:getRealSize()
45 | end;
46 |
47 | setScale = function( self, scaleX, scaleY )
48 | self.w, self.h = self.w * scaleX, self.h * ( scaleY or scaleX )
49 | self.scale = scaleX * self.scale
50 | end;
51 |
52 | }
--------------------------------------------------------------------------------
/dxGUI/TextShadowed.lua:
--------------------------------------------------------------------------------
1 | dxGUI.text:subclass{
2 | type = 'textShadowed';
3 | off = 2;
4 | shadowColor = tocolor( 0, 0, 0, 50 );
5 |
6 | draw = function( self )
7 | local r, g, b, a = color.HEXtoRGB( self.shadowColor )
8 | local _, _, _, ta = color.HEXtoRGB( self.color )
9 | local shadowColor = tocolor( r, g, b, a * ta / 255 )
10 | dxDrawText( self.text, self.x + self.off, self.y + self.off, self.x + self.w + self.off, self.y + self.h + self.off, shadowColor, self.scale, self.font, self.alignX, self.alignY,
11 | self.clip, self.wordBreak, self.postGUI, self.colorCoded, self.subPixelPositioning )
12 | dxDrawText( self.text, self.x + self.off, self.y - self.off, self.x + self.w + self.off, self.y + self.h - self.off, shadowColor, self.scale, self.font, self.alignX, self.alignY,
13 | self.clip, self.wordBreak, self.postGUI, self.colorCoded, self.subPixelPositioning )
14 | dxDrawText( self.text, self.x - self.off, self.y - self.off, self.x + self.w - self.off, self.y + self.h - self.off, shadowColor, self.scale, self.font, self.alignX, self.alignY,
15 | self.clip, self.wordBreak, self.postGUI, self.colorCoded, self.subPixelPositioning )
16 | dxDrawText( self.text, self.x - self.off, self.y + self.off, self.x + self.w - self.off, self.y + self.h + self.off, shadowColor, self.scale, self.font, self.alignX, self.alignY,
17 | self.clip, self.wordBreak, self.postGUI, self.colorCoded, self.subPixelPositioning )
18 | dxDrawText( self.text, self.x, self.y, self.x + self.w, self.y + self.h, self.color, self.scale, self.font, self.alignX, self.alignY,
19 | self.clip, self.wordBreak, self.postGUI, self.colorCoded, self.subPixelPositioning )
20 | end;
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/dxGUI/Bar.lua:
--------------------------------------------------------------------------------
1 | dxConstruction:subclass{
2 | type = 'bar';
3 | progress = 0;
4 | progressSide = 1;
5 | isEnabled = true;
6 |
7 | create = function( self )
8 | if not dxConstruction.create( self, false ) then
9 | return false
10 | end
11 |
12 | local bar = self.objects.bar
13 |
14 | if not bar then
15 | self:errorHandler( 'No bar for progressbar' )
16 | return false
17 | end
18 |
19 | self.barX = bar.x
20 | self.barY = bar.y
21 | self.barMaxW = bar.w
22 | self.barMaxH = bar.h
23 |
24 | self:setEnabled( self.isEnabled )
25 | return self
26 | end;
27 |
28 | setProgress = function( self, progress )
29 | if type( progress ) == 'number' then
30 | self.progress = math.clamp( 0, progress, 1 )
31 |
32 | if self.progressSide == 1 then
33 |
34 | elseif self.progressSide == 2 then
35 | self.objects.bar.w = self.barMaxW * progress
36 | elseif self.progressSide == 3 then
37 |
38 | elseif self.progressSide == 4 then
39 |
40 | end
41 |
42 | if self.onChanged then
43 | self:onChanged( progress )
44 | end
45 | return true
46 | end
47 | return false
48 | end;
49 |
50 | getProrgess = function( self )
51 | return self.progress
52 | end;
53 |
54 | setEnabled = function( self, state )
55 | if type( state ) == 'boolean' then
56 | self.isEnabled = state
57 | return true
58 | end
59 | return false
60 | end;
61 |
62 | getEnabled = function( self )
63 | return self.isEnabled
64 | end;
65 |
66 | onClick = function( self, cX, cY )
67 | if self.isEnabled then
68 | if self.progressSide == 1 then
69 | self:setProgress( 1 - (cY - self.y) / self.h )
70 | elseif self.progressSide == 2 then
71 | self:setProgress( (cX - self.x) / self.w )
72 | elseif self.progressSide == 3 then
73 | self:setProgress( (cY - self.y) / self.h )
74 | elseif self.progressSide == 4 then
75 | self:setProgress( 1 - (cX - self.x) / self.w )
76 | end
77 | end
78 | end;
79 | }
80 |
--------------------------------------------------------------------------------
/dxGUI/shaders/circle.fx:
--------------------------------------------------------------------------------
1 | //
2 | // Example shader - circle.fx
3 | //
4 |
5 | //
6 | // Based on code from:
7 | // http://www.geeks3d.com/20130705/shader-library-circle-disc-fake-sphere-in-glsl-opengl-glslhacker/
8 | //
9 |
10 | float sCircleHeightInPixel = 100;
11 | float sCircleWidthInPixel = 100;
12 | float sBorderWidthInPixel = 10;
13 | float sAngleStart = -3.14;
14 | float sAngleEnd = 3.14;
15 |
16 | //------------------------------------------------------------------------------------------
17 | // PixelShaderFunction
18 | // 1. Read from PS structure
19 | // 2. Process
20 | // 3. Return pixel color
21 | //------------------------------------------------------------------------------------------
22 | float4 PixelShaderFunction(float4 Diffuse : COLOR0, float2 TexCoord : TEXCOORD0) : COLOR0
23 | {
24 | float2 uv = float2( TexCoord.x, TexCoord.y ) - float2( 0.5, 0.5 );
25 |
26 | // Clip unwanted pixels from partial pie
27 | float angle = atan2( -uv.x, uv.y ); // -PI to +PI
28 | if ( sAngleStart > sAngleEnd )
29 | {
30 | if ( angle < sAngleStart && angle > sAngleEnd )
31 | return 0;
32 | }
33 | else
34 | {
35 | if ( angle < sAngleStart || angle > sAngleEnd )
36 | return 0;
37 | }
38 |
39 | // Calc border width to use
40 | float2 vec = normalize( uv );
41 | float CircleRadiusInPixel = lerp( sCircleWidthInPixel, sCircleHeightInPixel, vec.y * vec.y );
42 | float borderWidth = sBorderWidthInPixel / CircleRadiusInPixel;
43 |
44 | // Check if pixel is inside circle
45 | float dist = sqrt( dot( uv, uv ) );
46 | if ( ( dist > 0.5 ) || ( dist < 0.5 - borderWidth ) )
47 | return 0;
48 | else
49 | return Diffuse;
50 | }
51 |
52 | //------------------------------------------------------------------------------------------
53 | // Techniques
54 | //------------------------------------------------------------------------------------------
55 | technique tec0
56 | {
57 | pass P0
58 | {
59 | PixelShader = compile ps_2_0 PixelShaderFunction();
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/dxGUI/Tabs.lua:
--------------------------------------------------------------------------------
1 | dxGUI.tabs = dxConstruction:subclass{
2 | type = 'tabs';
3 | active = 1;
4 |
5 | create = function( self )
6 |
7 | if self.body then
8 | self.objects = table.copy( self.body, false )
9 | else
10 | self.objects = {}
11 | end
12 |
13 | if not self.items then
14 | self.items = {}
15 | end
16 |
17 | dxConstruction.create( self, false )
18 |
19 | for id, thistabItems in pairs( self.items ) do
20 | if self.active == id then
21 | for guiID, data in pairs( thistabItems ) do
22 | self:initObject( data, guiID )
23 | self:addObject( data, guiID )
24 | end
25 | else
26 | for guiID, data in pairs( thistabItems ) do
27 | self:initObject( data, guiID )
28 | end
29 | end
30 | end
31 |
32 | return self
33 | end;
34 |
35 | addItem = function( self, item, id )
36 | id = id or #self.items + 1
37 |
38 | for guiID, data in pairs( item ) do
39 | self:initObject( data, guiID )
40 | end
41 |
42 | self.items[id] = item
43 | return id
44 | end;
45 |
46 | setPosition = function( self, x, y )
47 | x, y = math.floor( x ), math.floor( y )
48 | for key, object in pairs( self.objects ) do
49 | object:setPosition( x + object.x - self.x, y + object.y - self.y )
50 | end
51 | self.x, self.y = x, y
52 | end;
53 |
54 | setActiveTab = function( self, id )
55 | if self.items[ self.active ] then
56 | for guiID, data in pairs( self.items[ self.active ] ) do
57 | self:removeObject( guiID )
58 | end
59 | end
60 | if id and self.items[id] then
61 | for guiID, data in pairs( self.items[id] ) do
62 | self:addObject( data, guiID )
63 | end
64 | self.active = id
65 | return true
66 | else
67 | self.active = nil
68 | end
69 | return false
70 | end;
71 |
72 | getActiveTab = function( self, id )
73 | return self.active
74 | end;
75 |
76 | objectPairs = function( self )
77 | local t = {}
78 | if self.body then
79 | for guiID, data in pairs( self.body ) do
80 | table.insert( t, data )
81 | end
82 | end
83 | for id, tab in pairs( self.items ) do
84 | for guiID, data in pairs( tab ) do
85 | table.insert( t, data )
86 | end
87 | end
88 | return next, t
89 | end;
90 | }
91 |
--------------------------------------------------------------------------------
/dxGUI/Button.lua:
--------------------------------------------------------------------------------
1 | dxConstruction:subclass{
2 | type = 'button';
3 | enabled = true;
4 |
5 | create = function( self )
6 | if not dxConstruction.create( self, false ) then
7 | return false
8 | end
9 |
10 | self:setText( self.text )
11 |
12 | self:setEnabled( self.enabled )
13 | return self
14 | end;
15 |
16 | setText = function( self, text )
17 | if self.objects.text then
18 | return self.objects.text:setText( text )
19 | end
20 | return false
21 | end;
22 |
23 | getText = function( self, text )
24 | if self.objects.text then
25 | return self.objects.text:getText( text )
26 | end
27 | return ''
28 | end;
29 |
30 | setShow = function( self, show )
31 | if type( show ) == 'boolean' then
32 | self:setStatus( 'default' )
33 | self.show = show
34 | return true
35 | end
36 | return false
37 | end;
38 |
39 | setEnabled = function( self, enabled )
40 | if type( enabled ) == 'boolean' then
41 | self.enabled = enabled
42 | if enabled then
43 | self:setStatus( 'default' )
44 | else
45 | self:setStatus( 'disabled' )
46 | end
47 | return true
48 | else
49 | error( 'Bad argument #1, got ' .. type( enabled ), 2 )
50 | end
51 | end;
52 |
53 | isEnabled = function( self )
54 | return self.enabled
55 | end;
56 |
57 | onClick = function( self, button, state )
58 | if self.enabled and button == 'left' or button == 'right' then
59 | if self.enabled and state == 'down' then
60 | self:setStatus( 'clicked' )
61 | elseif self.enabled and state == 'up' then
62 | if button == 'left' and self.onLeftMouseClick then
63 | self:onLeftMouseClick( )
64 | elseif self.onRightMouseClick then
65 | self:onRightMouseClick( )
66 | end
67 | self:setStatus( self:isOnCursor() and 'oncursor' or 'default' )
68 | end
69 | end
70 | end;
71 |
72 | onCursorMove = function( self, inStatus )
73 | if self.enabled then
74 | local currentStatus = self:getStatus()
75 | if inStatus and currentStatus == 'default' then
76 | self:setStatus( 'oncursor' )
77 | elseif inStatus and currentStatus == 'oncursor' then
78 | -- oncursor is now
79 | else
80 | self:setStatus( inStatus and getKeyState( 'mouse1' ) and 'clicked' or 'default' )
81 | end
82 | end
83 | end;
84 | }
85 |
--------------------------------------------------------------------------------
/dxGUI/Checkbox.lua:
--------------------------------------------------------------------------------
1 | dxConstruction:subclass{
2 | type = 'checkbox';
3 | status = 'active_not_selected';
4 | selected = false;
5 | active = true;
6 |
7 | create = function( self )
8 | if not dxConstruction.create( self, false ) then
9 | return false
10 | end
11 |
12 | self:setText( self.text )
13 |
14 | self:setActive( self.active )
15 |
16 | self:setSelected( self.selected )
17 | return self
18 | end;
19 |
20 | setText = function( self, text )
21 | return self.objects.text and self.objects.text:setText( text )
22 | end;
23 |
24 | setActive = function( self, active )
25 | if type( active ) == 'boolean' then
26 | self.active = active
27 | self:setStatus( ( active and 'active' or 'non_active' )
28 | .. ( self:getSelected() and '_selected' or '_non_selected' ) )
29 | return true
30 | end
31 | error( 'Bad argument #1, got ' .. type( active ), 2 )
32 | end;
33 |
34 | setStatus = function( self, status )
35 | if self.stats[status] then
36 | self.status = status
37 | table.unite( self.objects, self.stats[status], true )
38 | return true
39 | elseif self.stats[ '*' .. status] then
40 | table.unite( self.objects, self.stats['*' .. status], true )
41 | end
42 | return false
43 | end;
44 |
45 | getSelected = function ( self )
46 | return self.selected
47 | end;
48 |
49 | setSelected = function ( self, selected )
50 | if type( selected ) == 'boolean' then
51 | self.selected = selected
52 | self:setStatus( ( self.active and 'active' or 'non_active' )
53 | .. ( self:getSelected() and '_selected' or '_non_selected' ) )
54 | return true
55 | end
56 | error( 'Bad argument #1, got ' .. type( selected ), 2 )
57 | end;
58 |
59 | onClick = function( self, button, state )
60 | if self.active and button == 'left' and state == 'down' then
61 | self:setSelected( not self:getSelected() )
62 | if self.onChanged then
63 | self:onChanged( self:getSelected() )
64 | end
65 | elseif self.active and button == 'left' and state == 'up' then
66 | self:setStatus( 'default' )
67 | end
68 | return true
69 | end;
70 |
71 | onCursorMove = function( self, inStatus )
72 | if inStatus and self.status == 'default' then
73 | self:setStatus( 'oncursor' )
74 | elseif inStatus and self.status == 'oncursor' then
75 |
76 | else
77 | self:setStatus( 'default' )
78 | end
79 | end;
80 | }
81 |
--------------------------------------------------------------------------------
/dxGUI/DxConstructionMasked.lua:
--------------------------------------------------------------------------------
1 |
2 | local maskShader
3 |
4 | dxConstruction:subclass{
5 | type = 'constructionMasked';
6 |
7 | create = function( self, isGlobal )
8 |
9 | self.newRenderTarget = self.newRenderTarget or { withAlpha = true }
10 |
11 | if not dxConstruction.create( self ) then
12 | return false
13 | end
14 |
15 | if not self.mask then
16 | outputDebugString( 'This construction must have mask', 2 )
17 | return false
18 | end
19 |
20 | if not maskShader then
21 | maskShader = dxCreateShader( "dxGUI/shaders/hud_mask.fx" )
22 | end
23 | self.maskShader = maskShader
24 |
25 | self.mask.x = self.mask.x or 0
26 | self.mask.y = self.mask.y or 0
27 |
28 | self.mask.w = self.mask.w or self.w
29 | self.mask.h = self.mask.h or self.h
30 | self.mask.style = self.style
31 |
32 |
33 | self.mask.show = true
34 |
35 | local maskType = self.mask.type
36 |
37 | if not maskType then
38 | outputDebugString( 'Mask does not has type', 2 )
39 | return false
40 | end
41 |
42 | if self.style[maskType] or dxGUI[maskType] then
43 | self.mask.renderTarget = dxCreateRenderTarget( self.mask.w, self.mask.h, true )
44 |
45 | self:initObject( self.mask )
46 | else
47 | outputDebugString( 'Unknow type for mask ' .. tostring( maskType ), 2 )
48 | return false
49 | end
50 |
51 | return self
52 | end;
53 |
54 | draw = function( self )
55 | if self.show == true then
56 | self.mask.renderTarget:setAsTarget( true )
57 | self.mask:updateAnims()
58 | self.mask:draw()
59 | self.renderTarget:setAsTarget( true, self.x, self.y )
60 | for _, object in ipairs( self.plane ) do
61 | if object.show == true then
62 | object:updateAnims()
63 | object:draw()
64 | end
65 | end
66 | dxSetRenderTarget()
67 | DxRenderTarget.setDrawingOffset()
68 | self.maskShader:setValue( "sPicTexture", self.renderTarget )
69 | self.maskShader:setValue( "sMaskTexture", self.mask.renderTarget )
70 | dxDrawImage( self.x, self.y, self.w, self.h, self.maskShader )
71 | end
72 | end;
73 |
74 | setScale = function( self, scale, scaleY )
75 | dxConstruction.setScale( self, scale, scaleY )
76 |
77 | self.mask:setPosition( self.mask.x * scale, self.mask.y * scaleY )
78 | if self.mask.setScale then
79 | self.mask:setScale( scale, scaleY )
80 | else
81 | self.mask.w, self.mask.h = self.mask.w * scale, self.mask.h * scaleY
82 | end
83 |
84 | if self.mask.renderTarget then
85 | self.mask.renderTarget:destroy()
86 | self.mask.renderTarget = dxCreateRenderTarget( self.mask.w, self.mask.h, true )
87 | end
88 | end;
89 | }
90 |
--------------------------------------------------------------------------------
/dxGUI/Gradient.lua:
--------------------------------------------------------------------------------
1 | local shader
2 |
3 | dxGUI.baseClass:subclass{
4 | type = 'gradient';
5 | colorFrom = tocolor( 255, 255, 255 );
6 | colorTo = tocolor( 0, 0, 0 );
7 | isHorizontal = false;
8 | easing = 'Linear';
9 |
10 | rotation = 0;
11 | rotationCenterOffsetX = 0;
12 | rotationCenterOffsetY = 0;
13 | color = 0xFFFFFFFF;
14 |
15 | create = function( self )
16 | if not self.gradientFunction and not self.shader then
17 | if not shader then
18 | shader = dxCreateShader( "dxGUI/shaders/gradient.fx" )
19 | end
20 |
21 | self.shader = shader
22 | end
23 |
24 | return self
25 | end;
26 |
27 | -- gradientFunction = function( self, progress )
28 | -- local from = self.fromRGB
29 | -- local to = self.toRGB
30 | -- if not from or not to then
31 | -- from = { color.HEXtoRGB( self.colorFrom ) }
32 | -- to = { color.HEXtoRGB( self.colorTo ) }
33 | -- self.fromRGB = from
34 | -- self.toRGB = to
35 | -- end
36 | -- progress = getEasingValue( progress, self.easing )
37 |
38 | -- return tocolor( from[1] + ( to[1] - from[1] ) * progress,
39 | -- from[2] + ( to[2] - from[2] ) * progress,
40 | -- from[3] + ( to[3] - from[3] ) * progress,
41 | -- from[4] + ( to[4] - from[4] ) * progress
42 | -- )
43 | -- end;
44 |
45 | setGradientFunction = function( self, func )
46 | if func then
47 | self.gradientFunction = func
48 | self.shader = nil
49 | else
50 | if not shader then
51 | shader = dxCreateShader( "dxGUI/shaders/gradient.fx" )
52 | end
53 |
54 | self.shader = shader
55 | end
56 | end;
57 |
58 | draw = function( self )
59 | if self.shader then
60 | if self.isHorizontal then
61 | self.shader:setValue( 'gradientVector', 1, 0 )
62 | else
63 | self.shader:setValue( 'gradientVector', 0, 1 )
64 | end
65 | local r, g, b, a = color.HEXtoRGB( self.colorFrom )
66 | self.shader:setValue( 'sGradientFromColor', r / 255, g / 255, b / 255, a / 255 )
67 | r, g, b, a = color.HEXtoRGB( self.colorTo )
68 | self.shader:setValue( 'sGradientToColor', r / 255, g / 255, b / 255, a / 255 )
69 | dxDrawImage( self.x, self.y, self.w, self.h, self.shader, self.rotation,
70 | self.rotationCenterOffsetX, self.rotationCenterOffsetY, self.color, self.postGUI )
71 | else
72 | if self.isHorizontal then
73 | for x = 0, self.w -1 do
74 | dxDrawRectangle( self.x + x, self.y, 1, self.h, self:gradientFunction( (x+1) / self.w ), self.postGUI )
75 | end
76 | else
77 | for y = 0, self.h -1 do
78 | dxDrawRectangle( self.x, self.y + y, self.w, 1, self:gradientFunction( (y+1) / self.h ), self.postGUI )
79 | end
80 | end
81 | end
82 | end;
83 | }
84 |
--------------------------------------------------------------------------------
/dxGUI/shaders/tex_matrix.fx:
--------------------------------------------------------------------------------
1 | //
2 | // tex_matrix.fx
3 | //
4 |
5 |
6 | //-------------------------------------------
7 | // Returns a translation matrix
8 | //-------------------------------------------
9 | float3x3 makeTranslationMatrix ( float2 pos )
10 | {
11 | return float3x3(
12 | 1, 0, 0,
13 | 0, 1, 0,
14 | pos.x, pos.y, 1
15 | );
16 | }
17 |
18 |
19 | //-------------------------------------------
20 | // Returns a rotation matrix
21 | //-------------------------------------------
22 | float3x3 makeRotationMatrix ( float angle )
23 | {
24 | float s = sin(angle);
25 | float c = cos(angle);
26 | return float3x3(
27 | c, s, 0,
28 | -s, c, 0,
29 | 0, 0, 1
30 | );
31 | }
32 |
33 |
34 | //-------------------------------------------
35 | // Returns a scale matrix
36 | //-------------------------------------------
37 | float3x3 makeScaleMatrix ( float2 scale )
38 | {
39 | return float3x3(
40 | scale.x, 0, 0,
41 | 0, scale.y, 0,
42 | 0, 0, 1
43 | );
44 | }
45 |
46 |
47 | //-------------------------------------------
48 | // Returns a combined matrix of doom
49 | //-------------------------------------------
50 | float3x3 makeTextureTransform ( float2 prePosition, float2 scale, float2 scaleCenter, float rotAngle, float2 rotCenter, float2 postPosition )
51 | {
52 | float3x3 matPrePosition = makeTranslationMatrix( prePosition );
53 | float3x3 matToScaleCen = makeTranslationMatrix( -scaleCenter );
54 | float3x3 matScale = makeScaleMatrix( scale );
55 | float3x3 matFromScaleCen = makeTranslationMatrix( scaleCenter );
56 | float3x3 matToRotCen = makeTranslationMatrix( -rotCenter );
57 | float3x3 matRot = makeRotationMatrix( rotAngle );
58 | float3x3 matFromRotCen = makeTranslationMatrix( rotCenter );
59 | float3x3 matPostPosition = makeTranslationMatrix( postPosition );
60 |
61 | float3x3 result =
62 | mul(
63 | mul(
64 | mul(
65 | mul(
66 | mul(
67 | mul(
68 | mul(
69 | matPrePosition
70 | ,matToScaleCen)
71 | ,matScale)
72 | ,matFromScaleCen)
73 | ,matToRotCen)
74 | ,matRot)
75 | ,matFromRotCen)
76 | ,matPostPosition)
77 | ;
78 | return result;
79 | }
80 |
--------------------------------------------------------------------------------
/dxGUI/shaders/hud_mask.fx:
--------------------------------------------------------------------------------
1 | //
2 | // Example shader - hud_mask.fx
3 | //
4 |
5 | #include "/dxGUI/shaders/tex_matrix.fx"
6 |
7 | ///////////////////////////////////////////////////////////////////////////////
8 | // Global variables
9 | ///////////////////////////////////////////////////////////////////////////////
10 | texture sPicTexture;
11 | texture sMaskTexture;
12 | int borderColor = 0x00FFFFFF;
13 |
14 | float2 gUVPrePosition = float2( 0, 0 );
15 | float2 gUVScale = float( 1 ); // UV scale
16 | float2 gUVScaleCenter = float2( 0.5, 0.5 );
17 | float gUVRotAngle = float( 0 ); // UV Rotation
18 | float2 gUVRotCenter = float2( 0.5, 0.5 );
19 | float2 gUVPosition = float2( 0, 0 ); // UV position
20 |
21 |
22 | ///////////////////////////////////////////////////////////////////////////////
23 | // Functions
24 | ///////////////////////////////////////////////////////////////////////////////
25 |
26 |
27 | //-------------------------------------------
28 | // Returns UV transform using external settings
29 | //-------------------------------------------
30 | float3x3 getTextureTransform()
31 | {
32 | return makeTextureTransform( gUVPrePosition, gUVScale, gUVScaleCenter, gUVRotAngle, gUVRotCenter, gUVPosition );
33 | }
34 |
35 | ///////////////////////////////////////////////////////////////////////////////
36 | // Techniques
37 | ///////////////////////////////////////////////////////////////////////////////
38 | technique hello
39 | {
40 | pass P0
41 | {
42 | // Set up texture stage 0
43 | Texture[0] = sPicTexture;
44 | TextureTransform[0] = getTextureTransform();
45 | TextureTransformFlags[0] = Count2;
46 | AddressU[0] = border;
47 | AddressV[0] = border;
48 | borderColor[0] = borderColor;
49 | // Color mix texture and diffuse
50 | ColorOp[0] = Modulate;
51 | ColorArg1[0] = Texture;
52 | ColorArg2[0] = Diffuse;
53 | // Alpha mix texture and diffuse
54 | AlphaOp[0] = Modulate;
55 | AlphaArg1[0] = Texture;
56 | AlphaArg2[0] = Diffuse;
57 |
58 |
59 | // Set up texture stage 1
60 | Texture[1] = sMaskTexture;
61 | TexCoordIndex[1] = 0;
62 | AddressU[1] = Clamp;
63 | AddressV[1] = Clamp;
64 | // Color pass through from stage 0
65 | ColorOp[1] = SelectArg1;
66 | ColorArg1[1] = Current;
67 | // Alpha modulate mask texture with stage 0
68 | AlphaOp[1] = Modulate;
69 | AlphaArg1[1] = Current;
70 | AlphaArg2[1] = Texture;
71 |
72 |
73 | // Disable texture stage 2
74 | ColorOp[2] = Disable;
75 | AlphaOp[2] = Disable;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/dxGUI/dxInput.lua:
--------------------------------------------------------------------------------
1 | local stickyTimer
2 |
3 | local currentInput
4 |
5 | Input = class{
6 | sticked = false;
7 | sticky = 70; -- ms
8 |
9 | create = function( self )
10 | self.controll = {}
11 | return self
12 | end;
13 |
14 | get = function( )
15 | return currentInput
16 | end;
17 |
18 | getBind = function( self, key, state )
19 | local controll = self.controll
20 | for i = 1, #controll do
21 | if controll[i][1] == key and controll[i][2] == state then
22 | return controll[i], i
23 | end
24 | end
25 | return false
26 | end;
27 |
28 | bind = function( self, key, state, funct, ... )
29 | if self:getBind( key, state ) then
30 | return false
31 | end
32 | table.insert( self.controll, { key, state, funct, ... } )
33 | end;
34 |
35 | unbind = function( self, key, state )
36 | local bind, pos = self:getBind( key, press )
37 | if bind then
38 | table.remove( self.controll, pos )
39 | return true
40 | end
41 | return false
42 | end;
43 |
44 | onKey = function( self, key, press )
45 | local bind = self:getBind( key, press )
46 | if bind then
47 | bind[3]( unpack( bind, 4 ) )
48 | if key == 'escape' and press then
49 | cancelEvent()
50 | end
51 | end
52 | end;
53 |
54 | activate = function( self )
55 | if currentInput then
56 | currentInput:deactivate()
57 | end
58 | currentInput = self
59 | if self.onCharacter then
60 | guiSetInputMode( 'no_binds' )
61 | end
62 | if self.onActivate then
63 | self:onActivate()
64 | end
65 | end;
66 |
67 | isActive = function( self )
68 | return currentInput == self
69 | end;
70 |
71 | deactivate = function( self )
72 | currentInput = false
73 | if self.onDeactivate then
74 | self:onDeactivate()
75 | end
76 | guiSetInputMode( 'allow_binds' )
77 | end;
78 | }
79 |
80 | ---------------------------
81 |
82 | addEventHandler( "onClientPaste", root, function( text )
83 | if currentInput and currentInput.onPaste then
84 | currentInput:onPaste( text )
85 | end
86 | end )
87 |
88 | addEventHandler( 'onClientCharacter', root, function( character )
89 | if currentInput and currentInput.onCharacter then
90 | currentInput:onCharacter( character )
91 | end
92 | end )
93 |
94 | addEventHandler( 'onClientKey', root, function( button, press )
95 | if currentInput then
96 | if currentInput.sticked then
97 | if stickyTimer and isTimer( stickyTimer ) then
98 | killTimer( stickyTimer )
99 | end
100 | if press then
101 | stickyTimer = Timer( function()
102 | if currentInput then
103 | currentInput:onKey( button, press )
104 | else
105 | killTimer( stickyTimer )
106 | end
107 | end, currentInput.sticky, 0 )
108 | end
109 | end
110 | currentInput:onKey( button, press )
111 | end
112 | end )
113 |
--------------------------------------------------------------------------------
/dxGUI/Cursor.lua:
--------------------------------------------------------------------------------
1 |
2 | dxGUI.baseClass:subclass{
3 | type = 'cursor';
4 | rotation = 0;
5 | rotationCenterOffsetX = 0;
6 | rotationCenterOffsetY = 0;
7 | color = 0xFFFFFFFF;
8 | postGUI = false;
9 |
10 | reguestScreen = true;
11 |
12 | create = function( self )
13 |
14 | self:setStatus( 'default' )
15 | self.draw = self.drawImage
16 |
17 | return self
18 | end;
19 |
20 | drawImage = function( self )
21 | dxDrawImage( self.x, self.y, self.w, self.h, self.image, self.rotation,
22 | self.rotationCenterOffsetX, self.rotationCenterOffsetY, self.color, self.postGUI )
23 | end;
24 |
25 | syncedDraw = function( self )
26 | local rX, rY = getCursorPosition()
27 | if rX then
28 | dxDrawImage( rX * dxConstruction.screen.w, rY * dxConstruction.screen.h, self.w, self.h, self.image, self.rotation,
29 | self.rotationCenterOffsetX, self.rotationCenterOffsetY, self.color, self.postGUI )
30 | end
31 | end;
32 |
33 | replaceMTACursor = function( self, state )
34 | if state == true then
35 | if self.draw == self.drawImage then
36 | self:addEventHandler( "onClientResourceStop", resourceRoot, self.onResourceStop )
37 | end
38 | self.draw = self.syncedDraw
39 | setCursorAlpha( 0 )
40 | else
41 | if self.draw == self.syncedDraw then
42 | self:removeEventHandler( "onClientResourceStop", resourceRoot, self.onResourceStop )
43 | end
44 | self.draw = self.drawImage
45 | setCursorAlpha( 255 )
46 | end
47 | end;
48 |
49 | setStatus = function( self, status )
50 | if self.status == status then
51 | return false
52 | end
53 | if self.statusSounds and self.statusSounds[status] then
54 | local sound = Sound( self.statusSounds[status] )
55 | if sound then
56 | addEventHandler( 'onClientSoundStopped', sound, function( )
57 | sound:destroy()
58 | end )
59 | end
60 | end
61 | if self.stats and self.stats[status] then
62 | self.status = status
63 | table.unite( self, self.stats[status], true )
64 | return true
65 | end
66 | return false
67 | end;
68 |
69 | setShow = function( self, show )
70 | if type( show ) == 'boolean' then
71 | if not show then
72 | for _, object in pairs( self.screen.plane ) do
73 | if object.needCursor and object:isShow() then
74 | return false
75 | end
76 | end
77 | end
78 | self.show = show
79 | if self.draw == self.syncedDraw then
80 | showCursor( show )
81 | setCursorAlpha( show and 0 or 255 )
82 | end
83 | return true
84 | end
85 | return false
86 | end;
87 |
88 | onResourceStop = function( self )
89 | self:setShow( false )
90 | setCursorAlpha( 255 )
91 | end;
92 |
93 | }
94 |
95 | Anim{
96 | name = 'cursor_aways_on_top';
97 |
98 | create = function( self )
99 | return self
100 | end;
101 |
102 | update = function( self, cursor )
103 | local screenPlane = cursor.screen.plane
104 | if screenPlane[ #screenPlane ] ~= cursor then
105 | table.removeValue( screenPlane, cursor )
106 | table.insert( screenPlane, cursor )
107 | end
108 | return true
109 | end;
110 |
111 | }
--------------------------------------------------------------------------------
/dxGUI/Slider.lua:
--------------------------------------------------------------------------------
1 | dxConstruction:subclass{
2 | type = 'slider';
3 | direction = false; -- false = horizontal; true = vertical
4 | resize = false;
5 | deadZone = 0;
6 |
7 | create = function( self )
8 | if not dxConstruction.create( self, false ) then
9 | return false
10 | end
11 |
12 | self:setProgress( self.progress or 0.5 )
13 | self.input = Input:create{ }
14 | return self
15 | end;
16 |
17 | setProgress = function( self, progress )
18 | if progress and progress >= 0 and progress <= 1 then
19 | progress = math.min( 1, math.max( progress, 0 ) )
20 | self.progress = progress
21 | local slider = self.objects.slider
22 | local bar = self.objects.bar or self
23 |
24 | if self.resize and self.list then
25 | if self.direction then
26 | slider:setSize( slider.w, 1 / ( math.max( #self.list.items - math.ceil( self.list.h / self.list.construction.h ), 0 ) + 1 ) * self.h * ( 1 - self.deadZone ) )
27 | else
28 | slider:setSize( 1 / ( #self.list.items - math.ceil( self.list.w / self.list.construction.w ) + 1 ) * self.h, slider.h )
29 | end
30 | end
31 |
32 | if self.direction then
33 | slider:setPosition( slider.x, self.y + ( 1 - self.deadZone ) * ( self.h - slider.h ) * progress + self.h * self.deadZone / 2 )
34 | else
35 | slider:setPosition( self.x + ( 1 - self.deadZone ) * self.w * progress + self.w * self.deadZone / 2 - slider.w / 2, slider.y )
36 | end
37 |
38 | if self.onChangeProgress then
39 | self:onChangeProgress( progress )
40 | end
41 | end
42 | end;
43 |
44 | getProgress = function( self )
45 | return self.progress
46 | end;
47 |
48 | onClick = function( self, button, state, cX, cY )
49 | local bar = self.objects.bar
50 | if self.direction then
51 | self:setProgress( math.max( 0, math.min( 1, ( cY - bar.y - bar.h * self.deadZone / 2 ) / ( bar.h - bar.h * self.deadZone ) ) ) )
52 | else
53 | self:setProgress( math.max( 0, math.min( 1, ( cX - bar.x - bar.w * self.deadZone / 2 ) / ( bar.w - bar.w * self.deadZone ) ) ) )
54 | end
55 | end;
56 |
57 | onCursorMove = function( self, inBox, cX, cY )
58 | if getKeyState( 'mouse1' ) then
59 | local bar = self.objects.bar
60 | if self.direction then
61 | self:setProgress( math.max( 0, math.min( 1, ( cY - bar.y - bar.h * self.deadZone / 2 ) / ( bar.h - bar.h * self.deadZone ) ) ) )
62 | else
63 | self:setProgress( math.max( 0, math.min( 1, ( cX - bar.x - bar.w * self.deadZone / 2 ) / ( bar.w - bar.w * self.deadZone ) ) ) )
64 | end
65 | end
66 | end;
67 |
68 | attachList = function( self, list )
69 | if self.list then
70 | for id, anim in pairs( self.preAnims ) do
71 | if anim.name == 'slider-list-sync' then
72 | self:removeAnim( id )
73 | break
74 | end
75 | end
76 | self.list = list
77 | end
78 | if list then
79 | local anim = Anim.find( 'slider-list-sync' )
80 | self:addAnim( anim, list )
81 | self.list = list
82 | end
83 | end;
84 | }
85 |
86 | Anim{
87 | name = 'slider-list-sync';
88 |
89 | create = function( self, gui, list )
90 | self.list = list
91 | gui.onChangeProgress = function( gui, progress )
92 | local maxOffsetY = list.maxHorizontalItems and ( math.ceil( #list.items / list.maxHorizontalItems ) * list.construction.h - list.h ) or 0
93 | self.list.offsetY = maxOffsetY * progress
94 | end
95 | return self
96 | end;
97 |
98 | update = function( self, gui )
99 | local list = self.list
100 | local maxOffsetY = list.maxHorizontalItems and ( math.ceil( #list.items / list.maxHorizontalItems ) * list.construction.h - list.h ) or 0
101 | local onChange = self.onChangeProgress
102 | self.onChangeProgress = nil
103 | if maxOffsetY > 0 then
104 | gui:setProgress( list.offsetY / maxOffsetY )
105 | else
106 | gui:setProgress( 0 )
107 | end
108 | self.onChangeProgress = onChange
109 | return true
110 | end;
111 | }
112 |
--------------------------------------------------------------------------------
/dxGUI/Blur.lua:
--------------------------------------------------------------------------------
1 |
2 | local blurShader
3 |
4 | local screenSources = {}
5 |
6 | dxGUI.baseClass:subclass{
7 | type = 'blur';
8 | rotation = 0;
9 | rotationCenterOffsetX = 0;
10 | rotationCenterOffsetY = 0;
11 | color = 0xFFFFFFFF;
12 | postGUI = false;
13 |
14 | blurFactor = 0.9;
15 | screenSourceRes = { 1.0, 1.0 };
16 |
17 | drawScreen = true;
18 | reguestScreen = true;
19 | autoUpdate = true;
20 |
21 | create = function( self )
22 |
23 | if not self:loadShaders( )
24 | or not self:loadScreenSource()
25 | then
26 | return false
27 | end
28 |
29 | if self.autoUpdate then
30 | self:addAnim( Anim.find( 'blur-autoupdate' ) )
31 | end
32 |
33 | return self
34 | end;
35 |
36 | loadShaders = function( self )
37 | if not blurShader then
38 | blurShader = dxCreateShader( "dxGUI/shaders/blur.fx" )
39 | end
40 |
41 | if not blurShader then
42 | outputDebugString( 'Can`t create blur shaders', 2 )
43 | return false
44 | end
45 |
46 | return true
47 | end;
48 |
49 | loadScreenSource = function( self )
50 | local ssW = dxConstruction.screen.w * self.screenSourceRes[1]
51 | local ssH = dxConstruction.screen.h * self.screenSourceRes[2]
52 | local blurFactor = self.blurFactor
53 |
54 | self.screenSource = nil
55 |
56 | for screenSource, data in pairs( screenSources ) do
57 | if data[1] == ssW and data[2] == ssH and data[3] == blurFactor then
58 | self.screenSource = screenSource
59 | break;
60 | end
61 | end
62 |
63 | if not self.screenSource then
64 | self.screenSource = dxCreateScreenSource( ssW, ssH )
65 | if not self.screenSource then
66 | outputDebugString( 'Can`t create screen source', 2 )
67 | return false
68 | end
69 | local renderTarget = DxRenderTarget( ssW, ssH, false )
70 | if not renderTarget then
71 | outputDebugString( 'Can`t create render target', 2 )
72 | return false
73 | end
74 |
75 | dxUpdateScreenSource( self.screenSource, true )
76 | screenSources[self.screenSource] = { ssW, ssH, self.blurFactor, true, renderTarget }
77 | end
78 |
79 | return screenSources[self.screenSource]
80 | end;
81 |
82 | draw = function( self )
83 | local screenSourceData = screenSources[self.screenSource] or self:loadScreenSource()
84 |
85 | if screenSourceData then
86 | screenSourceData[4] = true
87 | dxDrawImage( self.x, self.y, self.w, self.h, screenSourceData[5], self.rotation,
88 | self.rotationCenterOffsetX, self.rotationCenterOffsetY, self.color, self.postGUI )
89 | end
90 | end;
91 |
92 | updateBlur = function( self )
93 | local screenSourceData = screenSources[self.screenSource] or self:loadScreenSource()
94 |
95 | if not screenSourceData then
96 | return false
97 | end
98 |
99 | if not screenSourceData[4] then
100 | screenSourceData[4] = true
101 | dxUpdateScreenSource( self.screenSource, false )
102 | end
103 |
104 | local blurRenderTarget = screenSourceData[5]
105 |
106 | blurRenderTarget:setAsTarget( true )
107 |
108 | local ssW = dxConstruction.screen.w * self.screenSourceRes[1]
109 | local ssH = dxConstruction.screen.h * self.screenSourceRes[2]
110 |
111 | dxDrawImage( 0, 0, ssW, ssH, self.screenSource )
112 |
113 | if self.drawScreen and self.screen then
114 | local plane = self.screen:getObjectPlane( self )
115 | for i = 1, plane - 1 do
116 | self.screen.plane[i]:draw()
117 | end
118 | end
119 |
120 | blurShader:setValue( 'texture0', blurRenderTarget )
121 |
122 | for i = 0, 8 do
123 | blurShader:setValue('factor', 0.0020 * self.blurFactor + (i / 8 * 0.001 * self.blurFactor) )
124 | dxDrawImage( 0, 0, ssW, ssH, blurShader )
125 | end
126 |
127 | DxRenderTarget.setAsTarget()
128 | end;
129 | }
130 |
131 | Anim{
132 | name = 'blur-autoupdate';
133 |
134 | create = function( self )
135 | return self
136 | end;
137 |
138 | update = function( self, blur )
139 | blur:updateBlur()
140 | return true
141 | end;
142 | }
143 |
144 | addEventHandler( 'onClientRender', root, function()
145 | local toRemove = {}
146 | for screenSource, data in pairs( screenSources ) do
147 | if data[4] then
148 | data[4] = false
149 | else
150 | screenSource:destroy()
151 | data[5]:destroy()
152 | table.insert( toRemove, screenSource )
153 | end
154 | end
155 | for i = 1, #toRemove do
156 | screenSources[ toRemove[i] ] = nil
157 | end
158 | end, true, 'low-999' )
159 |
--------------------------------------------------------------------------------
/classutils.lua:
--------------------------------------------------------------------------------
1 |
2 | local rawget = rawget
3 | local setmetatable = setmetatable
4 |
5 | local class_mt = {}
6 |
7 | function class_mt:inherit( newClass )
8 | newClass = newClass or {}
9 | newClass.__parent = self
10 | return setmetatable( newClass, class_mt )
11 | end
12 |
13 | function class_mt:__index( key )
14 | local get = rawget( self, key )
15 | if get == nil then
16 | return rawget( self, '__parent' )[key]
17 | else
18 | return get
19 | end
20 | end
21 |
22 | function class_mt:__call( ... )
23 | local child = setmetatable( {}, class_mt )
24 | child.__parent = self
25 | if not child.create then
26 | self:error( 'Can not find constructor for class', 2 )
27 | return false
28 | end
29 | return child:create( ... )
30 | end
31 |
32 | function class_mt:error( str, level )
33 | outputDebugString( str, level or 3 )
34 | outputDebugString( debug.traceback() )
35 | end
36 |
37 | function class( newClass )
38 | newClass = newClass or {}
39 | newClass.__index = newClass
40 | newClass.__parent = class_mt
41 | return setmetatable( newClass, class_mt )
42 | end
43 |
44 | -- Event interfeace
45 |
46 | -- Метатаблица таблиц со слабыми ключами
47 | local weakMetatable = { __mode = 'k' }
48 |
49 | function class_mt:addEventHandler( eventName, element, _function, ... )
50 | -- Таблица будет очищатся сборщиком мусора
51 | self.__bindetFunctions = self.__bindetFunctions or setmetatable( {}, weakMetatable )
52 | local bind = self.__bindetFunctions[_function] or function( ... )
53 | _function( self, ... )
54 | end
55 | self.__bindetFunctions[_function] = bind
56 | addEventHandler( eventName, element, bind, ... )
57 | end
58 |
59 | function class_mt:removeEventHandler( eventName, element, _function )
60 | if removeEventHandler( eventName, element, self.__bindetFunctions[_function] ) then
61 | else
62 | self:error( 'Can not remove event handler', 2 )
63 | end
64 | end
65 |
66 | -- RPC interfeace
67 |
68 | addEvent( 'RPC', true ) -- server ==> client, client ==> server with out callback
69 | addEvent( 'RPCC', true ) -- server ==> client, client ==> server with callback
70 | addEvent( 'RPCR', true ) -- callback event
71 |
72 | function class_mt:createRPC( element )
73 |
74 | if self.__RPC then
75 | self:error( 'Unexpected creation of a new calling interfeace' )
76 | return false
77 | else
78 | element = element or createElement( 'RPC' )
79 | self.__RPC = element
80 | end
81 |
82 | self:addEventHandler( 'RPC', element, self.__RPC_Handler )
83 | self:addEventHandler( 'RPCC', element, self.__RPCC_Handler )
84 | self:addEventHandler( 'RPCR', element, self.__RPCR_Handler )
85 |
86 | return self.__RPC
87 | end
88 |
89 | function class_mt:__RPC_Handler( functionName, ... )
90 | self[functionName]( self, ... )
91 | end
92 |
93 | function class_mt:__RPCR_Handler( callID, ... )
94 | local callHandlers = self.__callHandlers
95 | if callHandlers then
96 | for i = #callHandlers, 1, -1 do
97 | if callHandlers[i][1] == callID then
98 | callHandlers[i][2]( ... )
99 | end
100 | end
101 | end
102 | end
103 |
104 | if triggerServerEvent then
105 | -- Client side
106 |
107 | function class_mt:RPC( functionName, ... )
108 | triggerServerEvent( 'RPC', self.__RPC, functionName, ... )
109 | end
110 |
111 | function class_mt:RPCC( callID, functionName, ... )
112 | triggerServerEvent( 'RPCC', self.__RPC, callID, functionName, ... )
113 | end
114 |
115 | function class_mt:__RPCC_Handler( callID, functionName, ... )
116 | triggerServerEvent( 'RPCR', self.__RPC, callID, self[functionName]( self, ... ) )
117 | end
118 |
119 | else
120 | -- Server side
121 |
122 | function class_mt:RPC( players, functionName, ... )
123 | triggerClientEvent( players, 'RPC', self.__RPC, functionName, ... )
124 | end
125 |
126 | function class_mt:RPCC( callID, players, functionName, ... )
127 | triggerClientEvent( players, 'RPC', self.__RPC, callID, functionName, ... )
128 | end
129 |
130 | function class_mt:__RPCC_Handler( callID, functionName, ... )
131 | triggerClientEvent( client, 'RPCR', self.__RPC, callID, self[functionName]( self, ... ) )
132 | end
133 |
134 | end
135 |
136 | function class_mt:addCallHandler( callID, _funct )
137 | local callHandlers = self.__callHandlers
138 | if callHandlers then
139 | else
140 | callHandlers = {}
141 | self.__callHandlers = callHandlers
142 | end
143 | table.insert( callHandlers, { callID, _funct } )
144 | return true
145 | end
146 |
147 | function class_mt:removeCallHandler( callID, _funct )
148 | local callHandlers = self.__callHandlers
149 | if callHandlers then
150 | else
151 | return false
152 | end
153 | if _funct then
154 | for i = #callHandlers, 1, -1 do
155 | if callHandlers[i][1] == callID and callHandlers[i][2] == _funct then
156 | table.remove( callHandlers, i )
157 | end
158 | end
159 | else
160 | for i = #callHandlers, 1, -1 do
161 | if callHandlers[i][1] == callID then
162 | table.remove( callHandlers, i )
163 | end
164 | end
165 | end
166 | return true
167 | end
168 |
169 | function class_mt:removeRPC()
170 | if self.__RPC then
171 | self:removeEventHandler( 'RPC', self.__RPC, self.__RPC_Handler )
172 | self:removeEventHandler( 'RPCC', self.__RPC, self.__RPCC_Handler )
173 | self:removeEventHandler( 'RPCR', self.__RPC, self.__RPCR_Handler )
174 |
175 | self.__RPC = nil
176 | return true
177 | end
178 | return false
179 | end
180 |
181 | function class_mt:destroyRPC( )
182 | self.__RPC:destroy()
183 | self.__RPC = nil
184 | end
185 |
186 | --
--------------------------------------------------------------------------------
/dxGUI/baseClass.lua:
--------------------------------------------------------------------------------
1 | dxGUI = {
2 |
3 | baseClass = class{
4 |
5 | __call = function( self, newObject, ... )
6 | setmetatable( newObject, self )
7 | return newObject:create( ... )
8 | end;
9 |
10 | create = function( self )
11 | if not ( self.x or self.y or self.w or self.h ) then
12 | return false
13 | end
14 | return self
15 | end;
16 |
17 | subclass = function( self, newClass )
18 | newClass.__index = newClass
19 | self.__call = self.__call
20 | setmetatable( newClass, self )
21 | if newClass.type then
22 | dxGUI[ newClass.type ] = newClass
23 | end
24 | return newClass
25 | end;
26 |
27 | setPosition = function( self, x, y )
28 | if not ( x or y ) then
29 | return false
30 | end
31 | self.x, self.y = x, y
32 | return true
33 | end;
34 |
35 | getPosition = function( self )
36 | return self.x, self.y
37 | end;
38 |
39 | setSize = function( self, w, h )
40 | if type( w ) == 'number' then
41 | self.w = w
42 | end
43 | if type( h ) == 'number' then
44 | self.h = h
45 | end
46 | end;
47 |
48 | getSize = function( self )
49 | return self.w, self.h
50 | end;
51 |
52 | setShow = function( self, show )
53 | if type( show ) == 'boolean' then
54 | self.show = show
55 | return true
56 | end
57 | return false
58 | end;
59 |
60 | isShow = function( self )
61 | return self.show
62 | end;
63 |
64 | isOnCursor = function( self )
65 | local cX, cY = getCursorPosition( )
66 | if not cX then return false; end
67 | local sX, sY = guiGetScreenSize()
68 | cX, cY = cX * sX, cY * sY -- To absolute value
69 | if self.x < cX and self.x + self.w > cX and self.y < cY and self.y + self.h > cY then
70 | return true
71 | end
72 | return false
73 | end;
74 |
75 | setColor = function( self, ... )
76 | local arg = { ... }
77 | local color
78 | if #arg == 1 then
79 | color = ...
80 | else
81 | color = tocolor( ... )
82 | end
83 | if not color then
84 | return false
85 | end
86 | self.color = color
87 | return true
88 | end;
89 |
90 | getColor = function( self )
91 | return self.color or 0xFFFFFFFF
92 | end;
93 |
94 | setAlpha = function( self, alpha )
95 | if type( alpha ) ~= 'number' then
96 | return false
97 | end
98 | alpha = math.ceil( alpha )
99 | if self.color then
100 | local r, g, b = color.HEXtoRGB( self:getColor() )
101 | self.color = tocolor( r, g, b, alpha )
102 | return true
103 | elseif self.alpha then
104 | self.alpha = alpha
105 | return true
106 | else
107 | return false
108 | end
109 | end;
110 |
111 | getAlpha = function( self )
112 | if self.getColor then
113 | -- math.floor( self:getColor() / 2^24 )
114 | return bitExtract( self:getColor(), 24, 8 )
115 | elseif self.alpha then
116 | return self.alpha
117 | else
118 | return 255
119 | end
120 | end;
121 |
122 | setPostGUI = function( self, state )
123 | if state == true or state == false then
124 | self.postGUI = state
125 | return true
126 | end
127 | return false
128 | end;
129 |
130 | getPostGUI = function( self )
131 | return self.postGUI == true
132 | end;
133 |
134 | addAnim = function( self, anim, ... )
135 | self.preAnims = self.preAnims or {}
136 | local loadedAnim = anim( self, ... )
137 | if loadedAnim then
138 | table.insert( self.preAnims, loadedAnim )
139 | return loadedAnim
140 | else
141 | self:errorHandler( 'Can not load anim "' .. tostring( anim.name ) .. '"' )
142 | return false
143 | end
144 | end;
145 |
146 | removeAnim = function( self, id, reason )
147 | local anim = self.preAnims[id]
148 | if anim then
149 | if anim.onStop then
150 | anim:onStop( self, reason )
151 | end
152 | self.preAnims[id] = nil
153 | end
154 | end;
155 |
156 | removeAllAnims = function( self )
157 | if self.preAnims then
158 | for i = 1, #self.preAnims do
159 | self:removeAnim( i, 'removed' )
160 | end
161 | return true
162 | end
163 | return false
164 | end;
165 |
166 | updateAnims = function( self )
167 | if not self.preAnims then return false; end
168 | for i, anim in pairs( self.preAnims ) do
169 | if not anim:update( self ) then
170 | self:removeAnim( i, 'finished' )
171 | end
172 | end
173 | end;
174 |
175 | setStatus = function( self, status )
176 | if self.status == status then
177 | return false
178 | end
179 | if self.statusSounds and self.statusSounds[status] then
180 | local sound = Sound( self.statusSounds[status] )
181 | if sound then
182 | addEventHandler( 'onClientSoundStopped', sound, function( )
183 | sound:destroy()
184 | end )
185 | end
186 | end
187 | if self.stats and self.stats[status] then
188 | self.status = status
189 | table.unite( self.objects, self.stats[status], true )
190 | return true
191 | end
192 | return false
193 | end;
194 |
195 | getStatus = function( self, status )
196 | return self.status or 'default'
197 | end;
198 |
199 | createTexture = function( self, callback )
200 | local render = DxRenderTarget( self.w, self.h, true )
201 | if not render then
202 | callback( false )
203 | return false
204 | end
205 |
206 | local draw = self.draw
207 |
208 | local skipFrame = false
209 | self.draw = function( self )
210 | if skipFrame then
211 | local pixels = dxGetTexturePixels( render )
212 | dxDrawImage( self.x, self.y, self.w, self.h, render )
213 | destroyElement( render )
214 |
215 | self.draw = draw
216 |
217 | callback( dxCreateTexture( pixels ) )
218 | else
219 | local x, y = self:getPosition()
220 | local show = self.show
221 |
222 | self.show = true
223 |
224 | render:setAsTarget( true, x, y )
225 | dxSetBlendMode( 'modulate_add' )
226 |
227 | draw( self )
228 |
229 | dxSetBlendMode( 'blend' )
230 | dxSetRenderTarget( )
231 |
232 | if show then
233 | dxSetBlendMode( 'add' )
234 | dxDrawImage( self.x, self.y, self.w, self.h, render )
235 | dxSetBlendMode( 'blend' )
236 | end
237 |
238 | self.show = show
239 | skipFrame = true
240 | end
241 | end
242 |
243 | return true
244 | end;
245 |
246 | toImage = function( self )
247 | return self:createTexture(
248 | function( texture )
249 | self.image = texture
250 | self.type = 'image'
251 | self.draw = dxGUI.image.draw
252 | setmetatable( self, dxGUI.image )
253 | end
254 | )
255 | end;
256 |
257 | errorHandler = function( self, message )
258 | iprint( 'dxGUI:', message )
259 | end;
260 |
261 | };
262 |
263 | }
264 |
--------------------------------------------------------------------------------
/dxGUI/TextLines.lua:
--------------------------------------------------------------------------------
1 | dxGUI.baseClass:subclass{
2 | type = 'textLines';
3 | text = '';
4 | w = 0;
5 | h = 0;
6 | color = 0xFF000000;
7 | scale = 1;
8 | font = 'default';
9 | alignX = 'left'; -- Can be "left", "center" or "right"
10 | alignY = 'top'; -- Can be "top", "center" or "bottom"
11 | clip = false;
12 | wordBreak = false;
13 | postGUI = false;
14 | colorCoded = false;
15 | subPixelPositioning = false;
16 | lineSpacing = 1.0;
17 | offsetY = 0;
18 |
19 | invert = false;
20 |
21 | create = function( self )
22 | self.lines = {}
23 | self.fontSize = dxGetFontHeight( self.scale, self.font )
24 | self:setText( self.text )
25 |
26 | return self
27 | end;
28 |
29 | draw = function( self )
30 | local lineSpace = self.fontSize * self.lineSpacing
31 |
32 | local offY = self.offsetY % lineSpace
33 | local itemOffsetY = self.offsetY / self.fontSize
34 |
35 | local drawFunction, postGUI
36 |
37 | if self.invert then
38 | drawFunction = function( x, y )
39 | for i = 1, math.min( #self.lines - math.floor( self.offsetY / lineSpace ), math.ceil( (self.h + offY) / lineSpace ) ) do
40 | dxDrawText( self.lines[math.floor(i + self.offsetY/lineSpace)], x, y + self.h - lineSpace * i, x + self.w, y + self.h - lineSpace * (i - 1), self.color, self.scale, self.font, self.alignX, self.alignY,
41 | self.clip, self.wordBreak, postGUI, self.colorCoded, self.subPixelPositioning )
42 | end
43 | end
44 | else
45 | drawFunction = function( x, y )
46 | for i = 1, math.min( #self.lines - math.floor( self.offsetY / lineSpace ), math.ceil( (self.h + offY) / lineSpace ) ) do
47 | dxDrawText( self.lines[math.floor(i + self.offsetY/lineSpace)], x, y + lineSpace * (i - 1), x + self.w, y + lineSpace * i, self.color, self.scale, self.font, self.alignX, self.alignY,
48 | self.clip, self.wordBreak, postGUI, self.colorCoded, self.subPixelPositioning )
49 | end
50 | end
51 | end
52 |
53 |
54 | if offY == 0 and self.h % lineSpace == 0 then
55 | if self.renderTarget then
56 | self.renderTarget:destroy()
57 | self.renderTarget = nil
58 | postGUI = self.postGUI
59 | end
60 | drawFunction( self.x, self.y )
61 | else
62 | if not self.renderTarget then
63 | self.renderTarget = DxRenderTarget( self.w, self.h, true )
64 | postGUI = false
65 | end
66 | if self.renderTarget then
67 | local prevRenderTarget = DxRenderTarget.getCurrentTarget()
68 | self.renderTarget:setAsTarget( true )
69 | dxSetBlendMode( 'modulate_add' )
70 | drawFunction( 0, offY )
71 |
72 | dxSetBlendMode( 'add' )
73 | DxRenderTarget.setAsTarget( prevRenderTarget )
74 | dxDrawImage( self.x, self.y, self.w, self.h, self.renderTarget, 0,
75 | 0, 0, 0xFFFFFFFF, self.postGUI )
76 | dxSetBlendMode( 'blend' )
77 | else
78 | dxDrawText( 'NO VIDEO MEMORY FOR CREATE RENDERTARGET', self.x, self.y, self.x + self.w, self.y + self.h, 5 )
79 | end
80 | end
81 | end;
82 |
83 | getText = function( self )
84 | return self.text
85 | end;
86 |
87 | setText = function( self, text )
88 | if type( text ) == 'number' then
89 | text = tostring( text )
90 | end
91 | if type( text ) == 'string' then
92 | self.text = text
93 |
94 | self.lines = self:splitText( text )
95 | return true
96 | end
97 |
98 | return false
99 | end;
100 |
101 | addText = function( self, text )
102 | if type( text ) == 'number' then
103 | text = tostring( text )
104 | end
105 | if type( text ) == 'string' then
106 | self.text = self.text .. '\n' .. text
107 | local currentLines = self.lines
108 | local newLines = self:splitText( text )
109 | if self.invert then
110 | table.reverse( newLines )
111 | end
112 | local offset = #newLines
113 | for i = 1, #currentLines do
114 | newLines[i + offset] = currentLines[i]
115 | end
116 | self.lines = newLines
117 | return true
118 | end
119 |
120 | return false
121 | end;
122 |
123 | addLine = function( self, text, pos )
124 | if type( text ) == 'number' then
125 | text = tostring( text )
126 | end;
127 | if type( text ) == 'string' then
128 | table.insert( self.lines, pos or (#self.lines + 1), text )
129 | return true
130 | end
131 | return false
132 | end;
133 |
134 | splitText = function( self, text )
135 | local spaceSize = dxGetTextWidth( ' ', self.scale, self.font, self.colorCoded )
136 |
137 | local lines = { }
138 | local currentLine = 0
139 | local currnetLineSize = 0
140 | local lastColor
141 |
142 | local function splitWord( word )
143 | if currnetLineSize ~= 0 then
144 | currentLine = currentLine + 1
145 | lines[currentLine] = self.colorCoded and lastColor or ''
146 | currnetLineSize = 0
147 | end
148 |
149 | local wordSize = #word
150 | for i = wordSize, 1, -1 do
151 | local subWord = word:sub( 1, i )
152 | if dxGetTextWidth( subWord, self.scale, self.font, self.colorCoded ) <= self.w then
153 | lines[currentLine] = ( currnetLineSize == 0 and word ) or ( lines[currentLine] .. ' ' .. word )
154 | if self.colorCoded then
155 | local _, _, findColor = subWord:reverse():find( '(%x%x%x%x%x%x#)' )
156 | lastColor = findColor and findColor:reverse() or lastColor
157 | end
158 | local word2 = word:sub( i, wordSize )
159 | local word2Size = dxGetTextWidth( word2, self.scale, self.font, self.colorCoded )
160 | if word2Size <= self.w then
161 | if currnetLineSize == 0 then
162 | if self.colorCoded and lastColor then
163 | lines[currentLine] = lastColor .. word
164 | else
165 | lines[currentLine] = word
166 | end
167 | else
168 | lines[currentLine] = lines[currentLine] .. ' ' .. word
169 | end
170 | currnetLineSize = word2Size + spaceSize
171 | return
172 | else
173 | return splitWord( word2 )
174 | end
175 | end
176 | end
177 |
178 | end
179 | for block in utf8.gmatch( text, '([%w%p ]+\n?)' ) do
180 | currentLine = currentLine + 1
181 | lines[currentLine] = ''
182 | currnetLineSize = 0
183 | for word in utf8.gmatch( block, '([%w%p]+)' ) do
184 | local _, _, findColor = word:find( '(#%x%x%x%x%x%x)' )
185 | lastColor = findColor and findColor or lastColor
186 |
187 | local wordSize = dxGetTextWidth( word, self.scale, self.font, self.colorCoded )
188 | if wordSize + currnetLineSize <= self.w then
189 | --lines[currentLine] = ( currnetLineSize == 0 and word ) or ( lines[currentLine] .. ' ' .. word )
190 | lines[currentLine] = ( currnetLineSize == 0 and lines[currentLine] .. word ) or ( lines[currentLine] .. ' ' .. word )
191 |
192 | currnetLineSize = wordSize + currnetLineSize + spaceSize
193 | elseif wordSize <= self.w then
194 |
195 | currentLine = currentLine + 1
196 | --lines[currentLine] = self.colorCoded and
197 | currnetLineSize = 0
198 |
199 | lines[currentLine] = ( currnetLineSize == 0 and ( lastColor or '' ) .. word ) or ( lines[currentLine] .. ' ' .. word )
200 | currnetLineSize = wordSize + currnetLineSize + spaceSize
201 | else
202 | splitWord( word )
203 | end
204 | end
205 | end
206 | return lines
207 | end;
208 |
209 | move = function( self, countY )
210 | local maxOffsetY = (#self.lines - 1) * self.fontSize * self.lineSpacing - self.h
211 |
212 | if countY and countY ~= 0 and maxOffsetY > 0 then
213 | if self.offsetY + countY > maxOffsetY then
214 | self.offsetY = maxOffsetY
215 | elseif self.offsetY + countY < 0 then
216 | self.offsetY = 0
217 | else
218 | self.offsetY = self.offsetY + countY
219 | end
220 | end
221 | end;
222 |
223 | getLinesCount = function( self )
224 | return #self.lines
225 | end;
226 |
227 | getRealSize = function( self )
228 | return dxGetTextWidth( self.text, self.scale, self.font ),
229 | #self.lines * self.fontSize * self.lineSpacing
230 | end;
231 |
232 | setRealSize = function( self )
233 | self.w, self.h = self:getRealSize()
234 | end;
235 |
236 | setScale = function( self, scaleX, scaleY )
237 | self.w, self.h = self.w * scaleX, self.h * ( scaleY or scaleX )
238 | self.scale = scaleX * self.scale
239 | end;
240 |
241 | }
--------------------------------------------------------------------------------
/dxGUI/Edit.lua:
--------------------------------------------------------------------------------
1 | dxConstruction:subclass{
2 | type = 'editField';
3 | isBlocked = false;
4 | maxLen = 255;
5 | caret = 0;
6 | filter = false;
7 |
8 | create = function( self )
9 | --if not self.objects.text then
10 | -- return false
11 | --end
12 | if not dxConstruction.create( self, false ) then
13 | return false
14 | end
15 |
16 | if not self.objects.text then
17 | self:warning( 'Can not create edit field without text' )
18 | return false
19 | end
20 |
21 | --self.construction.objects.text.input = Input{
22 | self.input = Input()
23 | self.input.sticked = true
24 | self.input:bind( 'backspace', true, self.onUserDelete, self, -1 )
25 | self.input:bind( 'delete', true, self.onUserDelete, self, 1 )
26 | self.input:bind( 'arrow_l', true, self.onUserCareteMove, self, -1 )
27 | self.input:bind( 'arrow_r', true, self.onUserCareteMove, self, 1 )
28 | self.input:bind( 'home', true, self.moveCarete, self, -math.huge )
29 | self.input:bind( 'end', true, self.moveCarete, self, math.huge )
30 | self.input:bind( 'enter', true, self.input.deactivate, self.input )
31 |
32 | self.input.onCharacter = function( input, character )
33 | self:add( character )
34 | end;
35 |
36 | self.input.onPaste = function( input, text )
37 | self:add( text )
38 | end
39 |
40 | self:setPlaceholderText( self.placeholderText )
41 | self:setPlaceholderShow( self:getText() == '' )
42 |
43 | self.input.onActivate = function( input )
44 | self:setPlaceholderShow( false )
45 | self.beforeActiveText = self:getText()
46 | self.caret = utfLen( self:getText() )
47 | if self.isBlocked then
48 | input:deactivate()
49 | else
50 | if self:isOnCursor() then
51 | self:setStatus( 'oncursor_active' )
52 | else
53 | self:setStatus( 'active' )
54 | end
55 | if self.onStartEdit then
56 | self:onStartEdit()
57 | end
58 | end
59 | end;
60 |
61 | self.input.onDeactivate = function( input )
62 | if self.onFinishEdit then
63 | self:onFinishEdit( self:getText(), self.beforeActiveText )
64 | end
65 | if self:getText() == '' then
66 | self:setPlaceholderShow( true )
67 | end
68 | if self:isOnCursor() then
69 | self:setStatus( 'oncursor_dective' )
70 | else
71 | self:setStatus( 'default' )
72 | end
73 | end;
74 |
75 | self.defaultTextAlign = self.objects.text.alignX or 'left'
76 | self:setText( tostring( self.text or '' ), false )
77 | self.text = nil
78 | return self
79 | end;
80 |
81 | setText = function ( self, text, callEvent )
82 | local textObject = self.objects.text
83 | if textObject:setText( text ) then
84 | if textObject:getRealSize() >= textObject.w then
85 | textObject.alignX = 'right'
86 | else
87 | textObject.alignX = self.defaultTextAlign
88 | end
89 | self:setPlaceholderShow( text == '' and Input.get() ~= self.input )
90 |
91 | local textSize = utfLen( text )
92 | if textSize < self.caret then
93 | self.caret = textSize + 1
94 | end
95 |
96 | if self.onChanged and ( callEvent == nil or callEvent ) then
97 | self:onChanged( text )
98 | end
99 | end
100 | end;
101 |
102 | getText = function( self )
103 | return self.objects.text:getText( )
104 | end;
105 |
106 | setPlaceholderText = function( self, text )
107 | if self.objects.placeholder then
108 | return self.objects.placeholder:setText( text )
109 | end
110 | return true
111 | end;
112 |
113 | setPlaceholderShow = function( self, state )
114 | if self.objects.placeholder then
115 | return self.objects.placeholder:setShow( state )
116 | end
117 | return false
118 | end;
119 |
120 | getPlaceholderShow = function( self )
121 | if self.objects.placeholder then
122 | return self.objects.placeholder:isShow( )
123 | end
124 | return false
125 | end;
126 |
127 | setBlocked = function( self, isBlocked )
128 | if isBlocked == true or isBlocked == false then
129 | self.isBlocked = isBlocked
130 | if isBlocked then
131 | self.input:deactivate()
132 | self:setStatus( 'disabled' )
133 | else
134 | if self:isOnCursor() then
135 | self:setStatus( 'oncursor_dective' )
136 | else
137 | self:setStatus( 'default' )
138 | end
139 | end
140 | return true
141 | end
142 | return false
143 | end;
144 |
145 | moveCarete = function( self, count )
146 | local textSize = utfLen( self.objects.text:getText( ) )
147 | if self.caret + count < 0 then
148 | self.caret = 0
149 | elseif self.caret + count > textSize then
150 | self.caret = textSize
151 | else
152 | self.caret = self.caret + count
153 | end
154 | end;
155 |
156 | add = function( self, s )
157 | if type( s ) ~= 'string' then
158 | error( 'Bad argument #1 to "editField:add" (string expected got ' .. type( s ) .. ')', 2 )
159 | end
160 |
161 | if self.filter then
162 | s = s:gsub( self.filter, '' )
163 | end
164 |
165 | if s == '' then return false; end
166 | if self.isBlocked then return false; end
167 |
168 | local text = self:getText()
169 |
170 | local currentLen = utfLen( text )
171 | local stringLen = utfLen( s )
172 | if currentLen + stringLen > self.maxLen then
173 | s = utfSub( s, 0, self.maxLen - currentLen )
174 | stringLen = utfLen( s )
175 | end
176 | self:setText( utfSub( text, 0, self.caret ) .. s .. utfSub( text, self.caret + 1, currentLen ) )
177 | self.caret = self.caret + stringLen
178 | return true
179 | end;
180 |
181 | removeLeft = function( self, count )
182 | if self.caret == 0 then return false; end
183 | if self.isBlocked then return false; end
184 | local text = self:getText()
185 |
186 | count = self.caret - count >= 0 and count or self.caret
187 | local deleteTo = self.caret + 1
188 | self.caret = self.caret - count
189 |
190 | self:setText( utfSub( text, 0, self.caret ) .. utfSub( text, deleteTo, utfLen( text ) ) )
191 | return true
192 | end;
193 |
194 | removeRight = function( self, count )
195 | count = math.min( #self:getText() - self.caret, count )
196 | if count == 0 then return false; end
197 | if self.isBlocked then return false; end
198 | local text = self:getText()
199 |
200 | self:setText( utfSub( text, 0, self.caret ) .. utfSub( text, self.caret + count + 1, utfLen( text ) ) )
201 | return true
202 | end;
203 |
204 | setShow = function( self, show )
205 | if type( show ) == 'boolean' then
206 | if self.isBlocked then
207 | self:setStatus( 'disabled' )
208 | else
209 | if self.input:isActive() then
210 | if self:isOnCursor() then
211 | self:setStatus( 'oncursor_active' )
212 | else
213 | self:setStatus( 'active' )
214 | end
215 | else
216 | if self:isOnCursor() then
217 | self:setStatus( 'oncursor_dective' )
218 | else
219 | self:setStatus( 'default' )
220 | end
221 | end
222 | end
223 | self.show = show
224 | return true
225 | end
226 | return false
227 | end;
228 |
229 | onShow = function( self, show )
230 | if not show then
231 | if self.input:isActive() then
232 | self.input:deactivate()
233 | end
234 | end
235 | end;
236 |
237 | isFullWordMode = function( self )
238 | return getKeyState( 'lctrl' ) or getKeyState( 'rctrl' )
239 | end;
240 |
241 | isDeleteFullLineMode = function( self )
242 | return (getKeyState( 'lctrl' ) or getKeyState( 'rctrl' ))
243 | and (getKeyState( 'lshift' ) or getKeyState( 'rshift' ))
244 | end;
245 |
246 | onCursorMove = function( self, inStatus )
247 | if self.isBlocked then
248 |
249 | else
250 | if inStatus then
251 | if self.input:isActive() then
252 | self:setStatus( 'oncursor_active' )
253 | else
254 | self:setStatus( 'oncursor_dective' )
255 | end
256 | else
257 | self:setStatus( 'default' )
258 | end
259 | end
260 | end;
261 |
262 | getUserMoveCount = function( self, pos )
263 | if self:isFullWordMode() then
264 | local text = self:getText()
265 | local spacePatterg
266 | local wordPattern
267 |
268 | if pos > 0 then
269 | spacePatterg = '^%s+(.-)$'
270 | wordPattern = '^%w+(.-)$'
271 | text = utf8.sub( text, self.caret + 1 )
272 | else
273 | spacePatterg = '^(.-)%s+$'
274 | wordPattern = '^(.-)%w+$'
275 | text = utf8.sub( text, 0, self.caret )
276 | end
277 |
278 | local len = utf8.len(text)
279 |
280 | -- Remove spaces only
281 | local newStr = utf8.match( text, spacePatterg )
282 | if newStr then
283 | return (len - utf8.len( newStr )) * pos
284 | end
285 |
286 | -- Remove whole word
287 | local newStr = utf8.match( text, wordPattern )
288 | if newStr then
289 | return (len - utf8.len( newStr )) * pos
290 | end
291 | end
292 |
293 | return pos
294 | end;
295 |
296 | onUserCareteMove = function( self, pos )
297 | self:moveCarete( self:getUserMoveCount( pos ) )
298 | end;
299 |
300 | onUserDelete = function( self, pos )
301 | local count
302 | if self:isDeleteFullLineMode() then
303 | count = math.huge * pos
304 | else
305 | count = self:getUserMoveCount( pos )
306 | end
307 |
308 | if count > 0 then
309 | self:removeRight( count )
310 | else
311 | self:removeLeft( -count )
312 | end
313 | end;
314 |
315 | onClick = function( self, button, state, cX, cY )
316 | dxConstruction.onClick( self, button, state, cX, cY )
317 |
318 | local textObject = self.objects.text
319 |
320 | local text = textObject:getText()
321 |
322 | local startTextX = textObject:getPosition()
323 | if textObject.alignX == "center" then
324 | startTextX = startTextX + (textObject.w - textObject:getRealSize()) / 2
325 | elseif textObject.alignX == "right" then
326 | startTextX = startTextX + textObject.w - textObject:getRealSize()
327 | end
328 |
329 | local dxGetTextWidth = dxGetTextWidth
330 | local utfSub = utfSub
331 | local scale = textObject.scale
332 | local font = textObject.font
333 | local colorCoded = textObject.colorCoded
334 |
335 | local newCaretePos = math.getChekedValueInRange( 0, utfLen( text ),
336 | function( value )
337 | return ( startTextX + dxGetTextWidth( utfSub( text, 0, value ), scale, font, colorCoded )
338 | - dxGetTextWidth( utfSub( text, value - 1, value ), scale, font, colorCoded ) / 4 ) <= cX
339 | end
340 | )
341 |
342 | self.caret = newCaretePos
343 | end;
344 | }
345 |
346 | Anim{
347 | name = 'Carete';
348 |
349 | create = function( self, gui, time, r, g, b )
350 | self.time = time or 500
351 | if r and g and b then
352 | self.vColor = { r, g, b }
353 | else
354 | self.vColor = { color.HEXtoRGB( gui.objects.text:getColor() ) }
355 | end
356 |
357 | local caret = gui:initObject{ type = 'rectangle', x = 0, y = 0, w = 2 }
358 | gui:addObject( caret, 'caret' )
359 | return self
360 | end;
361 |
362 | update = function( self, gui )
363 | local textObject = gui.objects.text
364 |
365 | gui.objects.caret.show = gui.input == Input.get()
366 |
367 | local offsetX
368 | local caret = gui.caret
369 | if textObject.alignX == 'left' then
370 | offsetX = dxGetTextWidth( utfSub( gui.objects.text.text, 1, caret ), textObject.scale, textObject.font )
371 | elseif textObject.alignX == 'right' then
372 | local textSize = utfLen( gui.objects.text.text )
373 | offsetX = math.max( 0,
374 | textObject.w - dxGetTextWidth( utfSub( gui.objects.text.text, caret + 1, textSize ), textObject.scale, textObject.font )
375 | )
376 | else
377 | local textSize = dxGetTextWidth( gui.objects.text.text, textObject.scale, textObject.font )
378 | offsetX = dxGetTextWidth( utfSub( gui.objects.text.text, 1, caret ), textObject.scale, textObject.font ) + (textObject.w - textSize) / 2
379 | end
380 |
381 | local tH = dxGetFontHeight( textObject.scale, textObject.font )
382 |
383 | local alpha = math.sin( ( getTickCount() % self.time / self.time ) * math.pi ) * 255
384 |
385 | gui.objects.caret.x = textObject.x + offsetX
386 | gui.objects.caret.color = tocolor( self.vColor[1], self.vColor[2], self.vColor[3], alpha )
387 | gui.objects.caret.y = gui.objects.text.y + ( gui.objects.text.h - tH ) / 2
388 | gui.objects.caret.h = tH
389 |
390 | return true
391 | end;
392 | }
393 |
--------------------------------------------------------------------------------
/utilits.lua:
--------------------------------------------------------------------------------
1 |
2 | function table.find( t, value )
3 | for i, _value in pairs( t ) do
4 | if _value == value then
5 | return i
6 | end
7 | end
8 | return false
9 | end
10 |
11 | function table.findIn( t, In, value )
12 | if type( t ) ~= 'table' then
13 | error( 'Bad argument #1, got ' .. type( t ), 2 )
14 | end
15 | if In == nil then
16 | error( 'Bad argument #2, got nil', 2 )
17 | end
18 | for i, data in pairs( t ) do
19 | if data[In] == value then
20 | return i, data
21 | end
22 | end
23 | return false
24 | end
25 |
26 | function table.getSize( t )
27 | local i = 0
28 | local key = next( t )
29 | while key do
30 | i = i + 1
31 | key = next( t, key )
32 | end
33 | return i
34 | end
35 |
36 | function table.allFindIn( t, In, value )
37 | local resul = {}
38 | for i, data in pairs( t ) do
39 | if data[In] == value then
40 | table.insert( resul, i )
41 | end
42 | end
43 | return #resul ~= 0 and resul or false
44 | end
45 |
46 | function table.unite( table1, table2, recursive )
47 | if type( table1 ) ~= 'table' then
48 | error( 'Bad argument #1, got ' .. type( table1 ), 2 )
49 | end
50 | if type( table2 ) ~= 'table' then
51 | error( 'Bad argument #2, got ' .. type( table2 ), 2 )
52 | end
53 |
54 | if recursive then
55 | for key, value in pairs( table2 ) do
56 | if type( value ) == 'table' then
57 | table1[key] = table.unite( table1[key] or {}, value, true )
58 | else
59 | table1[key] = value;
60 | end
61 | end
62 | else
63 | for key, value in pairs( table2 ) do
64 | table1[key] = value;
65 | end
66 | end
67 |
68 | return table1
69 | end
70 |
71 | function table.uniteWithoutReplace( table1, table2 )
72 | if type( table1 ) ~= 'table' then
73 | error( 'Bad argument #1, got ' .. type( table1 ), 2 )
74 | end
75 | if type( table2 ) ~= 'table' then
76 | error( 'Bad argument #1, got ' .. type( table2 ), 2 )
77 | end
78 | for key, value in pairs( table2 ) do
79 | if type( value ) == 'table' then
80 | table1[key] = table.uniteWithoutReplace( table1[key] or {}, value )
81 | else
82 | table1[key] = table1[key] or value;
83 | end
84 | end
85 | return table1
86 | end
87 |
88 | function table.copy( t, recursive )
89 | if not t then
90 | error( 'Bad argument #1, got ' .. type( t ), 2 )
91 | end
92 | local new = {}
93 | for key, value in pairs( t ) do
94 | if type( value ) ~= 'table' or not recursive then
95 | new[key] = value;
96 | else
97 | new[key] = table.copy( value, recursive )
98 | end
99 | end
100 | return new
101 | end
102 |
103 | function table.removeValue( t, value )
104 | for i, _value in pairs( t ) do
105 | if _value == value then
106 | table.remove( t, i )
107 | return i
108 | end
109 | end
110 | return false
111 | end
112 |
113 | function table.getRandomValue( t, anyKeys )
114 | if type( t ) ~= 'table' then
115 | error( 'Bad argument #1, got ' .. type( t ), 2 )
116 | end
117 | if anyKeys then
118 | local size = math.random( table.getSize( t ) )
119 | if size ~= 0 then
120 | local key, value = next( t )
121 | for i = 2, size do
122 | key, value = next( t, key )
123 | end
124 | return value, key
125 | else
126 | return false
127 | end
128 | else
129 | local size = #t
130 | if size == 0 then
131 | return false
132 | end
133 | local i = math.random( 1, size )
134 | if i ~= 0 then
135 | return t[i], i
136 | end
137 | end
138 | end
139 |
140 | function table.reverse( t )
141 | local size = #t
142 | for i = 1, math.floor( size / 2 ) do
143 | t[i], t[size - i + 1] = t[size - i + 1], t[i]
144 | end
145 | end
146 |
147 | function table.erase( t )
148 | if type( t ) ~= 'table' then
149 | error( 'Bad argument #1, got ' .. type( t ), 2 )
150 | end
151 | for k in pairs( t ) do
152 | t[k] = nil
153 | end
154 | end
155 |
156 | -----
157 |
158 | function string:getLineCount( )
159 | local lineCount = 1
160 | for _ in text:gmatch( '\n' ) do
161 | lineCount = lineCount + 1
162 | end
163 | return lineCount
164 | end
165 |
166 | function string.moneyFormating( money, separator )
167 | local formatted = tostring( money )
168 | local formatType = '%1' .. ( separator or ' ' ).. '%2'
169 | while true do
170 | formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", formatType)
171 | if (k==0) then
172 | break
173 | end
174 | end
175 | return formatted
176 | end
177 |
178 | ----
179 |
180 | do
181 | local PI = math.pi
182 | local PI2 = 2 * math.pi
183 | local atan2 = math.atan2
184 | local abs = math.abs
185 | function math.isInViewRange( x1, y1, x2, y2, rot1, halfViewAngle )
186 | x2 = ( PI - atan2( ( x1 - x2 ), ( y1 - y2 ) ) ) % PI2
187 | x1 = abs( rot1 - x2 )
188 | if x1 > PI then
189 | x1 = PI2 - x1
190 | end
191 | return x1 < halfViewAngle
192 | end
193 | end
194 |
195 | function math.round(number, decimals, method)
196 | decimals = decimals or 0
197 | local factor = 10 ^ decimals
198 | if (method == "ceil" or method == "floor") then return math[method](number * factor) / factor
199 | else return tonumber(("%."..decimals.."f"):format(number)) end
200 | end
201 |
202 | function math.clamp( min, value, max )
203 | return math.max( min, math.min( value, max ) )
204 | end
205 |
206 | function math.isBetween( from, to, val )
207 | if from < to then
208 | return from <= val and val <= to
209 | else
210 | return to <= val and val <= from
211 | end
212 | end
213 |
214 | function math.toBitvise( ... )
215 | local arg = { ... }
216 | local int = 0x0
217 | for i = 1, #arg do
218 | if type( arg[i] ) == 'boolean' then
219 | if arg[i] then
220 | int = int + 2^( i - 1 )
221 | end
222 | else
223 | error( 'Wrong argumnet #' .. i .. ' in function math.toBitvise', 2 )
224 | end
225 | end
226 | return int
227 | end
228 |
229 | function math.fromBitvise( int, valuesCount )
230 | if type( int ) ~= 'number' then
231 | error( 'Wrong argument #1 in function math.toBitvise', level )
232 | end
233 | valuesCount = valuesCount or 32
234 | if type( valuesCount ) ~= 'number' then
235 | error( 'Wrong argument #2 in function math.toBitvise', level )
236 | end
237 | local o = {}
238 | for i = 1, valuesCount do
239 | o[i] = bitTest( int, 2^(i - 1) )
240 | end
241 | return unpack( o )
242 | end
243 |
244 | function math.getMilliseconds( ms, sec, min, hours )
245 | ms = ms or 0
246 | sec = sec or 0
247 | min = min or 0
248 | hours = hours or 0
249 | return ((((hours * 60) + min) * 60) + sec) * 1000 + ms
250 | end;
251 |
252 | function math.getChekedValueInRange( from, to, checkFun )
253 | if from > to then
254 | error( "Wrong range", 2 )
255 | end
256 |
257 | local half
258 | local floor = math.floor
259 |
260 | local function checkInstance( )
261 | if from == to then
262 | return
263 | end
264 | if from + 1 == to then
265 | from = checkFun( to ) and to or from
266 | return
267 | end
268 | half = floor( from + ( to - from ) / 2 )
269 | if checkFun( half ) then
270 | from = half
271 | else
272 | to = half
273 | end
274 | return checkInstance( )
275 | end
276 | checkInstance()
277 | return from
278 | end
279 |
280 | -----
281 |
282 | local tocolor = tocolor
283 |
284 | color = {
285 |
286 | HEXtoRGB = function( color )
287 | local b = color % 2^8
288 | local g = math.floor( ( ( color - b ) % 2^16 ) / 2^8 )
289 | local r = math.floor( ( ( color - b - g * 2^8 ) % 2^24 ) / 2^16 )
290 | local a = math.floor( ( ( color - b - g * 2^8 - r * 2^16 ) % 2^32 ) / 2^24 )
291 | return r, g, b, a
292 | end;
293 |
294 | RGBtoHEX = function( r, g, b, a )
295 | return tocolor( r, g, b, a )
296 | end;
297 |
298 | RGBtoHEXstring = function( r, g, b, a )
299 | if a then
300 | return string.format( '#%.2X%.2X%.2X%.2X', r, g, b, a )
301 | else
302 | return string.format( '#%.2X%.2X%.2X', r, g, b )
303 | end
304 | end;
305 |
306 | -- http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c
307 |
308 | --[[
309 | * Converts an RGB color value to HSL. Conversion formula
310 | * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
311 | * Assumes r, g, and b are contained in the set [0, 255] and
312 | * returns h, s, and l in the set [0, 1].
313 | *
314 | * @param Number r The red color value
315 | * @param Number g The green color value
316 | * @param Number b The blue color value
317 | * @return Array The HSL representation
318 | ]]
319 | RGBtoHSL = function (r, g, b, a)
320 | r, g, b = r / 255, g / 255, b / 255
321 |
322 | local max, min = math.max(r, g, b), math.min(r, g, b)
323 | local h, s, l
324 |
325 | l = (max + min) / 2
326 |
327 | if max == min then
328 | h, s = 0, 0 -- achromatic
329 | else
330 | local d = max - min
331 | local s
332 | if l > 0.5 then s = d / (2 - max - min) else s = d / (max + min) end
333 | if max == r then
334 | h = (g - b) / d
335 | if g < b then h = h + 6 end
336 | elseif max == g then h = (b - r) / d + 2
337 | elseif max == b then h = (r - g) / d + 4
338 | end
339 | h = h / 6
340 | end
341 |
342 | return h, s, l, a or 255
343 | end;
344 |
345 | --[[
346 | * Converts an HSL color value to RGB. Conversion formula
347 | * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
348 | * Assumes h, s, and l are contained in the set [0, 1] and
349 | * returns r, g, and b in the set [0, 255].
350 | *
351 | * @param Number h The hue
352 | * @param Number s The saturation
353 | * @param Number l The lightness
354 | * @return Array The RGB representation
355 | ]]
356 | HSLtoRGB = function(h, s, l, a)
357 | local r, g, b
358 |
359 | if s == 0 then
360 | r, g, b = l, l, l -- achromatic
361 | else
362 | function hue2rgb(p, q, t)
363 | if t < 0 then t = t + 1 end
364 | if t > 1 then t = t - 1 end
365 | if t < 1/6 then return p + (q - p) * 6 * t end
366 | if t < 1/2 then return q end
367 | if t < 2/3 then return p + (q - p) * (2/3 - t) * 6 end
368 | return p
369 | end
370 |
371 | local q
372 | if l < 0.5 then q = l * (1 + s) else q = l + s - l * s end
373 | local p = 2 * l - q
374 |
375 | r = hue2rgb(p, q, h + 1/3)
376 | g = hue2rgb(p, q, h)
377 | b = hue2rgb(p, q, h - 1/3)
378 | end
379 |
380 | return r * 255, g * 255, b * 255, a * 255
381 | end;
382 |
383 | --[[
384 | * Converts an RGB color value to HSV. Conversion formula
385 | * adapted from http://en.wikipedia.org/wiki/HSV_color_space.
386 | * Assumes r, g, and b are contained in the set [0, 255] and
387 | * returns h, s, and v in the set [0, 1].
388 | *
389 | * @param Number r The red color value
390 | * @param Number g The green color value
391 | * @param Number b The blue color value
392 | * @return Array The HSV representation
393 | ]]
394 | RGBtoHSV = function (r, g, b, a)
395 | r, g, b, a = r / 255, g / 255, b / 255, a / 255
396 | local max, min = math.max(r, g, b), math.min(r, g, b)
397 | local h, s, v
398 | v = max
399 |
400 | local d = max - min
401 | if max == 0 then s = 0 else s = d / max end
402 |
403 | if max == min then
404 | h = 0 -- achromatic
405 | else
406 | if max == r then
407 | h = (g - b) / d
408 | if g < b then h = h + 6 end
409 | elseif max == g then h = (b - r) / d + 2
410 | elseif max == b then h = (r - g) / d + 4
411 | end
412 | h = h / 6
413 | end
414 |
415 | return h, s, v, a
416 | end;
417 |
418 | --[[
419 | * Converts an HSV color value to RGB. Conversion formula
420 | * adapted from http://en.wikipedia.org/wiki/HSV_color_space.
421 | * Assumes h, s, and v are contained in the set [0, 1] and
422 | * returns r, g, and b in the set [0, 255].
423 | *
424 | * @param Number h The hue
425 | * @param Number s The saturation
426 | * @param Number v The value
427 | * @return Array The RGB representation
428 | ]]
429 | HSVtoRGB = function(h, s, v, a)
430 | local r, g, b
431 |
432 | local i = math.floor(h * 6);
433 | local f = h * 6 - i;
434 | local p = v * (1 - s);
435 | local q = v * (1 - f * s);
436 | local t = v * (1 - (1 - f) * s);
437 |
438 | i = i % 6
439 |
440 | if i == 0 then r, g, b = v, t, p
441 | elseif i == 1 then r, g, b = q, v, p
442 | elseif i == 2 then r, g, b = p, v, t
443 | elseif i == 3 then r, g, b = p, q, v
444 | elseif i == 4 then r, g, b = t, p, v
445 | elseif i == 5 then r, g, b = v, p, q
446 | end
447 |
448 | return r * 255, g * 255, b * 255, a * 255
449 | end;
450 | }
451 |
452 | function Element:getPositionFromOffset( vector )
453 | local m = self:getMatrix( )
454 | return m:transformPosition( vector )
455 | end
456 |
457 | function Timer.createAttachedToTime( _func, secondsFromStartDay, interval, count, callBack )
458 | local rt = getRealTime()
459 | local thisDayStart = rt.timestamp - rt.hour * 60 * 60 - rt.minute * 60 - rt.second
460 | local timeToStart
461 | if rt.timestamp > thisDayStart + secondsFromStartDay then
462 | local executesCount = math.ceil( (rt.timestamp - thisDayStart - secondsFromStartDay) / interval )
463 | timeToStart = thisDayStart - rt.timestamp + (secondsFromStartDay + interval * executesCount)
464 | else
465 | timeToStart = (thisDayStart + secondsFromStartDay) - rt.timestamp
466 | end
467 |
468 | local firstFunction = function( )
469 | count = count - 1
470 | _func()
471 | if count ~= 0 then
472 | local timer = Timer( _func, interval * 1000, count == -1 and 0 or count )
473 | if callBack then
474 | callBack( timer )
475 | end
476 | end
477 | end
478 |
479 | Timer( firstFunction, timeToStart * 1000, 1 )
480 | end
--------------------------------------------------------------------------------
/dxGUI/DxConstruction.lua:
--------------------------------------------------------------------------------
1 | dxConstruction = dxGUI.baseClass:subclass{
2 | type = 'construction';
3 | reguestScreen = true;
4 |
5 | create = function( self, isGlobal )
6 | if self.plane then
7 | outputDebugString( 'dxConstruction:create: construction is init', 2 )
8 | return false
9 | end
10 | self.plane = {}
11 |
12 | if isGlobal ~= false and self.isGlobal ~= false then
13 | dxConstruction.screen:addObject( self )
14 | self.screen = dxConstruction.screen
15 | end
16 |
17 | if self.screen then
18 | if not self.w then
19 | self.w = self.screen.w
20 | end
21 | if not self.h then
22 | self.h = self.screen.h
23 | end
24 | end
25 |
26 | if self.newRenderTarget then
27 | self.renderTarget = dxCreateRenderTarget(
28 | self.newRenderTarget.w or self.w,
29 | self.newRenderTarget.h or self.h,
30 | self.newRenderTarget.withAlpha or false
31 | )
32 | end
33 |
34 | if not self.objects then
35 | self.objects = {}
36 | end
37 |
38 | local objects = {}
39 | for guiID, data in pairs( self.objects ) do
40 | if data.p then
41 | objects[ data.p ] = guiID
42 | end
43 | end
44 | for guiID, data in pairs( self.objects ) do
45 | if data.p then
46 | -- do notig
47 | else
48 | table.insert( objects, guiID )
49 | end
50 | end
51 |
52 | for plane = 1, #objects do
53 | local guiID = objects[plane]
54 | local object = self:initObject( self.objects[guiID], guiID )
55 | if object then
56 | self:addObject( object )
57 | end
58 | end
59 |
60 | if self.anims then
61 | for i = #self.anims, 1, -1 do
62 | local animData = self.anims[i]
63 | local anim = Anim.find( animData[1] )
64 | --outputDebugString( animData[1] )
65 | self.anims[i] = nil
66 | if anim then
67 | self:addAnim( anim, unpack( animData, 2 ) )
68 | end
69 | end
70 | end
71 | self.anims = nil
72 |
73 | return self
74 | end;
75 |
76 | inheritStyle = function( self, dataType )
77 | -- Наследование стилей
78 | if self.style and self.style[dataType] and not dxGUI[dataType] then
79 | local parent = self.style[dataType].parent
80 | local lastDataType = dataType
81 | while true do
82 | self.style[lastDataType].type = lastDataType
83 | local parentClass = dxGUI[parent]
84 | if not parentClass and self.style[parent] and self.style[parent].parent then
85 | parentClass = dxGUI[ self.style[parent].parent ]
86 | end
87 | if parentClass then
88 | parentClass:subclass{ type = lastDataType }
89 | table.uniteWithoutReplace( self.style[lastDataType], self.style[parent] )
90 | if not parentClass and self.style[parent].parent then
91 | lastDataType = parent
92 | parent = self.style[parent].parent
93 | else
94 | break;
95 | end
96 | else
97 | if parent then
98 | self:errorHandler( string.format('"%s" request "%s" but it not found.', dataType, parent ) )
99 | else
100 | self:errorHandler( string.format( '"dataType" unknow object type', dataType ) )
101 | end
102 | break;
103 | end
104 | end
105 | end
106 | end;
107 |
108 | initObject = function( self, data, guiID )
109 | local dataType = data.type
110 | self:inheritStyle( dataType )
111 |
112 | if dxGUI[dataType] then
113 | if self.style and self.style[dataType] then
114 | -- применение стиля
115 | table.uniteWithoutReplace( data, self.style[dataType] )
116 | end
117 |
118 | if not data.x or not data.y then
119 | error( 'Object with out coordinates ' .. tostring( guiID ) )
120 | end
121 | -- Растянуть по размеру
122 | if not data.w then
123 | data.w = self.w - data.x
124 | end
125 | if not data.h then
126 | data.h = self.h - data.y
127 | end
128 | if dxGUI[dataType].reguestScreen then
129 | data.style = self.style
130 | end
131 |
132 | local newObject = dxGUI[dataType]( data )
133 | if not newObject then
134 | outputDebugString( 'Can not init object "' .. tostring( guiID ) .. '"[' .. tostring( dataType ) .. ']', 2 )
135 | return false
136 | end
137 | newObject:setShow( newObject.show == nil or newObject.show )
138 |
139 | if newObject.anims then
140 | for i = #newObject.anims, 1, -1 do
141 | local animData = newObject.anims[i]
142 | local anim = Anim.find( animData[1] )
143 | if anim then
144 | newObject.anims[i] = nil
145 | newObject:addAnim( anim, unpack( animData, 2 ) )
146 | else
147 | self:errorHandler( 'Can not find anim "' .. tostring( animData[1] ) .. '" in ' .. tostring( guiID ) )
148 | end
149 | end
150 | newObject.anims = nil
151 | end
152 | return newObject
153 | else
154 | outputDebugString( 'dxGUI: unsupportned object type ' .. tostring( data.type ) .. ' guiID ' .. tostring( guiID ) ..', skipping', 2 )
155 | return false
156 | end
157 | end;
158 |
159 | addObject = function( self, data, id )
160 | if type( data ) ~= 'table' then
161 | error( 'Bad argumet #1, got ' .. type( data ), 2 )
162 | end
163 | if data.reguestScreen then
164 | data.screen = self
165 | end
166 | if id then
167 | self.objects[id] = data
168 | end
169 | self:setObjectPosition( data, data.x, data.y )
170 |
171 | if not data.p then
172 | table.insert( self.plane, data )
173 | elseif self.plane[ data.p ] then
174 | table.insert( self.plane, data.p or ( #self.plane + 1 ), data )
175 | else
176 | self.plane[ data.p ] = data
177 | end
178 |
179 | end;
180 |
181 | removeObject = function( self, arg )
182 | local id, object
183 | if type( arg ) == "table" then
184 | object = arg
185 | id = self:getObjectID( object )
186 | else
187 | id = arg
188 | object = self.objects[id]
189 | end
190 | if not object then return false; end
191 | for i = 1, #self.plane do
192 | if self.plane[i] == object then
193 | table.remove( self.plane, i )
194 | if id then
195 | self.objects[id] = nil
196 | end
197 | local x, y = object:getPosition( )
198 | object:setPosition( x - self.x, y - self.y )
199 | return true
200 | end
201 | end
202 | return false
203 | end;
204 |
205 | getObjectPlane = function( self, object )
206 | return table.find( self.plane, object )
207 | end;
208 |
209 | getObjectID = function( self, object )
210 | return table.find( self.objects, object )
211 | end;
212 |
213 | getObjectInPosition = function( self, x, y )
214 | if not x or not y then
215 | error( 'Coordinate required', 2 )
216 | end
217 | for i = #self.plane, 1, -1 do
218 | local object = self.plane[i]
219 | if object:isShow() and object.x < x and object.w + object.x > x and object.y < y and object.h + object.y > y then
220 | return object, i
221 | end
222 | end
223 | return false
224 | end;
225 |
226 | setObjectPosition = function( self, object, x, y )
227 | return object:setPosition( self.x + x, self.y + y )
228 | end;
229 |
230 | getObjectPosition = function( self, object )
231 | local x, y = object:getPosition()
232 | return x - self.x, y - self.y
233 | end;
234 |
235 | setObjectAlign = function( self, object, alignX, alignY )
236 | local x, y = self:getObjectPosition( object )
237 | if alignX == 'left' then
238 | x = 0
239 | elseif alignX == 'center' then
240 | x = ( self.w - object.w ) / 2
241 | elseif alignX == 'right' then
242 | x = self.w - object.w
243 | end
244 | if alignY == 'top' then
245 | y = 0
246 | elseif alignY == 'center' then
247 | y = ( self.h - object.h ) / 2
248 | elseif alignY == 'bottom' then
249 | y = self.h - object.h
250 | end
251 | return self:setObjectPosition( object, x, y )
252 | end;
253 |
254 | setPosition = function( self, x, y )
255 | x, y = math.floor( x ), math.floor( y )
256 | for key, object in self:objectPairs() do
257 | object:setPosition( x + object.x - self.x, y + object.y - self.y )
258 | end
259 | self.x, self.y = x, y
260 | end;
261 |
262 | setAlign = function( self, alignX, alignY )
263 | local x, y = self.x, self.y
264 | if alignX == 'left' then
265 | x = 0
266 | elseif alignX == 'center' then
267 | x = ( self.screen.w - self.w ) / 2
268 | elseif alignX == 'right' then
269 | x = self.screen.w - self.w
270 | end
271 | if alignY == 'top' then
272 | y = 0
273 | elseif alignY == 'center' then
274 | y = ( self.screen.h - self.h ) / 2
275 | elseif alignY == 'bottom' then
276 | y = self.screen.h - self.h
277 | end
278 | return self:setPosition( x, y )
279 | end;
280 |
281 | draw = function( self )
282 | if self.show == true then
283 | if self.renderTarget then
284 | self.renderTarget:setAsTarget()
285 | end
286 |
287 | for _, object in ipairs( self.plane ) do
288 | if object.show == true then
289 | object:updateAnims()
290 | object:draw()
291 | end
292 | end
293 |
294 | if self.renderTarget then
295 | dxSetRenderTarget()
296 | end
297 | end
298 | end;
299 |
300 | createTexture = function( self, callback )
301 | local render = DxRenderTarget( self.w, self.h, true )
302 | if not render then
303 | return false
304 | end
305 |
306 |
307 | local draw = self.draw
308 | local oldRenderTarget = self.renderTarget
309 |
310 | local skipFrame = false
311 | self.renderTarget = render
312 | self.draw = function( self )
313 | if skipFrame then
314 | local pixels = dxGetTexturePixels( render )
315 | dxDrawImage( self.x, self.y, self.w, self.h, render )
316 | destroyElement( render )
317 | self.renderTarget = nil
318 |
319 | callback( dxCreateTexture( pixels ) )
320 |
321 | self.draw = draw
322 | self.renderTarget = oldRenderTarget
323 | else
324 | local x, y = self:getPosition()
325 | local show = self.show
326 |
327 | self.show = true
328 |
329 | self:setPosition( 0, 0 )
330 | render:setAsTarget( true )
331 | draw( self )
332 | dxSetRenderTarget( )
333 | self:setPosition( x, y )
334 |
335 | if show then
336 | dxDrawImage( self.x, self.y, self.w, self.h, render )
337 | end
338 |
339 | self.show = show
340 | skipFrame = true
341 | end
342 | end
343 |
344 | return true
345 | end;
346 |
347 |
348 | onClick = function( self, button, state, cX, cY )
349 | local gui, pos = self:getObjectInPosition( cX, cY )
350 | if not gui then return; end
351 | if gui.input then
352 | gui.input:activate()
353 | elseif currentInput ~= nullInput then
354 | currentInput:deactivate()
355 | end
356 |
357 | if gui.type == 'construction' then
358 | table.remove( self.objects, pos )
359 | table.insert( self.objects, 1 )
360 | end
361 |
362 | if gui.onClick then
363 | gui:onClick( button, state, cX, cY )
364 | end
365 |
366 | end;
367 |
368 | onWheel = function( self, upOrDown )
369 | local cX, cY = getCursorPosition()
370 | local sX, sY = guiGetScreenSize()
371 |
372 | local gui, pos = self:getObjectInPosition( cX * sX, cY * sY )
373 | if not gui then return; end
374 |
375 | if gui.onWheel then
376 | gui:onWheel( upOrDown )
377 | end
378 | end;
379 |
380 | onCursorMove = function( self, inBox, cX, cY, lastX, lastY, lastTree, tree, level )
381 | if inBox then
382 | local gui = self:getObjectInPosition( cX, cY )
383 | if lastTree[level] ~= gui then
384 | for k = #lastTree, level, -1 do
385 | if lastTree[k].onCursorMove then
386 | lastTree[k]:onCursorMove( false, cX, cY, lastX, lastY )
387 | end
388 | end
389 | end
390 | if not gui then return; end
391 | tree[level] = gui
392 | if gui.onCursorMove then
393 | return gui:onCursorMove( true, cX, cY, lastX, lastY, lastTree, tree, level + 1 )
394 | end
395 | elseif lastTree then
396 | for k = #lastTree, level, -1 do
397 | if lastTree[k].onCursorMove then
398 | lastTree[k]:onCursorMove( false, cX, cY, lastX, lastY )
399 | end
400 | end
401 | end
402 | end;
403 |
404 | showAll = function( self, state )
405 | for _, object in pairs( self.plane ) do
406 | object:setShow( state )
407 | end
408 | self.show = state
409 | end;
410 |
411 | setShow = function( self, show )
412 | if show == true or show == false then
413 | self.show = show
414 | self:onShow( show )
415 | return true
416 | end
417 | return false
418 | end;
419 |
420 | onShow = function( self, show )
421 | for _, object in self:objectPairs() do
422 | if object.onShow and object:isShow() then
423 | object:onShow( show )
424 | end
425 | end
426 | end;
427 |
428 | getAlpha = function( self )
429 | return self.alpha
430 | end;
431 |
432 | setAlpha = function( self, alpha )
433 | alpha = math.max( 0, math.min( 255, alpha ) )
434 | for _, object in pairs( self.plane ) do
435 | if not object.originalAlpha then
436 | object.originalAlpha = object:getAlpha() or 255
437 | end
438 | object:setAlpha( alpha * object.originalAlpha / 255 )
439 | end
440 | self.alpha = alpha
441 | end;
442 |
443 | setPostGUI = function( self, state )
444 | if state == true or state == false then
445 | for _, object in self:objectPairs() do
446 | if object.setPostGUI then
447 | object:setPostGUI( state )
448 | end
449 | end
450 | end
451 | return false
452 | end;
453 |
454 | setScale = function( self, scale, scaleY )
455 | if self.saveProportions then
456 | scaleY = scale
457 | else
458 | scaleY = scaleY or scale
459 | end
460 | if self.renderTarget then
461 | local rtW, rtH = dxGetMaterialSize( self.renderTarget )
462 | self.renderTarget:destroy()
463 | self.renderTarget = dxCreateRenderTarget( rtW * scale, rtH * scaleY, self.newRenderTarget and self.newRenderTarget.withAlpha or false )
464 | end
465 | self.w, self.h = self.w * scale, self.h * scaleY
466 | for _, object in self:objectPairs() do
467 | local x, y = self:getObjectPosition( object )
468 | self:setObjectPosition( object, x * scale, y * scaleY )
469 | if object.setScale then
470 | object:setScale( scale, scaleY )
471 | else
472 | object.w, object.h = object.w * scale, object.h * scaleY
473 | end
474 |
475 | end
476 | end;
477 |
478 | fitOnScreen = function( self, saveProportions )
479 | if saveProportions == false then
480 | local scaleX
481 | if self.w > self.screen.w then
482 | scaleX = self.screen.w / self.w
483 | else
484 | scaleX = 1
485 | end
486 |
487 | local scaleY
488 | if self.h > self.screen.h then
489 | scaleY = self.screen.h / self.h
490 | else
491 | scaleY = 1
492 | end
493 |
494 | if scaleX ~= 1 or scaleY ~= 1 then
495 | self:setScale( scaleX, scaleY )
496 | end
497 | else
498 | local scaleX
499 | if self.w > self.screen.w then
500 | scaleX = self.screen.w / self.w
501 | else
502 | scaleX = 1
503 | end
504 |
505 | local scaleY
506 | if self.h > self.screen.h then
507 | scaleY = self.screen.h / self.h
508 | else
509 | scaleY = 1
510 | end
511 |
512 | scaleX = scaleX < scaleY and scaleX or scaleY
513 | if scaleX ~= 1 then
514 | self:setScale( scaleX )
515 | end
516 | end
517 | end;
518 |
519 | showCursor = function( self, state )
520 | if state then
521 | CursorManager.addGui( self )
522 | else
523 | CursorManager.removeGui( self )
524 | end
525 | end;
526 |
527 | objectPairs = function( self )
528 | return function( planeObjects, plane )
529 | plane = plane - 1
530 | if plane ~= 0 then
531 | return plane, planeObjects[plane]
532 | end
533 | end, self.plane, #self.plane + 1
534 | end;
535 |
536 | }
537 |
538 |
539 | ---------------------------------------
540 | do
541 | local sX, sY = guiGetScreenSize()
542 | dxConstruction.screen = setmetatable(
543 | {
544 | x = 0;
545 | y = 0;
546 | w = sX;
547 | h = sY;
548 | objects = {};
549 | plane = {};
550 | show = true;
551 | screen = false;
552 | },
553 | {
554 | __index = dxConstruction;
555 | }
556 | )
557 |
558 | local mainScreen = dxConstruction.screen
559 | local dxSetAspectRatioAdjustmentEnabled = dxSetAspectRatioAdjustmentEnabled
560 | addEventHandler( 'onClientRender', root, function()
561 | dxSetAspectRatioAdjustmentEnabled( false )
562 | mainScreen:draw()
563 | end )
564 |
565 | addEventHandler( 'onClientClick', root, function( button, state, cX, cY )
566 | mainScreen:onClick( button, state, cX, cY )
567 | end )
568 |
569 |
570 | local lastX, lastY
571 | local lastTree = {}
572 |
573 | addEventHandler( 'onClientCursorMove', root, function( _, _, cX, cY )
574 | if not isCursorShowing() then return end
575 | local tree = { }
576 | mainScreen:onCursorMove( true, cX, cY, lastX, lastY, lastTree, tree, 1 )
577 |
578 | lastTree = tree
579 | lastX, lastY = cX, cY
580 | end )
581 |
582 | addEventHandler( 'onClientKey', root, function( key )
583 | if isCursorShowing() then
584 | if key == 'mouse_wheel_up' then
585 | mainScreen:onWheel( 1 )
586 | elseif key == 'mouse_wheel_down' then
587 | mainScreen:onWheel( -1 )
588 | end
589 | end
590 | end )
591 |
592 | end
593 |
--------------------------------------------------------------------------------
/dxGUI/List.lua:
--------------------------------------------------------------------------------
1 | dxGUI.baseClass:subclass{
2 | type = 'list';
3 | reguestScreen = true;
4 | postGUI = false;
5 | offsetX = 0;
6 | offsetY = 0;
7 | active = 0;
8 | scrolling = 'vertical';
9 | maxHorizontalItems = 1;
10 | selectionType = 'single';
11 | keyboardInput = false;
12 | autofolding = true;
13 |
14 | create = function( self )
15 | if self.scrolling == 'vertical' then
16 | self.maxHorizontalItems = self.maxHorizontalItems or 1
17 | elseif self.scrolling == 'horizontal' then
18 | self.maxVerticalItems = self.maxVerticalItems or 1
19 | elseif not ( self.maxHorizontalItems or self.maxVerticalItems ) then
20 | error( 'No item count', 2 )
21 | return false
22 | end
23 | local items = self.items or {}
24 | self.items = {}
25 | for i, item in ipairs( items ) do
26 | self:addItem( item, i )
27 | end
28 |
29 | self.input = Input{ }
30 |
31 | if self.maxHorizontalItems ~= 1 then
32 | self.input:bind( 'arrow_l', true, self.moveActive, self, -1, 0 )
33 | self.input:bind( 'arrow_r', true, self.moveActive, self, 1, 0 )
34 | end
35 | self.input:bind( 'arrow_u', true, self.moveActive, self, 0, -1 )
36 | self.input:bind( 'arrow_d', true, self.moveActive, self, 0, 1 )
37 |
38 | return self
39 | end;
40 |
41 | addItem = function( self, item, pos )
42 | if type( item ) ~= 'table' then
43 | return false
44 | end
45 | local newItem = {
46 | x = 0;
47 | y = 0;
48 | w = self.construction.w / ( self.scale and self.scale[1] or 1 );
49 | h = self.construction.h / ( self.scale and self.scale[2] or 1 );
50 | objects = table.copy( self.construction.objects, true );
51 | anims = table.copy( self.construction.anims or {}, true );
52 | style = self.style;
53 | show = true;
54 | }
55 | table.unite( newItem.objects, item, true )
56 | dxConstruction( newItem, false )
57 |
58 | newItem.screen = self
59 | self:setItemSelect( newItem, false )
60 |
61 | if self.postGUI and not self.renderTarget then
62 | newItem:setPostGUI( true )
63 | end
64 |
65 | pos = pos or #self.items + 1
66 | if self.autofolding then
67 | table.insert( self.items, pos, newItem )
68 | elseif not self.items[pos] then
69 | self.items[pos] = newItem
70 | else
71 | return false
72 | end
73 | self:setItemStatus( pos, 'default' )
74 |
75 | -- сделать скалинг по конструкции
76 | if self.scale then
77 | newItem:setScale( self.scale[1], self.scale[2] )
78 | end
79 | return newItem, pos
80 | end;
81 |
82 | removeItem = function( self, pos )
83 | if self.items[pos] then
84 | local value
85 | if self.autofolding then
86 | value = table.remove( self.items, pos )
87 | else
88 | value = self.items[pos]
89 | self.items[pos] = nil
90 | end
91 | if not self.items[ self.active ] then
92 | self:setActiveItem( self.active - 1 )
93 | end
94 |
95 | if self.scrolling == 'vertical' then
96 | if pos / self.maxHorizontalItems * self.construction.h < self.offsetY then
97 | self:move( 0, -self.construction.h )
98 | end
99 | else
100 | if pos / self.maxVerticalItems * self.construction.w < self.offsetX then
101 | self:move( -self.construction.w, 0 )
102 | end
103 | end
104 | local itemCount = #self.items
105 | local maxOffsetX = self.maxVerticalItems and ( math.floor( itemCount / self.maxVerticalItems ) * self.construction.w - self.w ) or 0
106 | local maxOffsetY = self.maxHorizontalItems and ( math.ceil( itemCount / self.maxHorizontalItems ) * self.construction.h - self.h ) or 0
107 |
108 | if self.offsetX > maxOffsetX and maxOffsetX >= 0 then
109 | self.offsetX = maxOffsetX
110 | end
111 | if self.offsetY > maxOffsetY and maxOffsetY >= 0 then
112 | self.offsetY = maxOffsetY
113 | end
114 | return value
115 | end
116 | return false
117 | end;
118 |
119 | getItemsCount = function( self )
120 | return #self.items
121 | end;
122 |
123 | clean = function( self )
124 | self.items = {}
125 | self.active = 0
126 | self.offsetX = 0
127 | self.offsetY = 0
128 | end;
129 |
130 | draw = function( self )
131 | local offX, offY = self.offsetX % self.construction.w, self.offsetY % self.construction.h
132 | local itemOffsetX = self.offsetX / self.construction.w
133 | local itemOffsetY = self.offsetY / self.construction.h
134 |
135 | if offX == 0 and offY == 0 and self.w % self.construction.w == 0 and self.h % self.construction.h == 0 then
136 | if self.renderTarget then
137 | self.renderTarget:destroy()
138 | self.renderTarget = nil
139 | for i = 1, #self.items do
140 | self.items[i]:setPostGUI( self.postGUI )
141 | end
142 | end
143 | for iItemY = itemOffsetY, itemOffsetY + ( self.maxVerticalItems or math.ceil( self.h / self.construction.h ) ) - 1 do
144 | for iItemX = itemOffsetX + 1, itemOffsetX + ( self.maxHorizontalItems or self.w / self.construction.w ) do
145 | local item = self.items[ iItemY * self.maxHorizontalItems + iItemX ]
146 | if item then
147 | item:setPosition( self.x + ( iItemX - itemOffsetX - 1 ) * self.construction.w, self.y + ( iItemY - itemOffsetY ) * self.construction.h )
148 | item:updateAnims()
149 | item:draw()
150 | end
151 | end
152 | end
153 | else
154 | if not self.renderTarget then
155 | self.renderTarget = DxRenderTarget( self.w, self.h, true )
156 | for i = 1, #self.items do
157 | self.items[i]:setPostGUI( false )
158 | end
159 | end
160 | if self.renderTarget then
161 | local prevRenderTarget = DxRenderTarget.getCurrentTarget()
162 | self.renderTarget:setAsTarget( true )
163 | dxSetBlendMode( 'modulate_add' )
164 |
165 | for iItemY = math.floor( itemOffsetY ), math.floor( itemOffsetY ) + ( self.maxVerticalItems or math.ceil( self.h / self.construction.h ) ) + 1 do
166 | for iItemX = math.floor( itemOffsetX ) + 1, math.ceil( itemOffsetX ) + ( self.maxHorizontalItems or self.w / self.construction.w ) do
167 | local item = self.items[ iItemY * self.maxHorizontalItems + iItemX ]
168 | if item then
169 | item:setPosition( ( iItemX - math.floor( itemOffsetX ) - 1 ) * self.construction.w - offX, ( iItemY - math.floor( itemOffsetY ) ) * self.construction.h - offY )
170 | item:updateAnims()
171 | item:draw()
172 | end
173 | end
174 | end
175 |
176 | dxSetBlendMode( 'add' )
177 | DxRenderTarget.setAsTarget( prevRenderTarget )
178 | dxDrawImage( self.x, self.y, self.w, self.h, self.renderTarget, 0,
179 | 0, 0, 0xFFFFFFFF, self.postGUI )
180 | dxSetBlendMode( 'blend' )
181 | else
182 | dxDrawText( 'NO VIDEO MEMORY FOR CREATE RENDERTARGET', self.x, self.y, self.x + self.w, self.y + self.h, 5 )
183 | end
184 | end
185 | end;
186 |
187 | setAlpha = function( self, alpha )
188 | for i = 1, #self.items do
189 | self.items[i]:setAlpha( alpha )
190 | end
191 | end;
192 |
193 | cleanSelect = function( self )
194 | for pos, item in pairs( self.items ) do
195 | if item.select then
196 | self:setItemSelect( pos, false )
197 | end
198 | end
199 | return true
200 | end;
201 |
202 | setSelectionType = function( self, newType )
203 | if newType == 'single' or newType == 'multi' then
204 | if self.selectionType ~= newType then
205 | self:cleanSelect()
206 | self.selectionType = newType
207 | else
208 | return true
209 | end
210 | else
211 | return false
212 | end
213 | end;
214 |
215 | setItemSelect = function( self, index, selected )
216 | if not index or not self.items[index] then
217 | return false
218 | end
219 | if self.selectionType == 'single' and selected == true then
220 | self:cleanSelect()
221 | end
222 | if ( self:getActiveItem() == index and 'active' ) then
223 | if not self:setItemStatus( index, 'active' ) then
224 | self:setItemStatus( index, ( selected and 'selected' ) or 'default' )
225 | end
226 | else
227 | self:setItemStatus( index, ( selected and 'selected' ) or 'default' )
228 | end
229 | self.items[index].select = selected
230 | if self.onItemSelect then
231 | self:onItemSelect( index, self.items[index] )
232 | end
233 | return true
234 | end;
235 |
236 | getItem = function( self, index )
237 | return index and self.items[index]
238 | end;
239 |
240 | getSelectedItems = function( self )
241 | local resul = {}
242 | for i = 1, #self.items do
243 | if self.items[i].select then
244 | table.insert( resul, { i, self.items[i] } )
245 | end
246 | end
247 | return resul
248 | end;
249 |
250 | setItemStatus = function( self, index, status )
251 | if self.stats and self.stats[status] and self.items[index] then
252 | table.unite( self.items[index].objects, self.stats[status], true )
253 | self.items[index].status = status
254 | return true
255 | end
256 | return false
257 | end;
258 |
259 | getItemStatus = function( self, index )
260 | if self.items[index] then
261 | return self.items[index].status
262 | end
263 | return false
264 | end;
265 |
266 | setActiveItem = function( self, index )
267 | if not self.items[index] then return false; end
268 | self:setItemStatus( index, 'active' )
269 | if self.active ~= index and self.items[self.active] then
270 | self:setItemStatus( self.active, self.items[self.active].select and 'selected' or 'default' )
271 | end
272 | self.active = index
273 |
274 | local reqOffX = self.maxVerticalItems and ( math.floor( ( self.active - 1 ) / self.maxVerticalItems ) * self.construction.w )
275 | local reqOffY = self.maxHorizontalItems and ( math.floor( ( self.active - 1 ) / self.maxHorizontalItems ) * self.construction.h )
276 | if reqOffX then
277 | local minOffX = reqOffX - self.w + self.construction.w
278 | if self.offsetX < minOffX then
279 | self:move( minOffX - self.offsetX, 0 )
280 | elseif self.offsetX > reqOffX then
281 | self:move( reqOffX - self.offsetX, 0 )
282 | end
283 | end
284 | if reqOffY then
285 | local minOffY = reqOffY - self.h + self.construction.h
286 | if self.offsetY < minOffY then
287 | self:move( 0, minOffY - self.offsetY )
288 | elseif self.offsetY > reqOffY then
289 | self:move( 0, reqOffY - self.offsetY )
290 | end
291 | end
292 | return true
293 | end;
294 |
295 | getActiveItem = function( self )
296 | return self.active
297 | end;
298 |
299 | getItemPosition = function( self, index )
300 | local cItemY = math.ceil( index / self.maxHorizontalItems )
301 | local cItemX = index - ( (cItemY-1) * self.maxHorizontalItems )
302 | return self.x + (cItemX - 1) * self.construction.w - self.offsetX,
303 | self.y + (cItemY - 1) * self.construction.h - self.offsetY
304 | end;
305 |
306 | getItemIndexFromPosition = function( self, x, y )
307 | local cItemX = math.ceil( ( x - self.x + self.offsetX ) / self.construction.w )
308 | local cItemY = math.ceil( ( y - self.y + self.offsetY ) / self.construction.h )
309 | if cItemX < 1 or cItemY < 1 then
310 | return false
311 | end
312 | return ( cItemY - 1 ) * self.maxHorizontalItems + cItemX
313 |
314 | end;
315 |
316 | getItemInPosition = function( self, x, y )
317 | local cItem = self:getItemIndexFromPosition( x, y )
318 | if self.items[cItem] then
319 | return cItem, self.items[cItem]
320 | else
321 | return false
322 | end
323 | end;
324 |
325 | onCursorMove = function( self, inBox, cX, cY, lastX, lastY, lastTree, tree, level )
326 | local cItem = inBox and self:getItemInPosition( cX, cY )
327 |
328 | if self.prevCurorMovedItem and ( self.prevCurorMovedItem ~= cItem or not cItem ) then
329 | local oldItem = self.items[ self.prevCurorMovedItem ]
330 | if oldItem then
331 | if oldItem.onCursorMove then
332 | oldItem:onCursorMove( false, cX, cY, lastX, lastY, lastTree, tree, level )
333 | end
334 | if self.active == self.prevCurorMovedItem then
335 | self:setItemStatus( self.prevCurorMovedItem, 'active' )
336 | elseif oldItem.select then
337 | self:setItemStatus( self.prevCurorMovedItem, 'selected' )
338 | else
339 | self:setItemStatus( self.prevCurorMovedItem, 'default' )
340 | end
341 | end
342 | end
343 |
344 | if not cItem then return; end
345 | self:setItemStatus( cItem, 'oncursor' )
346 |
347 | local item = self.items[cItem]
348 | if item.onCursorMove then
349 | item:onCursorMove( inBox, cX, cY, lastX, lastY, lastTree, tree, level )
350 | end
351 |
352 | if getKeyState( 'mouse1' ) then
353 | self:setActiveItem( cItem )
354 | end
355 |
356 | self.prevCurorMovedItem = cItem
357 | end;
358 |
359 | setScale = function( self, scale, scaleY )
360 | self.construction.w = self.construction.w * scale
361 | self.construction.h = self.construction.h * scaleY
362 |
363 | self.w, self.h = self.w * scale, self.h * scaleY
364 | for i = 1, #self.items do
365 | self.items[i]:setScale( scale, scaleY )
366 | end
367 | self.scale = { scale, scaleY }
368 | end;
369 |
370 | setPostGUI = function( self, state )
371 | if state == true or state == false then
372 | self.postGUI = state
373 | if self.renderTarget then
374 | for i = 1, #self.items do
375 | self.items[i]:setPostGUI( false )
376 | end
377 | else
378 | for i = 1, #self.items do
379 | self.items[i]:setPostGUI( state )
380 | end
381 | end
382 | end
383 | return false
384 | end;
385 |
386 | onClick = function( self, button, state, cX, cY )
387 | local cItem = self:getItemInPosition( cX, cY )
388 | if not cItem then return; end
389 | local item = self.items[cItem]
390 |
391 | if self.renderTarget then
392 | item:onClick( button, state, cX - self.x, cY - self.y )
393 | else
394 | item:onClick( button, state, cX, cY )
395 | end
396 |
397 | if button == 'left' then
398 | if state == 'up' then
399 | self:setActiveItem( cItem )
400 | self:setItemSelect( cItem, not item.selected )
401 | else
402 | self:setActiveItem( cItem )
403 | self:setItemSelect( cItem, not item.selected )
404 | end
405 | end
406 | if self.onItemClick then
407 | self:onItemClick( cItem, item, button, state, cX, cY )
408 | end
409 | return true
410 | end;
411 |
412 | onWheel = function( self, upOrDown )
413 | if self.scrolling == 'vertical' then
414 | self:move( 0, -upOrDown * self.construction.h )
415 | else
416 | self:move( -upOrDown * self.construction.w, 0 )
417 | end
418 | end;
419 |
420 | move = function( self, countX, countY )
421 |
422 | local itemCount = #self.items
423 | local maxOffsetX = self.maxVerticalItems and ( math.ceil( itemCount / self.maxVerticalItems ) * self.construction.w - self.w ) or 0
424 | local maxOffsetY = self.maxHorizontalItems and ( math.ceil( itemCount / self.maxHorizontalItems ) * self.construction.h - self.h ) or 0
425 |
426 | if countX and countX ~= 0 and maxOffsetX > 0 then
427 | if self.offsetX + countX > maxOffsetX then
428 | self.offsetX = maxOffsetX
429 | elseif self.offsetX + countX < 0 then
430 | self.offsetX = 0
431 | else
432 | self.offsetX = self.offsetX + countX
433 | end
434 | end
435 |
436 | if countY and countY ~= 0 and maxOffsetY > 0 then
437 | if self.offsetY + countY > maxOffsetY then
438 | self.offsetY = maxOffsetY
439 | elseif self.offsetY + countY < 0 then
440 | self.offsetY = 0
441 | else
442 | self.offsetY = self.offsetY + countY
443 | end
444 | end
445 |
446 | return true
447 | end;
448 |
449 | moveActive = function( self, countX, countY )
450 | local count
451 | if self.maxHorizontalItems then
452 | count = self.maxHorizontalItems * countY + countX
453 | else
454 | count = self.maxVerticalItems * countX + countY
455 | end
456 | local newActive = self.active + count
457 | -- if newActive > 1 and not self.items[newActive] then
458 | -- local maxCount = #self.items
459 | -- newActive = #self.items
460 | -- end
461 | return self:setActiveItem( newActive )
462 | end;
463 |
464 | -- переписать
465 | sortByText = function( self, reverse )
466 | local t = {}
467 | for id, item in pairs( self.items ) do
468 | t[id] = { utf8.lower( item.objects.text.text ), id }
469 | end
470 | table.sort( t, function( a, b )
471 | local lA, lB = utf8.len( a[1] ), utf8.len( b[1] )
472 | for i = 1, lA < lB and lA or lB do
473 | local bA = utf8.byte( a[1], i, i + 1 )
474 | local bB = utf8.byte( b[1], i, i + 1 )
475 | if bA and bB then
476 | if bA < bB then
477 | return true
478 | elseif bA > bB then
479 | return false
480 | end
481 | else
482 | return true
483 | end
484 | end
485 | return true
486 | end )
487 | local oldItems = self.items
488 | self.items = {}
489 | for i = ( reverse and #t or 1 ), ( reverse and 1 or #t ), ( reverse and -1 or 1 ) do
490 | self.items[i] = oldItems[ t[i][2] ]
491 | end
492 | return true
493 | end;
494 |
495 | find = function( self, itemDataName, value )
496 | return table.findIn( self.items, itemDataName, value )
497 | end;
498 |
499 | setKeyboardInputEnabled = function( self, state )
500 | if state then
501 | local buffer = ''
502 | local timer
503 | self.input.onCharacter = function( input, character )
504 | if timer and isTimer( timer ) then
505 | timer:destroy()
506 | end
507 | buffer = buffer .. character
508 | timer = Timer( function()
509 | buffer = ''
510 | end, 1000, 1 )
511 | for i = 1, #self.items do
512 | if utf8.lower( self.items[i].objects.text.text ):find( buffer ) then
513 | self:setActiveItem( i )
514 | return
515 | end
516 | end
517 | end
518 | else
519 | self.input.onCharacter = nil
520 | end
521 | self.keyboardInput = state
522 | return true
523 | end;
524 |
525 | objectPairs = function( self )
526 | return next, self.items
527 | end;
528 | }
529 |
--------------------------------------------------------------------------------
/dxGUI/Anim.lua:
--------------------------------------------------------------------------------
1 | local anims = {}
2 |
3 | Anim = class{
4 |
5 | create = function( self, newAnim )
6 | newAnim = class( newAnim )
7 | if newAnim.name then
8 | anims[ newAnim.name ] = newAnim
9 | end
10 | return newAnim
11 | end;
12 |
13 | find = function( name )
14 | return anims[name] or false
15 | end;
16 |
17 | }
18 |
19 | local getTickCount = getTickCount
20 | local prevTick = getTickCount()
21 | local thisTick = prevTick
22 | function getAnimTicks()
23 | return prevTick, thisTick
24 | end
25 |
26 | addEventHandler( 'onClientRender', root, function()
27 | prevTick = thisTick
28 | thisTick = getTickCount()
29 | end, true, 'high' )
30 |
31 | ----------------
32 |
33 | Anim{
34 | name = 'change-color';
35 | time = 1000;
36 | easing = 'Linear';
37 |
38 | create = function( self, gui, time, toColor, easing, fromColor )
39 | for id, anim in pairs( preAnims .anims ) do
40 | if anim.name == self.name then
41 | gui:removeAnim( id )
42 | end
43 | end
44 | fromColor = fromColor or gui:getColor()
45 | self.from = { color.RGBtoHSL( color.HEXtoRGB( fromColor ) ) }
46 | self.to = { color.RGBtoHSL( color.HEXtoRGB( toColor ) ) }
47 | self.time = time or self.time
48 | self.easting = easting or self.easting
49 | self.startTime = getAnimTicks()
50 | return self
51 | end;
52 |
53 | update = function( self, gui )
54 | local currtime = getAnimTicks()
55 | local k = getEasingValue( ( currtime - self.startTime ) / self.time, self.easting )
56 | gui:setColor(
57 | color.HSLtoRGB(
58 | self.from[1] + ( self.to[1] - self.from[1] ) * k,
59 | self.from[2] + ( self.to[2] - self.from[2] ) * k,
60 | self.from[3] + ( self.to[3] - self.from[3] ) * k,
61 | self.from[4] + ( self.to[4] - self.from[4] ) * k
62 | )
63 | )
64 | if currtime < ( self.startTime + self.time ) then
65 | return true
66 | else
67 | return false
68 | end
69 | end;
70 |
71 | onStop = function( self, gui )
72 | gui:setColor(
73 | color.HSLtoRGB(
74 | self.to[1],
75 | self.to[2],
76 | self.to[3],
77 | self.to[4]
78 | )
79 | )
80 | end;
81 | }
82 |
83 | -------------------
84 |
85 | Anim{
86 | name = 'move';
87 | eastingX = 'Linear';
88 | eastingY = 'Linear';
89 |
90 | create = function( self, gui, time, x, y, eastingX, eastingY, fromX, fromY )
91 | for id, anim in pairs( gui.preAnims ) do
92 | if anim.name == self.name then
93 | gui:removeAnim( id )
94 | end
95 | end
96 | local guiX, guiY = gui:getPosition()
97 | self.from = { fromX or guiX, fromY or guiY }
98 | self.to = { x or guiX, y or guiY }
99 | self.time = time
100 | self.eastingX = eastingX or self.eastingX
101 | self.eastingY = eastingY or self.eastingY
102 | self.startTime = getTickCount()
103 | return self
104 | end;
105 |
106 | update = function( self, gui )
107 | local currtime = getTickCount()
108 | local kX = getEasingValue( ( currtime - self.startTime ) / self.time, self.eastingX )
109 | local kY = getEasingValue( ( currtime - self.startTime ) / self.time, self.eastingY )
110 | gui:setPosition( self.from[1] + ( self.to[1] - self.from[1] ) * kX, self.from[2] + ( self.to[2] - self.from[2] ) * kY )
111 | if currtime < ( self.startTime + self.time ) then
112 | return true
113 | else
114 | gui:setPosition( self.to[1], self.to[2] )
115 | return false
116 | end
117 | end;
118 |
119 | onStop = function( self, gui )
120 | gui:setPosition( self.to[1], self.to[2] )
121 | end;
122 | }
123 |
124 | ------------------
125 |
126 | Anim{
127 | name = 'gui-out-cursor-moving';
128 |
129 | create = function( self, gui )
130 | self.onGuiClick = gui.onClick
131 | gui.onClick = function( gui, button, state, cX, cY )
132 | self.state = true
133 | self.onGuiClick( gui, button, state, cX, cY )
134 | end
135 | return self
136 | end;
137 |
138 | update = function( self, gui )
139 | if self.state then
140 | if getKeyState( 'mouse1' ) then
141 | local cX, cY = getCursorPosition( )
142 | if cX then
143 | local sX, sY = guiGetScreenSize()
144 | gui:onCursorMove( false, cX * sX, cY * sY )
145 | else
146 | self.state = false
147 | end
148 | else
149 | self.state = false
150 | end
151 | end
152 | return true
153 | end;
154 | }
155 |
156 | -- All
157 |
158 | Anim{
159 | name = 'softShow';
160 |
161 | create = function( self, gui, timeIn, timeOut )
162 | self.timeIn = timeIn
163 | self.timeOut = timeOut or timeIn
164 | self.originalSetShow = gui.setShow
165 | self.originalIsShow = gui.isShow
166 | self.state = 'default'
167 | self.show = gui.show
168 |
169 | gui.setShow = function( gui, state )
170 | local startTick = getTickCount()
171 | if self.show ~= state then
172 | if state then
173 | if self.state == 'out' then
174 | startTick = startTick - self.timeIn * ( 1 - gui:getAlpha() / 255 )
175 | else
176 | gui:setAlpha( 0 )
177 | end
178 | self.originalSetShow( gui, true )
179 | self.state = 'in'
180 | else
181 | if self.state == 'in' then
182 | startTick = startTick - self.timeOut * ( 1 - gui:getAlpha() / 255 )
183 | end
184 | self.state = 'out'
185 | end
186 | self.show = state
187 | end
188 | self.startTick = startTick
189 | end;
190 |
191 | gui.isShow = function( gui )
192 | return self.show
193 | end
194 |
195 | return self
196 | end;
197 |
198 | update = function( self, gui )
199 | local thisTick = getTickCount()
200 | if self.state == 'out' then
201 | local progress = ( thisTick - self.startTick ) / self.timeOut
202 | if progress > 1 then
203 | self.originalSetShow( gui, false )
204 | gui:setAlpha( 255 )
205 | self.state = 'default'
206 | self.startTick = thisTick
207 | else
208 | gui:setAlpha( 255 * ( 1 - progress ) )
209 | end
210 | elseif self.state == 'in' then
211 | local progress = ( thisTick - self.startTick ) / self.timeIn
212 | if progress > 1 then
213 | gui:setAlpha( 255 )
214 | self.state = 'default'
215 | else
216 | gui:setAlpha( 255 * progress )
217 | end
218 | end
219 | return true
220 | end;
221 |
222 | onStop = function ( self, gui )
223 | gui.setShow = self.originalSetShow
224 | gui.isShow = self.originalIsShow
225 | end;
226 | }
227 |
228 | -- Text
229 |
230 | Anim{
231 | name = 'hoppingText';
232 |
233 | create = function( self, gui, time, scaleCount )
234 | self.time = time
235 | self.scaleCount = scaleCount
236 | self.startScale = gui.scale
237 | return self
238 | end;
239 |
240 | update = function( self, gui )
241 | gui.scale = self.startScale + math.sin( ( getTickCount() % self.time / self.time ) * math.pi ) * self.scaleCount
242 | return true
243 | end;
244 |
245 | onStop = function ( self, gui )
246 | end;
247 | }
248 |
249 | Anim{
250 | name = 'by_letter_adding';
251 |
252 | create = function( self, gui )
253 |
254 | gui.setText = function( gui, text )
255 | text = tostring( text )
256 | self.toText = text
257 | self.currentLetter = 0
258 | self.letterCount = utf8.len( text )
259 | gui.text = ''
260 | return true
261 | end;
262 |
263 | return self
264 | end;
265 |
266 | update = function( self, gui )
267 | if self.letterCount > self.currentLetter then
268 | self.currentLetter = self.currentLetter + 1
269 | gui.text = utf8.sub( self.toText, 1, self.currentLetter )
270 | end
271 |
272 | return true
273 | end;
274 |
275 | onStop = function ( self, gui )
276 | end;
277 | }
278 |
279 | -- editField
280 | Anim{
281 | name = 'maskedText';
282 |
283 | create = function( self, gui, time )
284 | self.text = gui.objects.text:getText()
285 |
286 | self.setTextOriginal = gui.setText
287 | self.getTextOriginal = gui.getText
288 |
289 | gui.setText = function( gui, text )
290 | self.text = text
291 | gui.objects.text:setText( string.rep( "*", utfLen(text) ) )
292 | end;
293 |
294 | gui.getText = function( gui )
295 | return self.text
296 | end;
297 |
298 | return self
299 | end;
300 |
301 | update = function( self, gui )
302 | return true
303 | end;
304 |
305 | onStop = function ( self, gui )
306 | gui.setText = self.setTextOriginal
307 | gui.getText = self.getTextOriginal
308 | end;
309 | }
310 |
311 | -- List
312 | Anim{
313 | name = 'softItemMoved';
314 |
315 | create = function( self, gui, time, easing )
316 | self.time = time
317 | self.moveOriginal = gui.move
318 | self.originalUpdateAnims = gui.updateAnims
319 | self.state = 'default'
320 |
321 | gui.move = function( gui, countX, countY )
322 |
323 | local itemCount = #gui.items
324 | local maxOffsetX = gui.maxVerticalItems and ( math.ceil( itemCount / gui.maxVerticalItems ) * gui.construction.w - gui.w ) or 0
325 | local maxOffsetY = gui.maxHorizontalItems and ( math.ceil( itemCount / gui.maxHorizontalItems ) * gui.construction.h - gui.h ) or 0
326 |
327 | if self.state == 'default' then
328 |
329 | self.from = { gui.offsetX, gui.offsetY }
330 |
331 | local toX
332 | if countX and countX ~= 0 and maxOffsetX > 0 then
333 | if gui.offsetX + countX > maxOffsetX then
334 | toX = maxOffsetX
335 | elseif gui.offsetX + countX < 0 then
336 | toX = 0
337 | else
338 | toX = gui.offsetX + countX
339 | end
340 | else
341 | toX = gui.offsetX
342 | end
343 |
344 | local toY
345 | if countY and countY ~= 0 and maxOffsetY > 0 then
346 | if gui.offsetY + countY > maxOffsetY then
347 | toY = maxOffsetY
348 | elseif gui.offsetY + countY < 0 then
349 | toY = 0
350 | else
351 | toY = gui.offsetY + countY
352 | end
353 | else
354 | toY = gui.offsetY
355 | end
356 |
357 | if toX ~= gui.offsetX or toY ~= gui.offsetY then
358 | self.to = { toX, toY }
359 | self.state = 'move'
360 | self.startTick = getTickCount( )
361 | end
362 | else
363 | local toX
364 | if countX and countX ~= 0 and maxOffsetX > 0 then
365 | if self.to[1] + countX > maxOffsetX then
366 | toX = maxOffsetX
367 | elseif self.to[1] + countX < 0 then
368 | toX = 0
369 | else
370 | toX = self.to[1] + countX
371 | end
372 | else
373 | toX = self.to[1]
374 | end
375 |
376 | local toY
377 | if countY and countY ~= 0 and maxOffsetY > 0 then
378 | if self.to[2] + countY > maxOffsetY then
379 | toY = maxOffsetY
380 | elseif self.to[2] + countY < 0 then
381 | toY = 0
382 | else
383 | toY = self.to[2] + countY
384 | end
385 | else
386 | toY = self.to[2]
387 | end
388 |
389 | if toX ~= self.to[1] or toY ~= self.to[2] then
390 | self.to = { toX, toY }
391 | self.state = 'move'
392 | --self.startTick = getTickCount( )
393 | end
394 | end
395 | end;
396 |
397 | return self
398 | end;
399 |
400 | update = function( self, gui )
401 | local thisTick = getTickCount()
402 | if self.state == 'move' then
403 | local progress = ( thisTick - self.startTick ) / self.time
404 | if progress > 1 then
405 | gui.offsetX, gui.offsetY = self.to[1], self.to[2]
406 | self.to = nil
407 | self.from = nil
408 | self.state = 'default'
409 | return true
410 | end
411 | gui.offsetX, gui.offsetY = self.from[1] + ( self.to[1] - self.from[1] ) * progress,
412 | self.from[2] + ( self.to[2] - self.from[2] ) * progress
413 | end
414 | return true
415 | end;
416 |
417 | onStop = function ( self, gui )
418 | gui.move = self.moveOriginal
419 | end;
420 | }
421 |
422 | -- TextLines
423 | Anim{
424 | name = 'softTextLinesMoved';
425 |
426 | create = function( self, gui, time, easing )
427 | self.time = time
428 | self.moveOriginal = gui.move
429 | self.originalUpdateAnims = gui.updateAnims
430 | self.state = 'default'
431 |
432 | gui.move = function( gui, countY )
433 | local maxOffsetY = (#gui.lines - 1) * gui.fontSize * gui.lineSpacing - gui.h
434 |
435 | if self.state == 'default' then
436 |
437 | self.from = gui.offsetY
438 |
439 | local toY
440 | if countY and countY ~= 0 and maxOffsetY > 0 then
441 | if gui.offsetY + countY > maxOffsetY then
442 | toY = maxOffsetY
443 | elseif gui.offsetY + countY < 0 then
444 | toY = 0
445 | else
446 | toY = gui.offsetY + countY
447 | end
448 | else
449 | toY = gui.offsetY
450 | end
451 |
452 | if toY ~= gui.offsetY then
453 | self.to = toY
454 | self.state = 'move'
455 | self.startTick = getTickCount( )
456 | end
457 | else
458 |
459 | local toY
460 | if countY and countY ~= 0 and maxOffsetY > 0 then
461 | if self.to + countY > maxOffsetY then
462 | toY = maxOffsetY
463 | elseif self.to + countY < 0 then
464 | toY = 0
465 | else
466 | toY = self.to + countY
467 | end
468 | else
469 | toY = self.to
470 | end
471 |
472 | if toY ~= self.to then
473 | self.to = toY
474 | self.state = 'move'
475 | --self.startTick = getTickCount( )
476 | end
477 | end
478 | end;
479 |
480 | return self
481 | end;
482 |
483 | update = function( self, gui )
484 | local thisTick = getTickCount()
485 | if self.state == 'move' then
486 | local progress = ( thisTick - self.startTick ) / self.time
487 | if progress > 1 then
488 | gui.offsetY = self.to
489 | self.to = nil
490 | self.from = nil
491 | self.state = 'default'
492 | return true
493 | end
494 | gui.offsetY = self.from + ( self.to - self.from ) * progress
495 | end
496 | return true
497 | end;
498 |
499 | onStop = function ( self, gui )
500 | gui.move = self.moveOriginal
501 | end;
502 | }
503 |
504 | -- Tab
505 |
506 | Anim{
507 | name = 'softTabChange';
508 |
509 | create = function( self, gui, time )
510 | self.time = time
511 | self.setActiveTabOriginal = gui.setActiveTab
512 | self.state = 'default'
513 |
514 | gui.setActiveTab = function( gui, newTab )
515 | self.startTick = getTickCount()
516 | self.state = 'out'
517 | self.toTab = newTab
518 | end;
519 |
520 | return self
521 | end;
522 |
523 | update = function( self, gui )
524 | local thisTick = getTickCount()
525 | if self.state == 'out' then
526 | local progress = ( thisTick - self.startTick ) / self.time
527 | if progress > 1 then
528 | self.setActiveTabOriginal( gui, self.toTab )
529 | gui:setAlpha( 0 )
530 | self.state = 'in'
531 | self.startTick = thisTick
532 | else
533 | gui:setAlpha( 255 * ( 1 - progress ) )
534 | end
535 | elseif self.state == 'in' then
536 | local progress = ( thisTick - self.startTick ) / self.time
537 | if progress > 1 then
538 | gui:setAlpha( 255 )
539 | self.state = 'default'
540 | else
541 | gui:setAlpha( 255 * progress )
542 | end
543 | end
544 | return true
545 | end;
546 |
547 | onStop = function ( self, gui )
548 | gui.setActiveTab = self.setActiveTabOriginal
549 | end;
550 | };
551 |
552 | Anim{
553 | name = 'extremeHiding';
554 | time = 500;
555 |
556 | create = function( self, gui, time )
557 | self.time = time
558 | self.originalSetShow = gui.setShow
559 | self.originalDraw = gui.draw
560 | self.state = 'default'
561 | self.show = gui.show
562 |
563 | gui.setShow = function( gui, state )
564 | self.startTick = getTickCount()
565 | if self.show ~= state then
566 | if state then
567 | self.originalSetShow( gui, true )
568 | self.state = 'in'
569 | self.progress = 0
570 | else
571 | self.state = 'out'
572 | self.progress = 1
573 | end
574 | self.show = state
575 | gui.renderTarget = DxRenderTarget( gui.w, gui.h, true )
576 | gui.draw = function( gui )
577 | if gui.show then
578 | local x, y = gui:getPosition()
579 | gui:setPosition( 0, 0 )
580 | self.originalDraw( gui )
581 | dxSetRenderTarget( )
582 | gui:setPosition( x, y )
583 | local w, h = gui.w * self.progress, gui.h * self.progress
584 | local offX = (gui.w - w) / 2
585 | local offY = (gui.h - h) / 2
586 | dxDrawImageSection( x + offX, y + offY, w, h, offX, offY, w, h, gui.renderTarget, 0, 0, 0, tocolor( 255, 255, 255 ), gui.postGUI )
587 | end
588 | end
589 | end
590 | end;
591 |
592 | gui.isShow = function( gui )
593 | return self.show
594 | end
595 |
596 | return self
597 | end;
598 |
599 | update = function( self, gui )
600 | local thisTick = getTickCount()
601 | if self.state ~= 'default' then
602 | local timeProgress = ( thisTick - self.startTick ) / self.time
603 | self.progress = ( self.state == 'in' and timeProgress ) or ( 1 - timeProgress )
604 | if timeProgress >= 1 then
605 | -- gui.draw = self.originalDraw
606 | -- gui.renderTarget:destroy()
607 | if self.progress <= 0 then
608 | self.originalSetShow( gui, false )
609 | end
610 | self.progress = 1
611 | end
612 | end
613 | return true
614 | end;
615 |
616 | onStop = function ( self, gui )
617 | gui.setShow = self.originalSetShow
618 | end;
619 | }
620 |
621 | -- All
622 | -- Переписать так, чтобы клик обрабатывался только на меню
623 | Anim{
624 | name = 'mouseMoving';
625 | key = 'mouse1';
626 |
627 | create = function( self, gui, key, attachGUI )
628 | self.key = key
629 | self.attachGUI = attachGUI or gui
630 |
631 | self.isMove = false
632 |
633 | self.sX, self.sY = guiGetScreenSize()
634 |
635 | return self
636 | end;
637 |
638 | update = function( self, gui )
639 | local isOnCursor = self.attachGUI:isOnCursor()
640 | local isClick = getKeyState( self.key )
641 |
642 | if self.isMove then
643 | if isClick then
644 | local x, y = getCursorPosition( )
645 | x, y = x * self.sX, y * self.sY
646 |
647 | local gX, gY = gui:getPosition()
648 | gui:setPosition( gX + x - self.x, gY + y - self.y )
649 |
650 | self.x, self.y = x, y
651 | else
652 | self.isMove = false
653 | end
654 | else
655 | if isOnCursor and isClick then
656 | self.x, self.y = getCursorPosition( )
657 | self.x, self.y = self.x * self.sX, self.y * self.sY
658 | self.isMove = true
659 | end
660 | end
661 | return true
662 | end;
663 |
664 | onStop = function( self, gui )
665 | -- body
666 | end;
667 | }
668 |
669 | Anim{
670 | name = 'attach';
671 |
672 | create = function( self, gui, attachTo, offX, offY )
673 | self.attachTo = attachTo
674 | local gX, gY = gui:getPosition()
675 | local tX, tY = attachTo:getPosition()
676 | self.offX = offX or ( gX - tX )
677 | self.offY = offY or ( gY - tY )
678 | return self
679 | end;
680 |
681 | update = function( self, gui )
682 | local x, y = self.attachTo:getPosition()
683 | gui:setPosition( x + self.offX, y + self.offY )
684 | return true
685 | end;
686 |
687 | }
688 |
689 | Anim{
690 | name = 'updateScreenSource';
691 |
692 | create = function( self )
693 | return self
694 | end;
695 |
696 | update = function( self, gui )
697 | gui.image:update()
698 | return true
699 | end;
700 |
701 | }
702 |
--------------------------------------------------------------------------------
/dxGUI/Map.lua:
--------------------------------------------------------------------------------
1 | dxGUI.baseClass:subclass{
2 | type = 'map';
3 | reguestScreen = true;
4 |
5 | useRenderTarget = false;
6 |
7 | camRootRad = 0;
8 |
9 | minZoom = 0.5;
10 | maxZoom = 5;
11 | zoom = 2;
12 |
13 | restrictMovement = false;
14 |
15 | -- World map border
16 | leftX = -3000;
17 | rightX = 3000;
18 | topY = -3000;
19 | bottomY = 3000;
20 |
21 | mapIsCircle = false;
22 |
23 | drawBlips = false;
24 | drawOutBlips = true;
25 | checkBlipDistance = false;
26 | blipZoom = 1;
27 |
28 | drawPlayersNames = false;
29 | drawPlayersNamesDistance = 100;
30 |
31 | drawLocalPlayer = false;
32 |
33 | drawNorth = false;
34 |
35 | borderColor = 0xFFFFFFFF;
36 | maskShader = 'dxGUI/shaders/hud_mask.fx';
37 |
38 | create = function( self )
39 |
40 | self.radarW = self.radarW or self.w
41 | self.radarH = self.radarH or self.h
42 |
43 | self.map = self.map or DxTexture( self.mapPath )
44 |
45 | if not self.map then
46 | self:errorHandler( 'Failed to load map' )
47 | return false
48 | end
49 |
50 | self.mapW, self.mapH = self.map:getSize()
51 |
52 | self.mapCenterX = self.mapCenterX or self.mapW / 2
53 | self.mapCenterY = self.mapCenterY or self.mapH / 2
54 |
55 | local offsetX = ( self.mapW / 2 - self.mapCenterX )
56 | if offsetX ~= 0 then
57 | self.worldOffsetFromCenterX = ( self.rightX - self.leftX ) / ( self.mapW / 2 - self.mapCenterX ) * 4
58 | else
59 | self.worldOffsetFromCenterX = 0
60 | end
61 |
62 | local offsetY = ( self.mapCenterY - self.mapH / 2 )
63 | if offsetY ~= 0 then
64 | self.worldOffsetFromCenterY = ( self.topY - self.bottomY ) / offsetY * 4
65 | else
66 | self.worldOffsetFromCenterY = 0
67 | end
68 |
69 | self.UVPosition = { 0, 0 }
70 |
71 | if self.playerIconPath then
72 | self.playerIcon = DxTexture( self.playerIconPath )
73 |
74 | if not self.playerIcon then
75 | self:errorHandler( 'Failed to load player icon' )
76 | self.drawLocalPlayer = false
77 | end
78 |
79 | end
80 |
81 | if self.mapMaskPath then
82 | self.mapMask = DxTexture( self.mapMaskPath )
83 |
84 | if not self.mapMask then
85 | self:errorHandler( 'Failed to load map mask' )
86 | return false
87 | end
88 |
89 | else
90 | self.radarMask = DxTexture( self.w, self.h )
91 | end
92 |
93 | if self.blipsRootPath then
94 | self:loadBlips( self.blipsRootPath )
95 |
96 | if not self.playerIcon and self.drawLocalPlayer then
97 | self.playerIcon = self.blips[2]
98 | end
99 | elseif not self.blips then
100 | self.drawBlips = false
101 | end
102 |
103 | if self.blipObject then
104 | self.blipObjects = {}
105 | self:addEventHandler( 'onElementDestroy', root, self.onElementDestroy )
106 | end
107 |
108 | self.shader = dxCreateShader( self.maskShader )
109 |
110 | self:setDrawType( self.useRenderTarget )
111 |
112 | --dxSetShaderValue( self.shader, "sPicTexture", self.map )
113 | if self.mapMask then
114 | dxSetShaderValue( self.shader, "sMaskTexture", self.mapMask );
115 | end
116 |
117 | if self.mapIsCircle then
118 | self.mapRadius = self.mapRadius or self.w / 2
119 | end
120 |
121 | dxSetShaderValue( self.shader, "gUVPosition", 0, 0 )
122 | dxSetShaderValue( self.shader, "gUVRotAngle", 0 )
123 | dxSetShaderValue( self.shader, "gUVRotCenter", 0.5, 0.5 )
124 | dxSetShaderValue( self.shader, "gUVScale", 1, 1 )
125 | dxSetShaderValue( self.shader, "gUVScaleCenter", 0.5, 0.5 )
126 | dxSetShaderValue( self.shader, "borderColor", self.borderColor )
127 |
128 | return self
129 | end;
130 |
131 | loadBlips = function( self, blipsRootPath )
132 | local blips = self.blips
133 | if blips then
134 | for key, blipTexture in pairs( blips ) do
135 | blipTexture:destroy()
136 | end
137 | else
138 | blips = {}
139 | self.blips = blips
140 | end
141 |
142 | for i = 0, 63 do
143 | blips[i] = DxTexture( blipsRootPath .. i .. '.png' )
144 | end
145 | return true
146 | end;
147 |
148 | setDrawType = function( self, isRenderTargerType )
149 | if isRenderTargerType then
150 | if not self.renderTarget then
151 | self.renderTarget = DxRenderTarget( self.mapW, self.mapH )
152 | dxSetShaderValue( self.shader, "sPicTexture", self.renderTarget )
153 | self.draw = self.drawInRenderTarget
154 | return true
155 | end
156 | return false
157 | else
158 | if self.renderTarget then
159 | self.renderTarget:destroy()
160 | self.renderTarget = nil
161 | end
162 | dxSetShaderValue( self.shader, "sPicTexture", self.map )
163 | self.draw = self.drawOnShader
164 | return true
165 | end
166 | end;
167 |
168 | drawOnShader = function ( self )
169 |
170 | local camRootRad = self.camRootRad
171 | dxSetShaderValue( self.shader, "gUVRotAngle", -camRootRad )
172 |
173 | local shaderZoom = 1/self.zoom
174 | dxSetShaderValue( self.shader, "gUVScale", shaderZoom, shaderZoom * self.w/self.h )
175 | dxSetShaderValue( self.shader, "gUVPosition", self.UVPosition[1], self.UVPosition[2] )
176 |
177 | dxDrawImage( self.x, self.y, self.w, self.h, self.shader )
178 |
179 | if self.drawPlayerNames then
180 |
181 | local mapOffsetX = ( self.w - self.radarW ) / 2
182 | local mapOffsetY = ( self.h - self.radarH ) / 2
183 |
184 | local function drawPlayerName( player, pos )
185 | local x = drawCenterX + pos.x / 6000 * screenW * self.zoom
186 | local y = drawCenterY + pos.y / -6000 * screenW * self.zoom + 10
187 | dxDrawText( player:getName(), x, y, x, y, tocolor( 0, 0, 0 ), self.zoom + 0.2,
188 | "default-bold", "center", "top", false, false, false, true )
189 | end
190 |
191 | if self.mapPlayerNameMode == 1 then
192 | for _, player in pairs( getElementsByType( 'player' ) ) do
193 | drawPlayerName( player, player:getPosition() )
194 | end
195 | elseif self.mapPlayerNameMode == 2 then
196 | for _, player in pairs( getElementsByType( 'player' ) ) do
197 | local pPos = player:getPosition()
198 | if ( pPos - vPos ):getLength() < 100 then
199 | drawPlayerName( player, pPos )
200 | end
201 | end
202 | end
203 |
204 | end
205 |
206 | local lpPos = localPlayer:getPosition()
207 | local pX = lpPos.x
208 | local pY = lpPos.y
209 |
210 |
211 | local blipSizeMod = 32 / 4 * self.blipZoom
212 |
213 | local mapOffsetX = ( self.w - self.radarW ) / 2
214 | local mapOffsetY = ( self.h - self.radarH ) / 2
215 |
216 | local drawCenterX = self.x + self.w / 2
217 | local drawCenterY = self.y + self.h / 2
218 |
219 | if self.drawBlips then
220 |
221 | local bS, halfBS
222 |
223 | if self.mapIsCircle then
224 | local radius = self.mapRadius
225 |
226 | for _, blip in pairs( getElementsByType( 'blip' ) ) do
227 |
228 | local blipX, blipY, blipZ = getElementPosition( blip )
229 | local distance = ( ( lpPos.x - blipX )^2 + ( lpPos.y - blipY )^2 ) ^ 0.5
230 | local blipRot = math.atan2( blipX - pX, blipY - pY ) + camRootRad
231 | local attached = getElementAttachedTo( blip )
232 | if distance <= blip:getVisibleDistance() and attached ~= localPlayer then
233 |
234 | local pixelDistance = distance / self.bottomY * self.zoom * self.w / 2
235 | local iconID = blip:getIcon()
236 |
237 | local bcR, bcG, bcB, bcA = getBlipColor( blip )
238 | if iconID ~= 0 then
239 | bcR, bcG, bcB = 255, 255, 255
240 | end
241 | bS = blip:getSize() * blipSizeMod
242 | halfBS = bS / 2
243 |
244 | if pixelDistance < radius then
245 | dxDrawImage(
246 | drawCenterX + pixelDistance * math.cos( blipRot - math.pi/2 ) - halfBS,
247 | drawCenterY + pixelDistance * math.sin( blipRot - math.pi/2 ) - halfBS,
248 | bS, bS, self.blips[iconID], 0, 0, 0, tocolor(bcR, bcG, bcB, bcA)
249 | )
250 | elseif self.drawOutBlips then
251 | dxDrawImage(
252 | drawCenterX + radius * math.cos( blipRot - math.pi/2 ) - halfBS,
253 | drawCenterY + radius * math.sin( blipRot - math.pi/2 ) - halfBS,
254 | bS, bS, self.blips[iconID], 0, 0, 0, tocolor(bcR, bcG, bcB, bcA)
255 | )
256 | end
257 |
258 | end
259 | end
260 | else
261 |
262 | for _, blip in pairs( getElementsByType( 'blip' ) ) do
263 |
264 | local blipX, blipY, blipZ = getElementPosition( blip )
265 | local distance = ( ( lpPos.x - blipX )^2 + ( lpPos.y - blipY )^2 ) ^ 0.5
266 | local blipRot = math.atan2( blipX - pX, blipY - pY ) + camRootRad
267 | local attached = getElementAttachedTo( blip )
268 | if distance <= blip:getVisibleDistance() and attached ~= localPlayer then
269 |
270 | local pixelDistance = distance / self.bottomY * self.zoom * self.w / 2
271 | local iconID = blip:getIcon()
272 |
273 | local bcR, bcG, bcB, bcA = getBlipColor( blip )
274 | if iconID ~= 0 then
275 | bcR, bcG, bcB = 255, 255, 255
276 | end
277 | bS = blip:getSize() * blipSizeMod
278 | halfBS = bS / 2
279 |
280 | local drawBlipX = drawCenterX + pixelDistance * math.cos( blipRot - math.pi/2 ) - halfBS
281 | local drawBlipY = drawCenterY + pixelDistance * math.sin( blipRot - math.pi/2 ) - halfBS
282 |
283 | local clampX = math.clamp( self.x - halfBS + mapOffsetX, drawBlipX, self.x + self.w - halfBS - mapOffsetX )
284 | local clampY = math.clamp( self.y - halfBS + mapOffsetY, drawBlipY, self.y + self.h - halfBS - mapOffsetY )
285 |
286 | if self.drawOutBlips or ( clampX == drawBlipX and clampY == drawBlipY ) then
287 | dxDrawImage( clampX, clampY, bS, bS, self.blips[iconID], 0, 0, 0, tocolor(bcR, bcG, bcB, bcA) )
288 | end
289 |
290 | end
291 | end
292 |
293 | end
294 |
295 | end
296 |
297 | if self.drawNorth then
298 | if self.mapIsCircle then
299 | local bS = 2.5 * blipSizeMod
300 | local halfBS = bS / 2
301 | local blipRot = self.camRootRad
302 | dxDrawImage(
303 | drawCenterX + self.mapRadius * math.cos( blipRot - math.pi/2 ) - halfBS,
304 | drawCenterY + self.mapRadius * math.sin( blipRot - math.pi/2 ) - halfBS,
305 | bS, bS, self.blips[4], 0, 0, 0
306 | )
307 | else
308 | local radarRadius = ( (self.radarW/2)^2 + (self.radarH/2)^2 )^0.5
309 | dxDrawImage( mapOffsetX + self.x + math.max( 0, self.radarW / 2 + math.min( self.radarW / 2, math.cos( self.camRootRad - math.pi/2 ) * radarRadius ) ) - 1.25 * blipSizeMod,
310 | mapOffsetY + self.y + math.max( 0, self.radarH / 2 + math.min( self.radarH / 2, math.sin( self.camRootRad - math.pi/2 ) * radarRadius ) ) - 1.25 * blipSizeMod,
311 | 2.5 * blipSizeMod, 2.5 * blipSizeMod, self.blips[4] )
312 | end
313 | end
314 |
315 | if self.drawLocalPlayer then
316 | local _,_, rz = getElementRotation( localPlayer )
317 | dxDrawImage( self.x - 1.25 * blipSizeMod + self.w / 2 , self.y - 1.25 * blipSizeMod + self.h / 2, 2.5 * blipSizeMod, 2.5 * blipSizeMod, self.playerIcon, -rz + math.deg( camRootRad ), 0, 0 )
318 | end
319 |
320 | end;
321 |
322 | drawInRenderTarget = function( self )
323 |
324 | dxSetShaderValue( self.shader, "gUVScale", 1/self.zoom, 1/self.zoom * self.h/self.w * self.mapW / self.mapH )
325 | dxSetShaderValue( self.shader, "gUVPosition", self.UVPosition[1], self.UVPosition[2] )
326 |
327 | self.renderTarget:setAsTarget()
328 |
329 | dxDrawImage( 0, 0, self.mapW, self.mapH, self.map )
330 |
331 | local lpPos = localPlayer:getPosition()
332 |
333 | if self.drawBlips then
334 |
335 | local mapOffsetX = ( self.w - self.radarW ) / 2
336 | local mapOffsetY = ( self.h - self.radarH ) / 2
337 |
338 | for _, blip in pairs( getElementsByType( 'blip' ) ) do
339 |
340 | local blipX, blipY, blipZ = getElementPosition( blip )
341 |
342 | if not self.checkBlipDistance
343 | or ( ( lpPos.x - blipX )^2 + ( lpPos.y - blipY )^2 ) ^ 0.5 <= blip:getVisibleDistance()
344 | then
345 | local drawBlipX = self.mapW / ( -self.leftX + self.rightX ) * ( blipX - self.leftX )
346 | local drawBlipY = self.mapH / ( -self.topY + self.bottomY ) * ( -blipY - self.topY )
347 |
348 | self:drawBlip( blip, drawBlipX, drawBlipY )
349 | end
350 |
351 | end
352 | end
353 |
354 | dxSetRenderTarget()
355 |
356 | dxDrawImage( self.x, self.y, self.w, self.h, self.shader, 0, 0, 0, self.color )
357 | end;
358 |
359 | drawBlip = function( self, blip, x, y )
360 | if self.blipObject then
361 | local blipObject = self.blipObjects[blip]
362 | if not blipObject then
363 | blipObject = {
364 | x = 0;
365 | y = 0;
366 | w = self.blipObject.w / ( self.scale and self.scale[1] or 1 );
367 | h = self.blipObject.h / ( self.scale and self.scale[2] or 1 );
368 | objects = table.copy( self.blipObject.objects, true );
369 | anims = table.copy( self.blipObject.anims or {}, true );
370 | style = self.style;
371 | show = true;
372 |
373 | blip = blip;
374 |
375 | setBlipDrawPosition = self.blipObject.setBlipDrawPosition;
376 | onCreate = self.blipObject.onCreate;
377 | updateAnims = self.blipObject.updateAnims;
378 |
379 | }
380 | self.blipObjects[blip] = blipObject
381 | dxConstruction( blipObject, false )
382 |
383 | if blipObject.onCreate then
384 | blipObject:onCreate( )
385 | end
386 |
387 | self:addEventHandler( 'onClientElementDestroy', blip, function()
388 | self.blipObjects[blip] = nil
389 | end )
390 |
391 | end
392 | blipObject:setBlipDrawPosition( x, y )
393 |
394 | blipObject:updateAnims()
395 | blipObject:draw()
396 | else
397 | local iconID = blip:getIcon()
398 |
399 | local bcR, bcG, bcB, bcA = getBlipColor( blip )
400 | if iconID ~= 0 then
401 | bcR, bcG, bcB = 255, 255, 255
402 | end
403 |
404 | local bS = blip:getSize() * self.blipZoom
405 | dxDrawImage( x - 32 * 0.5 * bS, y - 32 * 0.5 * bS, 32 * bS, 32 * bS, self.blips[iconID], 0, 0, 0, tocolor(bcR, bcG, bcB, bcA) )
406 | end
407 | end;
408 |
409 | onClick = function( self, button, state, cX, cY )
410 | local dcX, dcY = self:getDrawCenter()
411 | -- cX = drawCenterX + vPos.x / 6000 * screenW * self.zoom - 16
412 | if self.onMapClick then
413 | local x = ( cX - dcX ) * ( self.rightX - self.leftX ) / self.w / self.zoom + self.worldOffsetFromCenterX * 1.25
414 | local y = ( cY - dcY ) * -( self.bottomY - self.topY ) / self.h / self.zoom * self.h/self.w * self.mapW / self.mapH + self.worldOffsetFromCenterY * 1.25
415 | self:onMapClick( button, state, cX, cY, x, y )
416 | end
417 | end;
418 |
419 | getDrawCenter = function( self )
420 | return self.x + self.w / 2 - self.w * self.UVPosition[1] * self.zoom,
421 | self.y + self.h / 2 - self.h * self.UVPosition[2] * self.zoom * self.w/self.h * self.mapH / self.mapW
422 | end;
423 |
424 | getOffScreenSize = function( self )
425 | local offscreenSizeX, offscreenSizeY
426 | if self.offscreenSizeX then
427 | offscreenSizeX = self.offscreenSizeX
428 | else
429 | offscreenSizeX = ( 1 - 1/self.zoom ) / 2
430 | end
431 | if self.offscreenSizeY then
432 | offscreenSizeY = self.offscreenSizeX
433 | else
434 | offscreenSizeY = ( 1 - 1/self.zoom * self.h/self.w ) / 2
435 | end
436 |
437 | return offscreenSizeX, offscreenSizeY
438 | end;
439 |
440 | setZoom = function( self, zoom )
441 | self.zoom = math.clamp( self.minZoom, zoom, self.maxZoom )
442 | if self.restrictMovement then
443 | local offscreenSizeX, offscreenSizeY = self:getOffScreenSize()
444 | self.UVPosition[1] = math.clamp( -offscreenSizeX, self.UVPosition[1], offscreenSizeX )
445 | self.UVPosition[2] = math.clamp( -offscreenSizeY, self.UVPosition[2], offscreenSizeY )
446 | end
447 | end;
448 |
449 | getZoom = function( self )
450 | return self.zoom
451 | end;
452 |
453 | move = function( self, moveX, moveY )
454 | if self.restrictMovement then
455 | local offscreenSizeX, offscreenSizeY = self:getOffScreenSize()
456 | self.UVPosition[1] = math.clamp( -offscreenSizeX, self.UVPosition[1] + moveX, offscreenSizeX )
457 | self.UVPosition[2] = math.clamp( -offscreenSizeY, self.UVPosition[2] + moveY, offscreenSizeY )
458 | else
459 | self.UVPosition[1] = self.UVPosition[1] + moveX
460 | self.UVPosition[2] = self.UVPosition[2] + moveY
461 | end
462 | end;
463 |
464 | setMapCenterPosition = function( self, x, y )
465 | self.UVPosition[1] = -0.5 + ( x - self.leftX ) / ( self.rightX - self.leftX )
466 | self.UVPosition[2] = -0.5 + ( y - self.bottomY ) / ( self.topY - self.bottomY )
467 | end;
468 |
469 | onElementDestroy = function( self )
470 | local blipObject = self.blipObjects[source]
471 | if blipObject then
472 | --blipObject:destroy()
473 | self.blipObjects[source] = nil
474 | end
475 | end;
476 |
477 | setScale = function( self, scale, scaleY )
478 | scaleY = scaleY or scale
479 | self.w, self.h = self.w * scale, self.h * scaleY
480 | if self.mapRadius then
481 | self.mapRadius = self.mapRadius * scale
482 | end
483 | self.blipZoom = self.blipZoom * scale
484 | self.radarW = self.radarW * scale
485 | self.radarH = self.radarH * scaleY
486 | self.blipZoom = self.blipZoom * scale
487 | end;
488 | }
489 |
490 | Anim{
491 | name = 'map_dynamicZoom';
492 |
493 | create = function( self, gui )
494 | return self
495 | end;
496 |
497 | update = function( self, gui )
498 | local vehicle = localPlayer:getOccupiedVehicle()
499 | local velocity = vehicle and vehicle:getVelocity() or localPlayer:getVelocity()
500 | gui.zoom = math.min( gui.minZoom/velocity:getLength(), gui.maxZoom )
501 | return true
502 | end;
503 | }
504 |
505 | Anim{
506 | name = 'map_dynamicRot';
507 |
508 | create = function( self, gui )
509 | return self
510 | end;
511 |
512 | update = function( self, gui )
513 | local _,_,camrot = getElementRotation( getCamera() )
514 | gui.camRootRad = math.rad(camrot)
515 | return true
516 | end;
517 | }
518 |
519 | Anim{
520 | name = 'map_dynamicPosition';
521 |
522 | create = function( self, gui, element )
523 | self.element = element
524 | return self
525 | end;
526 |
527 | update = function( self, gui )
528 | local vPos = self.element:getPosition()
529 | gui:setMapCenterPosition( vPos.x, vPos.y )
530 | return true
531 | end;
532 | }
533 |
534 | Anim{
535 | name = 'map_wheelZoom';
536 |
537 | zoomFactor = 0.2;
538 |
539 | create = function( self, gui, zoomFactor )
540 | self.zoomFactor = zoomFactor or self.zoomFactor
541 | return self
542 | end;
543 | }
544 |
545 | Anim{
546 | name = 'map_softMove';
547 | speedFactor = 0.25;
548 |
549 | create = function( self, gui, speedFactor )
550 | self.move = gui.move
551 | self.speedFactor = speedFactor or self.speedFactor
552 |
553 | self.moveBuffer = { 0, 0 }
554 |
555 | gui.move = function( gui, x, y )
556 | self.moveBuffer[1] = self.moveBuffer[1] + x
557 | self.moveBuffer[2] = self.moveBuffer[2] + y
558 | end
559 |
560 | return self
561 | end;
562 |
563 | update = function( self, gui )
564 | if self.moveBuffer[1] ~= 0 or self.moveBuffer[2] ~= 0 then
565 | local x = self.moveBuffer[1] * self.speedFactor / gui.zoom
566 | local y = self.moveBuffer[2] * self.speedFactor / gui.zoom
567 | self.move( gui, x, y )
568 | self.moveBuffer[1] = self.moveBuffer[1] - x
569 | self.moveBuffer[2] = self.moveBuffer[2] - y
570 | end
571 | return true
572 | end;
573 | }
574 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------