· 8 years ago · May 05, 2018, 03:46 AM
1local VERSION = 99.99
2
3// Disable the server bit by default, it sucks.
4local SERVER_ENABLED = true
5if ( SERVER && SERVER_ENABLED ) then
6 // Send the main files to the client.
7 AddCSLuaFile( "achievements.lua" )
8 AddCSLuaFile( "client/usermessage_hook.lua" )
9
10 // Send the achievement files to the client.
11 for _, achievement in pairs( file.FindInLua( "achievements/*.lua" ) ) do
12 AddCSLuaFile( "achievements/" .. achievement )
13 end
14
15 // Add materials and sounds to resource.
16 for _, mat in pairs( file.Find( "../materials/achievements/*" ) ) do
17 resource.AddFile( "materials/achievements/" .. mat )
18 end
19 for _, sound in pairs( file.Find( "../sound/achievements/*" ) ) do
20 resource.AddFile( "sound/achievements/" .. sound )
21 end
22
23 return
24end
25if ( SERVER ) then return end
26
27local options
28local function Options()
29 if ( options ) then
30 options:Remove()
31 end
32
33 options = vgui.Create( "DFrame" )
34 options:SetWide( 200 )
35 options:SetTitle( "Options" )
36 options:MakePopup()
37
38 local y = 30
39
40 // #########################
41 // Key choice.
42 // #########################
43 local choosekey = vgui.Create( "DMultiChoice", options )
44 choosekey:SetPos( 75, y )
45 choosekey:SetWide( 110 )
46 choosekey:SetTooltip( "The key that will open the achievements window.\nIf an invalid key is entered, it will default to F4." )
47 choosekey:AddChoice( "F7" )
48 choosekey:AddChoice( "F8" )
49 choosekey:AddChoice( "F10" )
50 choosekey:AddChoice( "F11" )
51 choosekey:AddChoice( "F12" )
52 choosekey:AddChoice( "G" )
53 choosekey:AddChoice( "H" )
54 choosekey:AddChoice( "B" )
55
56 // H4X! Makes anything typed turn upper case.
57 local te = choosekey.TextEntry
58 te.OriginalKeyCodeTyped = te.OnKeyCodeTyped
59 function te:OnKeyCodeTyped( val )
60 self:OriginalKeyCodeTyped( val )
61 timer.Simple( 0, function()
62 self:SetValue( string.upper( self:GetValue() ) )
63 end )
64 end
65 choosekey:SetConVar( "achievements_opentype" )
66
67 choosekeyLabel = vgui.Create( "DLabel", options )
68 choosekeyLabel:SetPos( 15, y )
69 choosekeyLabel:SetSize( 55, choosekey:GetTall() )
70 choosekeyLabel:SetText( "Open with:" )
71
72 y = y + choosekey:GetTall() + 5
73
74 // #########################
75 // Chat message.
76 // #########################
77 local chatmessageLabel = vgui.Create( "DLabel", options )
78 chatmessageLabel:SetPos( 15, y )
79 chatmessageLabel:SetText( "Chat message:" )
80 chatmessageLabel:SetWide( 170 )
81 y = y + chatmessageLabel:GetTall()
82
83 local chatmessage = vgui.Create( "DTextEntry", options )
84 chatmessage:SetPos( 15, y )
85 chatmessage:SetWide( 170 )
86 chatmessage:SetConVar( "achievements_chatmessage_fix" )
87 chatmessage:SetTooltip( "The message to broadcast when an achievement is gained.\n%achievement% will be replaced with the achievements name.\nLeave blank for no message." )
88 y = y + chatmessage:GetTall() + 10
89
90 // #########################
91 // Effect.
92 // #########################
93 local effect = vgui.Create( "DCheckBoxLabel", options )
94 effect:SetPos( 15, y )
95 effect:SetWide( 170 )
96 effect:SetText( "Confetti effect" )
97 effect:SetConVar( "achievements_effect" )
98 y = y + effect:GetTall() + 5
99
100 // #########################
101 // Autosave.
102 // #########################
103 local autosave = vgui.Create( "DCheckBoxLabel", options )
104 autosave:SetPos( 15, y )
105 autosave:SetWide( 170 )
106 autosave:SetText( "Autosave" )
107 autosave:SetConVar( "achievements_autosave" )
108 y = y + autosave:GetTall() + 5
109
110 // #########################
111 // Close button.
112 // #########################
113 y = y + 5
114 local close = vgui.Create( "DButton", options )
115 close:SetPos( 15, y )
116 close:SetWide( 170 )
117 close:SetText( "Close" )
118 close.DoClick = function() options:Remove() end
119 y = y + close:GetTall() + 5
120
121 // Make the window the right size, and in the right place.
122 y = y + 5
123 options:SetTall( y )
124 options:Center()
125end
126
127include( "client/usermessage_hook.lua" )
128
129surface.CreateFont( "Default", 12, 700, false, false, "Default12B" )
130surface.CreateFont( "Default", 13, 700, false, false, "Default13B" )
131surface.CreateFont( "Default", 14, 700, true, false, "Default14BA" )
132
133// ##################################################
134// Unlocked Popup
135// ##################################################
136
137local PANEL = {}
138function PANEL:Init()
139 self.Offset = 0
140 self.Direction = 1
141 self.Speed = 3
142 self.Slot = 1
143 self.Text = ""
144
145 surface.PlaySound( Sound( "achievements/achievement_earned.mp3" ) )
146end
147function PANEL:SetAchievement( text, image )
148 self.Text = text
149 self.Image = image or "achievements/generic"
150
151 self.Material = Material( self.Image )
152 if ( !self.Material ) then self.Image = nil end
153end
154function PANEL:SetSlot( slot )
155 self.Slot = slot
156end
157function PANEL:Think()
158 self.Offset = math.Clamp( self.Offset + ( self.Direction * FrameTime() * self.Speed ), 0, 1 )
159 self:InvalidateLayout()
160
161 if ( self.Direction == 1 && self.Offset == 1 ) then
162 self.Direction = 0
163 self.Down = CurTime() + 5
164 end
165 if ( self.Down != nil && CurTime() > self.Down ) then
166 self.Direction = -1
167 self.Down = nil
168 end
169 if ( self.Offset == 0 ) then
170 self.Removed = true
171 end
172end
173function PANEL:PerformLayout()
174 local w, h = 240, 94
175
176 self:SetSize( w, h )
177 self:SetPos( ScrW() - w, ScrH() - ( h * self.Offset * self.Slot ) )
178end
179
180function PANEL:Paint()
181 local w, h = self:GetWide(), self:GetTall()
182 local a = self.Offset * 255
183
184 surface.SetDrawColor( 47, 49, 45, a )
185 surface.DrawRect( 0, 0, w, h )
186
187 surface.SetDrawColor( 104, 106, 101, a )
188 surface.DrawOutlinedRect( 0, 0, w, h )
189
190 surface.SetDrawColor( 255, 255, 255, a )
191
192 if ( self.Image ) then
193 surface.SetMaterial( self.Material )
194 surface.DrawTexturedRect( 14, 14, 64, 64 )
195
196 surface.SetDrawColor( 70, 70, 70, a )
197 surface.DrawOutlinedRect( 13, 13, 66, 66 )
198 end
199
200 draw.DrawText( "Achievement Unlocked!", "Default12B", 88, 30, Color( 255, 255, 255, a ), TEXT_ALIGN_LEFT )
201 draw.DrawText( self.Text, "Default12B", 88, 46, Color( 216, 222, 211, a ), TEXT_ALIGN_LEFT )
202
203end
204vgui.Register( "rt_achievement_popup", PANEL )
205
206// ##################################################
207// Progress bar
208// ##################################################
209
210local function ToValues( col )
211 if ( !col ) then return 255, 255, 255, 255 end
212 return col.r, col.g, col.b, col.a
213end
214
215local PANEL = {}
216function PANEL:Init()
217 self.Percent = 0
218 self.Foreground = Color( 255, 255, 255 )
219end
220function PANEL:SetColour( fore )
221 self.Fore = fore
222end
223function PANEL:SetPercent( percent )
224 while ( percent > 1 ) do percent = percent / 100 end
225 self.Percent = percent
226end
227function PANEL:Paint()
228 local w, h = self:GetWide(), self:GetTall()
229 surface.SetDrawColor( 62, 62, 62, 255 )
230 surface.DrawRect( 0, 0, w, h )
231
232 surface.SetDrawColor( self.Fore.r, self.Fore.g, self.Fore.b, self.Fore.a )
233 surface.DrawRect( 0, 0, w * self.Percent, h )
234end
235vgui.Register( "rt_achievement_progress", PANEL )
236
237// ##################################################
238// Total progress bar
239// ##################################################
240
241local PANEL = {}
242function PANEL:Init()
243 self.Text = "0/0"
244 self.Percent = 100
245
246 self.Bar = vgui.Create( "rt_achievement_progress", self )
247 self.Bar:SetColour( Color( 158, 195, 79 ) )
248end
249function PANEL:PerformLayout()
250 local w, h = self:GetWide(), self:GetTall()
251 self.Bar:SetPos( 8, h - 25 )
252 self.Bar:SetSize( w - 16, 16 )
253end
254function PANEL:SetEarned( earned, total )
255 self.Text = earned .. "/" .. total
256
257 if ( total > 0 ) then
258 self.Percent = math.floor( ( earned / total ) * 100 )
259 else
260 self.Percent = 0
261 end
262
263 self.Bar:SetPercent( self.Percent / 100 )
264end
265function PANEL:Paint()
266 local w, h = self:GetWide(), self:GetTall()
267
268 surface.SetDrawColor( 26, 26, 26, 255 )
269 surface.DrawRect( 0, 0, w, h )
270
271 draw.SimpleText( "Achievements Earned:", "Default13B", 8, 7, Color( 217, 217, 217 ) )
272 draw.SimpleText( self.Text .. " ( " .. self.Percent .. "% )", "Default13B", w - 10, 7, Color( 217, 217, 217 ), TEXT_ALIGN_RIGHT )
273end
274vgui.Register( "rt_achievement_progress_total", PANEL )
275
276// ##################################################
277// Achievement Info
278// ##################################################
279local PANEL = {}
280function PANEL:Init()
281 self.Name, self.Description, self.Image, self.Percent, self.PercentText = "", "", "", 1, ""
282
283 self.LName = vgui.Create( "DLabel", self )
284 self.LName:SetFont( "Default14BA" )
285 self.LName:SetTextColor( Color( 158, 195, 79 ) )
286 self.LName:SetPos( 71, 5 )
287
288 self.LDesc = vgui.Create( "DLabel", self )
289 self.LDesc:SetFont( "Default13B" )
290 self.LDesc:SetTextColor( Color( 217, 217, 217 ) )
291 self.LDesc:SetPos( 72, 22 )
292
293 self.LPercent = vgui.Create( "DLabel", self )
294 self.LPercent:SetFont( "Default13B" )
295 self.LPercent:SetTextColor( Color( 210, 210, 210 ) )
296
297 self.Bar = vgui.Create( "rt_achievement_progress", self )
298 self.Bar:SetColour( Color( 201, 185, 149 ) )
299end
300function PANEL:PerformLayout()
301 local w, h = self:GetWide(), self:GetTall()
302 self.Bar:SetPos( 70, h - 22 )
303 self.Bar:SetSize( w - 200, 12 )
304
305 self.LName:SetSize( w, 15 )
306 self.LDesc:SetSize( w, 15 )
307
308 self.LPercent:SetSize( w, 15 )
309 self.LPercent:SetPos( self.Bar.X + self.Bar:GetWide() + 10, h - 24 )
310end
311function PANEL:Setup( name, desc, image, percent, percentText )
312 self.Name, self.Description, self.Image, self.Percent, self.PercentText = name, desc, image or "", percent, percentText or ""
313 self.Percent = math.Clamp( self.Percent, 0, 1 )
314
315 self.LName:SetText( name )
316 self.LDesc:SetText( desc )
317 self.LPercent:SetText( percentText or "" )
318
319 if ( self.Image != "" ) then
320 self.Material = Material( self.Image )
321 else
322 self.Material = nil
323 end
324
325 self.Bar:SetVisible( percentText != "" )
326 self.Bar:SetPercent( self.Percent )
327end
328function PANEL:Paint()
329 local w, h = self:GetWide(), self:GetTall()
330
331 if ( self.Percent == 1 ) then
332 draw.RoundedBox( 4, 0, 0, w, h, Color( 78, 78, 78 ) )
333 else
334 draw.RoundedBox( 4, 0, 0, w, h, Color( 56, 56, 56 ) )
335 end
336
337 if ( !self.Material ) then return end
338 surface.SetDrawColor( 255, 255, 255, 255 )
339 surface.SetMaterial( self.Material )
340 surface.DrawTexturedRect( 4, 4, 56, 56 )
341end
342vgui.Register( "rt_achievement_info", PANEL )
343
344// ##################################################
345// Main Window
346// ##################################################
347
348local PANEL = {}
349function PANEL:Init()
350 self:SetTitle( "My Achievements" )
351 self:MakePopup()
352 self:SetVisible( false )
353 self:SetDeleteOnClose( false )
354
355 self.Bar = vgui.Create( "rt_achievement_progress_total", self )
356 self.List = vgui.Create( "DPanelList", self )
357 self.List:EnableVerticalScrollbar()
358 self.List:SetPadding( 4 )
359 self.List:SetSpacing( 4 )
360
361 self.CloseB = vgui.Create( "DButton", self )
362 self.CloseB:SetText( "Close" )
363 self.CloseB.DoClick = function() self:SetVisible( false ) end
364
365 self.Options = vgui.Create( "DButton", self )
366 self.Options:SetText( "Options" )
367 self.Options.DoClick = function() Options( self ) end
368
369 self:SetSize( math.floor( ScrW() * 0.6 ), math.floor( ScrH() * 0.6 ) )
370 self:Center()
371
372 self.CCAchievements = {}
373end
374function PANEL:PerformLayout()
375 local w, h = self:GetWide(), self:GetTall()
376
377 self.Bar:SetPos( 15, 40 )
378 self.Bar:SetSize( w - 30, 50 )
379
380 self.List:SetPos( 15, 100 )
381 self.List:SetSize( w - 30, h - 140 )
382
383 self.Options:SetPos( 15, h - 34 )
384 self.Options:SetSize( 70, 24 )
385
386 self.CloseB:SetPos( w - 85, h - 34 )
387 self.CloseB:SetSize( 70, 24 )
388
389 for _, ach in pairs( self.CCAchievements or {} ) do
390 ach.Panel:SetSize( self.List:GetWide() - 18, 64 )
391 end
392 self.List:InvalidateLayout()
393
394 self.BaseClass.PerformLayout( self )
395end
396function PANEL:CCSortAchievements()
397 local tab = {}
398 for name, _ in pairs( self.CCAchievements ) do
399 tab[ #tab + 1 ] = name
400 end
401 table.sort( tab )
402
403 self.List:Clear()
404 for _, name in pairs( tab ) do
405 local info = self.CCAchievements[ name ]
406 if ( info && info.Panel ) then
407 self.List:AddItem( info.Panel )
408 end
409 end
410 self:InvalidateLayout()
411end
412function PANEL:CCSetupAchievement( name, desc, image, percent, percentText )
413 percent = math.Clamp( percent, 0, 1 )
414
415 local panel
416 if ( self.CCAchievements[ name ] == nil ) then
417 panel = vgui.Create( "rt_achievement_info", self )
418 self.List:AddItem( panel )
419 self:InvalidateLayout()
420 else
421 panel = self.CCAchievements[ name ].Panel
422 end
423 self.CCAchievements[ name ] = { Desc = desc, Image = image, Percent = percent, Panel = panel, Unlocked = percent == 1 }
424
425 self:CCUpdateAchievement( name, percent, percentText )
426end
427function PANEL:CCUpdateAchievement( name, percent, percentText )
428 if ( self.CCAchievements[ name ] == nil ) then return false end
429
430 local ach = self.CCAchievements[ name ]
431 ach.Percent = percent
432 ach.Panel:Setup( name, ach.Desc, ach.Image, percent, percentText )
433
434 if ( !ach.Unlocked && percent >= 1 ) then
435 local popup = vgui.Create( "rt_achievement_popup" )
436 popup:SetAchievement( name, ach.Image )
437 ach.Unlocked = true
438
439 if ( GetConVarNumber( "achievements_effect" ) > 0 ) then
440 local ed = EffectData()
441 ed:SetEntity(LocalPlayer())
442 util.Effect("achievement",ed, true, true)
443 end
444
445 local say = GetConVarString( "achievements_chatmessage_fix" )
446 if ( string.Trim( say ) != "" ) then
447 RunConsoleCommand( "say", string.Replace( say, "%achievement%", name ) )
448 end
449 end
450
451 local total = 0
452 for _, ach in pairs( self.CCAchievements ) do
453 if ( ach.Percent == 1 ) then total = total + 1 end
454 end
455 self.Bar:SetEarned( total, table.Count( self.CCAchievements ) )
456
457 self.CCTotalAchievements = table.Count( self.CCAchievements )
458end
459vgui.Register( "rt_achievements", PANEL, "DFrame" )
460
461
462// ##################################################
463// END OF VGUI
464// ##################################################
465
466local function Initialize()
467 local menu = vgui.Create( "rt_achievements" )
468
469 CCachievements = {}
470 function CCachievements.Register( name, desc, image, percent, percentText )
471 menu:CCSetupAchievement( name, desc, image, percent, percentText )
472 end
473 function CCachievements.GetNumber()
474 return ( menu.CCTotalAchievements or 0 )
475 end
476 function CCachievements.IsGamemode( name )
477 local gm = gmod.GetGamemode()
478 if ( !gm ) then gm = GAMEMODE end
479
480 if ( string.find( gm.Name or "", name ) ) then
481 return true
482 end
483
484 return false
485 end
486
487 CreateConVar( "achievements_chatmessage_fix", "I have earned the '%achievement%' achievement!", FCVAR_ARCHIVE )
488 CreateConVar( "achievements_effect", 1, FCVAR_ARCHIVE )
489 function CCachievements.Update( name, percent, percentText )
490 menu:CCUpdateAchievement( name, percent, percentText )
491 end
492 function CCachievements.Sort()
493 menu:CCSortAchievements()
494 end
495 function CCachievements.FormatTime( time, format )
496 local gah = { { "h", 3600 }, { "m", 60 }, { "s", 1 } }
497 for _, t in pairs( gah ) do
498 local amount = math.floor( time / t[ 2 ] )
499 format = string.Replace( format, t[ 1 ], string.rep( "0", 2 - string.len( amount ) ) .. amount )
500 time = time % t[ 2 ]
501 end
502 return format
503 end
504
505 // Create the table for saving values in.
506 if ( !sql.TableExists( "CCachievements" ) ) then
507 sql.Query( "CREATE TABLE IF NOT EXISTS CCachievements ( infoid TEXT NOT NULL PRIMARY KEY, value TEXT );" )
508 end
509
510 CCachievements.Loaded = {}
511 CCachievements.ToSave = {}
512 function CCachievements.SetValue( achievement, name, value )
513 if ( !CCachievements.FinishedLoading ) then return end
514 local name = achievement .. "." .. name
515
516 CCachievements.Loaded[ name ] = value
517 CCachievements.ToSave[ name ] = value
518 end
519 function CCachievements.GetValue( achievement, name, default )
520 local name = achievement .. "." .. name
521
522 if ( CCachievements.Loaded[ name ] == nil ) then
523 local val = sql.QueryValue( "SELECT value FROM CCachievements WHERE infoid = " .. SQLStr( name ) )
524 if ( val == nil ) then
525 val = default
526 elseif ( tonumber( val ) != nil ) then
527 val = tonumber( val )
528 end
529
530 CCachievements.Loaded[ name ] = val
531 return val
532 else
533 return CCachievements.Loaded[ name ]
534 end
535 end
536
537 CreateConVar( "achievements_autosave", 1 )
538 function CCachievements.SaveValues( autosave )
539 if ( autosave == true ) then
540 if ( GetConVarNumber( "achievements_autosave" ) <= 0 ) then return end
541 end
542
543 print( "[ACHIEVEMENTS ADD-ON] Saving achievements..." )
544 sql.Begin()
545 for name, value in pairs( CCachievements.ToSave ) do
546 sql.Query( "DELETE FROM CCachievements WHERE infoid = " .. SQLStr( name ) )
547 sql.Query( "INSERT INTO CCachievements ( infoid, value ) VALUES ( " .. SQLStr( name ) .. ", " .. SQLStr( value ) .. " )" )
548 end
549 sql.Commit()
550 end
551 concommand.Add( "achievements_save", CCachievements.SaveValues )
552 timer.Create( "achievements.SaveValues", 300, 0, CCachievements.SaveValues, true )
553 hook.Add( "ShutDown", "Achievements.SaveValues", CCachievements.SaveValues )
554
555 local chatOpen = false
556 hook.Add( "StartChat", "Achievements.StartChat", function() chatOpen = true end )
557 hook.Add( "FinishChat", "Achievements.FinishChat", function() chatOpen = false end )
558
559 // F4 bind.
560 CreateConVar( "achievements_opentype", "F4", FCVAR_ARCHIVE )
561 local function CheckKeys()
562 if ( menu:IsVisible() ) then return end
563
564 // If the chat's open we don't want anything triggering when we type.
565 if ( chatOpen ) then
566 if ( CCachievements.KEY && ( CCachievements.KEY < KEY_F1 || CCachievements.KEY > KEY_F12 ) ) then return end
567 end
568
569 RunString( "CCachievements.KEY = KEY_" .. string.upper( GetConVarString( "achievements_opentype" ) ) )
570 if ( input.IsKeyDown( CCachievements.KEY or KEY_F4 ) ) then
571 RunConsoleCommand( "CCachievements" )
572 return
573 end
574 end
575 timer.Create( "Achievements.CheckKeys", 0.1, 0, CheckKeys )
576
577 CreateConVar( "achievements_updatenag", 1, FCVAR_ARCHIVE )
578 local checkedForUpdate = false
579 local function ShowMenu()
580 if ( !checkedForUpdate && GetConVarNumber( "achievements_updatenag" ) > 0 ) then
581 // Check if it's time to nag about version again...
582 local day = os.date( "%j" )
583 local lastNagged = file.Read( "achievements/updatenag.txt" ) or ""
584 if ( lastNagged != day ) then
585 file.Write( "achievements/updatenag.txt", day )
586 end
587 checkedForUpdate = true
588 end
589
590 menu:InvalidateLayout()
591 menu:SetVisible( true )
592 end
593 concommand.Add( "CCachievements", ShowMenu )
594
595 local function Reload()
596 CCachievements.SaveValues()
597 if ( menu ) then menu:Remove() end
598
599 print( "[ACHIEVEMENTS ADD-ON]Reloading achievements..." )
600 include( "autorun/achievements.lua" )
601 end
602 concommand.Add( "achievements_reload", Reload )
603
604 local key
605 local function Reset( _, _, args )
606 if ( !key ) then
607 key = ""
608 for _ = 1, 6 do
609 key = key .. string.char( math.random( 97, 122 ) )
610 end
611 print( "Are you sure?\nEnter 'achievements_reset " .. key .. "' to confirm." )
612 elseif ( ( args[ 1 ] or "" ) == key ) then
613 print( "[ACHIEVEMENTS ADD-ON]Resetting achievements..." )
614 sql.Query( "DROP TABLE CCachievements" )
615 Reload()
616 key = nil
617 else
618 print( "Incorrect key. Please try again." )
619 key = nil
620 end
621 end
622 concommand.Add( "achievements_reset", Reset )
623
624 if ( !file.Exists( "achievements/convertedtosqlite.txt" ) ) then
625 include( "text_to_sqlite.lua" )
626 file.Write( "achievements/convertedtosqlite.txt", "1" )
627 end
628
629 // Include all the achievement files.
630 local files = file.FindInLua( "achievements/*.lua" )
631 table.sort( files ) // We do this so they stay in the same order.
632
633 print( "[ACHIEVEMENTS ADD-ON]Loading achievements..." )
634 local delay, n = 0.05, 0
635 for i, ach in ipairs( files ) do
636 timer.Simple( i * delay, include, "achievements/" .. ach )
637 n = i
638 end
639 timer.Simple( n * delay, function()
640 print( "Completed " .. CCachievements.GetNumber() .. " achievements [add-on] loaded!" )
641 CCachievements.Sort()
642 CCachievements.FinishedLoading = true
643 end )
644
645end
646hook.Add( "Initialize", "Achievements.Initialize", Initialize )
647
648// We've already loaded before.
649if ( CCachievements ) then Initialize() end