├── .github
├── FUNDING.yml
└── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── .gitignore
├── LICENSE
├── README.md
├── cfg
├── binds.lst
└── binds_default.cfg
├── class.lua
├── common
├── color.lua
└── vector.lua
├── conf.lua
├── engine
├── client
│ ├── bind.lua
│ ├── camera.lua
│ ├── canvas.lua
│ ├── chat.lua
│ ├── debugoverlay.lua
│ ├── graphics.lua
│ ├── gui
│ │ ├── autoloader.lua
│ │ ├── box
│ │ │ ├── init.lua
│ │ │ └── properties.lua
│ │ ├── button.lua
│ │ ├── checkbox.lua
│ │ ├── closebutton.lua
│ │ ├── commandbutton.lua
│ │ ├── commandbuttongroup.lua
│ │ ├── console
│ │ │ ├── init.lua
│ │ │ ├── textbox.lua
│ │ │ └── textboxautocompleteitemgroup.lua
│ │ ├── debugoverlaypanel.lua
│ │ ├── dropdownlist.lua
│ │ ├── dropdownlistitem.lua
│ │ ├── dropdownlistitemgroup.lua
│ │ ├── frame.lua
│ │ ├── framerate.lua
│ │ ├── frametab.lua
│ │ ├── frametabgroup.lua
│ │ ├── frametabpanel.lua
│ │ ├── frametabpanels.lua
│ │ ├── handlers.lua
│ │ ├── hudframe.lua
│ │ ├── hudprofiler.lua
│ │ ├── hudvoice.lua
│ │ ├── imagepanel.lua
│ │ ├── init.lua
│ │ ├── label.lua
│ │ ├── netgraph.lua
│ │ ├── optionsmenu
│ │ │ ├── audiooptionspanel.lua
│ │ │ ├── bindlistheader.lua
│ │ │ ├── bindlistitem.lua
│ │ │ ├── bindlistpanel.lua
│ │ │ ├── init.lua
│ │ │ ├── keyboardoptionsadvancedframe.lua
│ │ │ ├── keyboardoptionscommandbuttongroup.lua
│ │ │ ├── keyboardoptionspanel.lua
│ │ │ ├── multiplayeroptionspanel.lua
│ │ │ └── videooptionspanel.lua
│ │ ├── panel.lua
│ │ ├── passwordtextbox.lua
│ │ ├── progressbar.lua
│ │ ├── radiobutton.lua
│ │ ├── radiobuttongroup.lua
│ │ ├── rootpanel.lua
│ │ ├── scheme.lua
│ │ ├── scrollablepanel.lua
│ │ ├── scrollbar.lua
│ │ ├── slider.lua
│ │ ├── tabbedframe.lua
│ │ ├── testframe.lua
│ │ ├── text.lua
│ │ ├── textbox.lua
│ │ ├── textboxautocompleteitemgroup.lua
│ │ ├── throbber.lua
│ │ ├── viewport.lua
│ │ └── watch.lua
│ ├── handlers.lua
│ ├── init.lua
│ ├── input.lua
│ ├── network
│ │ ├── init.lua
│ │ ├── localhost_enet_peer.lua
│ │ └── localhost_enet_server.lua
│ ├── payloads.lua
│ ├── source.lua
│ └── sprite.lua
├── init.lua
├── server
│ ├── handlers.lua
│ ├── init.lua
│ ├── network
│ │ ├── host.lua
│ │ └── init.lua
│ └── payloads.lua
└── shared
│ ├── addon.lua
│ ├── baselib.lua
│ ├── buttons.lua
│ ├── concommand.lua
│ ├── config.lua
│ ├── convar.lua
│ ├── dblib.lua
│ ├── entities
│ ├── character.lua
│ ├── entity.lua
│ ├── init.lua
│ ├── networkvar.lua
│ ├── npc.lua
│ ├── player.lua
│ ├── trigger.lua
│ ├── trigger_changelevel.lua
│ └── trigger_transition.lua
│ ├── heaplib.lua
│ ├── hook.lua
│ ├── loadlib.lua
│ ├── map
│ ├── init.lua
│ ├── layer.lua
│ └── tileset.lua
│ ├── mathlib.lua
│ ├── network
│ ├── payload.lua
│ └── payloads.lua
│ ├── path
│ ├── init.lua
│ └── node.lua
│ ├── profile.lua
│ ├── strlib.lua
│ ├── tablib.lua
│ ├── tween.lua
│ └── typelenvalues.lua
├── fonts
├── SourceCodePro-Light.otf
├── SourceSansPro-Bold.otf
├── SourceSansPro-Light.otf
└── SourceSansPro-Regular.otf
├── game
├── client
│ ├── gui
│ │ ├── closedialog.lua
│ │ ├── hudabout.lua
│ │ ├── hudchat.lua
│ │ ├── hudchattextbox.lua
│ │ ├── huddialogue.lua
│ │ ├── hudgamemenu
│ │ │ ├── init.lua
│ │ │ ├── inventory.lua
│ │ │ ├── itembutton.lua
│ │ │ ├── itemgrid.lua
│ │ │ ├── navigation.lua
│ │ │ ├── navigationbutton.lua
│ │ │ ├── stat.lua
│ │ │ └── stats.lua
│ │ ├── hudhealth.lua
│ │ ├── hudmana.lua
│ │ ├── hudmoveindicator.lua
│ │ ├── hudspeechballoons.lua
│ │ ├── mainmenu.lua
│ │ ├── mainmenubutton.lua
│ │ ├── mainmenuclosebutton.lua
│ │ ├── optionsitem.lua
│ │ └── optionsitemgroup.lua
│ └── init.lua
├── init.lua
├── server
│ └── init.lua
└── shared
│ └── entities
│ ├── func_examine.lua
│ ├── item.lua
│ ├── item_apple.lua
│ ├── item_gold.lua
│ ├── prop_chest.lua
│ ├── prop_ore_rock.lua
│ ├── prop_tree.lua
│ ├── prop_worldgate_spawn.lua
│ ├── vanpc.lua
│ ├── vaplayer.lua
│ ├── weapon_bow.lua
│ └── weapon_staff.lua
├── images
├── entities
│ ├── item_apple
│ │ ├── 1.png
│ │ └── 2.png
│ ├── prop_chest.png
│ ├── prop_ore_rock.png
│ ├── prop_tree
│ │ └── 1.png
│ ├── prop_worldgate_spawn.png
│ ├── weapon_bow.png
│ └── weapon_staff.png
├── error.png
├── gui
│ ├── arrow_down.png
│ ├── arrow_down@2x.png
│ ├── check.png
│ ├── check@2x.png
│ ├── close.png
│ ├── close@2x.png
│ ├── close_large.png
│ ├── close_large@2x.png
│ ├── logo.png
│ ├── logo@2x.png
│ ├── logo_dark.png
│ ├── logo_dark@2x.png
│ ├── logo_small.png
│ ├── logo_small@2x.png
│ ├── radiobutton_foreground.png
│ ├── radiobutton_foreground@2x.png
│ ├── selection_dot.png
│ ├── selection_dot@2x.png
│ ├── throbber.png
│ └── throbber@2x.png
├── icon.png
├── icon_osx.png
├── moveindicator.lua
├── moveindicator.png
├── player.lua
├── player.png
└── tilesets
│ ├── architect.png
│ ├── developer.png
│ └── world.png
├── init.lua
├── main.lua
├── maps
├── Architect.tsx
├── Developer.tsx
├── World.tsx
├── test_01.lua
└── test_01.tmx
├── public
├── json.lua
├── md5.lua
├── utf8.lua
└── utf8data.lua
├── schemes
├── Chat.lua
├── Console.lua
└── Default.lua
├── scripts
└── test
│ ├── box.lua
│ ├── line.lua
│ └── textbox.lua
├── shaders
├── coloroverlay.frag
├── gaussianblur.frag
├── gaussianblur.lua
├── shader.lua
└── stroke.frag
└── sounds
└── footsteps
├── grassleft.lua
├── grassleft.wav
├── grassright.lua
└── grassright.wav
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # [andrewmcwatters]
4 | patreon: # Planimeter
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | custom: # Replace with a single custom sponsorship URL
9 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | Thumbs.db
2 | .DS_Store
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Planimeter
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/cfg/binds.lst:
--------------------------------------------------------------------------------
1 | Movement
2 | ========
3 |
4 | "Move forward" +forward
5 | "Move back" +back
6 | "Move left" +left
7 | "Move right" +right
8 | "Sprint" +speed
9 |
10 | Player
11 | ======
12 |
13 | "Use" +use
14 | "Game Menu" +gamemenu
15 | "Zoom In" zoomin
16 | "Zoom Out" zoomout
17 |
18 | Communication
19 | =============
20 |
21 | "Chat" chat
22 | "Voice" +voice
23 |
24 | Development
25 | ===========
26 |
27 | "Console" toggleconsole
28 |
--------------------------------------------------------------------------------
/cfg/binds_default.cfg:
--------------------------------------------------------------------------------
1 | w +forward
2 | s +back
3 | a +left
4 | d +right
5 | lshift +speed
6 | e +use
7 | tab +gamemenu
8 | wu zoomin
9 | wd zoomout
10 | y chat
11 | k +voice
12 | \ toggleconsole
13 |
--------------------------------------------------------------------------------
/common/color.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Color class
4 | --
5 | --==========================================================================--
6 |
7 | class( "color" )
8 |
9 | function color.copy( c )
10 | return color( unpack( c ) )
11 | end
12 |
13 | function color:color( r, g, b, a )
14 | if ( type( r ) == "color" ) then
15 | self[ 1 ] = r[ 1 ] or 0
16 | self[ 2 ] = r[ 2 ] or 0
17 | self[ 3 ] = r[ 3 ] or 0
18 | self[ 4 ] = g and g / 255 or ( r[ 4 ] or 0 )
19 | return
20 | end
21 |
22 | self[ 1 ] = r / 255 or 0
23 | self[ 2 ] = g / 255 or 0
24 | self[ 3 ] = b / 255 or 0
25 | self[ 4 ] = a / 255 or 0
26 | end
27 |
28 | function color.__eq( a, b )
29 | return a[ 1 ] == b[ 1 ] and
30 | a[ 2 ] == b[ 2 ] and
31 | a[ 3 ] == b[ 3 ] and
32 | a[ 4 ] == b[ 4 ]
33 | end
34 |
35 | function color:__tostring()
36 | return "color: (" ..
37 | self[ 1 ] .. ", " ..
38 | self[ 2 ] .. ", " ..
39 | self[ 3 ] .. ", " ..
40 | self[ 4 ] ..
41 | ")"
42 | end
43 |
44 | color.transparent = color( 0, 0, 0, 0 )
45 | color.white = color( 255, 255, 255, 255 )
46 | color.black = color( 0, 0, 0, 255 )
47 | color.red = color( 255, 0, 0, 255 )
48 |
49 | color.client = color( 168, 168, 123, 255 )
50 | color.server = color( 123, 158, 168, 255 )
51 |
52 | color.margin = color( 235, 179, 116, 167 )
53 | color.padding = color( 157, 194, 132, 167 )
54 | color.content = color( 122, 168, 215, 167 )
55 |
--------------------------------------------------------------------------------
/common/vector.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Vector class
4 | --
5 | --==========================================================================--
6 |
7 | class( "vector" )
8 |
9 | function vector.copy( v )
10 | return vector( v.x, v.y )
11 | end
12 |
13 | function vector:vector( x, y )
14 | self.x = x or 0
15 | self.y = y or 0
16 | end
17 |
18 | function vector:approximately( b )
19 | return math.approximately( self.x, b.x ) and
20 | math.approximately( self.y, b.y )
21 | end
22 |
23 | function vector:dot( b )
24 | local ret = 0
25 | ret = ret + self.x * b.x
26 | ret = ret + self.y * b.y
27 | return ret
28 | end
29 |
30 | function vector:length()
31 | return math.sqrt( self:lengthSqr() )
32 | end
33 |
34 | function vector:lengthSqr()
35 | return self.x ^ 2 + self.y ^ 2
36 | end
37 |
38 | function vector:normalize()
39 | local length = self:length()
40 | if ( length == 0 ) then
41 | return vector()
42 | end
43 |
44 | return vector( self.x / length, self.y / length )
45 | end
46 |
47 | function vector:normalizeInPlace()
48 | local length = self:length()
49 | self.x = length == 0 and 0 or self.x / length
50 | self.y = length == 0 and 0 or self.y / length
51 | end
52 |
53 | function vector:toAngle()
54 | return math.atan2( self.y, self.x )
55 | end
56 |
57 | function vector.__add( a, b )
58 | return vector( a.x + b.x, a.y + b.y )
59 | end
60 |
61 | function vector.__sub( a, b )
62 | return vector( a.x - b.x, a.y - b.y )
63 | end
64 |
65 | function vector.__mul( a, b )
66 | if ( type( a ) == "number" ) then
67 | return vector( a * b.x, a * b.y )
68 | elseif ( type( b ) == "number" ) then
69 | return vector( b * a.x, b * a.y )
70 | else
71 | return vector( a.x * b.x, a.y * b.y )
72 | end
73 | end
74 |
75 | function vector.__div( a, b )
76 | if ( type( b ) == "number" ) then
77 | return vector( a.x / b, a.y / b )
78 | else
79 | return vector( a.x / b.x, a.y / b.y )
80 | end
81 | end
82 |
83 | function vector.__eq( a, b )
84 | return a.x == b.x and a.y == b.y
85 | end
86 |
87 | function vector:__tostring()
88 | return "vector: (" .. self.x .. ", " .. self.y .. ")"
89 | end
90 |
91 | vector.origin = vector()
92 |
--------------------------------------------------------------------------------
/conf.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose:
4 | --
5 | --==========================================================================--
6 |
7 | require( "engine.shared.profile" )
8 | profile.push( "load" )
9 |
10 | argv = {}
11 | for _, v in ipairs( arg ) do argv[ v ] = true end
12 |
13 | if ( argv[ "--debug" ] ) then
14 | _DEBUG = true
15 | end
16 |
17 | if ( argv[ "--dedicated" ] ) then
18 | _SERVER = true
19 | _DEDICATED = true
20 | end
21 |
22 | if ( not _SERVER ) then
23 | _CLIENT = true
24 | end
25 |
26 | function love.conf( c )
27 | c.title = "Grid Engine"
28 | c.version = "11.3"
29 | if ( _DEDICATED ) then
30 | c.modules.keyboard = false
31 | c.modules.mouse = false
32 | c.modules.joystick = false
33 | c.modules.touch = false
34 | c.modules.image = false
35 | c.modules.graphics = false
36 | c.modules.audio = false
37 | c.modules.sound = false
38 | c.modules.system = false
39 | c.modules.font = false
40 | c.modules.window = false
41 | c.modules.video = false
42 | else
43 | c.window.icon = "images/icon.png"
44 | require( "love.system" )
45 | if ( love.system.getOS() == "OS X" ) then
46 | c.window.icon = "images/icon_osx.png"
47 | end
48 | c.window.resizable = true
49 | end
50 | c.identity = "grid"
51 |
52 | require( "engine.shared.loadlib" )
53 | require( "engine.shared.config" )
54 | config.load( c )
55 | end
56 |
--------------------------------------------------------------------------------
/engine/client/canvas.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. =============--
2 | --
3 | -- Purpose: Canvas class
4 | --
5 | --============================================================================--
6 |
7 | class( "canvas" )
8 |
9 | canvas._canvases = canvas._canvases or {}
10 |
11 | local function copy( k )
12 | if ( string.find( k, "__" ) == 1 ) then
13 | return
14 | end
15 |
16 | canvas[ k ] = function( self, ... )
17 | local self = self._canvas
18 | return self[ k ]( self, ... )
19 | end
20 | end
21 |
22 | local _R = debug.getregistry()
23 | for k in pairs( _R.Canvas ) do
24 | copy( k )
25 | end
26 |
27 | local function newCanvas( self, ... )
28 | self._canvas = love.graphics.newCanvas( ... )
29 | end
30 |
31 | function canvas.invalidateCanvases()
32 | for _, v in ipairs( canvas._canvases ) do
33 | if ( typeof( v, "fullscreencanvas" ) ) then
34 | newCanvas( v, unpack( v._args ) )
35 | end
36 |
37 | if ( v:shouldAutoRedraw() ) then
38 | v:invalidate()
39 | end
40 | end
41 | end
42 |
43 | local function noop()
44 | end
45 |
46 | function canvas:canvas( ... )
47 | self._args = { ... }
48 | self._drawFunc = noop
49 | self.needsRedraw = false
50 | self.autoRedraw = true
51 | table.insert( canvas._canvases, self )
52 |
53 | newCanvas( self, ... )
54 |
55 | setproxy( self )
56 | end
57 |
58 | local function render( self )
59 | local canvas = love.graphics.getCanvas()
60 | love.graphics.setCanvas( { self._canvas, stencil = true } )
61 | self:_drawFunc()
62 | love.graphics.setCanvas( canvas )
63 | end
64 |
65 | function canvas:draw( ... )
66 | if ( self.needsRedraw ) then
67 | local b = love.graphics.getBlendMode()
68 | love.graphics.setBlendMode( "alpha", "alphamultiply" )
69 | render( self )
70 | love.graphics.setBlendMode( b )
71 | self.needsRedraw = false
72 | end
73 |
74 | love.graphics.draw( self._canvas, ... )
75 | end
76 |
77 | function canvas:invalidate()
78 | self.needsRedraw = true
79 | end
80 |
81 | function canvas:remove()
82 | for i, v in ipairs( canvas._canvases ) do
83 | if ( v == self ) then
84 | table.remove( canvas._canvases, i )
85 | end
86 | end
87 |
88 | collectgarbage()
89 | collectgarbage()
90 | end
91 |
92 | function canvas:renderTo( func )
93 | self._drawFunc = func
94 | render( self )
95 | end
96 |
97 | accessor( canvas, "autoRedraw", "should" )
98 |
99 | function canvas:__tostring()
100 | local t = getmetatable( self )
101 | setmetatable( self, {} )
102 | local s = string.gsub( tostring( self ), "table", "canvas" )
103 | setmetatable( self, t )
104 | return s
105 | end
106 |
107 | canvas.__gc = canvas.remove
108 |
109 | class "fullscreencanvas" ( "canvas" )
110 |
111 | function fullscreencanvas:fullscreencanvas( ... )
112 | canvas.canvas( self, ... )
113 | end
114 |
115 | fullscreencanvas.__gc = canvas.remove
116 |
--------------------------------------------------------------------------------
/engine/client/chat.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Chat interface
4 | --
5 | --==========================================================================--
6 |
7 | local love = love
8 | local math = math
9 | local string = string
10 | local table = table
11 | local tostring = tostring
12 | local _G = _G
13 |
14 | module( "chat" )
15 |
16 | function addText( ... )
17 | if ( _G.g_Chat == nil ) then
18 | return
19 | end
20 |
21 | local args = { ... }
22 | table.tostring( args )
23 |
24 | local chat = _G.g_Chat.output
25 | local text = table.concat( args, "\t" )
26 | chat:activate()
27 | chat:insertText( text .. "\n" )
28 |
29 | local readingtime = math.max( string.readingtime( text ), 5 )
30 | chat:setHideTime( love.timer.getTime() + readingtime )
31 | end
32 |
--------------------------------------------------------------------------------
/engine/client/debugoverlay.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Debug Overlay interface
4 | --
5 | --==========================================================================--
6 |
7 | local _G = _G
8 |
9 | module( "debugoverlay" )
10 |
11 | function line( worldIndex, x, y, points, color, duration )
12 | local g_DebugOverlay = _G.g_DebugOverlay
13 | if ( g_DebugOverlay == nil ) then
14 | return
15 | end
16 |
17 | g_DebugOverlay:line( worldIndex, x, y, points, color, duration )
18 | end
19 |
20 | function rectangle( worldIndex, x, y, width, height, color, duration )
21 | local g_DebugOverlay = _G.g_DebugOverlay
22 | if ( g_DebugOverlay == nil ) then
23 | return
24 | end
25 |
26 | g_DebugOverlay:rectangle(
27 | worldIndex,
28 | x,
29 | y,
30 | width,
31 | height,
32 | color,
33 | duration
34 | )
35 | end
36 |
--------------------------------------------------------------------------------
/engine/client/graphics.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Extends the graphics module
4 | --
5 | --==========================================================================--
6 |
7 | require( "love.graphics" )
8 |
9 | local newImage = love.graphics.newImage
10 |
11 | local function getHighResolutionVariant( filename )
12 | local scale = love.window.getDPIScale()
13 | local extension = "." .. string.fileextension( filename )
14 | local hrvariant = string.gsub( filename, extension, "" )
15 | hrvariant = hrvariant .. "@" .. scale .. "x" .. extension
16 | if ( love.filesystem.getInfo( hrvariant ) ~= nil ) then
17 | return hrvariant
18 | end
19 | end
20 |
21 | function love.graphics.newImage( filename, ... )
22 | if ( love.window.getDPIScale() > 1 ) then
23 | local variant = getHighResolutionVariant( filename )
24 | if ( variant ) then
25 | filename = variant
26 | end
27 | end
28 |
29 | return newImage( filename, ... )
30 | end
31 |
--------------------------------------------------------------------------------
/engine/client/gui/autoloader.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: GUI autoloader
4 | --
5 | --==========================================================================--
6 |
7 | local error = error
8 | local ipairs = ipairs
9 | local pcall = pcall
10 | local rawget = rawget
11 | local require = require
12 | local setmetatable = setmetatable
13 | local string = string
14 | local _G = _G
15 |
16 | module( "gui" )
17 |
18 | local metatable = {}
19 | local panelDirectories = {
20 | "game.client",
21 | "engine.client"
22 | }
23 |
24 | function metatable.__index( t, k )
25 | -- Ignore private members.
26 | local privateMember = string.sub( k, 1, 1 ) == "_"
27 | if ( privateMember ) then
28 | return
29 | end
30 |
31 | -- Look in `/game/client/gui` and `/engine/client/gui` for
32 | -- panels not yet required and require them.
33 | --
34 | -- Otherwise, return a standard Lua error.
35 | for _, module in ipairs( panelDirectories ) do
36 | local library = module .. ".gui." .. k
37 | local status, err = pcall( require, library )
38 | if ( status == true ) then
39 | break
40 | end
41 |
42 | local message = "module '" .. library .. "' not found:"
43 | local notFound = string.find( err, message ) ~= 1
44 | if ( notFound ) then
45 | error( err, 2 )
46 | end
47 | end
48 |
49 | -- Return pass-through.
50 | local v = rawget( t, k )
51 | if ( v ~= nil ) then
52 | return v
53 | end
54 | end
55 |
56 | setmetatable( _M, metatable )
57 |
--------------------------------------------------------------------------------
/engine/client/gui/button.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Button class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.button" ( "gui.box" )
8 |
9 | local button = gui.button
10 |
11 | button.canFocus = true
12 |
13 | function button:button( parent, name, text )
14 | gui.box.box( self, parent, name )
15 | self:setDisplay( "block" )
16 | self:setPosition( "absolute" )
17 | self:setBorderWidth( 1 )
18 | self.width = 216
19 | self.height = 46
20 | self.text = gui.text( self, text )
21 | self.disabled = false
22 |
23 | self:setScheme( "Default" )
24 | end
25 |
26 | function button:draw()
27 | self:drawBackground()
28 | self:drawText()
29 |
30 | gui.box.draw( self )
31 | end
32 |
33 | function button:drawBorder()
34 | local color = self:getScheme( "button.borderColor" )
35 |
36 | if ( self:isDisabled() ) then
37 | color = self:getScheme( "button.disabled.borderColor" )
38 | gui.box.drawBorder( self, color )
39 | return
40 | end
41 |
42 | local mouseover = ( self.mouseover or self:isChildMousedOver() )
43 | if ( self.mousedown and mouseover ) then
44 | color = self:getScheme( "button.mousedown.borderColor" )
45 | elseif ( self.mousedown or mouseover or self.focus ) then
46 | color = self:getScheme( "button.mouseover.borderColor" )
47 | end
48 |
49 | gui.box.drawBorder( self, color )
50 | end
51 |
52 | function button:drawText()
53 | local color = self:getScheme( "button.textColor" )
54 |
55 | if ( self:isDisabled() ) then
56 | color = self:getScheme( "button.disabled.textColor" )
57 | end
58 |
59 | self.text:setColor( color )
60 | end
61 |
62 | gui.accessor( button, "text" )
63 | gui.accessor( button, "disabled", "is" )
64 |
65 | function button:keypressed( key, scancode, isrepeat )
66 | if ( not self.focus or self:isDisabled() ) then
67 | return
68 | end
69 |
70 | if ( key == "return"
71 | or key == "kpenter"
72 | or key == "space" ) then
73 | self:onClick()
74 | end
75 | end
76 |
77 | function button:mousepressed( x, y, button, istouch )
78 | local mouseover = ( self.mouseover or self:isChildMousedOver() )
79 | if ( mouseover and button == 1 ) then
80 | self.mousedown = true
81 | self:invalidate()
82 | end
83 | end
84 |
85 | function button:mousereleased( x, y, button, istouch )
86 | local mouseover = ( self.mouseover or self:isChildMousedOver() )
87 | if ( ( self.mousedown and mouseover ) and not self:isDisabled() ) then
88 | self:onClick()
89 | end
90 |
91 | if ( self.mousedown ) then
92 | self.mousedown = false
93 | self:invalidate()
94 | end
95 | end
96 |
97 | function button:onClick()
98 | end
99 |
100 | function button:setText( text )
101 | self.text:set( text )
102 | self:invalidate()
103 | end
104 |
105 | function button:getText()
106 | return self.text:get()
107 | end
108 |
--------------------------------------------------------------------------------
/engine/client/gui/closebutton.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Close Button class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.closebutton" ( "gui.button" )
8 |
9 | local closebutton = gui.closebutton
10 |
11 | closebutton.canFocus = false
12 |
13 | function closebutton:closebutton( parent, name )
14 | gui.button.button( self, parent, name )
15 | local padding = 36
16 | self:setPadding( padding )
17 | self.width = 2 * padding + 8 - 1
18 | self.height = 2 * padding + 16 - 2
19 | self.icon = self:getScheme( "icon" )
20 | end
21 |
22 | function closebutton:draw()
23 | local color = self:getScheme( "closebutton.iconColor" )
24 |
25 | if ( self.mousedown and self.mouseover ) then
26 | color = self:getScheme( "closebutton.mousedown.iconColor" )
27 | elseif ( self.mousedown or self.mouseover or self.focus ) then
28 | color = self:getScheme( "closebutton.mouseover.iconColor" )
29 | end
30 |
31 | love.graphics.setColor( color )
32 |
33 | local width = self:getWidth()
34 | local height = self:getHeight()
35 | local x = math.round( width / 2 - self.icon:getWidth() / 2 + 4 )
36 | local y = math.round( height / 2 - self.icon:getHeight() / 2 )
37 | love.graphics.draw( self.icon, x, y )
38 |
39 | gui.panel.draw( self )
40 | end
41 |
42 | function closebutton:onClick()
43 | local parent = self:getParent()
44 | parent:close()
45 | end
46 |
--------------------------------------------------------------------------------
/engine/client/gui/commandbutton.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Command Button class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.commandbutton" ( "gui.button" )
8 |
9 | local commandbutton = gui.commandbutton
10 |
11 | function commandbutton:commandbutton( parent, name, text )
12 | gui.button.button( self, parent, name, text )
13 | self.width = nil
14 | self.height = nil
15 | self:setDisplay( "inline-block" )
16 | self:setPosition( "static" )
17 | self:setPadding( 15, 18 )
18 | self:setBorderWidth( 0 )
19 |
20 | if ( text ) then
21 | parent:invalidateLayout()
22 | end
23 | end
24 |
25 | function commandbutton:drawBackground()
26 | local color = self:getScheme( "button.backgroundColor" )
27 | local width = self:getWidth()
28 | local height = self:getHeight()
29 |
30 | if ( self:isDisabled() ) then
31 | color = self:getScheme( "button.disabled.backgroundColor" )
32 | gui.panel.drawBackground( self, color )
33 | return
34 | else
35 | gui.panel.drawBackground( self, color )
36 | end
37 |
38 | local isFirstChild = self:isFirstChild()
39 | local x = isFirstChild and 1 or 0
40 | width = isFirstChild and width - 2 or width - 1
41 | height = height - 1
42 |
43 | local mouseover = ( self.mouseover or self:isChildMousedOver() )
44 | if ( self.mousedown and mouseover ) then
45 | color = self:getScheme( "button.mousedown.backgroundColor" )
46 | love.graphics.setColor( color )
47 | love.graphics.rectangle( "fill", x, 1, width, height )
48 | elseif ( self.mousedown or mouseover ) then
49 | color = self:getScheme( "button.mouseover.backgroundColor" )
50 | love.graphics.setColor( color )
51 | love.graphics.rectangle( "fill", x, 1, width, height )
52 | end
53 | end
54 |
55 | function commandbutton:drawBorder()
56 | if ( self:isLastChild() ) then
57 | return
58 | end
59 |
60 | local color = self:getScheme( "commandbutton.borderColor" )
61 | local width = self:getWidth()
62 | local height = self:getHeight()
63 |
64 | love.graphics.setColor( color )
65 | love.graphics.setLineStyle( "rough" )
66 | local lineWidth = 1
67 | love.graphics.setLineWidth( lineWidth )
68 | love.graphics.line(
69 | width - lineWidth / 2, 0, -- Top-right
70 | width - lineWidth / 2, height -- Bottom-right
71 | )
72 | end
73 |
74 | function commandbutton:isFirstChild()
75 | local children = self:getParent():getChildren()
76 | return self == children[ 1 ]
77 | end
78 |
79 | function commandbutton:isLastChild()
80 | local children = self:getParent():getChildren()
81 | return self == children[ #children ]
82 | end
83 |
84 | function commandbutton:setParent( panel )
85 | gui.panel.setParent( self, panel )
86 | panel:invalidate()
87 | end
88 |
--------------------------------------------------------------------------------
/engine/client/gui/commandbuttongroup.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Command Button Group class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.commandbuttongroup" ( "gui.box" )
8 |
9 | local commandbuttongroup = gui.commandbuttongroup
10 |
11 | function commandbuttongroup:commandbuttongroup( parent, name )
12 | gui.box.box( self, parent, name )
13 | self:setDisplay( "block" )
14 | self:setPosition( "absolute" )
15 |
16 | self:invalidateLayout()
17 | end
18 |
19 | function commandbuttongroup:draw()
20 | gui.box.draw( self )
21 |
22 | local color = self:getScheme( "commandbuttongroup.borderColor" )
23 | local width = self:getWidth()
24 | local height = self:getHeight()
25 |
26 | love.graphics.setColor( color )
27 | love.graphics.setLineStyle( "rough" )
28 | local lineWidth = 1
29 | love.graphics.setLineWidth( lineWidth )
30 | love.graphics.line(
31 | lineWidth / 2, height, -- Bottom-left
32 | lineWidth / 2, lineWidth / 2, -- Top-left
33 | width - lineWidth / 2, lineWidth / 2, -- Top-right
34 | width - lineWidth / 2, height -- Bottom-right
35 | )
36 | end
37 |
38 | function commandbuttongroup:invalidateLayout()
39 | local children = self:getChildren()
40 | local width = 0
41 | if ( children ) then
42 | for i, commandbutton in ipairs( children ) do
43 | commandbutton:setX( width )
44 | width = width + commandbutton:getWidth()
45 | end
46 | end
47 | self:setWidth( width )
48 |
49 | local parent = self:getParent()
50 | local margin = typeof( parent, "tabbedframe" ) and 24 or 36
51 | self:setPos(
52 | parent:getWidth() - self:getWidth() - margin,
53 | parent:getHeight() - self:getHeight()
54 | )
55 | gui.panel.invalidateLayout( self )
56 | end
57 |
--------------------------------------------------------------------------------
/engine/client/gui/console/textbox.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Console Text Box class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.console.textbox" ( "gui.textbox" )
8 |
9 | local textbox = gui.console.textbox
10 |
11 | function textbox:textbox( parent, name )
12 | gui.textbox.textbox( self, parent, name, "" )
13 |
14 | self:setEditable( false )
15 | self:setMultiline( true )
16 | self:setScheme( "Console" )
17 | end
18 |
19 | function textbox:draw()
20 | if ( self:getHeight() == 1 ) then
21 | return
22 | end
23 |
24 | gui.panel.drawBackground( self, self:getScheme( "textbox.backgroundColor" ) )
25 | self:drawText()
26 | self:drawCursor()
27 |
28 | gui.panel.draw( self )
29 |
30 | self:drawBorder()
31 | end
32 |
33 | function textbox:invalidateLayout()
34 | local parent = self:getParent()
35 | local margin = 36
36 | local titleBarHeight = 86
37 | local textboxHeight = 46
38 | local marginBottom = 9
39 | self:setWidth( parent:getWidth() - 2 * margin )
40 | self:setHeight(
41 | parent:getHeight() -
42 | titleBarHeight -
43 | textboxHeight -
44 | marginBottom -
45 | margin
46 | )
47 |
48 | gui.panel.invalidateLayout( self )
49 | end
50 |
--------------------------------------------------------------------------------
/engine/client/gui/console/textboxautocompleteitemgroup.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Console Text Box Autocomplete Item Group class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.console.textboxautocompleteitemgroup" ( "gui.textboxautocompleteitemgroup" )
8 |
9 | local textboxautocompleteitemgroup = gui.console.textboxautocompleteitemgroup
10 |
11 | function textboxautocompleteitemgroup:textboxautocompleteitemgroup( parent, name )
12 | gui.textboxautocompleteitemgroup.textboxautocompleteitemgroup( self, parent, name )
13 | end
14 |
15 | function textboxautocompleteitemgroup:invalidateLayout()
16 | local itemWidth = 0
17 | local font = self:getScheme( "font" )
18 | local maxWidth = 0
19 | local listItems = self:getItems()
20 | if ( listItems ) then
21 | local y = 1
22 | local padding = 18
23 | for _, listItem in ipairs( listItems ) do
24 | listItem:setX( 1 )
25 | listItem:setY( y )
26 |
27 | itemWidth = font:getWidth( listItem:getText() ) + 2 * padding
28 | if ( itemWidth > maxWidth ) then
29 | maxWidth = itemWidth
30 | end
31 | y = y + listItem:getHeight()
32 | end
33 |
34 | self:setWidth( maxWidth + 2 )
35 | for _, listItem in ipairs( listItems ) do
36 | listItem:setWidth( maxWidth )
37 | end
38 | end
39 |
40 | self:updatePos()
41 | end
42 |
--------------------------------------------------------------------------------
/engine/client/gui/dropdownlistitem.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Drop-Down List Item class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.dropdownlistitem" ( "gui.radiobutton" )
8 |
9 | local dropdownlistitem = gui.dropdownlistitem
10 |
11 | function dropdownlistitem:dropdownlistitem( parent, name, text )
12 | gui.radiobutton.radiobutton( self, nil, name, text )
13 | self:setPadding( 15, 18, 14 )
14 | self:setDisplay( "block" )
15 | self:setPosition( "static" )
16 |
17 | parent:addItem( self )
18 |
19 | local parent = self:getParent()
20 | self.width = nil
21 | self.height = nil
22 | self.text:set( text )
23 |
24 | self:invalidateLayout()
25 | end
26 |
27 | function dropdownlistitem:draw()
28 | self:drawBackground()
29 | self:drawText()
30 |
31 | gui.box.draw( self )
32 | end
33 |
34 | function dropdownlistitem:drawBackground()
35 | local color = self:getScheme( "dropdownlistitem.backgroundColor" )
36 | local width = self:getWidth()
37 | local height = self:getHeight()
38 |
39 | if ( self:isSelected() ) then
40 | color = self:getScheme( "dropdownlistitem.selected.backgroundColor" )
41 | elseif ( ( self.mouseover or self:isChildMousedOver() ) ) then
42 | color = self:getScheme( "dropdownlistitem.mouseover.backgroundColor" )
43 | end
44 |
45 | love.graphics.setColor( color )
46 | love.graphics.rectangle( "fill", 0, 0, width, height )
47 | end
48 |
49 | function dropdownlistitem:drawText()
50 | local color = self:getScheme( "button.textColor" )
51 |
52 | if ( self:isDisabled() ) then
53 | color = self:getScheme( "button.disabled.textColor" )
54 | elseif ( self:isSelected() ) then
55 | color = self:getScheme( "dropdownlistitem.selected.textColor" )
56 | elseif ( ( self.mouseover or self:isChildMousedOver() ) ) then
57 | color = self:getScheme( "dropdownlistitem.mouseover.textColor" )
58 | end
59 |
60 | self.text:setColor( color )
61 | end
62 |
63 | local function getParentFrame( self )
64 | local panel = self:getParent():getDropDownList()
65 | while ( panel ~= nil ) do
66 | panel = panel:getParent()
67 | if ( typeof( panel, "frame" ) ) then
68 | return panel
69 | end
70 | end
71 | end
72 |
73 | function dropdownlistitem:mousepressed( x, y, button, istouch )
74 | if ( ( self.mouseover or self:isChildMousedOver() ) and button == 1 ) then
75 | self.mousedown = true
76 | end
77 |
78 | local parentFrame = getParentFrame( self )
79 | if ( parentFrame ) then
80 | parentFrame:setFocusedFrame( true )
81 | end
82 | end
83 |
84 | function dropdownlistitem:invalidateLayout()
85 | local parent = self:getParent()
86 | local t, r, b, l = parent:getBorderWidth()
87 | self:setWidth( parent:getWidth() - r - l )
88 | gui.panel.invalidateLayout( self )
89 | end
90 |
--------------------------------------------------------------------------------
/engine/client/gui/dropdownlistitemgroup.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Drop-Down List Item Group class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.dropdownlistitemgroup" ( "gui.radiobuttongroup" )
8 |
9 | local dropdownlistitemgroup = gui.dropdownlistitemgroup
10 |
11 | function dropdownlistitemgroup:dropdownlistitemgroup( parent, name )
12 | gui.radiobuttongroup.radiobuttongroup( self, nil, name )
13 | self:setParent( parent:getRootPanel() )
14 | self.height = nil
15 | self:setBorderWidth( 1 )
16 | self:setBorderColor( self:getScheme( "dropdownlistitem.borderColor" ) )
17 | self:setDisplay( "block" )
18 | self:setPosition( "absolute" )
19 | self.width = parent:getWidth()
20 | self:setUseFullscreenCanvas( true )
21 | self.dropDownList = parent
22 | end
23 |
24 | function dropdownlistitemgroup:addItem( item, default )
25 | item:setParent( self )
26 | gui.radiobuttongroup.addItem( self, item )
27 |
28 | if ( default or #self:getItems() == 1 ) then
29 | item:setDefault( true )
30 | end
31 |
32 | self:invalidateLayout()
33 | end
34 |
35 | function dropdownlistitemgroup:draw()
36 | if ( self:getItems() == nil ) then
37 | return
38 | end
39 |
40 | gui.box.draw( self )
41 | end
42 |
43 | accessor( dropdownlistitemgroup, "dropDownList" )
44 |
45 | function dropdownlistitemgroup:invalidateLayout()
46 | self:updatePos()
47 | self:setWidth( self:getDropDownList():getWidth() )
48 | gui.panel.invalidateLayout( self )
49 | end
50 |
51 | function dropdownlistitemgroup:isVisible()
52 | local dropDownList = self:getDropDownList()
53 | return dropDownList:isVisible() and dropDownList:isActive()
54 | end
55 |
56 | function dropdownlistitemgroup:mousepressed( x, y, button, istouch )
57 | if ( button == 1 ) then
58 | local dropDownList = self:getDropDownList()
59 | if ( dropDownList ~= gui._topPanel and
60 | ( not ( self.mouseover or self:isChildMousedOver() ) ) ) then
61 | dropDownList:setActive( false )
62 | end
63 | end
64 |
65 | return gui.panel.mousepressed( self, x, y, button, istouch )
66 | end
67 |
68 | function dropdownlistitemgroup:onValueChanged( oldValue, newValue )
69 | local dropDownList = self:getDropDownList()
70 | dropDownList:setActive( false )
71 | dropDownList:onValueChanged( oldValue, newValue )
72 | end
73 |
74 | function dropdownlistitemgroup:updatePos()
75 | local dropDownList = self:getDropDownList()
76 | if ( dropDownList == nil ) then
77 | return
78 | end
79 |
80 | local x, y = dropDownList:localToScreen()
81 | y = y + dropDownList:getHeight()
82 |
83 | local windowPadding = 4
84 | local overflow = y + self:getHeight() + windowPadding
85 | if ( overflow > love.graphics.getHeight() ) then
86 | overflow = overflow - love.graphics.getHeight()
87 | y = y - overflow
88 | end
89 |
90 | if ( y < windowPadding ) then
91 | y = windowPadding
92 | end
93 |
94 | self:setPos( x, y )
95 | end
96 |
--------------------------------------------------------------------------------
/engine/client/gui/framerate.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Frame Rate class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.framerate" ( "gui.label" )
8 |
9 | local framerate = gui.framerate
10 |
11 | function framerate:framerate( parent, name )
12 | gui.label.label( self, parent, name, text )
13 |
14 | self:setScheme( "Console" )
15 | self.font = self:getScheme( "font" )
16 | self.height = self.font:getHeight()
17 |
18 | self:setScheme( "Default" )
19 | self:setTextAlign( "right" )
20 | self:invalidateLayout()
21 | end
22 |
23 | function framerate:update( dt )
24 | -- HACKHACK: Fade this out for readability.
25 | if ( ( g_MainMenu and not g_MainMenu:isVisible() ) and
26 | ( g_GameMenu and g_GameMenu:isVisible() ) ) then
27 | self:setOpacity( 1 - g_GameMenu:getOpacity() )
28 | else
29 | self:setOpacity( 1 )
30 | end
31 |
32 | local framerate = self:getFramerate()
33 | local text = self:getText()
34 | if ( text ~= framerate ) then
35 | self:setText( self:getFramerate() )
36 | self:invalidate()
37 | end
38 | end
39 |
40 | function framerate:getFramerate()
41 | local fps = love.timer.getFPS() .. " FPS"
42 | local ms = 1000 * love.timer.getAverageDelta()
43 | ms = string.format( "%.3f", ms ) .. " ms"
44 | return fps .. " / " .. ms
45 | end
46 |
47 | function framerate:invalidateLayout()
48 | local parent = self:getParent()
49 | local margin = gui.scale( 96 )
50 | local width = self:getWidth()
51 | local height = self:getHeight()
52 | local x = parent:getWidth() - margin - width
53 | local y = parent:getHeight() - margin - height
54 | self:setPos( x, y )
55 |
56 | gui.panel.invalidateLayout( self )
57 | end
58 |
--------------------------------------------------------------------------------
/engine/client/gui/frametab.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Frame Tab class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.frametab" ( "gui.radiobutton" )
8 |
9 | local frametab = gui.frametab
10 |
11 | function frametab:frametab( parent, name, text )
12 | gui.radiobutton.radiobutton( self, parent, name, text )
13 | self:setDisplay( "inline-block" )
14 | self:setPadding( 22 )
15 | self.text:set( text )
16 | self.width = nil
17 | self.height = nil
18 | end
19 |
20 | function frametab:draw()
21 | self:drawBackground()
22 | self:drawText()
23 | gui.box.draw( self )
24 | end
25 |
26 | function frametab:drawBackground()
27 | local color = self:getScheme( "frametab.backgroundColor" )
28 | local mouseover = self.mouseover or self:isChildMousedOver()
29 | local width = self:getWidth()
30 | local height = self:getHeight()
31 |
32 |
33 | if ( self:isSelected() ) then
34 | color = self:getScheme( "frametab.selected.backgroundColor" )
35 | elseif ( mouseover ) then
36 | gui.panel.drawBackground( self, color )
37 | color = self:getScheme( "frametab.mouseover.backgroundColor" )
38 | end
39 |
40 | love.graphics.setColor( color )
41 |
42 | local selected = mouseover or self:isSelected()
43 | mouseover = mouseover and not self:isSelected()
44 | love.graphics.rectangle(
45 | "fill",
46 | 0,
47 | 0,
48 | width - ( selected and 1 or 0 ),
49 | height - ( mouseover and 1 or 0 )
50 | )
51 |
52 | local lineWidth = 1
53 | if ( selected ) then
54 | love.graphics.setColor( self:getScheme( "frametab.backgroundColor" ) )
55 | love.graphics.setLineStyle( "rough" )
56 | love.graphics.setLineWidth( lineWidth )
57 | love.graphics.line(
58 | width - lineWidth / 2, 0, -- Top-left
59 | width - lineWidth / 2, height -- Bottom-left
60 | )
61 | end
62 |
63 | selected = self:isSelected()
64 | love.graphics.setColor( self:getScheme( "frametab.borderColor" ) )
65 | love.graphics.setLineStyle( "rough" )
66 | love.graphics.setLineWidth( lineWidth )
67 | love.graphics.line(
68 | width - lineWidth / 2, 0,
69 | width - lineWidth / 2, height - ( selected and 0 or 1 )
70 | )
71 |
72 | if ( not selected ) then
73 | love.graphics.line(
74 | 0, height - lineWidth / 2, -- Top-right
75 | width, height - lineWidth / 2 -- Bottom-right
76 | )
77 | end
78 | end
79 |
80 | function frametab:mousepressed( x, y, button, istouch )
81 | if ( ( self.mouseover or self:isChildMousedOver() ) and button == 1 ) then
82 | self.mousedown = true
83 |
84 | if ( not self:isDisabled() ) then
85 | local frametabgroup = self:getGroup()
86 | if ( frametabgroup ) then
87 | frametabgroup:setSelectedId( self.id )
88 | self:onClick()
89 | end
90 | end
91 | end
92 |
93 | self:invalidate()
94 | end
95 |
96 | function frametab:mousereleased( x, y, button, istouch )
97 | self.mousedown = false
98 | self:invalidate()
99 | end
100 |
--------------------------------------------------------------------------------
/engine/client/gui/frametabgroup.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Frame Tab Group class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.frametabgroup" ( "gui.radiobuttongroup" )
8 |
9 | local frametabgroup = gui.frametabgroup
10 |
11 | function frametabgroup:frametabgroup( parent, name )
12 | gui.radiobuttongroup.radiobuttongroup( self, parent, name )
13 | self.height = 61
14 | self:setScheme( "Default" )
15 | end
16 |
17 | function frametabgroup:addTab( tabName, default )
18 | local frametab = gui.frametab( self, tabName .. " Frame Tab", tabName )
19 | self:addItem( frametab )
20 | local numItems = #self:getItems()
21 | frametab:setValue( numItems )
22 |
23 | if ( default or numItems == 1 ) then
24 | frametab:setDefault( true )
25 | end
26 | end
27 |
28 | function frametabgroup:addItem( tab )
29 | gui.radiobuttongroup.addItem( self, tab )
30 | self:invalidateLayout()
31 | end
32 |
33 | function frametabgroup:draw()
34 | love.graphics.setColor( self:getScheme( "frametab.borderColor" ) )
35 | love.graphics.setLineStyle( "rough" )
36 | local lineWidth = 1
37 | love.graphics.setLineWidth( lineWidth )
38 | love.graphics.line(
39 | lineWidth / 2, 0, -- Top-left
40 | lineWidth / 2, self:getHeight() -- Bottom-left
41 | )
42 |
43 | gui.panel.draw( self )
44 | end
45 |
46 | function frametabgroup:invalidateLayout()
47 | local tabs = self:getItems()
48 | if ( tabs ) then
49 | local x = 1
50 | for _, tab in ipairs( tabs ) do
51 | tab:setX( x )
52 | x = x + tab:getWidth()
53 | end
54 | self:setWidth( x )
55 | end
56 |
57 | gui.panel.invalidateLayout( self )
58 | end
59 |
60 | function frametabgroup:onValueChanged( oldValue, newValue )
61 | local tabPanels = self:getParent():getTabPanels()
62 | tabPanels:setSelectedChild( newValue )
63 | end
64 |
--------------------------------------------------------------------------------
/engine/client/gui/frametabpanel.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Frame Tab Panel class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.frametabpanel" ( "gui.box" )
8 |
9 | local frametabpanel = gui.frametabpanel
10 |
11 | function frametabpanel:frametabpanel( parent, name )
12 | gui.box.box( self, parent, name )
13 | self:setDisplay( "block" )
14 | self:setPosition( "absolute" )
15 | end
16 |
17 | function frametabpanel:draw()
18 | gui.panel.drawBackground( self, self:getScheme( "frame.backgroundColor" ) )
19 | gui.box.draw( self )
20 | end
21 |
22 | function frametabpanel:invalidateLayout()
23 | self:setDimensions( self:getParent():getDimensions() )
24 | gui.panel.invalidateLayout( self )
25 | end
26 |
27 | local function getParentFrame( self )
28 | local panel = self
29 | while ( panel ~= nil ) do
30 | panel = panel:getParent()
31 | if ( typeof( panel, "frame" ) ) then
32 | return panel
33 | end
34 | end
35 | end
36 |
37 | function frametabpanel:keypressed( key, scancode, isrepeat )
38 | local parentFrame = getParentFrame( self )
39 | local parentFocus = parentFrame and parentFrame.focus
40 | if ( key == "tab" and ( self.focus or parentFocus ) ) then
41 | gui.frame.moveFocus( self )
42 | end
43 |
44 | return gui.panel.keypressed( self, key, scancode, isrepeat )
45 | end
46 |
--------------------------------------------------------------------------------
/engine/client/gui/frametabpanels.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Frame Tab Panels class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.frametabpanels" ( "gui.box" )
8 |
9 | local frametabpanels = gui.frametabpanels
10 |
11 | function frametabpanels:frametabpanels( parent, name, text )
12 | gui.box.box( self, parent, name )
13 | self:setDisplay( "block" )
14 | self:setPosition( "absolute" )
15 | self.width = parent:getWidth()
16 | self.height = parent:getHeight() - 62
17 | end
18 |
19 | function frametabpanels:addPanel( frametabpanel, default )
20 | local panel = frametabpanel( self )
21 | panel:setDimensions( self:getDimensions() )
22 |
23 | if ( not default and #self:getChildren() ~= 1 ) then
24 | panel:setVisible( false )
25 | end
26 |
27 | return panel
28 | end
29 |
30 | function frametabpanels:setSelectedChild( i )
31 | for j, v in ipairs( self:getChildren() ) do
32 | v:setVisible( i == j )
33 | v:invalidate()
34 | end
35 | end
36 |
--------------------------------------------------------------------------------
/engine/client/gui/hudframe.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Frame HUD
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.hudframe" ( "gui.frame" )
8 |
9 | local hudframe = gui.hudframe
10 |
11 | function hudframe:hudframe( parent, name, title )
12 | gui.frame.frame( self, parent, name, title )
13 |
14 | self:setResizable( false )
15 | self:setMovable( false )
16 |
17 | if ( self.closeButton ) then
18 | self.closeButton:remove()
19 | self.closeButton = nil
20 | end
21 |
22 | self:setUseFullscreenCanvas( false )
23 | self:invalidateLayout()
24 | end
25 |
26 | local HUDFRAME_ANIM_TIME = 0.2
27 |
28 | function hudframe:activate()
29 | if ( not self:isVisible() ) then
30 | self:setOpacity( 0 )
31 | self:animate( {
32 | opacity = 1
33 | }, HUDFRAME_ANIM_TIME, "easeOutQuint" )
34 | end
35 |
36 | self:moveToFront()
37 | self:setVisible( true )
38 | end
39 |
40 | function hudframe:close()
41 | if ( self.closing ) then
42 | return
43 | end
44 |
45 | self.closing = true
46 |
47 | self:animate( {
48 | opacity = 0,
49 | }, HUDFRAME_ANIM_TIME, "easeOutQuint", function()
50 | self:setVisible( false )
51 | self:setOpacity( 1 )
52 |
53 | self.closing = nil
54 | end )
55 | end
56 |
57 | function hudframe:draw()
58 | self:drawTranslucency()
59 | gui.frame.draw( self )
60 | end
61 |
62 | function hudframe:drawBackground()
63 | if ( gui._translucencyCanvas == nil ) then
64 | gui.panel.drawBackground( self, self:getScheme(
65 | "frame.backgroundColor"
66 | ) )
67 | return
68 | end
69 |
70 | gui.box.drawBackground( self )
71 | end
72 |
73 | function hudframe:drawTitle()
74 | local property = "frame.titleTextColor"
75 | love.graphics.setColor( self:getScheme( property ) )
76 | local font = self:getScheme( "titleFont" )
77 | love.graphics.setFont( font )
78 | local x = math.round( 36 )
79 | local y = math.round( x - 4 )
80 | love.graphics.print( self:getTitle(), x, y )
81 | end
82 |
83 | function hudframe:update( dt )
84 | if ( gui._translucencyCanvas and self:isVisible() ) then
85 | self:invalidate()
86 | end
87 |
88 | gui.frame.update( self, dt )
89 | end
90 |
--------------------------------------------------------------------------------
/engine/client/gui/hudprofiler.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Profiler HUD
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.hudprofiler" ( "gui.hudframe" )
8 |
9 | local hudprofiler = gui.hudprofiler
10 |
11 | function hudprofiler:hudprofiler( parent )
12 | local name = "HUD Profiler"
13 | gui.hudframe.hudframe( self, parent, name, name )
14 | self.width = 320 -- - 31
15 | self.height = 432
16 | self:setBorderColor( self:getScheme( "borderColor" ) )
17 |
18 | local budgets = profile._stack
19 | for i, budget in ipairs( budgets ) do
20 | local box = gui.box( self, budget.name .. " Budget Info" )
21 | box:setDisplay( "block" )
22 | box:setMargin( 16, 0 )
23 | local text = gui.text( box, budget.name )
24 | text:setColor( self:getScheme( "textColor" ) )
25 | gui.progressbar( box )
26 | end
27 |
28 | self:invalidateLayout()
29 | end
30 |
31 | function hudprofiler:draw()
32 | self:drawTranslucency()
33 | self:drawBackground()
34 |
35 | gui.box.draw( self )
36 |
37 | self:drawTitle()
38 | -- self:drawBorder( self:getScheme( "borderColor" ) )
39 |
40 | if ( convar.getConvar( "gui_draw_frame_focus" ):getBoolean() and
41 | self.focus ) then
42 | self:drawSelection()
43 | end
44 | end
45 |
46 | function hudprofiler:getTitle()
47 | return "Profiler"
48 | end
49 |
50 | function hudprofiler:invalidateLayout()
51 | local x = love.graphics.getWidth() - self:getWidth() - 18
52 | local y = love.graphics.getHeight() - self:getHeight() - 18
53 | self:setPos( x, y )
54 | gui.frame.invalidateLayout( self )
55 | end
56 |
57 | concommand( "+profiler", "Opens the profiler", function()
58 | local visible = _G.g_Profiler:isVisible()
59 | if ( not visible ) then
60 | _G.g_Profiler:activate()
61 | end
62 | end, { "game" } )
63 |
64 | concommand( "-profiler", "Closes the profiler", function()
65 | local visible = _G.g_Profiler:isVisible()
66 | if ( visible ) then
67 | _G.g_Profiler:close()
68 | end
69 | end, { "game" } )
70 |
71 | local function onReloadScript()
72 | local profiler = g_Profiler
73 | if ( profiler == nil ) then
74 | return
75 | end
76 |
77 | local visible = profiler:isVisible()
78 | profiler:remove()
79 | profiler = gui.hudprofiler( g_Viewport )
80 | g_Profiler = profiler
81 | if ( visible ) then
82 | profiler:activate()
83 | end
84 | end
85 |
86 | onReloadScript()
87 |
--------------------------------------------------------------------------------
/engine/client/gui/hudvoice.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Voice HUD
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.hudvoice" ( "gui.box" )
8 |
9 | local hudvoice = gui.hudvoice
10 |
11 | function hudvoice:hudvoice( parent, name )
12 | gui.box.box( self, parent, name )
13 | end
14 |
15 | function hudvoice:draw()
16 | gui.box.draw( self )
17 | end
18 |
--------------------------------------------------------------------------------
/engine/client/gui/imagepanel.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Image class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.imagepanel" ( "gui.box" )
8 |
9 | local imagepanel = gui.imagepanel
10 |
11 | function imagepanel:imagepanel( parent, name, image )
12 | gui.box.box( self, parent, name )
13 | self:setDisplay( "block" )
14 | self:setPosition( "absolute" )
15 |
16 | self.imageDatum = nil
17 | self.imageQuad = nil
18 | self:setImage( image )
19 | end
20 |
21 | function imagepanel:draw()
22 | local image = self:getImage()
23 | if ( image ) then
24 | gui.panel._maskedPanel = self
25 | love.graphics.stencil( gui.panel.drawMask )
26 | love.graphics.setStencilTest( "greater", 0 )
27 | love.graphics.setColor( self:getColor() )
28 | love.graphics.draw( image, self:getQuad() )
29 | love.graphics.setStencilTest()
30 | else
31 | self:drawMissingImage()
32 | end
33 | end
34 |
35 | function imagepanel:drawMissingImage()
36 | love.graphics.setColor( color( color.red, 0.42 * 255 ) )
37 | love.graphics.setLineStyle( "rough" )
38 | local lineWidth = 1
39 | local width = self:getWidth()
40 | local height = self:getHeight()
41 | love.graphics.setLineWidth( lineWidth )
42 | love.graphics.line(
43 | width - lineWidth / 2, 0, -- Top-right
44 | width - lineWidth / 2, height - lineWidth / 2, -- Bottom-right
45 | 0, height - lineWidth / 2 -- Bottom-left
46 | )
47 | end
48 |
49 | gui.accessor( imagepanel, "quad", nil, "imageQuad" )
50 | gui.accessor( imagepanel, "image", nil, "imageDatum" )
51 |
52 | function imagepanel:setImage( image )
53 | if ( type( image ) == "image" ) then
54 | self.imageDatum = image
55 | elseif ( image ~= nil and love.filesystem.getInfo( image ) ~= nil ) then
56 | self.imageDatum = love.graphics.newImage( image )
57 | else
58 | self.imageDatum = nil
59 | end
60 |
61 | self:updateQuad()
62 | end
63 |
64 | function imagepanel:setWidth( width )
65 | gui.panel.setWidth( self, width )
66 | self:updateQuad()
67 | end
68 |
69 | function imagepanel:setHeight( height )
70 | gui.panel.setHeight( self, height )
71 | self:updateQuad()
72 | end
73 |
74 | function imagepanel:updateQuad()
75 | local missingImage = self:getImage() == nil
76 | if ( missingImage ) then
77 | return
78 | end
79 |
80 | local w = self:getWidth() - ( missingImage and 1 or 0 )
81 | local h = self:getHeight() - ( missingImage and 1 or 0 )
82 | local sw = self.imageDatum:getWidth()
83 | local sh = self.imageDatum:getHeight()
84 | if ( self.imageQuad == nil ) then
85 | self.imageQuad = love.graphics.newQuad( 0, 0, w, h, sw, sh )
86 | else
87 | self.imageQuad:setViewport( 0, 0, w, h )
88 | end
89 | end
90 |
--------------------------------------------------------------------------------
/engine/client/gui/init.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: GUI interface
4 | --
5 | --==========================================================================--
6 |
7 | require( "engine.client.gui.scheme" )
8 |
9 | local love = love
10 | local math = math
11 | local require = require
12 | local string = string
13 | local type = type
14 | local typerror = typerror
15 | local _G = _G
16 |
17 | module( "gui" )
18 |
19 | require( "engine.client.gui.autoloader" )
20 | require( "engine.client.gui.handlers" )
21 |
22 | function accessor( class, member, verb, key, default )
23 | if ( type( class ) ~= "table" ) then
24 | typerror( 1, "table", class )
25 | end
26 |
27 | class[ "set" .. string.capitalize( member ) ] = function( self, value )
28 | self[ key or member ] = value
29 | self:invalidate()
30 | end
31 |
32 | class[ ( verb or "get" ) .. string.capitalize( member ) ] = function( self )
33 | return self[ key or member ] or default
34 | end
35 | end
36 |
37 | function invalidateTree()
38 | _rootPanel:invalidateLayout()
39 | _rootPanel:invalidateCanvas()
40 |
41 | if ( _viewportCanvas ) then
42 | _viewportCanvas:remove()
43 | end
44 |
45 | _viewportCanvas = nil
46 | _translucencyCanvas = nil
47 | end
48 |
49 | function preDrawWorld()
50 | _rootPanel:preDrawWorld()
51 | end
52 |
53 | function scale( n )
54 | return math.round( n * ( love.graphics.getHeight() / 1080 ) )
55 | end
56 |
57 | function setFocusedPanel( panel, focus )
58 | if ( _focusedPanel ) then
59 | _focusedPanel.focus = nil
60 |
61 | if ( _focusedPanel.onLostFocus ) then
62 | _focusedPanel:onLostFocus()
63 | end
64 |
65 | if ( _focusedPanel ) then
66 | _focusedPanel:invalidate()
67 | end
68 | end
69 |
70 | if ( focus ) then
71 | _focusedPanel = panel
72 | if ( panel and panel.canFocus ) then
73 | panel.focus = focus
74 |
75 | if ( _focusedPanel.onFocus ) then
76 | _focusedPanel:onFocus()
77 | end
78 |
79 | panel:invalidate()
80 | end
81 | else
82 | _focusedPanel = nil
83 | if ( panel ) then
84 | panel.focus = nil
85 | panel:invalidate()
86 | end
87 | end
88 | end
89 |
--------------------------------------------------------------------------------
/engine/client/gui/label.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Label class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.label" ( "gui.box" )
8 |
9 | local label = gui.label
10 |
11 | function label:label( parent, name, text )
12 | gui.box.box( self, parent, name )
13 | self:setPosition( "absolute" )
14 |
15 | self.font = self:getScheme( "font" )
16 | self.width = 216
17 | self.height = self.font:getHeight()
18 | self.text = text or "Label"
19 | end
20 |
21 | function label:draw()
22 | love.graphics.setColor( self:getScheme( "label.textColor" ) )
23 |
24 | local font = self:getFont()
25 | love.graphics.setFont( font )
26 |
27 | local text = self:getText()
28 | local limit = self:getWidth()
29 | local align = self:getTextAlign()
30 | love.graphics.printf( text, 0, 0, limit, align )
31 |
32 | gui.box.draw( self )
33 | end
34 |
35 | gui.accessor( label, "font" )
36 | gui.accessor( label, "text" )
37 | gui.accessor( label, "textAlign" )
38 |
--------------------------------------------------------------------------------
/engine/client/gui/netgraph.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Net Graph class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.netgraph" ( "gui.box" )
8 |
9 | local netgraph = gui.netgraph
10 |
11 | function netgraph:netgraph( parent, name )
12 | gui.box.box( self, parent, name )
13 | self:setDisplay( "block" )
14 | self:setPosition( "absolute" )
15 |
16 | self:setScheme( "Console" )
17 | self.font = self:getScheme( "font" )
18 | self.width = 216
19 | self.height = 3 * self.font:getHeight()
20 | self:invalidateLayout()
21 | end
22 |
23 | function netgraph:update( dt )
24 | -- HACKHACK: Fade this out for readability.
25 | if ( ( g_MainMenu and not g_MainMenu:isVisible() ) and
26 | ( g_GameMenu and g_GameMenu:isVisible() ) ) then
27 | self:setOpacity( 1 - g_GameMenu:getOpacity() )
28 | else
29 | self:setOpacity( 1 )
30 | end
31 |
32 | self:invalidate()
33 | end
34 |
35 | function netgraph:draw()
36 | self:drawSentReceived()
37 |
38 | gui.box.draw( self )
39 | end
40 |
41 | function netgraph:drawSentReceived()
42 | if ( not engine.client.isInGame() ) then
43 | return
44 | end
45 |
46 | self:setScheme( "Default" )
47 | love.graphics.setColor( self:getScheme( "label.textColor" ) )
48 |
49 | local font = self:getFont()
50 | love.graphics.setFont( font )
51 | local text = ""
52 |
53 | local network = engine.client.network
54 |
55 | if ( _SERVER ) then
56 | network = engine.server.network
57 | end
58 |
59 | -- Ping
60 | if ( not _SERVER ) then
61 | local ping = network._server:round_trip_time()
62 | text = text .. "Ping: " .. ping .. " ms\n"
63 | else
64 | text = text .. "\n"
65 | end
66 |
67 | -- Send
68 | local sent = network.getAverageSentData() or 0
69 | local rate = "B/s"
70 | if ( sent >= 1024 ) then
71 | sent = sent / 1024
72 | sent = string.format( "%.2f", sent )
73 | rate = "kB/s"
74 | end
75 | text = text .. "Data sent/sec: " .. sent .. " " .. rate .. "\n"
76 |
77 | -- Receive
78 | local received = network.getAverageReceivedData() or 0
79 | local rate = "B/s"
80 | if ( received >= 1024 ) then
81 | received = received / 1024
82 | received = string.format( "%.2f", received )
83 | rate = "kB/s"
84 | end
85 |
86 | text = text .. "Data received/sec: " .. received .. " " .. rate
87 | local limit = self:getWidth()
88 | local align = "right"
89 | love.graphics.printf( text, 0, 0, limit, align )
90 | end
91 |
92 | accessor( netgraph, "font" )
93 |
94 | function netgraph:invalidateLayout()
95 | local parent = self:getParent()
96 | local margin = gui.scale( 96 )
97 | local width = self:getWidth()
98 | local height = self:getHeight()
99 | local x = parent:getWidth() - margin - width
100 | local y = parent:getHeight() - margin - height - self.font:getHeight()
101 | self:setPos( x, y )
102 |
103 | gui.panel.invalidateLayout( self )
104 | end
105 |
--------------------------------------------------------------------------------
/engine/client/gui/optionsmenu/audiooptionspanel.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Audio Options Panel class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.audiooptionspanel" ( "gui.frametabpanel" )
8 |
9 | local audiooptionspanel = gui.audiooptionspanel
10 |
11 | function audiooptionspanel:audiooptionspanel( parent, name )
12 | name = name or "Audio Options Panel"
13 | gui.frametabpanel.frametabpanel( self, parent, name )
14 | local options = {}
15 | self.options = options
16 | local c = config.getConfig()
17 |
18 | local name = "Master Volume"
19 | local label = gui.label( self, name, name )
20 | local margin = 36
21 | local x = margin
22 | local y = margin
23 | label:setPos( x, y )
24 | label:setFont( self:getScheme( "fontBold" ) )
25 |
26 | name = "Master Volume Slider"
27 | local masterVolume = gui.slider( self, name )
28 | self.masterVolume = masterVolume
29 | options.masterVolume = c.sound.volume
30 | masterVolume:setMax( 1 )
31 | masterVolume:setValue( c.sound.volume )
32 | masterVolume.onValueChanged = function( slider, oldValue, newValue )
33 | options.masterVolume = newValue
34 | c.sound.volume = newValue
35 | end
36 | local marginBottom = 9
37 | y = y + label:getHeight() + marginBottom
38 | masterVolume:setPos( x, y )
39 |
40 | name = "Play Sound in Desktop"
41 | local desktopSound = gui.checkbox( self, name, name )
42 | self.desktopSound = desktopSound
43 | options.desktopSound = c.sound.desktop
44 | desktopSound:setChecked( c.sound.desktop )
45 | desktopSound.onCheckedChanged = function( checkbox, checked )
46 | options.desktopSound = checked
47 | c.sound.desktop = checked
48 | end
49 | x = 2 * x + masterVolume:getWidth()
50 | y = margin + label:getHeight() + marginBottom
51 | desktopSound:setPos( x, y )
52 | end
53 |
54 | function audiooptionspanel:activate()
55 | self:saveControlStates()
56 | end
57 |
58 | function audiooptionspanel:onOK()
59 | self:updateSound()
60 | end
61 |
62 | function audiooptionspanel:onCancel()
63 | self:resetControlStates()
64 | end
65 |
66 | audiooptionspanel.onApply = audiooptionspanel.onOK
67 |
68 | function audiooptionspanel:saveControlStates()
69 | local controls = {}
70 | self.controls = controls
71 | controls.masterVolume = self.masterVolume:getValue()
72 | controls.desktopSound = self.desktopSound:isChecked()
73 | end
74 |
75 | function audiooptionspanel:resetControlStates()
76 | local controls = self.controls
77 | self.masterVolume:setValue( controls.masterVolume )
78 | self.desktopSound:setChecked( controls.desktopSound )
79 | table.clear( controls )
80 | end
81 |
82 | function audiooptionspanel:updateSound()
83 | local options = self.options
84 | convar.setConvar( "snd_volume", options.masterVolume )
85 | convar.setConvar( "snd_desktop", options.desktopSound and 1 or 0 )
86 | end
87 |
--------------------------------------------------------------------------------
/engine/client/gui/optionsmenu/bindlistheader.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Bind List Header class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.bindlistheader" ( "gui.box" )
8 |
9 | local bindlistheader = gui.bindlistheader
10 |
11 | function bindlistheader:bindlistheader( parent, name, text )
12 | gui.box.box( self, parent, name )
13 | self.width = parent:getWidth()
14 | self.height = 46
15 | self.text = text or "Bind List Header"
16 |
17 | self:setScheme( "Default" )
18 | end
19 |
20 | function bindlistheader:draw()
21 | love.graphics.setColor( self:getScheme( "label.textColor" ) )
22 | local font = self:getScheme( "fontBold" )
23 | love.graphics.setFont( font )
24 |
25 | local margin = 18
26 | local x = math.round( margin )
27 | local y = math.round( self:getHeight() / 2 - font:getHeight() / 2 )
28 | love.graphics.print( self:getText(), x, y )
29 |
30 | local label = "Key or Button"
31 | x = math.round( self:getWidth() - margin - font:getWidth( label ) )
32 | love.graphics.print( label, x, y )
33 |
34 | love.graphics.setColor( self:getScheme( 'bindlistheader.borderColor' ) )
35 | x = margin
36 | y = self:getHeight() - 6 -- Padding-bottom
37 | local width = self:getWidth() - 2 * margin
38 | local height = 1
39 | love.graphics.rectangle( "fill", x, y, width, height )
40 |
41 | gui.box.draw( self )
42 | end
43 |
44 | accessor( bindlistheader, "text" )
45 |
--------------------------------------------------------------------------------
/engine/client/gui/optionsmenu/init.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Options Menu class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.optionsmenu" ( "gui.tabbedframe" )
8 |
9 | local optionsmenu = gui.optionsmenu
10 |
11 | function optionsmenu:optionsmenu( parent )
12 | local name = "Options Menu"
13 | gui.tabbedframe.tabbedframe( self, parent, name, "Options" )
14 | self.resizable = false
15 |
16 | local groupName = name .. " Command Button Group"
17 | local group = gui.commandbuttongroup( self, groupName )
18 |
19 | local buttonName = nil
20 | buttonName = name .. " OK Button"
21 | self.okButton = gui.commandbutton( group, buttonName, "OK" )
22 | self.okButton.onClick = function( commandbutton )
23 | local panels = self:getTabPanels():getChildren()
24 | for _, panel in ipairs( panels ) do
25 | panel:onOK()
26 | end
27 | convar.saveConfig()
28 | self:close()
29 | end
30 | buttonName = name .. " Cancel Button"
31 | self.cancelButton = gui.commandbutton( group, buttonName, "Cancel" )
32 | self.cancelButton.onClick = function( commandbutton )
33 | local panels = self:getTabPanels():getChildren()
34 | for _, panel in ipairs( panels ) do
35 | panel:onCancel()
36 | end
37 | self:close()
38 | end
39 | buttonName = name .. " Apply Button"
40 | self.applyButton = gui.commandbutton( group, buttonName, "Apply" )
41 | self.applyButton.onClick = function( commandbutton )
42 | local panels = self:getTabPanels():getChildren()
43 | for _, panel in ipairs( panels ) do
44 | panel:onApply()
45 | end
46 | convar.saveConfig()
47 | end
48 |
49 | require( "engine.client.gui.optionsmenu.keyboardoptionspanel" )
50 | self:addTab( "Keyboard", gui.keyboardoptionspanel )
51 |
52 | require( "engine.client.gui.optionsmenu.videooptionspanel" )
53 | self:addTab( "Video", gui.videooptionspanel )
54 |
55 | require( "engine.client.gui.optionsmenu.audiooptionspanel" )
56 | self:addTab( "Audio", gui.audiooptionspanel )
57 |
58 | -- require( "engine.client.gui.optionsmenu.multiplayeroptionspanel" )
59 | -- self:addTab( "Multiplayer", gui.multiplayeroptionspanel() )
60 | end
61 |
62 | function optionsmenu:activate()
63 | local panels = self:getTabPanels():getChildren()
64 | for _, panel in ipairs( panels ) do
65 | panel:activate()
66 | end
67 | gui.frame.activate( self )
68 | end
69 |
70 | function optionsmenu:invalidateLayout()
71 | self:moveToCenter()
72 | gui.tabbedframe.invalidateLayout( self )
73 | end
74 |
75 | local function onReloadScript()
76 | local mainmenu = g_MainMenu
77 | if ( mainmenu == nil or not mainmenu.optionsMenu ) then
78 | return
79 | end
80 |
81 | local optionsMenu = mainmenu.optionsMenu
82 | local visible = optionsMenu:isVisible()
83 | optionsMenu:remove()
84 | optionsMenu = gui.optionsmenu( mainmenu )
85 | mainmenu.optionsMenu = optionsMenu
86 | optionsMenu:moveToCenter()
87 | if ( visible ) then
88 | optionsMenu:activate()
89 | end
90 | end
91 |
92 | onReloadScript()
93 |
--------------------------------------------------------------------------------
/engine/client/gui/optionsmenu/keyboardoptionsadvancedframe.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Advanced Keyboard Options Frame class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.keyboardoptionsadvancedframe" ( "gui.frame" )
8 |
9 | local keyboardoptionsadvancedframe = gui.keyboardoptionsadvancedframe
10 |
11 | function keyboardoptionsadvancedframe:keyboardoptionsadvancedframe( parent, name, title )
12 | name = name or "Advanced Keyboard Options Frame"
13 | title = title or "Advanced Keyboard Options"
14 | gui.frame.frame( self, parent, name, title )
15 | self:setWidth( 480 )
16 | self:setHeight( 147 )
17 | self:setResizable( false )
18 |
19 | name = "Developer Console"
20 | self.console = gui.checkbox( self, name, "Enable Developer Console" )
21 | self.console:setChecked( convar.getConvar( "con_enable" ):getBoolean() )
22 | self.console.onCheckedChanged = function( checkbox, checked )
23 | convar.setConvar( "con_enable", checked and "1" or "0" )
24 | end
25 |
26 | local margin = 36
27 | local titleBarHeight = 86
28 | self.console:setPos( margin, titleBarHeight )
29 | end
30 |
--------------------------------------------------------------------------------
/engine/client/gui/optionsmenu/keyboardoptionscommandbuttongroup.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Keyboard Options Command Button Group class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.keyboardoptionscommandbuttongroup" ( "gui.commandbuttongroup" )
8 |
9 | local keyboardoptionscommandbuttongroup = gui.keyboardoptionscommandbuttongroup
10 |
11 | function keyboardoptionscommandbuttongroup:keyboardoptionscommandbuttongroup( parent, name )
12 | gui.commandbuttongroup.commandbuttongroup( self, parent, name )
13 | end
14 |
15 | function keyboardoptionscommandbuttongroup:invalidateLayout()
16 | local children = self:getChildren()
17 | local width = 0
18 | if ( children ) then
19 | for i, commandbutton in ipairs( children ) do
20 | commandbutton:setX( width )
21 | width = width + commandbutton:getWidth()
22 | end
23 | end
24 | self:setWidth( width )
25 |
26 | local parent = self:getParent()
27 | local margin = 24
28 | self:setPos( margin, parent:getHeight() - self:getHeight() )
29 | gui.panel.invalidateLayout( self )
30 | end
31 |
--------------------------------------------------------------------------------
/engine/client/gui/optionsmenu/keyboardoptionspanel.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Keyboard Options Panel class
4 | --
5 | --==========================================================================--
6 |
7 | require( "engine.client.gui.optionsmenu.bindlistpanel" )
8 | require( "engine.client.gui.optionsmenu.keyboardoptionscommandbuttongroup" )
9 | require( "engine.client.gui.optionsmenu.keyboardoptionsadvancedframe" )
10 |
11 | class "gui.keyboardoptionspanel" ( "gui.frametabpanel" )
12 |
13 | local keyboardoptionspanel = gui.keyboardoptionspanel
14 |
15 | function keyboardoptionspanel:keyboardoptionspanel( parent, name )
16 | name = name or "Keyboard Options Panel"
17 | gui.frametabpanel.frametabpanel( self, parent, name )
18 |
19 | self.bindList = gui.bindlistpanel( self )
20 | local margin = 24
21 | local height = 348 - margin
22 | self.bindList:setDimensions( 640 - 2 * margin, height )
23 | self.bindList:setMargin( margin )
24 | self.bindList:readBinds()
25 |
26 | local name = "Keyboard Options"
27 | local groupName = name .. " Command Button Group"
28 | local group = gui.keyboardoptionscommandbuttongroup( self, groupName )
29 |
30 | local buttonName = name .. " Use Defaults Button"
31 | self.useDefaultsButton = gui.commandbutton( group, buttonName, "Use Defaults" )
32 | self.useDefaultsButton.onClick = function( commandbutton )
33 | self.bindList:useDefaults()
34 | end
35 | buttonName = name .. " Advanced Button"
36 | self.advancedButton = gui.commandbutton( group, buttonName, "Advanced" )
37 | self.advancedButton.onClick = function( commandbutton )
38 | if ( self.advancedOptions == nil ) then
39 | self.advancedOptions = gui.keyboardoptionsadvancedframe( g_MainMenu )
40 | self.advancedOptions:activate()
41 | self.advancedOptions:moveToCenter()
42 | else
43 | self.advancedOptions:activate()
44 | end
45 | end
46 | end
47 |
48 | function keyboardoptionspanel:activate()
49 | end
50 |
51 | function keyboardoptionspanel:onOK()
52 | self.bindList:saveBinds()
53 | end
54 |
55 | function keyboardoptionspanel:onCancel()
56 | local innerPanel = self.bindList:getInnerPanel()
57 | innerPanel:removeChildren()
58 | self.bindList:readBinds()
59 | end
60 |
61 | keyboardoptionspanel.onApply = keyboardoptionspanel.onOK
62 |
--------------------------------------------------------------------------------
/engine/client/gui/optionsmenu/multiplayeroptionspanel.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Multiplayer Options Panel class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.multiplayeroptionspanel" ( "gui.frametabpanel" )
8 |
9 | local multiplayeroptionspanel = gui.multiplayeroptionspanel
10 |
11 | function multiplayeroptionspanel:multiplayeroptionspanel( parent, name )
12 | name = name or "Multiplayer Options Panel"
13 | gui.frametabpanel.frametabpanel( self, parent, name )
14 | local options = {}
15 | self.options = options
16 | local c = config.getConfig()
17 |
18 | local e = gui.createElement
19 |
20 | local panel = e( "box", {
21 | parent = self,
22 | position = "absolute",
23 | margin = 36
24 | }, {
25 | e( "text", { text = "Name", marginBottom = 9 } ),
26 | e( "textbox", { position = "static", text = "Unnamed" } )
27 | } )
28 |
29 | panel:setPos( panel:getMarginLeft(), panel:getMarginTop() )
30 |
31 | -- name = "Play Sound in Desktop"
32 | -- local desktopSound = gui.checkbox( self, name, name )
33 | -- self.desktopSound = desktopSound
34 | -- options.desktopSound = c.sound.desktop
35 | -- desktopSound:setChecked( c.sound.desktop )
36 | -- desktopSound.onCheckedChanged = function( checkbox, checked )
37 | -- options.desktopSound = checked
38 | -- c.sound.desktop = checked
39 | -- end
40 | -- x = 2 * x + self.name:getWidth()
41 | -- y = margin + label:getHeight() + marginBottom
42 | -- desktopSound:setPos( x, y )
43 |
44 | -- name = "Tickrates"
45 | -- local radiobuttongroup = gui.radiobuttongroup( self, name )
46 | -- name = "20"
47 | -- local radiobutton = gui.radiobutton( self, name .. " 1" )
48 | -- radiobuttongroup:addItem( radiobutton )
49 | -- x = x
50 | -- radiobutton:setPos( x, y )
51 | -- radiobutton:setDefault( true )
52 | end
53 |
54 | function multiplayeroptionspanel:activate()
55 | self:saveControlStates()
56 | end
57 |
58 | function multiplayeroptionspanel:onOK()
59 | self:updateOptions()
60 | end
61 |
62 | function multiplayeroptionspanel:onCancel()
63 | self:resetControlStates()
64 | end
65 |
66 | multiplayeroptionspanel.onApply = multiplayeroptionspanel.onOK
67 |
68 | function multiplayeroptionspanel:saveControlStates()
69 | local controls = {}
70 | self.controls = controls
71 | -- controls.name = self.name:getText()
72 | -- controls.desktopSound = self.desktopSound:isChecked()
73 | end
74 |
75 | function multiplayeroptionspanel:resetControlStates()
76 | local controls = self.controls
77 | -- self.name:setText( controls.name )
78 | -- self.desktopSound:setChecked( controls.desktopSound )
79 | table.clear( controls )
80 | end
81 |
82 | function multiplayeroptionspanel:updateOptions()
83 | local options = self.options
84 | convar.setConvar( "name", options.name )
85 | end
86 |
--------------------------------------------------------------------------------
/engine/client/gui/passwordtextbox.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Password Text Box class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.passwordtextbox" ( "gui.textbox" )
8 |
9 | local passwordtextbox = gui.passwordtextbox
10 |
11 | local function getInnerWidth( self )
12 | return self:getWidth() - 2 * self.padding
13 | end
14 |
15 | function passwordtextbox:passwordtextbox( parent, name, placeholder )
16 | gui.textbox.textbox( self, parent, name, placeholder or "Password" )
17 | self.password = ""
18 | end
19 |
20 | local utf8sub = string.utf8sub
21 |
22 | function passwordtextbox:doBackspace( count )
23 | count = count or 1
24 |
25 | if ( count == 0 ) then
26 | count = self.cursorPos + 1
27 | end
28 |
29 | if ( self.cursorPos > 0 ) then
30 | local sub1 = utf8sub( self.password, 1, self.cursorPos - count )
31 | if ( sub1 == self.password ) then
32 | sub1 = ""
33 | end
34 |
35 | local sub2 = utf8sub( self.password, self.cursorPos + 1 )
36 | self.password = sub1 .. sub2
37 | end
38 |
39 | gui.textbox.doBackspace( self, count )
40 | end
41 |
42 | local utf8len = string.utf8len
43 |
44 | function passwordtextbox:doDelete( count )
45 | count = count or 1
46 |
47 | if ( count == 0 ) then
48 | count = utf8len( self.password ) - self.cursorPos
49 | end
50 |
51 | local sub1 = utf8sub( self.password, 1, self.cursorPos )
52 | if ( self.cursorPos == 0 ) then
53 | sub1 = ""
54 | end
55 |
56 | local sub2 = utf8sub( self.password, self.cursorPos + 1 + count )
57 | self.password = sub1 .. sub2
58 | gui.textbox.doDelete( self, count )
59 | end
60 |
61 | function passwordtextbox:doCut()
62 | end
63 |
64 | function passwordtextbox:doCopy()
65 | end
66 |
67 | function passwordtextbox:getAutocomplete()
68 | end
69 |
70 | accessor( passwordtextbox, "password" )
71 |
72 | function passwordtextbox:insertText( text )
73 | local buffer = {}
74 | for i = 1, string.utf8len( text ) do
75 | table.insert( buffer, "•" )
76 | end
77 |
78 | local sub1 = utf8sub( self.password, self.cursorPos + 1 )
79 | local sub2 = utf8sub( self.password, 1, self.cursorPos )
80 | if ( self.cursorPos == 0 ) then
81 | sub2 = ""
82 | end
83 |
84 | self.password = sub2 .. text .. sub1
85 | gui.textbox.insertText( self, table.concat( buffer ) )
86 | end
87 |
88 | function passwordtextbox:isMultiline()
89 | return false
90 | end
91 |
92 | function passwordtextbox:setMultiline( multiline )
93 | assert( false )
94 | end
95 |
96 | function passwordtextbox:setText( text )
97 | local buffer = {}
98 | for i = 1, string.utf8len( text ) do
99 | table.insert( buffer, "•" )
100 | end
101 | gui.textbox.setText( self, table.concat( buffer ) )
102 | self.password = text
103 | end
104 |
105 | passwordtextbox.setPassword = passwordtextbox.setText
106 |
--------------------------------------------------------------------------------
/engine/client/gui/progressbar.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Progress Bar class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.progressbar" ( "gui.box" )
8 |
9 | local progressbar = gui.progressbar
10 |
11 | function progressbar:progressbar( parent, name )
12 | gui.box.box( self, parent, name )
13 | self:setDisplay( "block" )
14 | self:setBackgroundColor( self:getScheme( "progressbar.backgroundColor" ) )
15 | self.width = 216
16 | self.height = 2
17 | end
18 |
19 | function progressbar:draw()
20 | gui.panel.drawBackground( self, self:getScheme( "progressbar.backgroundColor" ) )
21 | self:drawBar()
22 |
23 | gui.box.draw( self )
24 | end
25 |
26 | function progressbar:drawBar()
27 | local color = self:getScheme( "progressbar.foregroundColor" )
28 | local value = self:getValue()
29 | local min = self:getMin()
30 | local max = self:getMax()
31 | local percent = math.remap( value, min, max, 0, 1 )
32 | local width = self:getWidth() * percent
33 | local height = self:getHeight()
34 | love.graphics.setColor( color )
35 | love.graphics.rectangle( "fill", 0, 0, width, height )
36 | end
37 |
38 | gui.accessor( progressbar, "min", nil, nil, 0 )
39 | gui.accessor( progressbar, "max", nil, nil, 1 )
40 | gui.accessor( progressbar, "value", nil, nil, 0 )
41 |
--------------------------------------------------------------------------------
/engine/client/gui/radiobuttongroup.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Radio Button Group class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.radiobuttongroup" ( "gui.box" )
8 |
9 | local radiobuttongroup = gui.radiobuttongroup
10 |
11 | function radiobuttongroup:radiobuttongroup( parent, name )
12 | gui.box.box( self, parent, name )
13 | self:setDisplay( "block" )
14 | self:setPosition( "absolute" )
15 | self.selectedId = 0
16 | self.disabled = false
17 | end
18 |
19 | function radiobuttongroup:addItem( item )
20 | self.items = self.items or {}
21 | item.id = #self.items + 1
22 | table.insert( self.items, item )
23 | item.group = self
24 | end
25 |
26 | function radiobuttongroup:removeItem( item )
27 | local items = self:getItems()
28 | for i, v in ipairs( items ) do
29 | if ( v == item ) then
30 | table.remove( items, i )
31 | if ( self.selectedId == i ) then
32 | self.selectedId = 0
33 | end
34 | self:invalidateLayout()
35 | end
36 | end
37 |
38 | if ( #items == 0 ) then
39 | self.items = nil
40 | end
41 | end
42 |
43 | accessor( radiobuttongroup, "items" )
44 | accessor( radiobuttongroup, "selectedId" )
45 |
46 | function radiobuttongroup:getSelectedItem()
47 | local items = self:getItems()
48 | if ( items ) then
49 | return items[ self:getSelectedId() ]
50 | end
51 | end
52 |
53 | function radiobuttongroup:getValue()
54 | local item = self:getSelectedItem()
55 | if ( item ) then
56 | return item:getValue()
57 | end
58 | end
59 |
60 | accessor( radiobuttongroup, "disabled", "is" )
61 |
62 | function radiobuttongroup:setSelectedId( selectedId, default )
63 | local oldSelectedId = self:getSelectedId()
64 | local items = self:getItems()
65 | local oldSelection = items[ oldSelectedId ]
66 | local newSelection = items[ selectedId ]
67 | if ( oldSelection and oldSelectedId ~= selectedId ) then
68 | oldSelection:setSelected( false )
69 | newSelection:setSelected( true )
70 | self.selectedId = selectedId
71 | self:onValueChanged( oldSelection:getValue(), newSelection:getValue() )
72 | else
73 | newSelection:setSelected( true )
74 | self.selectedId = selectedId
75 | if ( not default ) then
76 | self:onValueChanged( nil, newSelection:getValue() )
77 | end
78 | end
79 | end
80 |
81 | function radiobuttongroup:setValue( value )
82 | local items = self:getItems()
83 | for i, v in ipairs( items ) do
84 | if ( v:getValue() == value ) then
85 | self:setSelectedId( i )
86 | end
87 | end
88 | end
89 |
90 | function radiobuttongroup:onValueChanged( oldValue, newValue )
91 | end
92 |
--------------------------------------------------------------------------------
/engine/client/gui/rootpanel.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Root Panel class
4 | --
5 | --==========================================================================--
6 |
7 | require( "engine.client.gui.box" )
8 |
9 | class "gui.rootpanel" ( "gui.box" )
10 |
11 | local rootpanel = gui.rootpanel
12 |
13 | function rootpanel:rootpanel()
14 | self.x = 0
15 | self.y = 0
16 | self.width = love.graphics.getWidth()
17 | self.height = love.graphics.getHeight()
18 | self.name = "Root Panel"
19 | self.visible = true
20 | self.children = {}
21 | self.scale = 1
22 | self.opacity = 1
23 | self:setUseFullscreenCanvas( true )
24 | end
25 |
26 | function rootpanel:invalidateLayout()
27 | self:setDimensions( love.graphics.getWidth(), love.graphics.getHeight() )
28 |
29 | gui.panel.invalidateLayout( self )
30 | end
31 |
--------------------------------------------------------------------------------
/engine/client/gui/scheme.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Scheme class
4 | --
5 | --==========================================================================--
6 |
7 | class( "scheme" )
8 |
9 | scheme._schemes = scheme._schemes or {}
10 |
11 | local properties = {}
12 |
13 | function scheme.clear( name )
14 | properties[ name ] = {}
15 | end
16 |
17 | function scheme.getProperty( name, property )
18 | local cachedProperty = properties[ name ][ property ]
19 | if ( cachedProperty ) then
20 | return cachedProperty
21 | end
22 |
23 | local value = scheme._schemes[ name ]
24 | local type = type( value )
25 | if ( type ~= "scheme" ) then
26 | error( "attempt to index scheme '" .. name .. "' " ..
27 | "(a " .. type .. " value)", 3 )
28 | end
29 |
30 | for key in string.gmatch( property .. ".", "(%w-)%." ) do
31 | if ( value and value[ key ] ) then
32 | value = value[ key ]
33 | else
34 | error( "attempt to index property '" .. property .. "' " ..
35 | "(a nil value)", 3 )
36 | end
37 | end
38 |
39 | properties[ name ][ property ] = value
40 | return value
41 | end
42 |
43 | function scheme.isLoaded( name )
44 | return scheme._schemes[ name ] ~= nil
45 | end
46 |
47 | function scheme.load( name )
48 | require( "schemes." .. name )
49 | end
50 |
51 | function scheme:scheme( name )
52 | self.name = name
53 | scheme._schemes[ name ] = self
54 | scheme.clear( name )
55 | end
56 |
57 | function scheme:__tostring()
58 | return "scheme: \"" .. self.name .. "\""
59 | end
60 |
--------------------------------------------------------------------------------
/engine/client/gui/scrollablepanel.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Scrollable Panel class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.scrollablepanel" ( "gui.box" )
8 |
9 | local scrollablepanel = gui.scrollablepanel
10 |
11 | function scrollablepanel:scrollablepanel( parent, name )
12 | gui.box.box( self, parent, name )
13 | self.panel = gui.box( self, self:getName() .. " Inner Panel" )
14 | self.panel:setPosition( "absolute" )
15 |
16 | self.scrollbar = gui.scrollbar( self, self:getName() .. " Scrollbar" )
17 | self.scrollbar.onValueChanged = function( _, oldValue, newValue )
18 | self.panel:setY( -newValue )
19 | end
20 |
21 | self:setScheme( "Default" )
22 | end
23 |
24 | accessor( scrollablepanel, "innerHeight" )
25 | accessor( scrollablepanel, "innerPanel", nil, "panel" )
26 | accessor( scrollablepanel, "scrollbar" )
27 |
28 | function scrollablepanel:invalidateLayout()
29 | self:setDimensions( self:getDimensions() )
30 | gui.panel.invalidateLayout( self )
31 | end
32 |
33 | local function getParentFrame( self )
34 | local panel = self
35 | while ( panel ~= nil ) do
36 | panel = panel:getParent()
37 | if ( typeof( panel, "frame" ) ) then
38 | return panel
39 | end
40 | end
41 | end
42 |
43 | function scrollablepanel:keypressed( key, scancode, isrepeat )
44 | local parentFrame = getParentFrame( self )
45 | local parentFocus = parentFrame and parentFrame.focus
46 | if ( key == "tab" and ( self.focus or parentFocus ) ) then
47 | gui.frame.moveFocus( self.panel )
48 | end
49 |
50 | return gui.panel.keypressed( self, key, scancode, isrepeat )
51 | end
52 |
53 | function scrollablepanel:setWidth( width )
54 | self.panel:setWidth( width )
55 | gui.panel.setWidth( self, width )
56 | end
57 |
58 | function scrollablepanel:setHeight( height )
59 | self.scrollbar:setRangeWindow( height )
60 | gui.panel.setHeight( self, height )
61 | end
62 |
63 | function scrollablepanel:setInnerHeight( innerHeight )
64 | self.innerHeight = innerHeight
65 | self.panel:setHeight( innerHeight )
66 | self.scrollbar:setRange( 0, innerHeight )
67 | end
68 |
69 | function scrollablepanel:wheelmoved( x, y )
70 | local panel = self:getInnerPanel()
71 | if ( panel.mouseover or panel:isChildMousedOver() ) then
72 | local scrollbar = self:getScrollbar()
73 | if ( scrollbar ) then
74 | local font = self:getScheme( "font" )
75 | if ( y < 0 ) then
76 | gui.setFocusedPanel( nil, false )
77 | scrollbar:scrollDown( 3 * font:getHeight() )
78 | return true
79 | elseif ( y > 0 ) then
80 | gui.setFocusedPanel( nil, false )
81 | scrollbar:scrollUp( 3 * font:getHeight() )
82 | return true
83 | end
84 | end
85 | end
86 |
87 | return gui.panel.wheelmoved( self, x, y )
88 | end
89 |
--------------------------------------------------------------------------------
/engine/client/gui/text.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Text class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.text" ( "gui.box" )
8 |
9 | local text = gui.text
10 |
11 | function text:text( parent, text )
12 | gui.box.box( self, parent, nil )
13 | self:set( text )
14 | end
15 |
16 | function text:createCanvas()
17 | end
18 |
19 | function text:draw()
20 | assert( false )
21 | love.graphics.setColor( self:getColor() )
22 | love.graphics.draw( self._text )
23 | end
24 |
25 | function text:drawCanvas()
26 | if ( not self:isVisible() ) then
27 | return
28 | end
29 |
30 | love.graphics.push()
31 | local a = self:getOpacity()
32 | local c = self:getColor()
33 | love.graphics.setColor(
34 | a * c[ 1 ], a * c[ 2 ], a * c[ 3 ], a * c[ 4 ]
35 | )
36 | love.graphics.draw( self._text )
37 | love.graphics.pop()
38 | end
39 |
40 | function text:getWidth()
41 | local font = self:getFont()
42 | return font:getWidth( self:get() )
43 | end
44 |
45 | function text:getHeight()
46 | local font = self:getFont()
47 | return font:getHeight()
48 | end
49 |
50 | function text:set( text )
51 | self.text = text
52 |
53 | if ( self._text ) then
54 | self._text:set( text or "" )
55 | else
56 | self._text = love.graphics.newText( self:getFont(), text )
57 | end
58 | end
59 |
60 | text.setText = text.set
61 |
62 | function text:get()
63 | return rawget( self, "text" ) or ""
64 | end
65 |
66 | function text:setFont( font )
67 | self._text:setFont( font )
68 | end
69 |
70 | function text:getFont()
71 | return self._text and self._text:getFont() or self:getScheme( "font" )
72 | end
73 |
--------------------------------------------------------------------------------
/engine/client/gui/textboxautocompleteitemgroup.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Text Box Autocomplete Item Group class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.textboxautocompleteitemgroup" ( "gui.dropdownlistitemgroup" )
8 |
9 | local textboxautocompleteitemgroup = gui.textboxautocompleteitemgroup
10 |
11 | function textboxautocompleteitemgroup:textboxautocompleteitemgroup( parent, name )
12 | gui.dropdownlistitemgroup.dropdownlistitemgroup( self, parent, name )
13 | -- UNDONE: The drop-down list field is reserved for the control responsible
14 | -- for the drop-down list item group. The control does not necessarily have
15 | -- to be a dropdownlist.
16 | -- self.dropDownList = nil
17 | self.textbox = parent
18 | end
19 |
20 | function textboxautocompleteitemgroup:addItem( item )
21 | item:setParent( self )
22 | gui.radiobuttongroup.addItem( self, item )
23 |
24 | item.onClick = function( item )
25 | local value = item:getValue()
26 | self:removeChildren()
27 | local textbox = self:getTextbox()
28 | textbox:setText( value )
29 | end
30 |
31 | self:invalidateLayout()
32 | end
33 |
34 | accessor( textboxautocompleteitemgroup, "textbox" )
35 |
36 | function textboxautocompleteitemgroup:isVisible()
37 | local textbox = self:getTextbox()
38 | local children = self:getChildren()
39 | local hasChildren = children and #children > 0
40 | return textbox:isVisible() and textbox.focus and hasChildren
41 | end
42 |
43 | function textboxautocompleteitemgroup:mousepressed( x, y, button, istouch )
44 | if ( button == 1 ) then
45 | local textbox = self:getTextbox()
46 | if ( textbox ~= gui._topPanel and
47 | ( not ( self.mouseover or self:isChildMousedOver() ) ) ) then
48 | if ( self:getChildren() ) then
49 | self:removeChildren()
50 | end
51 | end
52 | end
53 |
54 | return gui.panel.mousepressed( self, x, y, button, istouch )
55 | end
56 |
57 | function textboxautocompleteitemgroup:onValueChanged( oldValue, newValue )
58 | end
59 |
--------------------------------------------------------------------------------
/engine/client/gui/throbber.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Throbber class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.throbber" ( "gui.imagepanel" )
8 |
9 | local throbber = gui.throbber
10 |
11 | function throbber:throbber( parent, name, image )
12 | gui.imagepanel.imagepanel( self, parent, name, image or "images/gui/throbber.png" )
13 | self:setDimensions( 16, 16 )
14 | self:setOpacity( 0 )
15 | end
16 |
17 | local missingImage = false
18 |
19 | function throbber:draw()
20 | gui.panel._maskedPanel = self
21 | love.graphics.stencil( gui.panel.drawMask )
22 | love.graphics.setStencilTest( "greater", 0 )
23 | love.graphics.setColor( self:getColor() )
24 | local image = self:getImage()
25 | local width = self:getWidth()
26 | local height = self:getHeight()
27 | love.graphics.draw(
28 | image,
29 | width / 2,
30 | height / 2,
31 | love.timer.getTime() % 2 * math.pi,
32 | 1,
33 | 1,
34 | width / 2,
35 | height / 2
36 | )
37 | love.graphics.setStencilTest()
38 |
39 | missingImage = image == nil
40 | if ( missingImage ) then
41 | self:drawMissingImage()
42 | end
43 |
44 | gui.panel.draw( self )
45 | end
46 |
47 | function throbber:enable()
48 | self.enabled = true
49 | end
50 |
51 | function throbber:disable()
52 | self.enabled = false
53 | end
54 |
55 | accessor( throbber, "enabled", "is" )
56 |
57 | function throbber:update( dt )
58 | local opacity = self:getOpacity()
59 | if ( self:isVisible() and opacity ~= 0 ) then
60 | self:invalidate()
61 | end
62 |
63 | -- FIXME: self:animate doesn't work for gui.throbber.
64 | if ( self.enabled and opacity ~= 1 ) then
65 | self:setOpacity( math.min( opacity + dt * ( 1 / 0.4 ), 1 ) )
66 | elseif ( opacity ~= 0 ) then
67 | self:setOpacity( math.max( opacity - dt * ( 1 / 0.4 ), 0 ) )
68 | end
69 | end
70 |
--------------------------------------------------------------------------------
/engine/client/gui/viewport.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Viewport Panel class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.viewport" ( "gui.box" )
8 |
9 | local viewport = gui.viewport
10 |
11 | function viewport:viewport( parent )
12 | gui.box.box( self, parent, "Viewport" )
13 | self.width = love.graphics.getWidth()
14 | self.height = love.graphics.getHeight()
15 | self:setUseFullscreenCanvas( true )
16 | self:moveToBack()
17 | end
18 |
19 | function viewport:invalidateLayout()
20 | self:setDimensions( love.graphics.getWidth(), love.graphics.getHeight() )
21 |
22 | gui.panel.invalidateLayout( self )
23 | end
24 |
25 | local VIEWPORT_ANIM_TIME = 0.2
26 |
27 | function viewport:hide()
28 | self:animate( {
29 | opacity = 0,
30 | }, VIEWPORT_ANIM_TIME, "easeOutQuint", function()
31 | self:setVisible( false )
32 | self:setOpacity( 1 )
33 | end )
34 | end
35 |
36 | function viewport:show()
37 | if ( not self:isVisible() ) then
38 | self:setOpacity( 0 )
39 | self:animate( {
40 | opacity = 1
41 | }, VIEWPORT_ANIM_TIME, "easeOutQuint" )
42 | end
43 |
44 | self:setVisible( true )
45 | end
46 |
47 | local function hideViewport()
48 | if ( g_Viewport == nil ) then
49 | return
50 | end
51 |
52 | g_Viewport:hide()
53 | end
54 |
55 | hook.set( "client", hideViewport, "onMainMenuActivate", "hideViewport" )
56 |
57 | local function showViewport()
58 | if ( g_Viewport == nil ) then
59 | return
60 | end
61 |
62 | g_Viewport:show()
63 | end
64 |
65 | hook.set( "client", showViewport, "onMainMenuClose", "showViewport" )
66 |
--------------------------------------------------------------------------------
/engine/client/gui/watch.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Watch class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.watch" ( "gui.label" )
8 |
9 | local watch = gui.watch
10 |
11 | function watch:watch( parent, name )
12 | gui.label.label( self, parent, name, text )
13 |
14 | self:setScheme( "Console" )
15 | self.font = self:getScheme( "font" )
16 | local margin = gui.scale( 96 )
17 | self.width = love.graphics.getWidth() - 2 * margin
18 | self.height = self.font:getHeight()
19 |
20 | self:setScheme( "Default" )
21 | self:invalidateLayout()
22 | end
23 |
24 | function watch:update( dt )
25 | -- HACKHACK: Fade this out for readability.
26 | if ( g_HudMoveIndicator and g_HudMoveIndicator._entity ) then
27 | self:setOpacity( 0 )
28 | else
29 | self:setOpacity( 1 )
30 | end
31 |
32 | local f, err = loadstring( "return " .. self:getExpression() )
33 | local ret = "?"
34 | if ( f ) then
35 | local status, err = pcall( f )
36 | if ( status == false ) then
37 | self:invalidate()
38 | return
39 | end
40 | else
41 | ret = err
42 | end
43 |
44 | self:setText( self:getExpression() .. " = " .. tostring(
45 | f and f() or ret
46 | ) )
47 | self:invalidate()
48 | end
49 |
50 | gui.accessor( watch, "expression" )
51 |
52 | function watch:invalidateLayout()
53 | local margin = gui.scale( 96 )
54 | local x = margin
55 | local y = margin + 28
56 | self:setPos( x, y )
57 |
58 | gui.panel.invalidateLayout( self )
59 | end
60 |
--------------------------------------------------------------------------------
/engine/client/input.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Input interface
4 | --
5 | --==========================================================================--
6 |
7 | local love = love
8 |
9 | module( "input" )
10 |
11 | function getKeyTrap()
12 | return _keyTrap
13 | end
14 |
15 | local mx, my = 0, 0
16 |
17 | function setKeyTrap( callback )
18 | _keyTrap = callback
19 | mx, my = love.mouse.getPosition()
20 | end
21 |
22 | function isKeyTrapped( key )
23 | local callback = getKeyTrap()
24 | if ( callback ) then
25 | love.mouse.setPosition( mx, my )
26 | setKeyTrap()
27 | return callback( key )
28 | else
29 | return false
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/engine/client/network/localhost_enet_peer.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Localhost ENet Peer class
4 | --
5 | --==========================================================================--
6 |
7 | class( "localhost_enet_peer" )
8 |
9 | function localhost_enet_peer:localhost_enet_peer()
10 | g_localhost_enet_peer = self
11 | end
12 |
13 | function localhost_enet_peer:connect_id()
14 | return -1
15 | end
16 |
17 | function localhost_enet_peer:disconnect( data )
18 | local event = {
19 | peer = g_localhost_enet_server,
20 | type = "disconnect",
21 | data = data
22 | }
23 | engine.client.onDisconnect( event )
24 | end
25 |
26 | function localhost_enet_peer:disconnect_now( data )
27 | self:disconnect( data )
28 | end
29 |
30 | function localhost_enet_peer:disconnect_later( data )
31 | self:disconnect( data )
32 | end
33 |
34 | function localhost_enet_peer:index()
35 | return -1
36 | end
37 |
38 | function localhost_enet_peer:ping()
39 | end
40 |
41 | function localhost_enet_peer:ping_inverval( interval )
42 | end
43 |
44 | function localhost_enet_peer:reset()
45 | end
46 |
47 | function localhost_enet_peer:send( data, channel, flag )
48 | local event = {
49 | peer = g_localhost_enet_server,
50 | type = "receive",
51 | data = data
52 | }
53 | engine.client.onReceive( event )
54 | end
55 |
56 | function localhost_enet_peer:state()
57 | return "unknown"
58 | end
59 |
60 | function localhost_enet_peer:receive()
61 | end
62 |
63 | function localhost_enet_peer:round_trip_time( value )
64 | end
65 |
66 | function localhost_enet_peer:last_round_trip_time( value )
67 | end
68 |
69 | function localhost_enet_peer:throttle_configure( interval, acceleration, deceleration )
70 | end
71 |
72 | function localhost_enet_peer:timeout( limit, minimum, maximum )
73 | end
74 |
75 | function localhost_enet_peer:__tostring()
76 | return "127.0.0.1:" .. convar.getConvar( "host_port" ):getNumber()
77 | end
78 |
--------------------------------------------------------------------------------
/engine/client/network/localhost_enet_server.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Localhost ENet Server class
4 | --
5 | --==========================================================================--
6 |
7 | class "localhost_enet_server" ( "localhost_enet_peer" )
8 |
9 | function localhost_enet_server:localhost_enet_server()
10 | g_localhost_enet_server = self
11 | end
12 |
13 | function localhost_enet_server:disconnect( data )
14 | local event = {
15 | peer = g_localhost_enet_peer,
16 | type = "disconnect",
17 | data = data
18 | }
19 | g_localhost_enet_peer = nil
20 | engine.server.onDisconnect( event )
21 | end
22 |
23 | function localhost_enet_server:send( data, channel, flag )
24 | local event = {
25 | peer = g_localhost_enet_peer,
26 | type = "receive",
27 | data = data
28 | }
29 | engine.server.onReceive( event )
30 | end
31 |
32 | function localhost_enet_server:__tostring()
33 | return "127.0.0.1:" .. convar.getConvar( "host_port" ):getNumber()
34 | end
35 |
--------------------------------------------------------------------------------
/engine/client/payloads.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Engine client payload handlers
4 | --
5 | --==========================================================================--
6 |
7 | local function onReceivePlayerInitialized( payload )
8 | localplayer = player.getById( payload:get( "id" ) )
9 |
10 | g_MainMenu:close()
11 |
12 | require( "engine.client.camera" )
13 | camera.setParentEntity( localplayer )
14 | camera.setZoom( 2 )
15 |
16 | if ( not _SERVER ) then
17 | localplayer:setGraphicsSize( love.graphics.getDimensions() )
18 | localplayer:initialSpawn()
19 | end
20 | end
21 |
22 | payload.setHandler( onReceivePlayerInitialized, "playerInitialized" )
23 |
24 | local function onReceiveServerInfo( payload )
25 | local mapName = payload:get( "map" )
26 |
27 | require( "engine.shared.map" )
28 | if ( not map.exists( mapName ) ) then
29 | engine.client.download( "maps/" .. mapName .. ".lua" )
30 | else
31 | map.load( mapName )
32 |
33 | require( "game" )
34 | require( "game.client" )
35 | game.client.load( args )
36 |
37 | engine.client.sendClientInfo()
38 | end
39 | end
40 |
41 | payload.setHandler( onReceiveServerInfo, "serverInfo" )
42 |
--------------------------------------------------------------------------------
/engine/server/handlers.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Engine server handlers
4 | --
5 | --==========================================================================--
6 |
7 | local debug = debug
8 | local engine = engine
9 | local ipairs = ipairs
10 | local love = love
11 | local print = print
12 | local require = require
13 | local tostring = tostring
14 | local unrequire = unrequire
15 | local _G = _G
16 |
17 | module( "engine.server" )
18 |
19 | function load( arg )
20 | require( "engine.server.network" )
21 |
22 | local network = engine.server.network
23 | local initialized = network.initializeServer()
24 | if ( not initialized ) then
25 | return false
26 | end
27 |
28 | _G._SERVER = true
29 |
30 | require( "game" )
31 | require( "game.server" )
32 |
33 | local game = _G.game.server
34 | game.load( arg )
35 |
36 | return true
37 | end
38 |
39 | function quit()
40 | -- Shutdown game
41 | local game = _G.game and _G.game.server or nil
42 | if ( game ) then
43 | game.shutdown()
44 | unrequire( "game.server" )
45 | _G.game.server = nil
46 | end
47 |
48 | unrequire( "game" )
49 | _G.game = nil
50 |
51 | -- Shutdown server
52 | local network = engine.server.network
53 | network.shutdownServer()
54 |
55 | unrequire( "engine.server.network" )
56 | engine.server.network = nil
57 | unrequire( "engine.server.payloads" )
58 | unrequire( "engine.server.handlers" )
59 |
60 | _G._SERVER = nil
61 | end
62 |
63 | function update( dt )
64 | local game = _G.game and _G.game.server or nil
65 | local entity = _G.entity
66 |
67 | if ( game ) then
68 | game.update( dt )
69 |
70 | if ( entity ) then
71 | local entities = entity.getAll()
72 | for _, entity in ipairs( entities ) do
73 | entity:update( dt )
74 | end
75 | end
76 | end
77 |
78 | local network = engine.server.network
79 | if ( network ) then
80 | network.update( dt )
81 | end
82 | end
83 |
84 | local function error_printer(msg, layer)
85 | print((debug.traceback("Error: " ..
86 | tostring(msg), 1+(layer or 1)):gsub("\n[^\n]+$", "")))
87 | end
88 |
89 | function errhand(msg)
90 | msg = tostring(msg)
91 |
92 | error_printer(msg, 2)
93 |
94 | end
95 |
--------------------------------------------------------------------------------
/engine/server/network/host.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Host wrapper class for ENet
4 | --
5 | --==========================================================================--
6 |
7 | local enet = require( "enet" )
8 |
9 | class( "host" )
10 |
11 | function host:host(
12 | bind_address,
13 | peer_count,
14 | channel_count,
15 | in_bandwidth,
16 | out_bandwidth
17 | )
18 | self._host = enet.host_create(
19 | bind_address,
20 | peer_count or 64,
21 | channel_count or 1,
22 | in_bandwidth or 0,
23 | out_bandwidth or 0
24 | )
25 | end
26 |
27 | function host:isValid()
28 | return self._host ~= nil
29 | end
30 |
31 | function host:connect( address, channel_count, data )
32 | return self._host:connect( address, channel_count, data )
33 | end
34 |
35 | function host:service( timeout )
36 | if ( timeout ) then
37 | return self._host:service( timeout )
38 | else
39 | return self._host:service()
40 | end
41 | end
42 |
43 | function host:check_events()
44 | return self._host:check_events()
45 | end
46 |
47 | function host:compress_with_range_coder()
48 | return self._host:compress_with_range_coder()
49 | end
50 |
51 | function host:flush()
52 | return self._host:flush()
53 | end
54 |
55 | function host:broadcast( data, channel, flag )
56 | if ( g_localhost_enet_peer ) then
57 | g_localhost_enet_peer:send( data, channel, flag )
58 | end
59 | return self._host:broadcast( data, channel, flag )
60 | end
61 |
62 | function host:channel_limit( limit )
63 | return self._host:channel_limit( limit )
64 | end
65 |
66 | function host:bandwidth_limit( incoming, outgoing )
67 | return self._host:bandwidth_limit( incoming, outgoing )
68 | end
69 |
70 | function host:total_sent_data()
71 | return self._host:total_sent_data()
72 | end
73 |
74 | function host:total_received_data()
75 | return self._host:total_received_data()
76 | end
77 |
78 | function host:service_time()
79 | return self._host:service_time()
80 | end
81 |
82 | function host:peer_count()
83 | return g_localhost_enet_peer and self._host:peer_count() + 1 or
84 | self._host:peer_count()
85 | end
86 |
87 | function host:get_peer( index )
88 | if ( g_localhost_enet_peer and index == 1 ) then
89 | return g_localhost_enet_peer
90 | end
91 | return g_localhost_enet_peer and self._host:get_peer( index + 1 ) or
92 | self._host:get_peer( index )
93 | end
94 |
95 | function host:get_socket_address()
96 | return self._host:get_socket_address()
97 | end
98 |
99 | function host:__tostring()
100 | return tostring( self.__host )
101 | end
102 |
--------------------------------------------------------------------------------
/engine/server/payloads.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Engine server payload handlers
4 | --
5 | --==========================================================================--
6 |
7 | local directoryWhitelist = {
8 | "maps"
9 | }
10 |
11 | local function onDownloadRequest( payload )
12 | local filename = payload:get( "filename" )
13 | if ( filename == nil ) then
14 | return
15 | end
16 |
17 | filename = string.fixslashes( filename )
18 | local peer = payload:getPeer()
19 |
20 | print( tostring( peer ) .. " requested \"" .. filename .. "\"..." )
21 |
22 | if ( string.find( filename, "/..", 1, true ) or
23 | string.ispathabsolute( filename ) ) then
24 | print( "Access denied to " .. tostring( peer ) .. "!" )
25 | return
26 | end
27 |
28 | if ( love.filesystem.getInfo( filename ) == nil ) then
29 | print( filename .. " does not exist!" )
30 | return
31 | end
32 |
33 | local directory = string.match( filename, "(.-)/" )
34 | if ( directory == nil or
35 | not table.hasvalue( directoryWhitelist, directory ) ) then
36 | print( tostring( peer ) ..
37 | " requested file outside of directory whitelist (" ..
38 | directory .. ")!" )
39 | return
40 | end
41 |
42 | engine.server.upload( filename, peer )
43 | end
44 |
45 | payload.setHandler( onDownloadRequest, "download" )
46 |
47 | local function sendEntity( player, entity )
48 | if ( entity == player ) then
49 | return
50 | end
51 |
52 | local payload = payload( "entitySpawned" )
53 | payload:set( "classname", entity:getClassname() )
54 | payload:set( "entIndex", entity.entIndex )
55 | payload:set( "networkVars", entity:getNetworkVarTypeLenValues() )
56 | player:send( payload )
57 | end
58 |
59 | local function sendEntities( player )
60 | local entities = entity.getAll()
61 | for _, entity in ipairs( entities ) do
62 | sendEntity( player, entity )
63 | end
64 | end
65 |
66 | local function onReceiveClientInfo( payload )
67 | local player = payload:getPlayer()
68 | local graphicsWidth = payload:get( "graphicsWidth" )
69 | local graphicsHeight = payload:get( "graphicsHeight" )
70 | player:setGraphicsSize( graphicsWidth, graphicsHeight )
71 | sendEntities( player )
72 | player:initialSpawn()
73 | end
74 |
75 | payload.setHandler( onReceiveClientInfo, "clientInfo" )
76 |
77 | local function onReceiveConcommand( payload )
78 | local player = payload:getPlayer()
79 | local name = payload:get( "name" )
80 | local argString = payload:get( "argString" )
81 | if ( player == localplayer ) then
82 | return
83 | end
84 |
85 | concommand.dispatch( player, name, argString, argTable )
86 | end
87 |
88 | payload.setHandler( onReceiveConcommand, "concommand" )
89 |
--------------------------------------------------------------------------------
/engine/shared/addon.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Addon interface
4 | --
5 | --==========================================================================--
6 |
7 | local hook = hook
8 | local love = love
9 | local print = print
10 | local require = require
11 | local string = string
12 | local table = table
13 | local ipairs = ipairs
14 |
15 | module( "addon" )
16 |
17 | function load( arg )
18 | local addons = love.filesystem.getDirectoryItems( "addons" )
19 | for i = #addons, -1, 1 do
20 | local v = addons[ i ]
21 | local info = love.filesystem.getInfo( v )
22 | local isDirectory = info and info.type == "directory" or false
23 | if ( string.fileextension( v ) ~= "zip" or not isDirectory ) then
24 | table.remove( addons, i )
25 | end
26 | end
27 |
28 | for _, v in ipairs( addons ) do
29 | mount( v )
30 | end
31 | end
32 |
33 | _addons = _addons or {}
34 |
35 | function getAddons()
36 | return _addons
37 | end
38 |
39 | function mount( addon )
40 | if ( love.filesystem.mount( "addons/" .. addon, "" ) ) then
41 | table.insert( getAddons(), addon )
42 | print( "Mounted \"" .. addon .. "\"!" )
43 | require( "addons." .. addon )
44 | hook.call( "shared", "onAddonMounted", addon )
45 | return true
46 | end
47 |
48 | print( "Failed to mount \"" .. addon .. "\"!" )
49 | return false
50 | end
51 |
52 | function unmount( addon )
53 | local mounted = table.hasvalue( getAddons(), addon )
54 | if ( not mounted ) then
55 | print( "Addon \"" .. addon .. "\" is not mounted!" )
56 | return false
57 | end
58 |
59 | if ( love.filesystem.unmount( "addons/" .. addon ) ) then
60 | hook.call( "shared", "onAddonUnmounted", addon )
61 | unrequire( "addons." .. addon )
62 | print( "Unmounted \"" .. addon .. "\"!" )
63 | return true
64 | end
65 |
66 | print( "Failed to unmount \"" .. addon .. "\"!" )
67 | return false
68 | end
69 |
--------------------------------------------------------------------------------
/engine/shared/buttons.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Input enumeration
4 | --
5 | --==========================================================================--
6 |
7 | _E = _E or {}
8 | _E.IN_FORWARD = bit.lshift( 1, 0 ) -- (1 << 0)
9 | _E.IN_BACK = bit.lshift( 1, 1 ) -- (1 << 1)
10 | _E.IN_LEFT = bit.lshift( 1, 2 ) -- (1 << 2)
11 | _E.IN_RIGHT = bit.lshift( 1, 3 ) -- (1 << 3)
12 | _E.IN_SPEED = bit.lshift( 1, 4 ) -- (1 << 4)
13 | _E.IN_USE = bit.lshift( 1, 5 ) -- (1 << 5)
14 |
--------------------------------------------------------------------------------
/engine/shared/dblib.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Extends the debug library
4 | --
5 | --==========================================================================--
6 |
7 | require( "debug" )
8 |
9 | function debug.getparameters( f )
10 | local info = debug.getinfo( f, "u" )
11 | local params = {}
12 | for i = 1, info.nparams do
13 | params[ i ] = debug.getlocal( f, i )
14 | end
15 | return params, info.isvararg
16 | end
17 |
--------------------------------------------------------------------------------
/engine/shared/entities/character.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Character class
4 | --
5 | --==========================================================================--
6 |
7 | entities.require( "entity" )
8 |
9 | class "character" ( "entity" )
10 |
11 | function character:character()
12 | entity.entity( self )
13 | end
14 |
15 | function character:addTask( task, name )
16 | if ( self._tasks == nil ) then
17 | self._tasks = {}
18 | end
19 |
20 | table.insert( self._tasks, {
21 | task = task,
22 | name = name
23 | } )
24 | end
25 |
26 | function character:moveTo( position, callback )
27 | if ( callback ) then
28 | callback()
29 | end
30 | end
31 |
32 | function character:nextTask()
33 | local tasks = self._tasks
34 | table.remove( tasks, 1 )
35 |
36 | if ( #tasks == 0 ) then
37 | self:removeTasks()
38 | end
39 | end
40 |
41 | function character:removeTasks()
42 | self._tasks = nil
43 | end
44 |
45 | function character:tick( timestep )
46 | self:updateTasks()
47 | entity.tick( self, timestep )
48 | end
49 |
50 | function character:updateTasks()
51 | local tasks = self._tasks
52 | if ( tasks == nil ) then
53 | return
54 | end
55 |
56 | local task = tasks[ 1 ]
57 | if ( task and not task.running ) then
58 | task.running = true
59 | task.task( self, function() self:nextTask() end )
60 | end
61 | end
62 |
63 | function character:__tostring()
64 | return "character: " .. self.__type
65 | end
66 |
--------------------------------------------------------------------------------
/engine/shared/entities/networkvar.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Network Variable class
4 | --
5 | --==========================================================================--
6 |
7 | class( "networkvar" )
8 |
9 | function networkvar:networkvar( entity, name )
10 | self.entity = entity
11 | self.name = name
12 | end
13 |
14 | function networkvar:onValueChanged()
15 | local entity = self.entity
16 | if ( entity == nil ) then
17 | return
18 | end
19 |
20 | entity:onNetworkVarChanged( self )
21 | end
22 |
23 | accessor( networkvar, "name" )
24 | accessor( networkvar, "value" )
25 |
26 | function networkvar:setValue( value )
27 | local oldValue = self.value
28 | self.value = value
29 | if ( oldValue ~= value ) then
30 | self:onValueChanged()
31 | end
32 | end
33 |
34 | function networkvar:__tostring()
35 | local value = "\"" .. tostring( self.value ) .. "\""
36 | if ( self.value == nil ) then
37 | value = "nil"
38 | end
39 | return "networkvar: " .. self.name .. " = " .. value
40 | end
41 |
--------------------------------------------------------------------------------
/engine/shared/entities/npc.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: NPC class
4 | --
5 | --==========================================================================--
6 |
7 | entities.require( "character" )
8 |
9 | class "npc" ( "character" )
10 |
11 | function npc:npc()
12 | character.character( self )
13 |
14 | self:networkNumber( "moveSpeed", 66 )
15 |
16 | if ( _CLIENT ) then
17 | require( "engine.client.sprite" )
18 | local sprite = sprite( "images.player" )
19 | sprite:setFilter( "nearest", "nearest" )
20 | self:setSprite( sprite )
21 | end
22 | end
23 |
24 | function npc:spawn()
25 | entity.spawn( self )
26 |
27 | local tileSize = game.tileSize
28 | local min = vector()
29 | local max = vector( tileSize, -tileSize )
30 | self:initializePhysics( "dynamic" )
31 | self:setCollisionBounds( min, max )
32 |
33 | game.call( "shared", "onNPCSpawn", self )
34 | end
35 |
36 | function npc:__tostring()
37 | return "npc: " .. self:getName()
38 | end
39 |
--------------------------------------------------------------------------------
/engine/shared/entities/trigger.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Trigger class
4 | --
5 | --==========================================================================--
6 |
7 | entities.require( "entity" )
8 |
9 | class "trigger" ( "entity" )
10 |
11 | function trigger:trigger()
12 | entity.entity( self )
13 |
14 | self:networkNumber( "width", 0 )
15 | self:networkNumber( "height", 0 )
16 | end
17 |
18 | function trigger:isVisibleToPlayer( player )
19 | local minA, maxA = player:getGraphicsBounds()
20 | local width = self:getNetworkVar( "width" )
21 | local height = self:getNetworkVar( "height" )
22 | local minB = self:localToWorld( vector() )
23 | local maxB = self:localToWorld( vector( width, -height ) )
24 | return math.aabbsintersect( minA, maxA, minB, maxB )
25 | end
26 |
--------------------------------------------------------------------------------
/engine/shared/entities/trigger_changelevel.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: trigger_changelevel
4 | --
5 | --==========================================================================--
6 |
7 | entities.require( "trigger" )
8 |
9 | class "trigger_changelevel" ( "trigger" )
10 |
11 | function trigger_changelevel:trigger_changelevel()
12 | trigger.trigger( self )
13 | end
14 |
15 | function trigger_changelevel:loadMap()
16 | local properties = self:getProperties()
17 | if ( properties == nil ) then
18 | return
19 | end
20 |
21 | local name = properties[ "map" ]
22 | if ( map.getByName( name ) ) then
23 | return
24 | end
25 |
26 | local worldIndex = map.findNextWorldIndex()
27 | map.load( name, nil, nil, worldIndex )
28 | end
29 |
30 | function trigger_changelevel:removeMap()
31 | local properties = self:getProperties()
32 | if ( properties ) then
33 | local name = properties[ "map" ]
34 | local r = map.getByName( name )
35 | if ( r ) then
36 | local players = player.getInOrNearMap( r )
37 | if ( players == nil ) then
38 | map.unload( name )
39 | end
40 | end
41 | end
42 | end
43 |
44 | function trigger_changelevel:tick( timestep )
45 | for _, player in ipairs( player.getAll() ) do
46 | if ( self:isVisibleToPlayer( player ) ) then
47 | if ( not self.loaded ) then
48 | self:loadMap()
49 | self.loaded = true
50 | end
51 | else
52 | if ( self.loaded ) then
53 | self:removeMap()
54 | self.loaded = false
55 | end
56 | end
57 | end
58 | end
59 |
60 | entities.linkToClassname( trigger_changelevel, "trigger_changelevel" )
61 |
--------------------------------------------------------------------------------
/engine/shared/heaplib.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Binary heap library
4 | --
5 | --==========================================================================--
6 |
7 | class( "heap" )
8 |
9 | local function getparentindex( i )
10 | return i % 2 == 0 and i / 2 or ( i - 1 ) / 2
11 | end
12 |
13 | local function upheap( self, i )
14 | if ( i <= 1 ) then
15 | return
16 | end
17 |
18 | local parent = getparentindex( i )
19 | if ( self[ parent ] >= self[ i ] ) then
20 | self[ parent ], self[ i ] = self[ i ], self[ parent ]
21 | upheap( self, parent )
22 | end
23 | end
24 |
25 | function heap:insert( value )
26 | table.insert( self, value )
27 | upheap( self, #self )
28 | end
29 |
30 | local function getleftchildindex( i )
31 | return 2 * i
32 | end
33 |
34 | local function getrightchildindex( i )
35 | return 2 * i + 1
36 | end
37 |
38 | local function downheap( self, i )
39 | local rindex = getrightchildindex( i )
40 | local size = #self
41 | local lindex = getleftchildindex( i )
42 | local min
43 | if ( rindex > size ) then
44 | if ( lindex > size ) then
45 | return
46 | else
47 | min = lindex
48 | end
49 | else
50 | if ( self[ lindex ] < self[ rindex ] ) then
51 | min = lindex
52 | else
53 | min = rindex
54 | end
55 | end
56 | if ( self[ i ] >= self[ min ] ) then
57 | self[ i ], self[ min ] = self[ min ], self[ i ]
58 | downheap( self, min )
59 | end
60 | end
61 |
62 | function heap:remove( pos )
63 | local size = #self
64 | self[ pos ] = self[ size ]
65 | self[ size ] = nil
66 | if ( size > 1 ) then
67 | downheap( self, 1 )
68 | end
69 | end
70 |
71 | function heap:__tostring()
72 | local t = getmetatable( self )
73 | setmetatable( self, {} )
74 | local s = string.gsub( tostring( self ), "table", "heap" )
75 | setmetatable( self, t )
76 | return s
77 | end
78 |
--------------------------------------------------------------------------------
/engine/shared/hook.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Hook interface
4 | --
5 | --==========================================================================--
6 |
7 | local pairs = pairs
8 | local pcall = pcall
9 | local print = print
10 | local table = table
11 |
12 | module( "hook" )
13 |
14 | _hooks = _hooks or {}
15 | _hooks.client = _hooks.client or {}
16 | _hooks.server = _hooks.server or {}
17 | _hooks.shared = _hooks.shared or {}
18 |
19 | function call( universe, event, ... )
20 | local eventHooks = _hooks[ universe ][ event ]
21 | if ( eventHooks == nil ) then
22 | return
23 | end
24 |
25 | for name, func in pairs( eventHooks ) do
26 | local v = { pcall( func, ... ) }
27 | if ( v[ 1 ] ) then
28 | if ( #v > 1 ) then
29 | table.remove( v, 1 )
30 | return unpack( v )
31 | end
32 | else
33 | print( "[hook \"" .. name .. "\" (" .. event .. ")]: " .. v[ 2 ] )
34 | remove( universe, event, name )
35 | end
36 | end
37 | end
38 |
39 | function set( universe, func, event, name )
40 | universe = universe or "shared"
41 | _hooks[ universe ][ event ] = _hooks[ universe ][ event ] or {}
42 | _hooks[ universe ][ event ][ name ] = func
43 | end
44 |
45 | function remove( universe, event, name )
46 | universe = universe or "shared"
47 | local eventHooks = _hooks[ universe ][ event ]
48 | if ( eventHooks and eventHooks[ name ] ) then
49 | eventHooks[ name ] = nil
50 | if ( table.len( eventHooks ) == 0 ) then
51 | _hooks[ universe ][ event ] = nil
52 | end
53 | end
54 | end
55 |
--------------------------------------------------------------------------------
/engine/shared/loadlib.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Extends the package library
4 | --
5 | --==========================================================================--
6 |
7 | require( "package" )
8 |
9 | if ( rawrequire == nil ) then
10 | rawrequire = require
11 | end
12 |
13 | local function getModuleFilename( modname )
14 | local module = string.gsub( modname, "%.", "/" )
15 | for _, paths in ipairs( {
16 | string.gmatch( package.path .. ";", "(.-);" ),
17 | string.gmatch( love.filesystem.getRequirePath() .. ";", "(.-);" )
18 | } ) do
19 | for path in paths do
20 | path = string.gsub( path, "%.[\\/]", "" )
21 | local filename = string.gsub( path, "?", module )
22 | if ( path ~= "" and
23 | love.filesystem.getInfo( filename ) ~= nil ) then
24 | return filename
25 | end
26 | end
27 | end
28 | end
29 |
30 | function require( modname )
31 | if ( package.watched[ modname ] ) then
32 | return rawrequire( modname )
33 | end
34 |
35 | local status, ret = pcall( rawrequire, modname )
36 | if ( status == false ) then
37 | error( ret, 2 )
38 | end
39 |
40 | local filename = getModuleFilename( modname )
41 | if ( filename ) then
42 | local modtime = love.filesystem.getInfo( filename ).modtime
43 | package.watched[ modname ] = modtime
44 | else
45 | package.watched[ modname ] = -1
46 | end
47 |
48 | -- print( "Loaded " .. modname )
49 | return ret
50 | end
51 |
52 | local function unload( modname )
53 | package.loaded[ modname ] = nil
54 | package.watched[ modname ] = nil
55 | end
56 |
57 | function unrequire( modname )
58 | unload( modname )
59 | -- print( "Unloaded " .. modname )
60 | end
61 |
62 | package.watched = package.watched or {}
63 |
64 | local function reload( modname, filename )
65 | unload( modname )
66 | print( "Updating " .. modname .. "..." )
67 |
68 | local status, err = pcall( require, modname )
69 | if ( status == true ) then
70 | if ( game ) then
71 | game.call( "shared", "onReloadScript", modname )
72 | else
73 | hook.call( "shared", "onReloadScript", modname )
74 | end
75 |
76 | return
77 | end
78 |
79 | print( err )
80 |
81 | local modtime = love.filesystem.getInfo( filename ).modtime
82 | package.watched[ modname ] = modtime
83 | end
84 |
85 | local function update( k, v )
86 | if ( v == -1 ) then
87 | return
88 | end
89 |
90 | local filename = getModuleFilename( k )
91 | if ( filename == nil ) then
92 | return
93 | end
94 |
95 | local modtime = love.filesystem.getInfo( filename ).modtime
96 | if ( modtime ~= v ) then
97 | reload( k, filename )
98 | end
99 | end
100 |
101 | function package.update( dt )
102 | for modname, modtime in pairs( package.watched ) do
103 | update( modname, modtime )
104 | end
105 | end
106 |
--------------------------------------------------------------------------------
/engine/shared/map/tileset.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Map Tileset class
4 | --
5 | --==========================================================================--
6 |
7 | class( "map.tileset" )
8 |
9 | local tileset = map.tileset
10 |
11 | function tileset:tileset( map, tilesetData )
12 | self:setMap( map )
13 | self.data = tilesetData
14 | self:parse()
15 | end
16 |
17 | accessor( tileset, "filename" )
18 | accessor( tileset, "firstGid", nil, "firstgid" )
19 | accessor( tileset, "image" )
20 | accessor( tileset, "imageWidth", nil, "imagewidth" )
21 | accessor( tileset, "imageHeight", nil, "imageheight" )
22 | accessor( tileset, "name" )
23 | accessor( tileset, "properties" )
24 | accessor( tileset, "map" )
25 | accessor( tileset, "spacing" )
26 | accessor( tileset, "margin" )
27 | accessor( tileset, "tileCount", nil, "tilecount" )
28 | accessor( tileset, "tileOffset", nil, "tileoffset" )
29 | accessor( tileset, "tiles" )
30 | accessor( tileset, "tileWidth", nil, "tilewidth" )
31 | accessor( tileset, "tileHeight", nil, "tileheight" )
32 |
33 | function tileset:parse()
34 | if ( self.data == nil ) then
35 | return
36 | end
37 |
38 | local data = self.data
39 | self:setName( data[ "name" ] )
40 | self:setFirstGid( data[ "firstgid" ] )
41 | self:setFilename( data[ "filename" ] )
42 | self:setTileWidth( data[ "tilewidth" ] )
43 | self:setTileHeight( data[ "tileheight" ] )
44 | self:setSpacing( data[ "spacing" ] )
45 | self:setMargin( data[ "margin" ] )
46 | if ( _CLIENT ) then
47 | local mapsDir = "maps/"
48 | local map = self:getMap()
49 | local path = string.stripfilename( map:getFilename() )
50 | mapsDir = mapsDir .. path
51 | path = mapsDir .. data[ "image" ]
52 | path = string.stripdotdir( path )
53 | self:setImage( path )
54 | end
55 | self:setImageWidth( data[ "imagewidth" ] )
56 | self:setImageHeight( data[ "imageheight" ] )
57 |
58 | require( "common.vector" )
59 | self:setTileOffset( vector.copy( data[ "tileoffset" ] ) )
60 |
61 | self:setProperties( table.copy( data[ "properties" ] ) )
62 | self:setTileCount( data[ "tilecount" ] )
63 | self:setTiles( table.copy( data[ "tiles" ] ) )
64 |
65 | self.data = nil
66 | end
67 |
68 | function tileset:setImage( image )
69 | self.image = love.graphics.newImage( image )
70 | self.image:setFilter( "nearest", "nearest" )
71 | end
72 |
73 | function tileset:__tostring()
74 | return "tileset: \"" .. self:getName() .. "\""
75 | end
76 |
--------------------------------------------------------------------------------
/engine/shared/mathlib.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Extends the math library
4 | --
5 | --==========================================================================--
6 |
7 | require( "math" )
8 |
9 | function math.aabbsintersect( minA, maxA, minB, maxB )
10 | return minA.x <= maxB.x and
11 | maxA.x >= minB.x and
12 | maxA.y <= minB.y and
13 | minA.y >= maxB.y
14 | end
15 |
16 | math.fepsilon = 1e-5
17 |
18 | function math.approximately( a, b )
19 | -- Calculate the difference.
20 | local diff = math.abs( a - b )
21 | a = math.abs( a )
22 | b = math.abs( b )
23 | -- Find the largest
24 | local largest = ( b > a ) and b or a
25 |
26 | if ( diff <= largest * math.fepsilon ) then
27 | return true
28 | end
29 |
30 | return false
31 | end
32 |
33 | function math.clamp( n, l, u )
34 | return n < l and l or ( n > u and u or n )
35 | end
36 |
37 | function math.gcd( a, b )
38 | local t = 0
39 | while b ~= 0 do
40 | t = b
41 | b = a % b
42 | a = t
43 | end
44 | return a
45 | end
46 |
47 | function math.lerp( f, t, dt )
48 | return ( f + ( t - f ) * dt )
49 | end
50 |
51 | function math.nearestmult( n, mult )
52 | return math.round( n / mult ) * mult
53 | end
54 |
55 | function math.nearestpow2( n )
56 | return 2 ^ math.ceil( math.log( n ) / math.log( 2 ) )
57 | end
58 |
59 | math.phi = ( 1 + math.sqrt( 5 ) ) / 2
60 |
61 | function math.pointinrect( px, py, x, y, width, height )
62 | return px >= x and
63 | py >= y and
64 | px < x + width and
65 | py < y + height
66 | end
67 |
68 | function math.pointonline( x1, y1, x2, y2, px, py )
69 | local m = ( y2 - y1 ) / ( x2 - x1 )
70 | local b = y1 - m * x1
71 | return py == m * px + b
72 | end
73 |
74 | function math.pointonlinesegment( x1, y1, x2, y2, px, py )
75 | -- Test x out of bounds
76 | if ( x2 > x1 and px > x2 ) then
77 | return false
78 | elseif ( x2 < x1 and px < x2 ) then
79 | return false
80 | end
81 |
82 | -- Test y out of bounds
83 | if ( y2 > y1 and py > y2 ) then
84 | return false
85 | elseif ( y2 < y1 and py < y2 ) then
86 | return false
87 | end
88 |
89 | return math.pointonline( x1, y1, x2, y2, px, py )
90 | end
91 |
92 | function math.remap(v, srcLow, srcHigh, destLow, destHigh)
93 | return destLow + (destHigh - destLow) * (v - srcLow) / (srcHigh - srcLow)
94 | end
95 |
96 | function math.remapClamp(v, srcLow, srcHigh, destLow, destHigh)
97 | return destLow + (destHigh - destLow) * math.clamp((v - srcLow) / (srcHigh - srcLow), 0, 1)
98 | end
99 |
100 | function math.round( n )
101 | return math.floor( n + 0.5 )
102 | end
103 |
--------------------------------------------------------------------------------
/engine/shared/network/payload.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Payload class
4 | --
5 | --==========================================================================--
6 |
7 | require( "engine.shared.typelenvalues" )
8 |
9 | class "payload" ( "typelenvalues" )
10 |
11 | payload._handlers = payload._handlers or {}
12 |
13 | -- Generate ids for packet structures
14 | do
15 | local payloads = "engine.shared.network.payloads"
16 | if ( package.loaded[ payloads ] ) then
17 | unrequire( payloads )
18 | end
19 |
20 | require( payloads )
21 |
22 | typelenvalues.generateIds( payload.structs )
23 | end
24 |
25 | function payload.initializeFromData( data )
26 | local payload = payload()
27 | payload.data = data
28 | payload:deserialize()
29 | return payload
30 | end
31 |
32 | function payload.setHandler( func, struct )
33 | payload._handlers[ struct ] = func
34 | end
35 |
36 | function payload:payload( struct )
37 | typelenvalues.typelenvalues( self, payload.structs, struct )
38 | end
39 |
40 | function payload:dispatchToHandler()
41 | local name = self:getStructName()
42 | if ( name == nil ) then
43 | return
44 | end
45 |
46 | local handler = payload._handlers[ name ]
47 | if ( handler ) then
48 | handler( self )
49 | end
50 | end
51 |
52 | accessor( payload, "peer" )
53 |
54 | function payload:getPlayer()
55 | return player.getByPeer( self.peer )
56 | end
57 |
58 | function payload:sendToServer()
59 | if ( _CLIENT ) then
60 | local network = engine.client.network
61 | network.sendToServer( self )
62 | end
63 | end
64 |
65 | function payload:broadcast()
66 | if ( _SERVER ) then
67 | local network = engine.server.network
68 | network.broadcast( self )
69 | end
70 | end
71 |
--------------------------------------------------------------------------------
/engine/shared/path/node.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Pathfinding Node class
4 | --
5 | --==========================================================================--
6 |
7 | class "node" ( "vector" )
8 |
9 | function node:node( x, y, parent )
10 | vector.vector( self, x, y )
11 | self.parent = parent
12 | self.g = 0
13 | self.h = 0
14 | self.f = 0
15 | end
16 |
17 | accessor( node, "parent" )
18 |
19 | node.__eq = vector.__eq
20 |
21 | function node.__lt( a, b )
22 | return a.f < b.f
23 | end
24 |
25 | function node.__le( a, b )
26 | return a.f <= b.f
27 | end
28 |
29 | function node:__tostring()
30 | return "node: (" .. self.x .. ", " .. self.y .. ")"
31 | end
32 |
--------------------------------------------------------------------------------
/engine/shared/profile.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Profiling interface
4 | --
5 | --==========================================================================--
6 |
7 | require( "love.timer" )
8 |
9 | local ipairs = ipairs
10 | local love = love
11 | local print = print
12 | local string = string
13 | local table = table
14 | local _G = _G
15 |
16 | module( "profile" )
17 |
18 | _stack = _stack or {}
19 | _parent = _parent or _stack
20 |
21 | local function getChildren()
22 | -- `children` == `_stack` or budget.
23 | local children = _stack
24 | if ( _parent ~= _stack ) then
25 | _parent.children = _parent.children or {}
26 | children = _parent.children
27 | end
28 | return children
29 | end
30 |
31 | local function getBudget( children, name )
32 | for _, budget in ipairs( children ) do
33 | if ( budget.name == name ) then
34 | return budget
35 | end
36 | end
37 | end
38 |
39 | function push( name )
40 | local children = getChildren()
41 | local budget = getBudget( children, name )
42 | if ( budget == nil ) then
43 | budget = { name = name, parent = _parent }
44 | table.insert( children, budget )
45 | end
46 |
47 | _parent = budget
48 | budget.startTime = love.timer.getTime()
49 | end
50 |
51 | function pop( name )
52 | _parent.endTime = love.timer.getTime()
53 | _parent.duration = _parent.endTime - _parent.startTime
54 | _parent = _parent.parent
55 |
56 | if ( _parent == nil ) then
57 | _parent = _stack
58 | end
59 | -- print( name .. " took " .. string.format( "%.3fms", 1000 * duration ) )
60 | end
61 |
--------------------------------------------------------------------------------
/engine/shared/tween.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Inbetweening interface
4 | --
5 | --==========================================================================--
6 |
7 | local math = math
8 |
9 | class( "tween" )
10 |
11 | local pi = math.pi
12 | local cos = math.cos
13 |
14 | tween.easing = {
15 | linear = function( p ) return p end,
16 | swing = function( p ) return 0.5 - cos( p * pi ) / 2 end,
17 | easeOutQuint = function( x, t, b, c, d )
18 | local temp = t / d - 1
19 | t = t / d - 1
20 | return c * ( ( temp ) * t * t * t * t + 1 ) + b
21 | end
22 | }
23 |
24 | function tween:tween( target, duration, vars )
25 | self.target = target
26 | self.startTime = nil
27 | self.tweens = {}
28 | self.duration = duration or 0.4
29 | self.easing = vars.easing or "swing"
30 | self.onUpdate = vars.onUpdate
31 | self.onComplete = vars.onComplete
32 |
33 | vars.easing = nil
34 | vars.onUpdate = nil
35 | vars.onComplete = nil
36 |
37 | for member, value in pairs( vars ) do
38 | self.tweens[ member ] = {
39 | startValue = self.target[ member ],
40 | endValue = value,
41 | }
42 | end
43 | end
44 |
45 | local startTime = 0
46 | local duration = 0
47 | local remaining = 0
48 | local max = math.max
49 | local percent = 0
50 | local startValue = 0
51 | local endValue = 0
52 | local eased = 0
53 | local onComplete = nil
54 |
55 | function tween:update( dt )
56 | if ( self.startTime == nil ) then
57 | self.startTime = love.timer.getTime()
58 | end
59 |
60 | startTime = self.startTime
61 | duration = self.duration
62 | remaining = max( 0, startTime + duration - love.timer.getTime() )
63 | percent = 1 - ( remaining / duration or 0 )
64 | self.pos = percent
65 |
66 | for member, t in pairs( self.tweens ) do
67 | startValue = t.startValue
68 | endValue = t.endValue
69 | eased = tween.easing[ self.easing ](
70 | percent, duration * percent, 0, 1, duration
71 | )
72 | self.target[ member ] = ( endValue - startValue ) * eased + startValue
73 |
74 | if ( self.onUpdate ) then
75 | self.onUpdate()
76 | end
77 | end
78 |
79 | if ( percent == 1 ) then
80 | onComplete = self.onComplete
81 | if ( onComplete ) then
82 | onComplete()
83 | end
84 | end
85 | end
86 |
--------------------------------------------------------------------------------
/fonts/SourceCodePro-Light.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/fonts/SourceCodePro-Light.otf
--------------------------------------------------------------------------------
/fonts/SourceSansPro-Bold.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/fonts/SourceSansPro-Bold.otf
--------------------------------------------------------------------------------
/fonts/SourceSansPro-Light.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/fonts/SourceSansPro-Light.otf
--------------------------------------------------------------------------------
/fonts/SourceSansPro-Regular.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/fonts/SourceSansPro-Regular.otf
--------------------------------------------------------------------------------
/game/client/gui/closedialog.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Close Dialog class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.closedialog" ( "gui.frame" )
8 |
9 | local closedialog = gui.closedialog
10 |
11 | function closedialog:closedialog( parent, name )
12 | gui.frame.frame( self, parent, "Close Dialog", "Quit Game" )
13 | self.width = 546
14 | self.height = 203
15 |
16 | self:doModal()
17 |
18 | local text = "Are you sure you want to quit the game?"
19 | local label = gui.label(
20 | self,
21 | "Close Dialog Label",
22 | text
23 | )
24 | label:setPos( 36, 86 )
25 | local font = self:getScheme( "font" )
26 | label:setWidth( font:getWidth( text ) )
27 |
28 | local buttonYes = gui.button( self, "Close Dialog Yes Button", "Yes" )
29 | buttonYes.height = nil
30 | buttonYes:setPadding( 14, 18, 13 )
31 | buttonYes:setPos( 36, 86 + label:getHeight() + 18 )
32 | buttonYes.onClick = function()
33 | love._shouldQuit = true
34 | love.quit()
35 | end
36 |
37 | local buttonNo = gui.button( self, "Close Dialog No Button", "No" )
38 | buttonNo.height = nil
39 | buttonNo:setPadding( 14, 18, 13 )
40 | buttonNo:setPos( 288, 86 + label:getHeight() + 18 )
41 | buttonNo.onClick = function()
42 | self:close()
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/game/client/gui/hudabout.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: About HUD
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.hudabout" ( "gui.box" )
8 |
9 | local hudabout = gui.hudabout
10 |
11 | function hudabout:hudabout( parent )
12 | gui.box.box( self, parent, "HUD About" )
13 | end
14 |
--------------------------------------------------------------------------------
/game/client/gui/hudchattextbox.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Chat Text Box HUD
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.hudchattextbox" ( "gui.textbox" )
8 |
9 | local hudchattextbox = gui.hudchattextbox
10 |
11 | function hudchattextbox:hudchattextbox( parent, name )
12 | gui.textbox.textbox( self, parent, name, "" )
13 | self.hudchat = parent
14 |
15 | self:setEditable( false )
16 | self:setMultiline( true )
17 | self:setScheme( "Chat" )
18 |
19 | self.borderOpacity = 0
20 | self:setVisible( false )
21 | end
22 |
23 | local CHAT_ANIM_TIME = 0.2
24 |
25 | function hudchattextbox:activate()
26 | if ( not self:isVisible() ) then
27 | self:setOpacity( 0 )
28 | self:animate( {
29 | opacity = 1
30 | }, CHAT_ANIM_TIME, "easeOutQuint" )
31 | end
32 |
33 | self:setVisible( true )
34 | end
35 |
36 | function hudchattextbox:hide()
37 | if ( self.hiding ) then
38 | return
39 | end
40 |
41 | self.hiding = true
42 |
43 | self:animate( {
44 | opacity = 0,
45 | }, CHAT_ANIM_TIME, "easeOutQuint", function()
46 | self:setVisible( false )
47 | self:setOpacity( 1 )
48 |
49 | self.hiding = nil
50 | self.hideTime = nil
51 | end )
52 | end
53 |
54 | function hudchattextbox:drawBorder()
55 | local width = self:getWidth()
56 | local height = self:getHeight()
57 | local color = color( self:getScheme( "textbox.borderColor" ) )
58 | color[ 4 ] = color[ 4 ] * self.borderOpacity
59 | love.graphics.setColor( color )
60 | local lineWidth = 1
61 | love.graphics.setLineWidth( lineWidth )
62 | love.graphics.rectangle(
63 | "line",
64 | lineWidth / 2,
65 | lineWidth / 2,
66 | width - lineWidth,
67 | height - lineWidth
68 | )
69 | end
70 |
71 | accessor( hudchattextbox, "hideTime" )
72 | accessor( hudchattextbox, "hudchat" )
73 |
74 | function hudchattextbox:invalidateLayout()
75 | local parent = self:getHudchat()
76 | local margin = 36
77 | local textboxHeight = 46
78 | local padding = 9
79 | local width = parent:getWidth()
80 | width = width - 2 * margin
81 | local height = parent:getHeight()
82 | height = height - textboxHeight - padding - 2 * margin
83 | self:setWidth( width )
84 | self:setHeight( height )
85 |
86 | gui.panel.invalidateLayout( self )
87 | end
88 |
89 | function hudchattextbox:update( dt )
90 | local parent = self:getHudchat()
91 | local hideTime = self:getHideTime()
92 | if ( hideTime and
93 | hideTime <= love.timer.getTime() and
94 | not parent:isVisible() ) then
95 | self:hide()
96 | end
97 |
98 | gui.textbox.update( self, dt )
99 | end
100 |
101 | function hudchattextbox:updateCursor()
102 | local hudchat = self:getHudchat()
103 | if ( not hudchat:isVisible() ) then
104 | love.mouse.setCursor()
105 | return
106 | end
107 |
108 | gui.textbox.updateCursor( self )
109 | end
110 |
--------------------------------------------------------------------------------
/game/client/gui/huddialogue.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Dialogue HUD
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.huddialogue" ( "gui.hudframe" )
8 |
9 | local huddialogue = gui.huddialogue
10 |
11 | function huddialogue:huddialogue( parent )
12 | local name = "HUD Dialogue"
13 | gui.hudframe.hudframe( self, parent, name, "" )
14 | self.width = 320 -- - 31
15 | self.height = 86
16 |
17 | local box = gui.box( self, name .. " Box" )
18 | box:setWidth( self.width )
19 | box:setHeight( self.height )
20 | box:setPadding( 18 )
21 |
22 | local header = gui.text( box, "h1" )
23 | header:setDisplay( "block" )
24 | header:setMarginBottom( 8 )
25 | header:setFont( self:getScheme( "fontBold" ) )
26 |
27 | local dialogue = gui.text( box, "p" )
28 | dialogue:setDisplay( "block" )
29 |
30 | self:invalidateLayout()
31 | self:activate()
32 | end
33 |
34 | function huddialogue:invalidateLayout()
35 | local x = love.graphics.getWidth() / 2
36 | x = x + 3 * game.tileSize
37 | local y = love.graphics.getHeight() / 2
38 | y = y - self:getHeight() / 2
39 | y = y - 2 * game.tileSize
40 | self:setPos( x, y )
41 | gui.frame.invalidateLayout( self )
42 | end
43 |
--------------------------------------------------------------------------------
/game/client/gui/hudgamemenu/init.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Game Menu HUD
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.hudgamemenu" ( "gui.hudframe" )
8 |
9 | local hudgamemenu = gui.hudgamemenu
10 |
11 | function hudgamemenu:hudgamemenu( parent )
12 | local name = "HUD Game Menu"
13 | gui.hudframe.hudframe( self, parent, name, name )
14 | self.width = 320 -- - 31
15 | self.height = 432
16 |
17 | require( "game.client.gui.hudgamemenu.navigation" )
18 | require( "game.client.gui.hudgamemenu.navigationbutton" )
19 | self.navigation = gui.hudgamemenunavigation( self )
20 | self.navigation:setPos( 36, 86 )
21 | self.navigation:setWidth( self.width - 2 * 36 )
22 | self.navigation:setHeight( 31 )
23 |
24 | require( "game.client.gui.hudgamemenu.inventory" )
25 | self.inventory = gui.hudgamemenuinventory( self )
26 | self.inventory:moveToBack()
27 | _G.g_Inventory = self.inventory
28 |
29 | require( "game.client.gui.hudgamemenu.stats" )
30 | self.stats = gui.hudgamemenustats( self )
31 | self.stats:moveToBack()
32 | self.stats:setVisible( false )
33 |
34 | self:invalidateLayout()
35 | end
36 |
37 | function hudgamemenu:getTitle()
38 | if ( self.navigation == nil ) then
39 | return "Game Menu"
40 | end
41 |
42 | local item = self.navigation:getSelectedItem()
43 | return item:getText()
44 | end
45 |
46 | function hudgamemenu:invalidateLayout()
47 | local x = love.graphics.getWidth() - self:getWidth() - 18
48 | local y = love.graphics.getHeight() - self:getHeight() - 18
49 | self:setPos( x, y )
50 | gui.frame.invalidateLayout( self )
51 | end
52 |
53 | function hudgamemenu:onRemove()
54 | _G.g_Inventory = nil
55 | gui.panel.onRemove( self )
56 | end
57 |
58 | concommand( "+gamemenu", "Opens the gamemenu", function()
59 | local visible = _G.g_GameMenu:isVisible()
60 | if ( not visible ) then
61 | _G.g_GameMenu:activate()
62 | end
63 | end, { "game" } )
64 |
65 | concommand( "-gamemenu", "Closes the gamemenu", function()
66 | local visible = _G.g_GameMenu:isVisible()
67 | if ( visible ) then
68 | _G.g_GameMenu:close()
69 | end
70 | end, { "game" } )
71 |
72 | local function onReloadScript()
73 | local gamemenu = g_GameMenu
74 | if ( gamemenu == nil ) then
75 | return
76 | end
77 |
78 | local visible = gamemenu:isVisible()
79 | gamemenu:remove()
80 | gamemenu = gui.hudgamemenu( g_Viewport )
81 | g_GameMenu = gamemenu
82 | if ( visible ) then
83 | gamemenu:activate()
84 | end
85 | end
86 |
87 | onReloadScript()
88 |
--------------------------------------------------------------------------------
/game/client/gui/hudgamemenu/inventory.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Game Menu Inventory class
4 | --
5 | --==========================================================================--
6 |
7 | require( "game.client.gui.hudgamemenu.itemgrid" )
8 |
9 | class "gui.hudgamemenuinventory" ( "gui.box" )
10 |
11 | local hudgamemenuinventory = gui.hudgamemenuinventory
12 |
13 | function hudgamemenuinventory:hudgamemenuinventory( parent )
14 | gui.box.box( self, parent, "Inventory" )
15 | self:setScheme( "Default" )
16 | self:setDimensions( parent:getDimensions() )
17 |
18 | self.grid = gui.itemgrid( self, "Inventory Item Grid" )
19 | self.grid:setPos( 36, 86 + 31 + 18 )
20 | self.grid:setDimensions( parent:getWidth() - 2 * 36, 314 )
21 | self.grid:setColumns( 4 )
22 | self.grid:setRows( 7 )
23 |
24 | self:addInventoryHooks()
25 | end
26 |
27 | function hudgamemenuinventory:addInventoryHooks()
28 | local function onPlayerGotItem( player, item, count )
29 | if ( player ~= localplayer ) then
30 | return
31 | end
32 |
33 | self.grid:addItem( item, count )
34 | end
35 |
36 | hook.set(
37 | "shared", onPlayerGotItem, "onPlayerGotItem", "updateInventory"
38 | )
39 |
40 | local function onPlayerRemovedItem( player, item, count )
41 | if ( player ~= localplayer ) then
42 | return
43 | end
44 |
45 | self.grid:removeItem( item )
46 | end
47 |
48 | hook.set(
49 | "shared", onPlayerRemovedItem, "onPlayerRemovedItem", "updateInventory"
50 | )
51 | end
52 |
53 | function hudgamemenuinventory:removeInventoryHooks()
54 | hook.remove( "shared", "onPlayerGotItem", "updateInventory" )
55 | hook.remove( "shared", "onPlayerRemovedItem", "updateInventory" )
56 | end
57 |
58 | function hudgamemenuinventory:onRemove()
59 | self:removeInventoryHooks()
60 | gui.panel.onRemove( self )
61 | end
62 |
63 | function hudgamemenuinventory:select( item )
64 | item = self.grid:hasItem( item )
65 | self:setSelectedItem( item )
66 | end
67 |
68 | accessor( hudgamemenuinventory, "selectedItem" )
69 |
70 | function hudgamemenuinventory:setSelectedItem( item )
71 | if ( self.selectedItem ) then
72 | self.selectedItem:setSelected( false )
73 | end
74 |
75 | if ( item ) then
76 | item:setSelected( true )
77 | end
78 |
79 | self.selectedItem = item
80 | end
81 |
82 | function hudgamemenuinventory:use( item, value )
83 | end
84 |
--------------------------------------------------------------------------------
/game/client/gui/hudgamemenu/itemgrid.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Item Grid class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.itemgrid" ( "gui.box" )
8 |
9 | local itemgrid = gui.itemgrid
10 |
11 | function itemgrid:itemgrid( parent, name )
12 | gui.box.box( self, parent, name )
13 | self:setDisplay( "block" )
14 | self:setPosition( "absolute" )
15 |
16 | self.items = {}
17 | self.columns = 0
18 | self.rows = 0
19 | end
20 |
21 | function itemgrid:addItem( item, count )
22 | local itemdata = _G.item.getClass( item ).data
23 | local hasItem = self:hasItem( item )
24 | if ( not itemdata.stackable or not hasItem ) then
25 | require( "game.client.gui.hudgamemenu.itembutton" )
26 | local itembutton = gui.itembutton( self, item )
27 | table.insert( self:getItems(), itembutton )
28 | end
29 |
30 | self:invalidateLayout()
31 | end
32 |
33 | accessor( itemgrid, "columns" )
34 | accessor( itemgrid, "rows" )
35 | accessor( itemgrid, "items" )
36 |
37 | function itemgrid:hasItem( item )
38 | local items = self:getItems()
39 | for _, v in ipairs( items ) do
40 | if ( item == v:getItem() ) then
41 | return v
42 | end
43 | end
44 | return nil
45 | end
46 |
47 | function itemgrid:invalidateLayout()
48 | local items = self:getItems()
49 | local columns = self:getColumns()
50 | local rows = self:getRows()
51 | local columnGap = ( self:getWidth() - columns * 44 ) / ( columns - 1 )
52 | local rowGap = ( self:getHeight() - rows * 44 ) / ( rows - 1 )
53 | for n, v in pairs( items ) do
54 | n = n - 1
55 | local x = n % columns
56 | local y = math.floor( n / columns )
57 | local xm = x * columnGap
58 | local ym = y * rowGap
59 | v:setPos( x * 44 + xm, y * 44 + ym )
60 | end
61 | gui.panel.invalidateLayout( self )
62 | end
63 |
64 | function itemgrid:removeItem( item )
65 | local items = self:getItems()
66 | for i, v in ipairs( items ) do
67 | if ( item == v:getItem() ) then
68 | table.remove( items, i )
69 | v:remove()
70 | break
71 | end
72 | end
73 |
74 | self:invalidateLayout()
75 | end
76 |
--------------------------------------------------------------------------------
/game/client/gui/hudgamemenu/navigation.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Game Menu Navigation class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.hudgamemenunavigation" ( "gui.radiobuttongroup" )
8 |
9 | local hudgamemenunavigation = gui.hudgamemenunavigation
10 |
11 | function hudgamemenunavigation:hudgamemenunavigation( parent )
12 | gui.radiobuttongroup.radiobuttongroup( self, parent, "Game Menu Navigation" )
13 | self:setScheme( "Default" )
14 |
15 | self.tabs = {
16 | "Inventory",
17 | "Stats"
18 | }
19 |
20 | for _, name in ipairs( self.tabs ) do
21 | local item = gui.hudgamemenunavigationbutton( self, name )
22 | self:addItem( item )
23 | end
24 | end
25 |
26 | function hudgamemenunavigation:addItem( item, default )
27 | item:setParent( self )
28 | gui.radiobuttongroup.addItem( self, item )
29 |
30 | if ( default or #self:getItems() == 1 ) then
31 | item:setDefault( true )
32 | end
33 |
34 | self:invalidateLayout()
35 | end
36 |
37 | function hudgamemenunavigation:draw()
38 | gui.box.draw( self )
39 |
40 | local property = "hudgamemenunavigation.backgroundColor"
41 | local lineWidth = 1
42 | local width = self:getWidth()
43 | love.graphics.setColor( self:getScheme( property ) )
44 | love.graphics.setLineStyle( "rough" )
45 | love.graphics.setLineWidth( lineWidth )
46 | love.graphics.line(
47 | 0, lineWidth / 2, -- Top-left
48 | width, lineWidth / 2 -- Top-right
49 | )
50 | end
51 |
52 | function hudgamemenunavigation:invalidateLayout()
53 | local listItems = self:getItems()
54 | if ( listItems ) then
55 | local x = 0
56 | for _, listItem in ipairs( listItems ) do
57 | listItem:setX( x )
58 | x = x + listItem:getWidth() + 18
59 | end
60 | end
61 | gui.panel.invalidateLayout( self )
62 | end
63 |
64 | function hudgamemenunavigation:onValueChanged( oldValue, newValue )
65 | local parent = self:getParent()
66 | for _, name in ipairs( self.tabs ) do
67 | name = string.lower( name )
68 | parent[ name ]:setVisible( false )
69 | end
70 |
71 | if ( newValue ) then
72 | local panel = parent[ string.lower( newValue ) ]
73 | panel:setVisible( true )
74 | panel:invalidateLayout()
75 | end
76 | end
77 |
--------------------------------------------------------------------------------
/game/client/gui/hudgamemenu/navigationbutton.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Game Menu Navigation Button class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.hudgamemenunavigationbutton" ( "gui.radiobutton" )
8 |
9 | local hudgamemenunavigationbutton = gui.hudgamemenunavigationbutton
10 |
11 | function hudgamemenunavigationbutton:hudgamemenunavigationbutton( parent, name )
12 | gui.radiobutton.radiobutton( self, parent, name, name )
13 | self.width = nil
14 | self.height = nil
15 | self:setDisplay( "inline-block" )
16 | self:setPosition( "static" )
17 | self:setMarginRight( 18 )
18 | self:setPaddingTop( 13 )
19 | self:setValue( name )
20 | end
21 |
22 | function hudgamemenunavigationbutton:draw()
23 | self:drawBorder()
24 | self:drawLabel()
25 | gui.box.draw( self )
26 | end
27 |
28 | function hudgamemenunavigationbutton:drawBorder()
29 | if ( not self:isSelected() ) then
30 | return
31 | end
32 |
33 | local property = "hudgamemenunavigationbutton.borderColor"
34 | local lineWidth = 1
35 | local width = self:getWidth()
36 | love.graphics.setColor( self:getScheme( property ) )
37 | love.graphics.setLineStyle( "rough" )
38 | love.graphics.setLineWidth( lineWidth )
39 | love.graphics.line(
40 | 0, lineWidth / 2, -- Top-left
41 | width, lineWidth / 2 -- Top-right
42 | )
43 | end
44 |
45 | function hudgamemenunavigationbutton:drawLabel()
46 | if ( self:isDisabled() ) then
47 | self.text:setColor( self:getScheme( "radiobutton.disabled.textColor" ) )
48 | elseif ( self:isSelected() ) then
49 | self.text:setColor( self:getScheme( "hudgamemenunavigationbutton.borderColor" ) )
50 | elseif ( self.mouseover or self:isChildMousedOver() ) then
51 | self.text:setColor( self:getScheme( "hudgamemenunavigationbutton.mouseover.textColor" ) )
52 | else
53 | self.text:setColor( self:getScheme( "hudgamemenunavigationbutton.textColor" ) )
54 | end
55 | end
56 |
--------------------------------------------------------------------------------
/game/client/gui/hudhealth.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Health HUD
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.hudhealth" ( "gui.box" )
8 |
9 | local hudhealth = gui.hudhealth
10 |
11 | function hudhealth:hudhealth( parent )
12 | local name = "HUD Health"
13 | gui.box.box( self, parent, name )
14 | self:setDisplay( "block" )
15 | self:setPosition( "absolute" )
16 |
17 | self.text = gui.text( self, "" )
18 | self.text:setDisplay( "block" )
19 | self.text:setColor( self:getScheme( "hudmoveindicator.textColor" ) )
20 | self.text:setFont( self:getScheme( "entityFont" ) )
21 |
22 | local label = gui.text( self, "Health" )
23 | label:setColor( self:getScheme( "hudmoveindicator.smallTextColor" ) )
24 |
25 | self:invalidateLayout()
26 | end
27 |
28 | function hudhealth:draw()
29 | self:drawHealth()
30 |
31 | gui.box.draw( self )
32 | end
33 |
34 | function hudhealth:drawHealth()
35 | local health = localplayer:getNetworkVar( "health" )
36 | self.text:set( health )
37 | end
38 |
39 | function hudhealth:invalidateLayout()
40 | local margin = gui.scale( 96 )
41 | local graphicsHeight = love.graphics.getHeight()
42 | local height = self:getHeight()
43 | self:setPos( margin, graphicsHeight - margin - height )
44 |
45 | gui.panel.invalidateLayout( self )
46 | end
47 |
--------------------------------------------------------------------------------
/game/client/gui/hudmana.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Mana HUD
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.hudmana" ( "gui.box" )
8 |
9 | local hudmana = gui.hudmana
10 |
11 | function hudmana:hudmana( parent )
12 | local name = "HUD Mana"
13 | gui.box.box( self, parent, name )
14 | self:setDisplay( "block" )
15 | self:setPosition( "absolute" )
16 |
17 | self.text = gui.text( self, "" )
18 | self.text:setDisplay( "block" )
19 | self.text:setColor( self:getScheme( "hudmoveindicator.textColor" ) )
20 | self.text:setFont( self:getScheme( "entityFont" ) )
21 |
22 | local label = gui.text( self, "Mana" )
23 | label:setColor( self:getScheme( "hudmoveindicator.smallTextColor" ) )
24 |
25 | self:invalidateLayout()
26 | end
27 |
28 | function hudmana:draw()
29 | self:drawMana()
30 |
31 | gui.box.draw( self )
32 | end
33 |
34 | function hudmana:drawMana()
35 | local mana = localplayer:getNetworkVar( "mana" )
36 | self.text:set( mana )
37 | end
38 |
39 | function hudmana:invalidateLayout()
40 | local margin = gui.scale( 96 )
41 | local graphicsHeight = love.graphics.getHeight()
42 | local height = self:getHeight()
43 | self:setPos(
44 | margin + g_HudHealth:getWidth() + margin / 2,
45 | graphicsHeight - margin - height
46 | )
47 |
48 | gui.panel.invalidateLayout( self )
49 | end
50 |
--------------------------------------------------------------------------------
/game/client/gui/mainmenubutton.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Main Menu Button class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.mainmenubutton" ( "gui.button" )
8 |
9 | local mainmenubutton = gui.mainmenubutton
10 |
11 | function mainmenubutton:mainmenubutton( parent, text )
12 | local name = text and text or "Blank" .. " Button"
13 | gui.button.button( self, parent, name, text or "" )
14 | self:setBorderWidth( 0 )
15 |
16 | local font = self:getScheme( "mainmenuFont" )
17 | self.text:set( text )
18 | self.text:setFont( font )
19 |
20 | self.height = font:getHeight()
21 | end
22 |
23 | function mainmenubutton:draw()
24 | local textColor = "mainmenubutton.dark.textColor"
25 | local mouseover = ( self.mouseover or self:isChildMousedOver() )
26 | if ( self:isDisabled() ) then
27 | textColor = "mainmenubutton.dark.disabled.textColor"
28 | elseif ( self.mousedown and mouseover ) then
29 | textColor = "mainmenubutton.dark.mousedown.textColor"
30 | elseif ( self.mousedown or mouseover or self.focus ) then
31 | textColor = "mainmenubutton.dark.mouseover.textColor"
32 | end
33 |
34 | self.text:setColor( self:getScheme( textColor ) )
35 |
36 | gui.box.draw( self )
37 | end
38 |
--------------------------------------------------------------------------------
/game/client/gui/mainmenuclosebutton.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Main Menu Close Button class
4 | --
5 | --==========================================================================--
6 |
7 | local gui = gui
8 | local love = love
9 |
10 | class "gui.mainmenuclosebutton" ( "gui.closebutton" )
11 |
12 | local mainmenuclosebutton = gui.mainmenuclosebutton
13 |
14 | function mainmenuclosebutton:mainmenuclosebutton( parent )
15 | gui.closebutton.closebutton( self, parent, "Main Menu Close Button" )
16 | self.width = 32
17 | self.height = self.width + 1
18 | self.icon = self:getScheme( "icon" )
19 | end
20 |
21 | function mainmenuclosebutton:draw()
22 | local iconColor = "mainmenuclosebutton.dark.iconColor"
23 | if ( self.mousedown and self.mouseover ) then
24 | iconColor = "mainmenuclosebutton.dark.mousedown.iconColor"
25 | elseif ( self.mousedown or self.mouseover ) then
26 | iconColor = "mainmenuclosebutton.dark.mouseover.iconColor"
27 | end
28 |
29 | local x = self:getWidth() / 2 - self.icon:getWidth() / 2
30 | local y = ( self:getHeight() - 1 ) / 2 - self.icon:getHeight() / 2
31 | love.graphics.setColor( self:getScheme( iconColor ) )
32 | love.graphics.draw( self.icon, x, y )
33 |
34 | gui.panel.draw( self )
35 | end
36 |
--------------------------------------------------------------------------------
/game/client/gui/optionsitem.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Options Item class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.optionsitem" ( "gui.dropdownlistitem" )
8 |
9 | local optionsitem = gui.optionsitem
10 |
11 | function optionsitem:optionsitem( parent, name, text )
12 | gui.dropdownlistitem.dropdownlistitem( self, parent, name, text .. " " )
13 | self.entityText = gui.text( self, "" )
14 | self.entityText:setFont( self:getScheme( "fontBold" ) )
15 | end
16 |
17 | function optionsitem:drawText()
18 | local color = self:getScheme( "button.textColor" )
19 |
20 | if ( self:isDisabled() ) then
21 | color = self:getScheme( "button.disabled.textColor" )
22 | elseif ( self:isSelected() ) then
23 | color = self:getScheme( "dropdownlistitem.selected.textColor" )
24 | elseif ( ( self.mouseover or self:isChildMousedOver() ) ) then
25 | color = self:getScheme( "dropdownlistitem.mouseover.textColor" )
26 | end
27 |
28 | self.text:setColor( color )
29 | self.entityText:setColor( color )
30 |
31 | local entity = self:getEntity()
32 | local text = ""
33 | if ( type( entity ) == "string" ) then
34 | text = entity
35 | else
36 | text = entity and entity:getName() or ""
37 | end
38 | self.entityText:set( text )
39 | end
40 |
41 | function optionsitem:setDefault( default )
42 | end
43 |
44 | function optionsitem:setSelected( selected )
45 | end
46 |
47 | accessor( optionsitem, "entity" )
48 |
--------------------------------------------------------------------------------
/game/client/gui/optionsitemgroup.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Options Item Group class
4 | --
5 | --==========================================================================--
6 |
7 | class "gui.optionsitemgroup" ( "gui.dropdownlistitemgroup" )
8 |
9 | local optionsitemgroup = gui.optionsitemgroup
10 |
11 | function optionsitemgroup:optionsitemgroup( parent, name )
12 | gui.dropdownlistitemgroup.dropdownlistitemgroup( self, parent, name )
13 | -- UNDONE: The drop-down list field is reserved for the control responsible
14 | -- for the drop-down list item group. The control does not necessarily have
15 | -- to be a dropdownlist.
16 | -- self.dropDownList = nil
17 |
18 | -- self:setParent( parent )
19 | end
20 |
21 | function optionsitemgroup:addItem( item )
22 | item:setParent( self )
23 | gui.radiobuttongroup.addItem( self, item )
24 |
25 | item.onClick = function( item )
26 | local value = item:getValue()
27 | value()
28 | end
29 |
30 | self:invalidateLayout()
31 | end
32 |
33 | function optionsitemgroup:invalidateLayout()
34 | self:updatePos()
35 | gui.panel.invalidateLayout( self )
36 | end
37 |
38 | function optionsitemgroup:updatePos()
39 | local parent = self:getParent()
40 | local x, y = self:getPos()
41 | local width, height = self:getDimensions()
42 | local windowPadding = 4
43 | if ( x + width > parent:getWidth() ) then
44 | x = x - width
45 | end
46 |
47 | local overflow = y + height
48 | if ( overflow > parent:getHeight() - windowPadding ) then
49 | overflow = overflow - parent:getHeight() + windowPadding
50 | y = y - overflow
51 | end
52 |
53 | if ( y < windowPadding ) then
54 | y = windowPadding
55 | end
56 |
57 | self:setPos( x, y )
58 | end
59 |
--------------------------------------------------------------------------------
/game/client/init.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Game client interface
4 | --
5 | --==========================================================================--
6 |
7 | require( "engine.client.camera" )
8 |
9 | local camera = camera
10 | local game = game
11 | local gui = gui
12 | local pcall = pcall
13 | local map = map
14 | local unrequire = unrequire
15 | local _G = _G
16 |
17 | module( "game.client" )
18 |
19 | function createDefaultPanels()
20 | -- Initialize speech balloons
21 | local hudspeechballoons = gui.hudspeechballoons( _G.g_Viewport )
22 |
23 | -- Initialize move indicator
24 | local hudmoveindicator = gui.hudmoveindicator( _G.g_Viewport )
25 | _G.g_HudMoveIndicator = hudmoveindicator
26 |
27 | -- Initialize about
28 | local hudabout = gui.hudabout( _G.g_Viewport )
29 |
30 | -- Initialize chat
31 | local chat = gui.hudchat( _G.g_Viewport )
32 | _G.g_Chat = chat
33 |
34 | -- Initialize game menu
35 | local gamemenu = gui.hudgamemenu( _G.g_Viewport )
36 | _G.g_GameMenu = gamemenu
37 |
38 | -- Initialize health
39 | local hudhealth = gui.hudhealth( _G.g_Viewport )
40 | _G.g_HudHealth = hudhealth
41 |
42 | -- Initialize mana
43 | local hudmana = gui.hudmana( _G.g_Viewport )
44 | _G.g_HudMana = hudmana
45 |
46 | -- Initialize dialogue
47 | -- local dialogue = gui.huddialogue( _G.g_Viewport )
48 | -- _G.g_Dialogue = dialogue
49 |
50 | -- Initialize profiler
51 | local profiler = gui.hudprofiler( _G.g_Viewport )
52 | _G.g_Profiler = profiler
53 | end
54 |
55 | function draw()
56 | if ( not _G.localplayer._initialized ) then
57 | return
58 | end
59 |
60 | -- Draw panels to worldspace
61 | gui.preDrawWorld()
62 |
63 | -- Draw world
64 | map.drawWorld()
65 | end
66 |
67 | function load( arg )
68 | _G.g_Viewport = gui.viewport( _G.g_RootPanel )
69 | _G.g_DebugOverlay = gui.debugoverlaypanel( _G.g_Viewport )
70 | end
71 |
72 | function onMainMenuActivate()
73 | end
74 |
75 | function onMainMenuClose()
76 | end
77 |
78 | function onPlayerChat( player, message )
79 | return true
80 | end
81 |
82 | function onReloadImage( filename )
83 | end
84 |
85 | function onReloadSound( filename )
86 | end
87 |
88 | function tick( timestep )
89 | end
90 |
91 | function quit()
92 | _G.g_DebugOverlay:remove()
93 | _G.g_DebugOverlay = nil
94 | _G.g_Viewport:remove()
95 | _G.g_Viewport = nil
96 | end
97 |
98 | shutdown = quit
99 |
100 | function update( dt )
101 | camera.update( dt )
102 | end
103 |
--------------------------------------------------------------------------------
/game/init.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose:
4 | --
5 | --==========================================================================--
6 |
7 | require( "engine.shared.hook" )
8 |
9 | _VADVENTURE = true
10 |
11 | local game = game or {}
12 | _G.game = game
13 |
14 | local hook = hook
15 | local _CLIENT = _CLIENT
16 | local _SERVER = _SERVER
17 | local _VADVENTURE = _VADVENTURE
18 | local _G = _G
19 |
20 | module( "game" )
21 |
22 | tileSize = 16
23 | initialMap = "test_01"
24 |
25 | function conf( c )
26 | c.title = "Vertex Adventure"
27 | c.author = "Planimeter"
28 | return c
29 | end
30 |
31 | function call( universe, event, ... )
32 | local interface = game[ universe ]
33 | if ( universe == "shared" ) then
34 | interface = game
35 | end
36 |
37 | local values = { hook.call( universe, event, ... ) }
38 | if ( #values > 0 ) then
39 | return unpack( values )
40 | end
41 |
42 | return interface[ event ]( ... )
43 | end
44 |
45 | function onAddonMounted( addon )
46 | end
47 |
48 | function onAddonUnmounted( addon )
49 | end
50 |
51 | function onNPCSpawn( npc )
52 | end
53 |
54 | function onPlayerConnect( player )
55 | if ( _SERVER ) then
56 | _G.player.sendTextAll( player:getName() .. " has joined the game." )
57 | end
58 | end
59 |
60 | function onPlayerDisconnect( player )
61 | if ( _SERVER ) then
62 | _G.player.sendTextAll( player:getName() .. " has disconnected." )
63 | end
64 | end
65 |
66 | if ( _VADVENTURE ) then
67 | function onPlayerGainedExperience( player, stat, xp )
68 | end
69 |
70 | function onPlayerGotItem( player, item, count )
71 | end
72 |
73 | function onPlayerRemovedItem( player, item, count )
74 | end
75 | end
76 |
77 | function onPlayerInitialSpawn( player )
78 | if ( _CLIENT and player == _G.localplayer ) then
79 | game.client.createDefaultPanels()
80 | end
81 | end
82 |
83 | if ( _VADVENTURE ) then
84 | function onPlayerLeveledUp( player, stat, level )
85 | end
86 | end
87 |
88 | function onPlayerSpawn( player )
89 | end
90 |
91 | function onReloadScript( modname )
92 | end
93 |
--------------------------------------------------------------------------------
/game/server/init.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Game server interface
4 | --
5 | --==========================================================================--
6 |
7 | local _G = _G
8 |
9 | module( "game.server" )
10 |
11 | function getPlayerClass()
12 | -- Vertex Adventure's player class is `vaplayer`. In a class-based
13 | -- multiplayer game, you might want to change this.
14 | local entities = _G.entities
15 | entities.require( "vaplayer" )
16 |
17 | -- Return the class
18 | local classmap = entities.getClassMap()
19 | return classmap[ "vaplayer" ]
20 | end
21 |
22 | function getSpawnPoint( player )
23 | -- Find the first `prop_worldgate_spawn` in the player's current map
24 | local map = player:getMap()
25 | local entity = _G.entity
26 | local spawnPoints = entity.findByClassname( "prop_worldgate_spawn", map )
27 | return spawnPoints and spawnPoints[ 1 ] or nil
28 | end
29 |
30 | function load( arg )
31 | end
32 |
33 | function onPlayerConnect( player )
34 | end
35 |
36 | function onPlayerSay( player, message )
37 | return true
38 | end
39 |
40 | function onPlayerUse( player, entity, value )
41 | return true
42 | end
43 |
44 | if ( _VADVENTURE ) then
45 | function onNPCTalkTo( npc, player, dialogue )
46 | -- Check if the player is too far away
47 | local pos1 = npc:getPosition()
48 | local pos2 = player:getPosition()
49 | local dist = pos1 - pos2
50 | local tileSize = _G.game.tileSize
51 | if ( dist:lengthSqr() > tileSize * tileSize ) then
52 | player:sendText( "You can't reach that." )
53 | return false
54 | end
55 | return true
56 | end
57 | end
58 |
59 | function tick( timestep )
60 | end
61 |
62 | function quit()
63 | end
64 |
65 | shutdown = quit
66 |
67 | function update( dt )
68 | end
69 |
--------------------------------------------------------------------------------
/game/shared/entities/func_examine.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: func_examine
4 | --
5 | --==========================================================================--
6 |
7 | entities.require( "entity" )
8 | require( "game" )
9 |
10 | if ( _CLIENT ) then
11 | require( "engine.client.chat" )
12 | end
13 |
14 | class "func_examine" ( "entity" )
15 |
16 | function func_examine:func_examine()
17 | entity.entity( self )
18 | end
19 |
20 | if ( _CLIENT ) then
21 | function func_examine:getOptions()
22 | return {
23 | {
24 | name = "Examine",
25 | value = function() self:examine() end
26 | }
27 | }
28 | end
29 |
30 | function func_examine:examine()
31 | local properties = self:getProperties()
32 | chat.addText( properties[ "text" ] )
33 | end
34 | end
35 |
36 | function func_examine:spawn()
37 | entity.spawn( self )
38 |
39 | local tileSize = game.tileSize
40 | local min = vector()
41 | local max = vector( tileSize, -tileSize )
42 | self:initializePhysics()
43 | self:setCollisionBounds( min, max )
44 | end
45 |
46 | entities.linkToClassname( func_examine, "func_examine" )
47 |
--------------------------------------------------------------------------------
/game/shared/entities/item.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: item
4 | --
5 | --==========================================================================--
6 |
7 | entities.require( "entity" )
8 | -- require( "game" )
9 |
10 | if ( _CLIENT ) then
11 | require( "engine.client.chat" )
12 | end
13 |
14 | class "item" ( "entity" )
15 |
16 | function item.getClass( classname )
17 | entities.require( classname )
18 | local classmap = entities.getClassMap()
19 | return classmap[ classname ]
20 | end
21 |
22 | item.data = {
23 | name = "Unknown Item",
24 | image = "images/error.png"
25 | }
26 |
27 | function item:item()
28 | entity.entity( self )
29 |
30 | -- local tileSize = game.tileSize
31 | -- local min = vector()
32 | -- local max = vector( tileSize, -tileSize )
33 | -- self:setCollisionBounds( min, max )
34 |
35 | local name = self.data.name
36 | self:setNetworkVar( "name", name )
37 |
38 | if ( _CLIENT ) then
39 | local filename = self.data.image
40 | local sprite = love.graphics.newImage( filename )
41 | sprite:setFilter( "nearest", "nearest" )
42 | self:setSprite( sprite )
43 | end
44 | end
45 |
46 | if ( _CLIENT ) then
47 | function item:getOptions()
48 | return {
49 | { name = "Pickup", value = function() self:pickup() end },
50 | { name = "Examine", value = function() self:examine() end }
51 | }
52 | end
53 |
54 | function item:getInventoryOptions()
55 | return {
56 | {
57 | name = "Use",
58 | value = function()
59 | g_Inventory:select( self.__type )
60 | end
61 | },
62 | { name = "Drop", value = function() self:drop() end },
63 | { name = "Examine", value = function() self:examine() end }
64 | }
65 | end
66 |
67 | function item:pickup()
68 | localplayer:pickup( self )
69 | end
70 |
71 | function item:drop()
72 | localplayer:drop( self.__type )
73 | end
74 |
75 | function item:examine()
76 | chat.addText( "It's an item." )
77 | end
78 | end
79 |
80 | function item:setCollisionBounds( min, max )
81 | entity.setCollisionBounds( self, min, max )
82 |
83 | local body = self:getBody()
84 | if ( body == nil ) then
85 | return
86 | end
87 |
88 | local fixtures = body:getFixtures()
89 | local fixture = fixtures[ 1 ]
90 | if ( fixture ) then
91 | fixture:setMask( 6 --[[ COLLISION_GROUP_PLAYER ]] )
92 | end
93 | end
94 |
95 | function item:use( activator, value )
96 | chat.addText( "Nothing interesting happens." )
97 | end
98 |
99 | function item:useItem( activator, value )
100 | chat.addText( "Nothing interesting happens." )
101 | end
102 |
--------------------------------------------------------------------------------
/game/shared/entities/item_apple.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: item_apple
4 | --
5 | --==========================================================================--
6 |
7 | require( "game.shared.entities.item" )
8 | require( "game" )
9 |
10 | class "item_apple" ( "item" )
11 |
12 | item_apple.data = {
13 | name = "Apple",
14 | image = "images/entities/item_apple/1.png"
15 | }
16 |
17 | function item_apple:item_apple()
18 | item.item( self )
19 | end
20 |
21 | if ( _CLIENT ) then
22 | function item_apple:getInventoryOptions()
23 | return {
24 | { name = "Eat", value = function() self:eat() end },
25 | {
26 | name = "Use",
27 | value = function()
28 | g_Inventory:select( self.__type )
29 | end
30 | },
31 | { name = "Drop", value = function() item.drop( self ) end },
32 | { name = "Examine", value = function() self:examine() end }
33 | }
34 | end
35 |
36 | function item_apple:examine()
37 | chat.addText( "Looks like an apple." )
38 | end
39 |
40 | function item_apple:eat()
41 | localplayer:useItem( self.__type )
42 | end
43 | end
44 |
45 | function item_apple:spawn()
46 | entity.spawn( self )
47 |
48 | local tileSize = game.tileSize
49 | local min = vector()
50 | local max = vector( tileSize, -tileSize )
51 | self:initializePhysics( "dynamic" )
52 | self:setCollisionBounds( min, max )
53 |
54 | local body = self:getBody()
55 | if ( body ) then
56 | body:setMass( 0.1496855 )
57 | end
58 | end
59 |
60 | function item_apple:useItem( activator, value )
61 | -- Give health
62 | local health = activator:getNetworkVar( "health" )
63 | activator:setNetworkVar( "health", health + 3 )
64 |
65 | -- Notify player
66 | activator:sendText( "The apple gives you some health." )
67 |
68 | -- Remove from inventory
69 | activator:removeItem( self.__type )
70 | end
71 |
72 | entities.linkToClassname( item_apple, "item_apple" )
73 |
--------------------------------------------------------------------------------
/game/shared/entities/item_gold.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: item_gold
4 | --
5 | --==========================================================================--
6 |
7 | require( "game.shared.entities.item" )
8 | require( "game" )
9 |
10 | class "item_gold" ( "item" )
11 |
12 | item_gold.data = {
13 | name = "Gold",
14 | image = "images/entities/item_apple/1.png",
15 | stackable = true
16 | }
17 |
18 | function item_gold:item_gold()
19 | item.item( self )
20 | end
21 |
22 | if ( _CLIENT ) then
23 | function item_gold:pickup()
24 | localplayer:pickup( self )
25 | end
26 |
27 | function item_gold:drop()
28 | localplayer:drop( self.__type )
29 | end
30 |
31 | function item_gold:examine()
32 | chat.addText( "Shiny." )
33 | end
34 | end
35 |
36 | function item_gold:spawn()
37 | entity.spawn( self )
38 |
39 | local tileSize = game.tileSize
40 | local min = vector()
41 | local max = vector( tileSize, -tileSize )
42 | self:initializePhysics( "dynamic" )
43 | self:setCollisionBounds( min, max )
44 | end
45 |
46 | entities.linkToClassname( item_gold, "item_gold" )
47 |
--------------------------------------------------------------------------------
/game/shared/entities/prop_chest.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: prop_chest
4 | --
5 | --==========================================================================--
6 |
7 | entities.require( "entity" )
8 | require( "game" )
9 |
10 | if ( _CLIENT ) then
11 | require( "engine.client.chat" )
12 | end
13 |
14 | class "prop_chest" ( "entity" )
15 |
16 | function prop_chest:prop_chest()
17 | entity.entity( self )
18 |
19 | self:setNetworkVar( "name", "Chest" )
20 |
21 | if ( _CLIENT ) then
22 | local filename = "images/entities/prop_chest.png"
23 | local sprite = love.graphics.newImage( filename )
24 | sprite:setFilter( "nearest", "nearest" )
25 | self:setSprite( sprite )
26 | end
27 |
28 | self.inventory = {}
29 | end
30 |
31 | if ( _CLIENT ) then
32 | function prop_chest:getOptions()
33 | return {
34 | { name = "Search", value = function() self:search() end },
35 | { name = "Examine", value = function() self:examine() end }
36 | }
37 | end
38 |
39 | local function moveTo( position )
40 | return function( character, next )
41 | character:moveTo( position, next )
42 | end
43 | end
44 |
45 | local function use( entity )
46 | return function( player, next )
47 | local payload = payload( "playerUse" )
48 | payload:set( "entity", entity )
49 | payload:set( "value", nil )
50 | payload:sendToServer()
51 |
52 | next()
53 | end
54 | end
55 |
56 | function prop_chest:search()
57 | -- Stop everything
58 | localplayer:removeTasks()
59 |
60 | -- Walk to the front of the chest
61 | local position = self:getPosition() + vector( 0, game.tileSize )
62 | localplayer:addTask( moveTo( position ) )
63 |
64 | -- Use it
65 | localplayer:addTask( use( self ) )
66 | end
67 |
68 | function prop_chest:examine()
69 | chat.addText( "I wonder what's inside." )
70 | end
71 | end
72 |
73 | function prop_chest:spawn()
74 | entity.spawn( self )
75 |
76 | local tileSize = game.tileSize
77 | local min = vector()
78 | local max = vector( tileSize, -tileSize )
79 | self:initializePhysics()
80 | self:setCollisionBounds( min, max )
81 | end
82 |
83 | function prop_chest:use( activator, value )
84 | activator:sendText( "You search the chest but find nothing." )
85 | end
86 |
87 | entities.linkToClassname( prop_chest, "prop_chest" )
88 |
--------------------------------------------------------------------------------
/game/shared/entities/prop_ore_rock.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: prop_ore_rock
4 | --
5 | --==========================================================================--
6 |
7 | entities.require( "entity" )
8 | require( "game" )
9 |
10 | if ( _CLIENT ) then
11 | require( "engine.client.chat" )
12 | end
13 |
14 | class "prop_ore_rock" ( "entity" )
15 |
16 | function prop_ore_rock:prop_ore_rock()
17 | entity.entity( self )
18 |
19 | self:setNetworkVar( "name", "Ore Rock" )
20 |
21 | if ( _CLIENT ) then
22 | local filename = "images/entities/prop_ore_rock.png"
23 | local sprite = love.graphics.newImage( filename )
24 | sprite:setFilter( "nearest", "nearest" )
25 | self:setSprite( sprite )
26 | end
27 |
28 | self:setDrawShadow( false )
29 | end
30 |
31 | if ( _CLIENT ) then
32 | function prop_ore_rock:getOptions()
33 | return {
34 | { name = "Pick", value = function() self:pick() end },
35 | { name = "Examine", value = function() self:examine() end }
36 | }
37 | end
38 |
39 | function prop_ore_rock:pick()
40 | chat.addText( "Nothing interesting happens." )
41 | end
42 |
43 | function prop_ore_rock:examine()
44 | chat.addText( "This rock contains ore." )
45 | end
46 | end
47 |
48 | function prop_ore_rock:spawn()
49 | entity.spawn( self )
50 |
51 | local tileSize = game.tileSize
52 | local min = vector()
53 | local max = vector( tileSize, -tileSize )
54 | self:initializePhysics()
55 | self:setCollisionBounds( min, max )
56 | end
57 |
58 | entities.linkToClassname( prop_ore_rock, "prop_ore_rock" )
59 |
--------------------------------------------------------------------------------
/game/shared/entities/prop_tree.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: prop_tree
4 | --
5 | --==========================================================================--
6 |
7 | entities.require( "entity" )
8 | require( "game" )
9 |
10 | if ( _CLIENT ) then
11 | require( "engine.client.chat" )
12 | end
13 |
14 | class "prop_tree" ( "entity" )
15 |
16 | function prop_tree:prop_tree()
17 | entity.entity( self )
18 |
19 | self:setNetworkVar( "name", "Tree" )
20 |
21 | if ( _CLIENT ) then
22 | local filename = "images/entities/prop_tree/1.png"
23 | local sprite = love.graphics.newImage( filename )
24 | sprite:setFilter( "nearest", "nearest" )
25 | self:setSprite( sprite )
26 | end
27 | end
28 |
29 | if ( _CLIENT ) then
30 | function prop_tree:getOptions()
31 | return {
32 | { name = "Chop Down", value = function() self:chopDown() end },
33 | { name = "Examine", value = function() self:examine() end }
34 | }
35 | end
36 |
37 | function prop_tree:chopDown()
38 | chat.addText( "Your hands alone really aren't going to cut it." )
39 | end
40 |
41 | function prop_tree:examine()
42 | chat.addText( "Looks like a tree." )
43 | end
44 | end
45 |
46 | function prop_tree:spawn()
47 | entity.spawn( self )
48 |
49 | local tileSize = game.tileSize
50 | local min = vector()
51 | local max = vector( 2 * tileSize, -tileSize )
52 | self:initializePhysics()
53 | self:setCollisionBounds( min, max )
54 | end
55 |
56 | entities.linkToClassname( prop_tree, "prop_tree" )
57 |
--------------------------------------------------------------------------------
/game/shared/entities/prop_worldgate_spawn.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: prop_worldgate_spawn
4 | --
5 | --==========================================================================--
6 |
7 | entities.require( "entity" )
8 | require( "game" )
9 |
10 | if ( _CLIENT ) then
11 | require( "engine.client.chat" )
12 | end
13 |
14 | class "prop_worldgate_spawn" ( "entity" )
15 |
16 | function prop_worldgate_spawn:prop_worldgate_spawn()
17 | entity.entity( self )
18 |
19 | if ( _CLIENT ) then
20 | local tileSize = game.tileSize
21 | self:setLocalPosition( vector( -tileSize, 0 ) )
22 | end
23 |
24 | self:setNetworkVar( "name", "World Gate" )
25 |
26 | if ( _CLIENT ) then
27 | local filename = "images/entities/prop_worldgate_spawn.png"
28 | local sprite = love.graphics.newImage( filename )
29 | sprite:setFilter( "nearest", "nearest" )
30 | self:setSprite( sprite )
31 | end
32 | end
33 |
34 | if ( _CLIENT ) then
35 | function prop_worldgate_spawn:getOptions()
36 | return {
37 | {
38 | name = "Examine",
39 | value = function() self:examine() end
40 | }
41 | }
42 | end
43 |
44 | function prop_worldgate_spawn:examine()
45 | chat.addText( "Rather tall and ominous." )
46 | end
47 | end
48 |
49 | function prop_worldgate_spawn:spawn()
50 | entity.spawn( self )
51 |
52 | local tileSize = game.tileSize
53 | local min = vector()
54 | local max = vector( tileSize, -tileSize )
55 | self:initializePhysics()
56 | self:setCollisionBounds( min, max )
57 | end
58 |
59 | function prop_worldgate_spawn:tick( timestep )
60 | local position = self:getPosition()
61 | local players = player.getAll()
62 | for _, player in ipairs( players ) do
63 | if ( player:getPosition() == position ) then
64 | player:moveTo( position + vector( 0, game.tileSize ) )
65 | end
66 | end
67 | end
68 |
69 | entities.linkToClassname( prop_worldgate_spawn, "prop_worldgate_spawn" )
70 |
--------------------------------------------------------------------------------
/game/shared/entities/vanpc.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: vanpc
4 | --
5 | --==========================================================================--
6 |
7 | entities.require( "npc" )
8 |
9 | if ( _CLIENT ) then
10 | require( "engine.client.chat" )
11 | end
12 |
13 | class "vanpc" ( "npc" )
14 |
15 | function vanpc:vanpc()
16 | npc.npc( self )
17 | self:setNetworkVar( "name", "Example NPC" )
18 | end
19 |
20 | if ( _CLIENT ) then
21 | function vanpc:getOptions()
22 | return {
23 | { name = "Talk to", value = function() self:talkTo() end },
24 | { name = "Examine", value = function() self:examine() end }
25 | }
26 | end
27 |
28 | local function moveTo( position )
29 | return function( character, next )
30 | character:moveTo( position, next )
31 | end
32 | end
33 |
34 | local function talkTo( entity )
35 | return function( player, next )
36 | local payload = payload( "npcTalkTo" )
37 | payload:set( "npc", entity )
38 | payload:set( "dialogue", nil )
39 | payload:sendToServer()
40 |
41 | next()
42 | end
43 | end
44 |
45 | function vanpc:talkTo()
46 | -- Stop everything
47 | localplayer:removeTasks()
48 |
49 | -- Walk up to the NPC
50 | local pos1 = localplayer:getPosition()
51 | local pos2 = self:getPosition()
52 | local tiles = path.getPath( pos1, pos2 )
53 | if ( tiles and #tiles > 1 ) then
54 | local nearestTile = tiles[ #tiles - 1 ]
55 | localplayer:addTask( moveTo( nearestTile ) )
56 | end
57 |
58 | -- Talk to it
59 | localplayer:addTask( talkTo( self ) )
60 | end
61 |
62 | function vanpc:examine()
63 | chat.addText( "Some guy." )
64 | end
65 | end
66 |
67 | if ( _SERVER ) then
68 | local function onNPCTalkTo( payload )
69 | local player = payload:getPlayer()
70 | local npc = payload:get( "npc" )
71 | local dialogue = payload:get( "dialogue" )
72 |
73 | local canTalkTo = game.call(
74 | "server", "onNPCTalkTo", npc, player, dialogue
75 | )
76 | if ( canTalkTo == false ) then
77 | return
78 | end
79 |
80 | npc:onTalkTo( player, dialogue )
81 | end
82 |
83 | payload.setHandler( onNPCTalkTo, "npcTalkTo" )
84 |
85 | function vanpc:onTalkTo( vaplayer, dialogue )
86 | local name = vaplayer:getName()
87 | vaplayer:sendText( "Hello, " .. name .. "!" )
88 | end
89 | end
90 |
91 | vanpc.dialogue = vanpc.dialogue or {}
92 | local dialogue = vanpc.dialogue
93 |
94 | table.insert( dialogue, {
95 | index = 1,
96 | text = "Are you a boy? Or are you a girl?",
97 | options = { 2, 3 }
98 | } )
99 |
100 | table.insert( dialogue, {
101 | index = 2,
102 | text = "Boy",
103 | next = 4
104 | } )
105 |
106 | table.insert( dialogue, {
107 | index = 3,
108 | text = "Girl",
109 | next = 4
110 | } )
111 |
112 | table.insert( dialogue, {
113 | index = 4,
114 | text = "Ah yes. We've been expecting you."
115 | } )
116 |
117 | entities.linkToClassname( vanpc, "vanpc" )
118 |
--------------------------------------------------------------------------------
/game/shared/entities/weapon_bow.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: weapon_bow
4 | --
5 | --==========================================================================--
6 |
7 | require( "game.shared.entities.item" )
8 | require( "game" )
9 |
10 | class "weapon_bow" ( "item" )
11 |
12 | weapon_bow.data = {
13 | name = "Bow",
14 | image = "images/entities/weapon_bow.png"
15 | }
16 |
17 | function weapon_bow:weapon_bow()
18 | item.item( self )
19 | end
20 |
21 | if ( _CLIENT ) then
22 | function weapon_bow:pickup()
23 | localplayer:pickup( self )
24 | end
25 |
26 | function weapon_bow:drop()
27 | localplayer:drop( self.__type )
28 | end
29 |
30 | function weapon_bow:examine()
31 | chat.addText( "It's a bow. What else did you expect?" )
32 | end
33 | end
34 |
35 | function weapon_bow:spawn()
36 | entity.spawn( self )
37 |
38 | local tileSize = game.tileSize
39 | local min = vector()
40 | local max = vector( tileSize, -tileSize )
41 | self:initializePhysics( "dynamic" )
42 | self:setCollisionBounds( min, max )
43 |
44 | local body = self:getBody()
45 | if ( body ) then
46 | body:setMass( 1.81437 )
47 | end
48 | end
49 |
50 | entities.linkToClassname( weapon_bow, "weapon_bow" )
51 |
--------------------------------------------------------------------------------
/game/shared/entities/weapon_staff.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: weapon_staff
4 | --
5 | --==========================================================================--
6 |
7 | require( "game.shared.entities.item" )
8 | require( "game" )
9 |
10 | class "weapon_staff" ( "item" )
11 |
12 | weapon_staff.data = {
13 | name = "Staff",
14 | image = "images/entities/weapon_staff.png"
15 | }
16 |
17 | function weapon_staff:weapon_staff()
18 | item.item( self )
19 | end
20 |
21 | if ( _CLIENT ) then
22 | function weapon_staff:pickup()
23 | localplayer:pickup( self )
24 | end
25 |
26 | function weapon_staff:drop()
27 | localplayer:drop( self.__type )
28 | end
29 |
30 | function weapon_staff:examine()
31 | chat.addText( "Brown and sticky." )
32 | end
33 | end
34 |
35 | function weapon_staff:spawn()
36 | entity.spawn( self )
37 |
38 | local tileSize = game.tileSize
39 | local min = vector()
40 | local max = vector( tileSize, -tileSize )
41 | self:initializePhysics( "dynamic" )
42 | self:setCollisionBounds( min, max )
43 | end
44 |
45 | entities.linkToClassname( weapon_staff, "weapon_staff" )
46 |
--------------------------------------------------------------------------------
/images/entities/item_apple/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/images/entities/item_apple/1.png
--------------------------------------------------------------------------------
/images/entities/item_apple/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/images/entities/item_apple/2.png
--------------------------------------------------------------------------------
/images/entities/prop_chest.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/images/entities/prop_chest.png
--------------------------------------------------------------------------------
/images/entities/prop_ore_rock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/images/entities/prop_ore_rock.png
--------------------------------------------------------------------------------
/images/entities/prop_tree/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/images/entities/prop_tree/1.png
--------------------------------------------------------------------------------
/images/entities/prop_worldgate_spawn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/images/entities/prop_worldgate_spawn.png
--------------------------------------------------------------------------------
/images/entities/weapon_bow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/images/entities/weapon_bow.png
--------------------------------------------------------------------------------
/images/entities/weapon_staff.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/images/entities/weapon_staff.png
--------------------------------------------------------------------------------
/images/error.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/images/error.png
--------------------------------------------------------------------------------
/images/gui/arrow_down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/images/gui/arrow_down.png
--------------------------------------------------------------------------------
/images/gui/arrow_down@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/images/gui/arrow_down@2x.png
--------------------------------------------------------------------------------
/images/gui/check.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/images/gui/check.png
--------------------------------------------------------------------------------
/images/gui/check@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/images/gui/check@2x.png
--------------------------------------------------------------------------------
/images/gui/close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/images/gui/close.png
--------------------------------------------------------------------------------
/images/gui/close@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/images/gui/close@2x.png
--------------------------------------------------------------------------------
/images/gui/close_large.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/images/gui/close_large.png
--------------------------------------------------------------------------------
/images/gui/close_large@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/images/gui/close_large@2x.png
--------------------------------------------------------------------------------
/images/gui/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/images/gui/logo.png
--------------------------------------------------------------------------------
/images/gui/logo@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/images/gui/logo@2x.png
--------------------------------------------------------------------------------
/images/gui/logo_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/images/gui/logo_dark.png
--------------------------------------------------------------------------------
/images/gui/logo_dark@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/images/gui/logo_dark@2x.png
--------------------------------------------------------------------------------
/images/gui/logo_small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/images/gui/logo_small.png
--------------------------------------------------------------------------------
/images/gui/logo_small@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/images/gui/logo_small@2x.png
--------------------------------------------------------------------------------
/images/gui/radiobutton_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/images/gui/radiobutton_foreground.png
--------------------------------------------------------------------------------
/images/gui/radiobutton_foreground@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/images/gui/radiobutton_foreground@2x.png
--------------------------------------------------------------------------------
/images/gui/selection_dot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/images/gui/selection_dot.png
--------------------------------------------------------------------------------
/images/gui/selection_dot@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/images/gui/selection_dot@2x.png
--------------------------------------------------------------------------------
/images/gui/throbber.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/images/gui/throbber.png
--------------------------------------------------------------------------------
/images/gui/throbber@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/images/gui/throbber@2x.png
--------------------------------------------------------------------------------
/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/images/icon.png
--------------------------------------------------------------------------------
/images/icon_osx.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/images/icon_osx.png
--------------------------------------------------------------------------------
/images/moveindicator.lua:
--------------------------------------------------------------------------------
1 | return {
2 | image = "images/moveindicator.png",
3 | width = 16,
4 | height = 16,
5 | frametime = 0.04,
6 | animations = {
7 | click = {
8 | from = 1,
9 | to = 8
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/images/moveindicator.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/images/moveindicator.png
--------------------------------------------------------------------------------
/images/player.lua:
--------------------------------------------------------------------------------
1 | return {
2 | image = "images/player.png",
3 | width = 16,
4 | height = 32,
5 | frametime = 0.25,
6 | animations = {
7 | idlenorth = {
8 | from = 1,
9 | to = 1
10 | },
11 | idleeast = {
12 | from = 2,
13 | to = 2
14 | },
15 | idlesouth = {
16 | from = 3,
17 | to = 3
18 | },
19 | idlewest = {
20 | from = 4,
21 | to = 4
22 | },
23 | -- idlenorth
24 | idlenortheast = {
25 | from = 1,
26 | to = 1,
27 | },
28 | -- idlesouth
29 | idlesoutheast = {
30 | from = 3,
31 | to = 3,
32 | },
33 | -- idlesouth
34 | idlesouthwest = {
35 | from = 3,
36 | to = 3,
37 | },
38 | -- idlenorth
39 | idlenorthwest = {
40 | from = 1,
41 | to = 1,
42 | },
43 | walknorth = {
44 | from = 33,
45 | to = 36
46 | },
47 | walkeast = {
48 | from = 65,
49 | to = 68,
50 | },
51 | walksouth = {
52 | from = 97,
53 | to = 100,
54 | },
55 | walkwest = {
56 | from = 129,
57 | to = 132,
58 | },
59 | walknortheast = {
60 | from = 161,
61 | to = 164,
62 | },
63 | walksoutheast = {
64 | from = 193,
65 | to = 196,
66 | },
67 | walksouthwest = {
68 | from = 225,
69 | to = 228,
70 | },
71 | walknorthwest = {
72 | from = 257,
73 | to = 260,
74 | }
75 | },
76 | events = {
77 | -- walknorth
78 | [ 33 ] = "rightfootstep",
79 | [ 35 ] = "leftfootstep",
80 | -- walkeast
81 | [ 65 ] = "rightfootstep",
82 | [ 67 ] = "leftfootstep",
83 | -- walksouth
84 | [ 96 ] = "rightfootstep",
85 | [ 98 ] = "leftfootstep",
86 | -- walkwest
87 | [ 129 ] = "leftfootstep",
88 | [ 131 ] = "rightfootstep",
89 | -- walknortheast
90 | [ 161 ] = "rightfootstep",
91 | [ 163 ] = "leftfootstep",
92 | -- walksoutheast
93 | [ 193 ] = "rightfootstep",
94 | [ 195 ] = "leftfootstep",
95 | -- walksouthwest
96 | [ 224 ] = "leftfootstep",
97 | [ 226 ] = "rightfootstep",
98 | -- walknorthwest
99 | [ 257 ] = "leftfootstep",
100 | [ 259 ] = "rightfootstep"
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/images/player.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/images/player.png
--------------------------------------------------------------------------------
/images/tilesets/architect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/images/tilesets/architect.png
--------------------------------------------------------------------------------
/images/tilesets/developer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/images/tilesets/developer.png
--------------------------------------------------------------------------------
/images/tilesets/world.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/images/tilesets/world.png
--------------------------------------------------------------------------------
/init.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Default engine entry point
4 | --
5 | --==========================================================================--
6 |
7 | assert( false, "attempt to load game from init.lua" )
8 |
--------------------------------------------------------------------------------
/main.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Engine entry point
4 | --
5 | --==========================================================================--
6 |
7 | require( "engine" )
8 |
--------------------------------------------------------------------------------
/maps/Architect.tsx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/maps/Developer.tsx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/maps/World.tsx:
--------------------------------------------------------------------------------
1 |
2 |
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 |
--------------------------------------------------------------------------------
/schemes/Chat.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Chat HUD scheme
4 | --
5 | --==========================================================================--
6 |
7 | local chat = scheme( "Chat" )
8 |
9 | chat.textbox = {
10 | backgroundColor = color( 0, 0, 0, 0 * 255 ),
11 | borderColor = color( 104, 106, 107, 0.66 * 255 ),
12 | textColor = color( 230, 230, 230, 255 ),
13 |
14 | mouseover = {
15 | borderColor = color( 163, 126, 71, 0.42 * 255 ),
16 | textColor = color( 163, 167, 168, 255 )
17 | },
18 |
19 | focus = {
20 | borderColor = color( 163, 126, 71, 0.27 * 255 ),
21 | textColor = color( 163, 167, 168, 255 )
22 | }
23 | }
24 |
25 | -- NOTE: The following arguments to `newFont` are undocumented.
26 | local dpiscale = love.graphics.getDPIScale()
27 | local r_window_highdpi = convar.getConvar( "r_window_highdpi" )
28 | if ( r_window_highdpi:getNumber() == 2 ) then
29 | dpiscale = 1
30 | end
31 |
32 | chat.font = love.graphics.newFont(
33 | "fonts/SourceSansPro-Regular.otf", 14, "normal", dpiscale
34 | )
35 |
--------------------------------------------------------------------------------
/schemes/Console.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Console scheme
4 | --
5 | --==========================================================================--
6 |
7 | local console = scheme( "Console" )
8 |
9 | console.textbox = {
10 | backgroundColor = color( 35, 35, 36, 0.66 * 255 ),
11 | borderColor = color( 104, 106, 107, 0.66 * 255 ),
12 | textColor = color( 104, 106, 107, 255 ),
13 |
14 | mouseover = {
15 | borderColor = color( 163, 126, 71, 0.42 * 255 ),
16 | textColor = color( 163, 167, 168, 255 )
17 | },
18 |
19 | focus = {
20 | borderColor = color( 163, 126, 71, 0.27 * 255 ),
21 | textColor = color( 163, 167, 168, 255 )
22 | }
23 | }
24 |
25 | -- NOTE: The following arguments to `newFont` are undocumented.
26 | local dpiscale = love.graphics.getDPIScale()
27 | local r_window_highdpi = convar.getConvar( "r_window_highdpi" )
28 | if ( r_window_highdpi:getNumber() == 2 ) then
29 | dpiscale = 1
30 | end
31 |
32 | console.font = love.graphics.newFont(
33 | "fonts/SourceCodePro-Light.otf", 12, "normal", dpiscale
34 | )
35 |
--------------------------------------------------------------------------------
/scripts/test/box.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Test box
4 | --
5 | --==========================================================================--
6 |
7 | local e = gui.createElement
8 |
9 | local name = "Quit Game"
10 |
11 | class "gui.boxtestframe" ( "gui.frame" )
12 |
13 | local boxtestframe = gui.boxtestframe
14 |
15 | function boxtestframe:boxtestframe( parent, name, title )
16 | gui.frame.frame( self, parent, name, title )
17 |
18 | local child = e( "box", {
19 | parent = self,
20 | position = "absolute",
21 | y = 86,
22 | paddingTop = 0,
23 | padding = 36
24 | }, {
25 | e( "text", {
26 | marginBottom = 9,
27 | text = "Are you sure you want to quit the game?",
28 | -- color = color.white
29 | } ),
30 | e( "box", {
31 | display = "block"
32 | }, {
33 | e( "button", {
34 | display = "inline",
35 | position = "static",
36 | -- width = "nil",
37 | -- height = "nil",
38 | padding = 14,
39 | marginRight = 36,
40 | text = "Quit",
41 | onClick = function() love._shouldQuit = true; love.quit() end
42 | } ),
43 | e( "button", {
44 | display = "inline",
45 | position = "static",
46 | -- width = "nil",
47 | -- height = "nil",
48 | padding = 14,
49 | text = "Cancel",
50 | onClick = function() self:close() end
51 | } )
52 | } )
53 | } )
54 |
55 | self:setWidth( child:getWidth() )
56 | self:setHeight( 86 + child:getHeight() )
57 | end
58 |
59 | local frame = gui.boxtestframe( nil, name, name )
60 | frame:setRemoveOnClose( true )
61 | frame:moveToCenter()
62 | frame:activate()
63 |
--------------------------------------------------------------------------------
/scripts/test/line.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Test line drawing
4 | --
5 | --==========================================================================--
6 |
7 | local name = "Line Drawing Test"
8 | local frame = gui.frame( nil, name, name )
9 |
10 | function frame:draw()
11 | gui.frame.draw( self )
12 |
13 | love.graphics.setColor( color.red )
14 | local lineWidth = 2
15 | love.graphics.setLineStyle( "rough" )
16 | love.graphics.setLineWidth( lineWidth )
17 |
18 | -- Horizontal Line
19 | -- love.graphics.line(
20 | -- lineWidth / 2, lineWidth / 2, -- Top-left
21 | -- self:getWidth(), lineWidth / 2 -- Top-right
22 | -- )
23 |
24 | -- Vertical Line
25 | -- love.graphics.line(
26 | -- self:getWidth() - lineWidth / 2, 0 -- Top-right
27 | -- self:getWidth() - lineWidth / 2, self:getHeight() -- Bottom-right
28 | -- )
29 |
30 | -- Three-point line
31 | love.graphics.line(
32 | lineWidth / 2, lineWidth / 2, -- Top-left
33 | self:getWidth() - lineWidth / 2, lineWidth / 2, -- Top-right
34 | self:getWidth() - lineWidth / 2, self:getHeight() -- Bottom-right
35 | )
36 | end
37 |
38 | frame:setRemoveOnClose( true )
39 | frame:moveToCenter()
40 | frame:activate()
41 |
--------------------------------------------------------------------------------
/scripts/test/textbox.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Test text box
4 | --
5 | --==========================================================================--
6 |
7 | local name = "Text Box Test"
8 |
9 | class "gui.textboxtestframe" ( "gui.frame" )
10 |
11 | local textboxtestframe = gui.textboxtestframe
12 |
13 | function textboxtestframe:textboxtestframe( parent, name, title )
14 | gui.frame.frame( self, parent, name, title )
15 |
16 | local textbox = gui.textbox( self )
17 |
18 | local margin = 36
19 | local minHeight = self:getMinHeight()
20 | minHeight = minHeight + textbox:getHeight()
21 | self:setMinHeight( minHeight + margin )
22 |
23 | local x = margin
24 | local y = 86 -- Title Bar Height
25 | local width = self:getWidth()
26 | local height = self:getHeight()
27 | width = width - 2 * margin
28 | height = height - y - margin
29 | textbox:setPos( x, y )
30 | textbox:setWidth( width )
31 | textbox:setHeight( height )
32 | textbox:setMultiline( true )
33 | self.textbox = textbox
34 |
35 | self:invalidateLayout()
36 | end
37 |
38 | function textboxtestframe:invalidateLayout()
39 | local textbox = self.textbox
40 | local margin = 36
41 | local y = 86 -- Title Bar Height
42 | local width = self:getWidth()
43 | local height = self:getHeight()
44 | width = width - 2 * margin
45 | height = height - y - margin
46 | textbox:setWidth( width )
47 | textbox:setHeight( height )
48 |
49 | gui.frame.invalidateLayout( self )
50 | end
51 |
52 | local frame = gui.textboxtestframe( nil, name, name )
53 | frame:setRemoveOnClose( true )
54 | frame:moveToCenter()
55 | frame:activate()
56 |
--------------------------------------------------------------------------------
/shaders/coloroverlay.frag:
--------------------------------------------------------------------------------
1 | //=========== Copyright © 2019, Planimeter, All rights reserved. =============//
2 | //
3 | // Purpose:
4 | //
5 | //============================================================================//
6 |
7 | vec4 effect( vec4 color, Image tex, vec2 texcoord, vec2 pixcoord )
8 | {
9 | return vec4( color.rgb, Texel( tex, texcoord ).a );
10 | }
11 |
--------------------------------------------------------------------------------
/shaders/gaussianblur.frag:
--------------------------------------------------------------------------------
1 | //=========== Copyright © 2019, Planimeter, All rights reserved. =============//
2 | //
3 | // Purpose: Gaussian blur fragment reference shader
4 | // https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch40.html
5 | //
6 | //============================================================================//
7 |
8 | uniform float sigma; // Gaussian sigma
9 | uniform vec2 dir; // horiz=(1.0, 0.0), vert=(0.0, 1.0)
10 | uniform int support; // int(sigma * 3.0) truncation
11 | vec4 effect( vec4 color, Image tex, vec2 texcoord, vec2 pixcoord )
12 | {
13 | vec2 loc = texcoord; // center pixel cooordinate
14 | vec4 acc = vec4( 0.0f ); // accumulator
15 | float norm = 0.0f;
16 | for (int i = -support; i <= support; i++) {
17 | float coeff = exp(-0.5 * float(i) * float(i) / (sigma * sigma));
18 | acc += (Texel(tex, loc + float(i) * dir)) * coeff;
19 | norm += coeff;
20 | }
21 | acc *= 1/norm; // normalize for unity gain
22 | return acc;
23 | }
24 |
--------------------------------------------------------------------------------
/shaders/gaussianblur.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Gaussian blur fragment shader
4 | --
5 | --==========================================================================--
6 |
7 | require( "shaders.shader" )
8 |
9 | class "gaussianblur" ( "shader" )
10 |
11 | local gaussianblur = shader._shaders[ "gaussianblur" ] or gaussianblur
12 |
13 | function gaussianblur:gaussianblur()
14 | local width, height = love.graphics.getDimensions()
15 | self.scale = 1 / 2
16 | width = width * self.scale
17 | height = height * self.scale
18 | self.horizontalPass = love.graphics.newCanvas( width, height, { dpiscale = 1 } )
19 | self.verticalPass = love.graphics.newCanvas( width, height, { dpiscale = 1 } )
20 | -- local fragmentShader = love.filesystem.read( "shaders/gaussianblur.frag" )
21 | -- self.shader = love.graphics.newShader( fragmentShader )
22 | end
23 |
24 | function gaussianblur:renderTo( func )
25 | local shader = love.graphics.getShader()
26 | love.graphics.setShader( self.shader )
27 |
28 | local width, height = love.graphics.getDimensions()
29 | width = width * self.scale
30 | height = height * self.scale
31 | self.shader:send( "dir", { 1 / width, 0 } )
32 | self.horizontalPass:renderTo( function()
33 | love.graphics.push()
34 | love.graphics.scale( self.scale, self.scale )
35 | func()
36 | love.graphics.pop()
37 | end )
38 |
39 | local b = love.graphics.getBlendMode()
40 | love.graphics.setBlendMode( "alpha", "premultiplied" )
41 |
42 | self.shader:send( "dir", { 0, 1 / height } )
43 | self.verticalPass:renderTo( function()
44 | love.graphics.clear()
45 | love.graphics.draw( self.horizontalPass )
46 | end )
47 |
48 | love.graphics.setBlendMode( b )
49 | love.graphics.setShader( shader )
50 | end
51 |
52 | function gaussianblur:draw()
53 | love.graphics.setColor( color.white )
54 | love.graphics.draw( self.verticalPass, 0, 0, 0, 1 / self.scale )
55 | end
56 |
57 | function gaussianblur:set( key, value )
58 | if ( key == "sigma" ) then
59 | -- self.shader:send( "sigma", value )
60 | -- self.shader:send( "norm", 1/(math.sqrt(2*math.pi)*value) )
61 | -- self.shader:send( "support", (value * 3.0) )
62 | self:generateShader( value, (value * 3.0) )
63 | end
64 |
65 | return self
66 | end
67 |
68 | function gaussianblur:generateShader( sigma, support )
69 | -- See `shaders/gaussianblur.frag`
70 | -- Loop unroll Gaussian convolution
71 | local norm = 0
72 | local forLoop = {}
73 | local line = "acc += (Texel(tex, loc + %.1f * dir)) * %f;"
74 | for i = -support, support do
75 | local coeff = math.exp(-0.5 * i * i / (sigma * sigma));
76 | table.insert( forLoop, ( norm > 0 and "\t" or "" ) ..
77 | string.format( line, i, coeff )
78 | )
79 | norm = norm + coeff;
80 | end
81 | table.insert( forLoop, "\tacc *= 1/" .. norm .. ";\r\n" )
82 |
83 | local fragmentShader = [[
84 | uniform vec2 dir;
85 | vec4 effect( vec4 color, Image tex, vec2 texcoord, vec2 pixcoord )
86 | {
87 | vec2 loc = texcoord;
88 | vec4 acc = vec4( 0.0 );
89 | ]] .. table.concat( forLoop, "\r\n" ) .. [[
90 | return acc;
91 | }
92 | ]]
93 | self.shader = love.graphics.newShader( fragmentShader )
94 | end
95 |
96 | shader.register( gaussianblur, "gaussianblur" )
97 |
--------------------------------------------------------------------------------
/shaders/shader.lua:
--------------------------------------------------------------------------------
1 | --=========== Copyright © 2019, Planimeter, All rights reserved. ===========--
2 | --
3 | -- Purpose: Shader class
4 | --
5 | --==========================================================================--
6 |
7 | class( "shader" )
8 |
9 | shader._shaders = shader._shaders or {}
10 |
11 | function shader.getShader( name, width, height )
12 | return shader._shaders[ name ]( width, height )
13 | end
14 |
15 | function shader.register( class, name )
16 | shader._shaders[ name ] = class
17 | getfenv( 2 )[ name ] = nil
18 | end
19 |
20 | function shader:shader( width, height )
21 | end
22 |
23 | function shader:renderTo( func )
24 | end
25 |
26 | function shader:draw()
27 | end
28 |
29 | function shader:set( key, value )
30 | end
31 |
32 | function shader:__tostring()
33 | local t = getmetatable( self )
34 | setmetatable( self, {} )
35 | local s = string.gsub( tostring( self ), "table", "shader" )
36 | setmetatable( self, t )
37 | return s
38 | end
39 |
--------------------------------------------------------------------------------
/shaders/stroke.frag:
--------------------------------------------------------------------------------
1 | //=========== Copyright © 2019, Planimeter, All rights reserved. =============//
2 | //
3 | // Purpose:
4 | //
5 | //============================================================================//
6 |
7 | uniform vec2 resolution;
8 | uniform float width;
9 |
10 | const int samples = 20;
11 | const float pi = 3.1415926535898f;
12 |
13 | vec4 effect( vec4 color, Image tex, vec2 texcoord, vec2 pixcoord )
14 | {
15 | // Stroke
16 | float alpha = 0.0f;
17 | float angle = 0.0f;
18 | for ( int i = 0; i < samples; i++ )
19 | {
20 | angle += 1.0f / ( float( samples ) / 2.0f ) * pi;
21 |
22 | float x = ( width / resolution.x ) * cos( angle );
23 | float y = ( width / resolution.y ) * sin( angle );
24 | vec2 offset = vec2( x, y );
25 |
26 | float sampleAlpha = Texel( tex, texcoord + offset ).a;
27 | alpha = max( alpha, sampleAlpha );
28 | }
29 |
30 | // Texture
31 | vec4 FragColor = color * alpha;
32 | vec4 texel = Texel( tex, texcoord );
33 | FragColor = mix( FragColor, texel, texel.a );
34 | return FragColor;
35 | }
36 |
--------------------------------------------------------------------------------
/sounds/footsteps/grassleft.lua:
--------------------------------------------------------------------------------
1 | return {
2 | source = "sounds/footsteps/grassleft.wav",
3 | volume = 0.25
4 | }
5 |
--------------------------------------------------------------------------------
/sounds/footsteps/grassleft.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/sounds/footsteps/grassleft.wav
--------------------------------------------------------------------------------
/sounds/footsteps/grassright.lua:
--------------------------------------------------------------------------------
1 | return {
2 | source = "sounds/footsteps/grassright.wav",
3 | volume = 0.25
4 | }
5 |
--------------------------------------------------------------------------------
/sounds/footsteps/grassright.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Planimeter/game-engine-2d/e8fc46be351dd75a7849d6e9e40fdca59f687c80/sounds/footsteps/grassright.wav
--------------------------------------------------------------------------------