├── .gitignore
├── Overlays
└── Fancy_Detail_Overlay.zip
├── PercentilePlugin
├── PercentilePlugin.Parser
│ ├── App.config
│ ├── PercentilePlugin.Parser.csproj
│ ├── PercentilePlugin.Parser.csproj.DotSettings
│ ├── Program.cs
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ └── packages.config
├── PercentilePlugin.Shared
│ ├── ClassJob.cs
│ ├── DateTimeExtension.cs
│ ├── Encounter.cs
│ ├── Instance.cs
│ ├── PercentileData.cs
│ ├── PercentilePlugin.Shared.csproj
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ └── packages.config
├── PercentilePlugin.sln
└── PercentilePlugin
│ ├── Logger.cs
│ ├── Options.cs
│ ├── PercentilePlugin.cs
│ ├── PercentilePlugin.csproj
│ ├── PercentileUi.Designer.cs
│ ├── PercentileUi.cs
│ ├── PercentileUi.resx
│ ├── Properties
│ └── AssemblyInfo.cs
│ ├── VersionChecker.cs
│ ├── app.config
│ └── packages.config
├── README.md
└── parsedata.bin
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 | [Ll]og/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | project.fragment.lock.json
46 | artifacts/
47 |
48 | *_i.c
49 | *_p.c
50 | *_i.h
51 | *.ilk
52 | *.meta
53 | *.obj
54 | *.pch
55 | *.pdb
56 | *.pgc
57 | *.pgd
58 | *.rsp
59 | *.sbr
60 | *.tlb
61 | *.tli
62 | *.tlh
63 | *.tmp
64 | *.tmp_proj
65 | *.log
66 | *.vspscc
67 | *.vssscc
68 | .builds
69 | *.pidb
70 | *.svclog
71 | *.scc
72 |
73 | # Chutzpah Test files
74 | _Chutzpah*
75 |
76 | # Visual C++ cache files
77 | ipch/
78 | *.aps
79 | *.ncb
80 | *.opendb
81 | *.opensdf
82 | *.sdf
83 | *.cachefile
84 | *.VC.db
85 | *.VC.VC.opendb
86 |
87 | # Visual Studio profiler
88 | *.psess
89 | *.vsp
90 | *.vspx
91 | *.sap
92 |
93 | # TFS 2012 Local Workspace
94 | $tf/
95 |
96 | # Guidance Automation Toolkit
97 | *.gpState
98 |
99 | # ReSharper is a .NET coding add-in
100 | _ReSharper*/
101 | *.[Rr]e[Ss]harper
102 | *.DotSettings.user
103 |
104 | # JustCode is a .NET coding add-in
105 | .JustCode
106 |
107 | # TeamCity is a build add-in
108 | _TeamCity*
109 |
110 | # DotCover is a Code Coverage Tool
111 | *.dotCover
112 |
113 | # NCrunch
114 | _NCrunch_*
115 | .*crunch*.local.xml
116 | nCrunchTemp_*
117 |
118 | # MightyMoose
119 | *.mm.*
120 | AutoTest.Net/
121 |
122 | # Web workbench (sass)
123 | .sass-cache/
124 |
125 | # Installshield output folder
126 | [Ee]xpress/
127 |
128 | # DocProject is a documentation generator add-in
129 | DocProject/buildhelp/
130 | DocProject/Help/*.HxT
131 | DocProject/Help/*.HxC
132 | DocProject/Help/*.hhc
133 | DocProject/Help/*.hhk
134 | DocProject/Help/*.hhp
135 | DocProject/Help/Html2
136 | DocProject/Help/html
137 |
138 | # Click-Once directory
139 | publish/
140 |
141 | # Publish Web Output
142 | *.[Pp]ublish.xml
143 | *.azurePubxml
144 | # TODO: Comment the next line if you want to checkin your web deploy settings
145 | # but database connection strings (with potential passwords) will be unencrypted
146 | #*.pubxml
147 | *.publishproj
148 |
149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
150 | # checkin your Azure Web App publish settings, but sensitive information contained
151 | # in these scripts will be unencrypted
152 | PublishScripts/
153 |
154 | # NuGet Packages
155 | *.nupkg
156 | # The packages folder can be ignored because of Package Restore
157 | **/packages/*
158 | # except build/, which is used as an MSBuild target.
159 | !**/packages/build/
160 | # Uncomment if necessary however generally it will be regenerated when needed
161 | #!**/packages/repositories.config
162 | # NuGet v3's project.json files produces more ignoreable files
163 | *.nuget.props
164 | *.nuget.targets
165 |
166 | # Microsoft Azure Build Output
167 | csx/
168 | *.build.csdef
169 |
170 | # Microsoft Azure Emulator
171 | ecf/
172 | rcf/
173 |
174 | # Windows Store app package directories and files
175 | AppPackages/
176 | BundleArtifacts/
177 | Package.StoreAssociation.xml
178 | _pkginfo.txt
179 |
180 | # Visual Studio cache files
181 | # files ending in .cache can be ignored
182 | *.[Cc]ache
183 | # but keep track of directories ending in .cache
184 | !*.[Cc]ache/
185 |
186 | # Others
187 | ClientBin/
188 | ~$*
189 | *~
190 | *.dbmdl
191 | *.dbproj.schemaview
192 | *.jfm
193 | *.pfx
194 | *.publishsettings
195 | node_modules/
196 | orleans.codegen.cs
197 |
198 | # Since there are multiple workflows, uncomment next line to ignore bower_components
199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
200 | #bower_components/
201 |
202 | # RIA/Silverlight projects
203 | Generated_Code/
204 |
205 | # Backup & report files from converting an old project file
206 | # to a newer Visual Studio version. Backup files are not needed,
207 | # because we have git ;-)
208 | _UpgradeReport_Files/
209 | Backup*/
210 | UpgradeLog*.XML
211 | UpgradeLog*.htm
212 |
213 | # SQL Server files
214 | *.mdf
215 | *.ldf
216 |
217 | # Business Intelligence projects
218 | *.rdl.data
219 | *.bim.layout
220 | *.bim_*.settings
221 |
222 | # Microsoft Fakes
223 | FakesAssemblies/
224 |
225 | # GhostDoc plugin setting file
226 | *.GhostDoc.xml
227 |
228 | # Node.js Tools for Visual Studio
229 | .ntvs_analysis.dat
230 |
231 | # Visual Studio 6 build log
232 | *.plg
233 |
234 | # Visual Studio 6 workspace options file
235 | *.opt
236 |
237 | # Visual Studio LightSwitch build output
238 | **/*.HTMLClient/GeneratedArtifacts
239 | **/*.DesktopClient/GeneratedArtifacts
240 | **/*.DesktopClient/ModelManifest.xml
241 | **/*.Server/GeneratedArtifacts
242 | **/*.Server/ModelManifest.xml
243 | _Pvt_Extensions
244 |
245 | # Paket dependency manager
246 | .paket/paket.exe
247 | paket-files/
248 |
249 | # FAKE - F# Make
250 | .fake/
251 |
252 | # JetBrains Rider
253 | .idea/
254 | *.sln.iml
255 |
256 | # CodeRush
257 | .cr/
258 |
259 | # Python Tools for Visual Studio (PTVS)
260 | __pycache__/
261 | *.pyc
--------------------------------------------------------------------------------
/Overlays/Fancy_Detail_Overlay.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Liquidize/FFXIV_PercentilePlugin/611d147b8c828d3f45d1d84a9c88c322e51563c8/Overlays/Fancy_Detail_Overlay.zip
--------------------------------------------------------------------------------
/PercentilePlugin/PercentilePlugin.Parser/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/PercentilePlugin/PercentilePlugin.Parser/PercentilePlugin.Parser.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {7D0A1B52-778B-4897-91BB-FB4967439A66}
8 | Exe
9 | PercentilePlugin
10 | PercentilePlugin.Parser
11 | v4.6.1
12 | 512
13 |
14 |
15 |
16 | AnyCPU
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 | 7.1
25 |
26 |
27 | AnyCPU
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 | 7.1
35 |
36 |
37 |
38 | ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll
39 |
40 |
41 | ..\packages\Newtonsoft.Json.Bson.1.0.1\lib\net45\Newtonsoft.Json.Bson.dll
42 |
43 |
44 | ..\packages\NLog.4.5.10\lib\net45\NLog.dll
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | {76c3faf6-98dd-48c1-808b-30a9f879e5a7}
71 | PercentilePlugin.Shared
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/PercentilePlugin/PercentilePlugin.Parser/PercentilePlugin.Parser.csproj.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | CSharp71
--------------------------------------------------------------------------------
/PercentilePlugin/PercentilePlugin.Parser/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Net;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 | using Newtonsoft.Json;
9 | using Newtonsoft.Json.Bson;
10 | using Newtonsoft.Json.Linq;
11 | using NLog;
12 | using NLog.Config;
13 | using NLog.Targets;
14 | using PercentilePlugin.Shared;
15 |
16 | namespace PercentilePlugin
17 | {
18 | internal class Program
19 | {
20 | public static Logger Logger;
21 |
22 | private static List jobs;
23 | private static List instances;
24 | private static PercentileData percentileData;
25 |
26 | private static readonly string APIKey = "";
27 |
28 | public static async Task Main(string[] args)
29 | {
30 | var config = new LoggingConfiguration();
31 | var consoleTarget = new ColoredConsoleTarget("target1")
32 | {
33 | Layout = @"${date:format=HH\:mm\:ss} ${level} ${message} ${exception}"
34 | };
35 | config.AddTarget(consoleTarget);
36 |
37 | var fileTarget = new FileTarget("target2")
38 | {
39 | FileName = "${basedir}/file.txt",
40 | Layout = "${longdate} ${level} ${message} ${exception}"
41 | };
42 | config.AddTarget(fileTarget);
43 | config.AddRuleForOneLevel(LogLevel.Error, fileTarget); // only errors to file
44 | config.AddRuleForAllLevels(consoleTarget); // all to console
45 |
46 | LogManager.Configuration = config;
47 |
48 | Logger = LogManager.GetLogger("Main");
49 |
50 | // Backup data before we begin.
51 | if (File.Exists("parsedata.bin"))
52 | {
53 | if (File.Exists("parsedata.bak")) File.Delete("parsedata.bak");
54 | Logger.Log(LogLevel.Info, "Backing Up Parse Data.");
55 | File.Copy("parsedata.bin", "parsedata.bak");
56 | }
57 |
58 | Logger.Log(LogLevel.Info, "Loading Parse Data.");
59 | percentileData = PercentileData.Load("parsedata.bin");
60 | Logger.Log(LogLevel.Info, "Parse Data Loaded, Last Update: " + percentileData.LastUpdated);
61 |
62 |
63 | Logger.Log(LogLevel.Info, "Obtaining Job/Class Data.");
64 | await BuildClasses(APIKey);
65 | Logger.Log(LogLevel.Info, "Job/Class Data Obtained.");
66 | Logger.Log(LogLevel.Info, "Obtaining Zone/Instance Data.");
67 | await BuildInstances(APIKey);
68 | Logger.Log(LogLevel.Info, "Zone/Instance Data Obtained.");
69 | Logger.Log(LogLevel.Info, "Obtaining latest Parse Data.");
70 | await BuildPercentiles();
71 | Logger.Log(LogLevel.Info, "Latest Parse Data Obtained.");
72 | Logger.Log(LogLevel.Info, "Cleaning Up New Data.");
73 |
74 | // Remove Duplicated Entries.
75 | var distinctDictionary =
76 | new Dictionary>>(percentileData.Rankings);
77 |
78 |
79 | Logger.Log(LogLevel.Info, "Removing Unused Fights.");
80 |
81 | // Remove unused fights
82 | foreach (var encounter in distinctDictionary.Keys)
83 | {
84 | var used = false;
85 | foreach (var instance in instances)
86 | if (instance.Encounters.FirstOrDefault(e => e.Value.Name.ToLower() == encounter.ToLower()).Value !=
87 | null)
88 | {
89 | Logger.Log(LogLevel.Debug, encounter + " is used.");
90 | used = true;
91 | }
92 |
93 | if (!used) Logger.Log(LogLevel.Warn, "Encounter: " + encounter + " Is no longer needed.");
94 | }
95 |
96 | Logger.Log(LogLevel.Info, "Unused Fights Removed.");
97 |
98 | percentileData.Rankings = distinctDictionary;
99 |
100 | Logger.Log(LogLevel.Info, "Saving Parse Data.");
101 |
102 | // Save
103 | var file = new FileStream("parsedata.bin", FileMode.OpenOrCreate);
104 | using (var writer = new BsonWriter(file))
105 | {
106 | var serializer = new JsonSerializer();
107 | serializer.Serialize(writer, percentileData);
108 | }
109 | file.Close();
110 | File.WriteAllText("parsejson.json", JsonConvert.SerializeObject(percentileData, Formatting.Indented));
111 | Logger.Log(LogLevel.Info, "Parse Data Saved.");
112 | Console.ReadKey();
113 | }
114 |
115 | public static double GetRealPercentile(double DPS, double[] sequence)
116 | {
117 | if (sequence.Length == 0) return 100;
118 | Array.Sort(sequence);
119 | var l = 0;
120 | var r = sequence.Length - 1;
121 | var index = sequence.Length / 2;
122 |
123 | while (l <= r)
124 | {
125 | index = l + (r - l) / 2;
126 |
127 | if (sequence[index] < DPS)
128 | l = index + 1;
129 | else
130 | r = index - 1;
131 | }
132 |
133 | return (100 * index + sequence.Length) / sequence.Length;
134 | }
135 |
136 | public static async Task BuildClasses(string apiKey)
137 | {
138 | if (jobs != null)
139 | jobs.Clear(); // Safety net that should never be needed.
140 | else
141 | jobs = new List();
142 |
143 | try
144 | {
145 | var request = WebRequest.Create("https://www.fflogs.com:443/v1/classes?api_key=" + apiKey);
146 | var response = await request.GetResponseAsync();
147 | if (response.GetResponseStream() != null)
148 | using (var reader =
149 | new StreamReader(response.GetResponseStream() ?? throw new InvalidOperationException()))
150 | {
151 | var json = reader.ReadToEnd();
152 | var array = JArray.Parse(json);
153 | if (array.HasValues)
154 | {
155 | var obj = array.First;
156 | if (obj.HasValues)
157 | {
158 | var specs = obj["specs"].ToObject();
159 | if (specs.HasValues)
160 | for (var i = 0; i < specs.Count; i++)
161 | {
162 | var job = specs[i];
163 | var cjob = new ClassJob();
164 | cjob.Key = job["id"].ToObject();
165 | cjob.Name = Convert.ToString(job["name"]);
166 | cjob.Abbrveiation = ClassJob.NameToAbbr(cjob.Name).ToUpper();
167 | jobs.Add(cjob);
168 | Logger.Log(LogLevel.Info, string.Format("Parsed Job: {0}.", cjob.Name));
169 | }
170 | }
171 | }
172 | }
173 |
174 | return await Task.FromResult(true);
175 | }
176 | catch (InvalidOperationException invalidOperation)
177 | {
178 | Logger.Log(LogLevel.Fatal, "Invalid Operation Occurred, Response Stream for Jobs is null.");
179 | Logger.Log(LogLevel.Fatal, invalidOperation.ToString());
180 | return await Task.FromResult(false);
181 | }
182 | catch (Exception ex)
183 | {
184 | Logger.Log(LogLevel.Fatal, ex.ToString());
185 | return await Task.FromResult(false);
186 | }
187 | }
188 |
189 | public static async Task BuildInstances(string apiKey)
190 | {
191 | if (instances != null)
192 | instances.Clear(); // safety net that should never be needed
193 | else
194 | instances = new List();
195 |
196 | try
197 | {
198 | var request = WebRequest.Create("https://www.fflogs.com:443/v1/zones?api_key=" + apiKey);
199 | var response = await request.GetResponseAsync();
200 | if (response.GetResponseStream() != null)
201 | using (var reader =
202 | new StreamReader(response.GetResponseStream() ?? throw new InvalidOperationException()))
203 | {
204 | var json = reader.ReadToEnd();
205 | var array = JArray.Parse(json);
206 | if (array.HasValues)
207 | foreach (var category in array)
208 | if (category["frozen"].ToObject() == false &&
209 | category["id"].ToObject() != 2 && category["id"].ToObject() != 14)
210 | foreach (var encounter in category["encounters"])
211 | {
212 | var encObj = new Encounter();
213 | encObj.Name =
214 | category["id"].ToObject() == 21 || category["id"].ToObject() == 25
215 | ? encounter["name"].ToObject() + " (Savage)"
216 | : encounter["name"].ToObject();
217 | encObj.Key = encounter["id"].ToObject();
218 | encObj.Category = category["id"].ToObject();
219 |
220 | var instance = instances.FirstOrDefault(i =>
221 | i.MapName == Instance.InstanceFromBoss(encObj.Name));
222 |
223 | if (instance == null)
224 | {
225 | instance = new Instance();
226 | instance.MapName = Instance.InstanceFromBoss(encObj.Name);
227 | instance.Encounters = new Dictionary();
228 | instances.Add(instance);
229 | }
230 |
231 | instance.Encounters.Add(encObj.Name, encObj);
232 | Logger.Log(LogLevel.Info,
233 | string.Format("Parsed Encounter: {0} From Instance: {1}.", encObj.Name,
234 | instance.MapName));
235 | }
236 | }
237 |
238 | return await Task.FromResult(true);
239 | }
240 | catch (InvalidOperationException invalidOperation)
241 | {
242 | Logger.Log(LogLevel.Fatal, "Invalid Operation Occurred, Response Stream for Instances/Zones is null.");
243 | Logger.Log(LogLevel.Fatal, invalidOperation.ToString());
244 | return await Task.FromResult(false);
245 | }
246 | catch (Exception ex)
247 | {
248 | Logger.Log(LogLevel.Fatal, ex.ToString());
249 | return await Task.FromResult(false);
250 | }
251 | }
252 |
253 | public static async Task BuildPercentiles()
254 | {
255 | try
256 | {
257 | foreach (var job in jobs)
258 | foreach (var instance in instances)
259 | foreach (var enc in instance.Encounters.Values)
260 | {
261 | if (enc.Category == 2) continue;
262 | var ranking = await GetRankingData(job, enc, APIKey);
263 | if (ranking == false)
264 | {
265 | Logger.Log(LogLevel.Error,
266 | "Failed Result: " + job.Name + " Encounter " + enc.Name);
267 | return await Task.FromResult(false);
268 | }
269 | }
270 | }
271 | catch (Exception ex)
272 | {
273 | return await Task.FromResult(false);
274 | }
275 |
276 | percentileData.LastUpdated = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
277 | return await Task.FromResult(true);
278 | }
279 |
280 | public static async Task GetRankingData(ClassJob job, Encounter enc, string apiKey)
281 | {
282 | try
283 | {
284 | Logger.Log(LogLevel.Info,
285 | "Starting Parse Data For: " + job.Name + " Encounter " + enc.Name);
286 |
287 | var currentTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
288 | var startTime = percentileData.LastUpdated != 0
289 | ? percentileData.LastUpdated
290 | : new DateTimeOffset(DateTime.Now.Subtract(TimeSpan.FromDays(500))).ToUnixTimeMilliseconds();
291 | var rankings = new List();
292 | var hasMorePages = true;
293 | var page = 1;
294 | while (hasMorePages)
295 | {
296 | var request = WebRequest.Create(string.Format(
297 | "https://www.fflogs.com:443/v1/rankings/encounter/{0}?spec={1}&page={2}&filter=date.{3}.{4}&api_key=" +
298 | apiKey,
299 | enc.Key, job.Key, page, startTime, currentTime));
300 | request.Timeout = 5000;
301 |
302 | var response = await request.GetResponseAsync();
303 | using (var reader = new StreamReader(response.GetResponseStream()))
304 | {
305 | var json = reader.ReadToEnd();
306 | var rankingObj = JObject.Parse(json);
307 |
308 | if (rankingObj.HasValues)
309 | {
310 | var count = rankingObj["count"].ToObject();
311 | hasMorePages = rankingObj["hasMorePages"].ToObject();
312 | var rankingArray = rankingObj["rankings"].ToObject();
313 | if (rankingArray.HasValues)
314 | foreach (var ranking in rankingArray)
315 | rankings.Add(ranking["total"].ToObject());
316 | }
317 | }
318 |
319 | Thread.Sleep(250);
320 | Logger.Log(LogLevel.Info, "Successfully Read Page: " + page);
321 | ++page;
322 | }
323 |
324 | Logger.Log(LogLevel.Info, "Successfully Read " + rankings.Count + " Rankings.");
325 |
326 | var name = enc.Name.ToLower();
327 | if (percentileData.Rankings.ContainsKey(name) != true)
328 | {
329 | percentileData.Rankings.Add(name, new Dictionary>());
330 | if (percentileData.Rankings[name].ContainsKey(job.Abbrveiation) != true)
331 | percentileData.Rankings[name].Add(job.Abbrveiation, new List());
332 | }
333 |
334 | percentileData.Rankings[name][job.Abbrveiation].AddRange(rankings);
335 | Logger.Log(LogLevel.Info, "Success For Job " + job.Name + " Encounter " + enc.Name);
336 | return await Task.FromResult(true);
337 | }
338 | catch (WebException ex)
339 | {
340 | Logger.Log(LogLevel.Warn, ex.Message);
341 | Logger.Log(LogLevel.Warn, "No Data Exists for that Encounter and Job.");
342 | return await Task.FromResult(true);
343 | }
344 | }
345 | }
346 | }
--------------------------------------------------------------------------------
/PercentilePlugin/PercentilePlugin.Parser/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("PercentilePlugin.Parser")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("PercentilePlugin.Parser")]
13 | [assembly: AssemblyCopyright("Copyright © 2018")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("7d0a1b52-778b-4897-91bb-fb4967439a66")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/PercentilePlugin/PercentilePlugin.Parser/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/PercentilePlugin/PercentilePlugin.Shared/ClassJob.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Net;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 | using Newtonsoft.Json;
9 | using Newtonsoft.Json.Linq;
10 |
11 | namespace PercentilePlugin.Shared
12 | {
13 | public class ClassJob
14 | {
15 | public string Name { get; set; }
16 | public string Abbrveiation { get; set; }
17 | public int Key { get; set; }
18 |
19 | public static string NameToAbbr(string name)
20 | {
21 | switch (name)
22 | {
23 | case "Astrologian":
24 | return "AST";
25 | case "Bard":
26 | return "BRD";
27 | case "Black Mage":
28 | return "BLM";
29 | case "Dark Knight":
30 | return "DRK";
31 | case "Dragoon":
32 | return "DRG";
33 | case "Machinist":
34 | return "MCH";
35 | case "Monk":
36 | return "MNK";
37 | case "Ninja":
38 | return "NIN";
39 | case "Paladin":
40 | return "PLD";
41 | case "Scholar":
42 | return "SCH";
43 | case "Summoner":
44 | return "SMN";
45 | case "Warrior":
46 | return "WAR";
47 | case "White Mage":
48 | return "WHM";
49 | case "Red Mage":
50 | return "RDM";
51 | case "Samurai":
52 | return "SAM";
53 | default:
54 | return "???";
55 | }
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/PercentilePlugin/PercentilePlugin.Shared/DateTimeExtension.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace PercentilePlugin.Shared
8 | {
9 | public static class DateTimeExtension
10 | {
11 | ///
12 | /// Converts a given DateTime into a Unix timestamp
13 | ///
14 | /// Any DateTime
15 | /// The given DateTime in Unix timestamp format
16 | public static long ToUnixTimestamp(this DateTime value)
17 | {
18 | return (long)Math.Truncate((value.ToUniversalTime().Subtract(new DateTime(1970, 1, 1))).TotalMilliseconds);
19 | }
20 |
21 | ///
22 | /// Gets a Unix timestamp representing the current moment
23 | ///
24 | /// Parameter ignored
25 | /// Now expressed as a Unix timestamp
26 | public static long UnixTimestamp(this DateTime ignored)
27 | {
28 | return (long)Math.Truncate((DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalMilliseconds);
29 | }
30 |
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/PercentilePlugin/PercentilePlugin.Shared/Encounter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Net;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 | using Newtonsoft.Json;
9 | using Newtonsoft.Json.Linq;
10 |
11 | namespace PercentilePlugin.Shared
12 | {
13 | public class Encounter
14 | {
15 | public string Name { get; set; }
16 | public string LastUpdated { get; set; }
17 | public int Key { get; set; }
18 | public int Category { get; set; }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/PercentilePlugin/PercentilePlugin.Shared/Instance.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics.Contracts;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Net;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 | using Newtonsoft.Json;
10 | using Newtonsoft.Json.Linq;
11 |
12 | namespace PercentilePlugin.Shared
13 | {
14 | public class Instance
15 | {
16 | public Dictionary Encounters { get; set; } = new Dictionary();
17 |
18 | public string MapName { get; set; } = "";
19 |
20 | public static string InstanceFromBoss(string boss)
21 | {
22 | switch (boss)
23 | {
24 | case "Famfrit, the Darkening Cloud":
25 | case "Belias, the Gigas":
26 | case "Construct 7":
27 | case "Yiazmat":
28 | return "The Ridorana Lighthouse";
29 | break;
30 | case "Mateus, the Corrupt":
31 | case "Hashmal, Bringer of Order":
32 | case "Rofocale":
33 | case "Argath Thadalfus":
34 | return "The Royal City of Rabanastre";
35 | break;
36 | case "Phantom Train (Savage)":
37 | return "Sigmascape V1.0 (Savage)";
38 | break;
39 | case "Demon Chadarnook (Savage)":
40 | return "Sigmascape V2.0 (Savage)";
41 | break;
42 | case "Guardian (Savage)":
43 | return "Sigmascape V3.0 (Savage)";
44 | break;
45 | case "Kefka (Savage)":
46 | case "God Kefka (Savage)":
47 | return "Sigmascape V4.0 (Savage)";
48 | break;
49 | case "Phantom Train":
50 | return "Sigmascape (V1.0)";
51 | break;
52 | case "Demon Chadarnook":
53 | return "Sigmascape (V2.0)";
54 | break;
55 | case "Guardian":
56 | return "Sigmascape (V3.0)";
57 | break;
58 | case "Kefka":
59 | return "Sigmascape (V4.0)";
60 | break;
61 | case "Susano":
62 | return "The Pool of Tribute (Extreme)";
63 | case "Lakshmi":
64 | return "Emanation (Extreme)";
65 | break;
66 | case "Shinryu":
67 | return "The Minstrel's Ballad: Shinryu's Domain";
68 | break;
69 | case "Byakko":
70 | return "The Jade Stoa (Extreme)";
71 | break;
72 | case "Tsukuyomi":
73 | return "The Minstrel's Ballad: Tsukuyomi's Pain";
74 | break;
75 | case "Suzaku":
76 | return "Hell's Kier (Extreme)";
77 | case "Bahamut Prime":
78 | return "The Unending Coil of Bahamut";
79 | break;
80 | case "The Ultima Weapon":
81 | return "Ultimacy";
82 | break;
83 | case "Chaos":
84 | return "Alphascape (V1.0)";
85 | break;
86 | case "Midgardsormr":
87 | return "Alphascape (V2.0)";
88 | break;
89 | case "Omega":
90 | return "Alphascape (V3.0)";
91 | break;
92 | case "Omega-M and Omega-F":
93 | return "Alphascape (V4.0)";
94 | break;
95 | case "Omega-M and Omega-F (Savage)":
96 | case "The Final Omega (Savage)":
97 | return "Alphascape V4.0 (Savage)";
98 | case "Chaos (Savage)":
99 | return "Alphascape V1.0 (Savage)";
100 | break;
101 | case "Midgardsormr (Savage)":
102 | return "Alphascape V2.0 (Savage)";
103 | break;
104 | case "Omega (Savage)":
105 | return "Alphascape V3.0 (Savage)";
106 | break;
107 | }
108 |
109 | return "";
110 | }
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/PercentilePlugin/PercentilePlugin.Shared/PercentileData.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 | using Newtonsoft.Json;
4 | using Newtonsoft.Json.Bson;
5 |
6 | namespace PercentilePlugin.Shared
7 | {
8 | public class PercentileData
9 | {
10 | public long LastUpdated { get; set; } = 0;
11 |
12 | public Dictionary>> Rankings { get; set; } =
13 | new Dictionary>>();
14 |
15 | public static PercentileData Load(string file)
16 | {
17 | // Return new instance if file does not exists
18 | if (File.Exists(file) != true) return new PercentileData();
19 |
20 | var fileStream = new FileStream(file, FileMode.OpenOrCreate);
21 | using (var reader = new BsonReader(fileStream))
22 | {
23 | var serializer = new JsonSerializer();
24 | return serializer.Deserialize(reader);
25 | }
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/PercentilePlugin/PercentilePlugin.Shared/PercentilePlugin.Shared.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {76C3FAF6-98DD-48C1-808B-30A9F879E5A7}
8 | Library
9 | Properties
10 | PercentilePlugin.Shared
11 | PercentilePlugin.Shared
12 | v4.5.1
13 | 512
14 |
15 |
16 |
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 |
25 |
26 | pdbonly
27 | true
28 | bin\Release\
29 | TRACE
30 | prompt
31 | 4
32 |
33 |
34 |
35 | ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | Designer
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/PercentilePlugin/PercentilePlugin.Shared/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("PercentilePlugin.Shared")]
9 | [assembly: AssemblyDescription("Shared data for the PercentileData Parser and FFXIV Plugin.")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("PercentilePlugin.Shared")]
13 | [assembly: AssemblyCopyright("Copyright © Liquidize 2018")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("76c3faf6-98dd-48c1-808b-30a9f879e5a7")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/PercentilePlugin/PercentilePlugin.Shared/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/PercentilePlugin/PercentilePlugin.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27428.2037
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PercentilePlugin", "PercentilePlugin\PercentilePlugin.csproj", "{BEA5D2A7-AA3C-4BC8-9227-03B80A848ADE}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PercentilePlugin.Parser", "PercentilePlugin.Parser\PercentilePlugin.Parser.csproj", "{7D0A1B52-778B-4897-91BB-FB4967439A66}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PercentilePlugin.Shared", "PercentilePlugin.Shared\PercentilePlugin.Shared.csproj", "{76C3FAF6-98DD-48C1-808B-30A9F879E5A7}"
11 | EndProject
12 | Global
13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
14 | Debug|Any CPU = Debug|Any CPU
15 | Release|Any CPU = Release|Any CPU
16 | EndGlobalSection
17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
18 | {BEA5D2A7-AA3C-4BC8-9227-03B80A848ADE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19 | {BEA5D2A7-AA3C-4BC8-9227-03B80A848ADE}.Debug|Any CPU.Build.0 = Debug|Any CPU
20 | {BEA5D2A7-AA3C-4BC8-9227-03B80A848ADE}.Release|Any CPU.ActiveCfg = Release|Any CPU
21 | {BEA5D2A7-AA3C-4BC8-9227-03B80A848ADE}.Release|Any CPU.Build.0 = Release|Any CPU
22 | {7D0A1B52-778B-4897-91BB-FB4967439A66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {7D0A1B52-778B-4897-91BB-FB4967439A66}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {7D0A1B52-778B-4897-91BB-FB4967439A66}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {7D0A1B52-778B-4897-91BB-FB4967439A66}.Release|Any CPU.Build.0 = Release|Any CPU
26 | {76C3FAF6-98DD-48C1-808B-30A9F879E5A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {76C3FAF6-98DD-48C1-808B-30A9F879E5A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {76C3FAF6-98DD-48C1-808B-30A9F879E5A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {76C3FAF6-98DD-48C1-808B-30A9F879E5A7}.Release|Any CPU.Build.0 = Release|Any CPU
30 | EndGlobalSection
31 | GlobalSection(SolutionProperties) = preSolution
32 | HideSolutionNode = FALSE
33 | EndGlobalSection
34 | GlobalSection(ExtensibilityGlobals) = postSolution
35 | SolutionGuid = {98CB1CAC-5D17-4EDE-83C8-283EB87020E2}
36 | EndGlobalSection
37 | EndGlobal
38 |
--------------------------------------------------------------------------------
/PercentilePlugin/PercentilePlugin/Logger.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace PercentilePlugin
9 | {
10 | ///
11 | /// Logger
12 | ///
13 | public class Logger
14 | {
15 | ///
16 | /// Log Entries
17 | ///
18 | public BindingList Logs { get; private set; }
19 |
20 | public Logger()
21 | {
22 | this.Logs = new BindingList();
23 | }
24 |
25 | public void Log(LogLevel level, string message)
26 | {
27 | #if !DEBUG
28 | if (level == LogLevel.Trace || level == LogLevel.Debug)
29 | {
30 | return;
31 | }
32 | #endif
33 | #if DEBUG
34 | System.Diagnostics.Trace.WriteLine(string.Format("{0}: {1}: {2}", level, DateTime.Now, message));
35 | #endif
36 |
37 | this.Logs.Add(new LogEntry(level, DateTime.Now, message));
38 | }
39 |
40 | public void Log(LogLevel level, string format, params object[] args)
41 | {
42 | Log(level, string.Format(format, args));
43 | }
44 | }
45 |
46 | public class LogEntry
47 | {
48 | public string Message { get; set; }
49 | public LogLevel Level { get; set; }
50 | public DateTime Time { get; set; }
51 |
52 | public LogEntry(LogLevel level, DateTime time, string message)
53 | {
54 | this.Message = message;
55 | this.Level = level;
56 | this.Time = time;
57 | }
58 | }
59 |
60 | public class LogEventArgs : EventArgs
61 | {
62 | public string Message { get; private set; }
63 | public LogLevel Level { get; private set; }
64 | public LogEventArgs(LogLevel level, string message)
65 | {
66 | this.Message = message;
67 | this.Level = level;
68 | }
69 | }
70 |
71 | public enum LogLevel
72 | {
73 | Trace,
74 | Debug,
75 | Info,
76 | Warning,
77 | Error
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/PercentilePlugin/PercentilePlugin/Options.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using Newtonsoft.Json;
3 |
4 | namespace PercentilePlugin
5 | {
6 | public class Options
7 | {
8 | public static Options Instance { get; private set; }
9 |
10 | public string RemoteVersionSeen { get; set; } = "0.0.0";
11 |
12 | public bool AutoUpdate { get; set; } = false;
13 |
14 | public void Save()
15 | {
16 | if (Directory.Exists("PercentilePlugin") != true) Directory.CreateDirectory("PercentilePlugin");
17 | File.WriteAllText("PercentilePlugin/options.json", JsonConvert.SerializeObject(this, Formatting.Indented));
18 | }
19 |
20 | public static void Load()
21 | {
22 | if (File.Exists("PercentilePlugin/options.json"))
23 | Instance = JsonConvert.DeserializeObject(File.ReadAllText("PercentilePlugin/options.json"));
24 | else
25 | Instance = new Options();
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/PercentilePlugin/PercentilePlugin/PercentilePlugin.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.Net;
6 | using System.Threading.Tasks;
7 | using System.Windows.Forms;
8 | using Advanced_Combat_Tracker;
9 | using Newtonsoft.Json;
10 | using Newtonsoft.Json.Bson;
11 | using PercentilePlugin.Shared;
12 |
13 | namespace PercentilePlugin
14 | {
15 | public class PercentilePlugin : IActPluginV1
16 | {
17 | private Label pluginLabel;
18 | public PercentileUi percentileUi;
19 | public static Logger Logger;
20 | public static PercentileData PercentileData;
21 |
22 | private static readonly int kRequiredNETVersionMajor = 4;
23 | private static readonly int kRequiredNETVersionMinor = 6;
24 | private static readonly int kRequiredNETVersionRevision = 1;
25 |
26 | public async void InitPlugin(TabPage pluginScreenSpace, Label pluginStatusText)
27 | {
28 | Logger = new Logger();
29 | Logger.Log(LogLevel.Info, "Plugin Init.");
30 |
31 |
32 | if (Directory.Exists("PercentilePlugin") != true)
33 | {
34 | Logger.Log(LogLevel.Warning, "Creating PercentilePlugin Directory....");
35 | Directory.CreateDirectory("PercentilePlugin");
36 | }
37 |
38 | Options.Load();
39 | Logger.Log(LogLevel.Info, "Loaded Options.");
40 | PercentileData = PercentileData.Load("PercentilePlugin/parsedata.bin");
41 | Logger.Log(LogLevel.Info, "Percentile Data Loaded.");
42 |
43 | percentileUi = new PercentileUi();
44 | pluginLabel = pluginStatusText;
45 | percentileUi.Dock = DockStyle.Fill;
46 | pluginScreenSpace.Controls.Add(percentileUi);
47 |
48 | CombatantData.ColumnDefs.Add("Percentile", new CombatantData.ColumnDef("Percentile", true, "FLOAT",
49 | "Percentile",
50 | Data => { return GetPercentile(Data).ToString(); },
51 | Data => { return GetPercentile(Data).ToString(); },
52 | (Left, Right) => { return GetPercentile(Left).CompareTo(GetPercentile(Right)); }));
53 | ActGlobals.oFormActMain.ValidateTableSetup();
54 | CombatantData.ExportVariables.Add("Percentile", new CombatantData.TextExportFormatter("percentile",
55 | "Percentile", "2 Week Historical Percentile based off current DPS.",
56 | (Data, Extra) => { return GetPercentile(Data).ToString(); }));
57 | ActGlobals.oFormActMain.ValidateLists();
58 | Logger.Log(LogLevel.Info, "Percentile Column Added.");
59 | pluginStatusText.Text = "Plugin Loaded.";
60 | percentileUi.lastCheckLbl.Text =
61 | "Last Updated: " + new DateTime(1970, 1, 1).AddMilliseconds(PercentileData.LastUpdated).ToLocalTime()
62 | .ToString("F");
63 | Logger.Log(LogLevel.Info, "Plugin Loaded");
64 |
65 | var checker = new VersionChecker();
66 | var local = checker.GetLocalVersion();
67 | var remote = checker.GetRemoteVersion();
68 | if (remote.Major == 0 && remote.Minor == 0)
69 | {
70 | var result = MessageBox.Show(
71 | "Github error while checking PercentilePlugin version. " +
72 | "Your current version is " + local + ".\n\n" +
73 | "Manually check for newer version now?",
74 | "PercentilePlugin Manual Check",
75 | MessageBoxButtons.YesNo);
76 | if (result == DialogResult.Yes)
77 | Process.Start(VersionChecker.kReleaseUrl);
78 | }
79 | else if (local < remote)
80 | {
81 | if (Options.Instance != null && string.IsNullOrEmpty(Options.Instance.RemoteVersionSeen))
82 | Options.Instance.RemoteVersionSeen = "0.0.0";
83 |
84 | var remote_seen_before = new Version(Options.Instance.RemoteVersionSeen);
85 | Options.Instance.RemoteVersionSeen = remote.ToString();
86 |
87 | var update_message = "There is a new version of PercentilePlugin is available at: \n" +
88 | VersionChecker.kReleaseUrl + " \n\n" +
89 | "New version " + remote + " \n" +
90 | "Current version " + local;
91 | if (remote == remote_seen_before)
92 | {
93 | Logger.Log(LogLevel.Warning, "Remote Version Seen Before.");
94 | }
95 | else
96 | {
97 | var result = MessageBox.Show(
98 | update_message + "\n\n" +
99 | "Get it now?",
100 | "PercentilePlugin update available",
101 | MessageBoxButtons.YesNo);
102 | if (result == DialogResult.Yes)
103 | Process.Start(VersionChecker.kReleaseUrl);
104 | }
105 |
106 | var net_version_str = FileVersionInfo
107 | .GetVersionInfo(typeof(int).Assembly.Location).ProductVersion;
108 | var net_version = net_version_str.Split('.');
109 | if (int.Parse(net_version[0]) < kRequiredNETVersionMajor ||
110 | int.Parse(net_version[1]) < kRequiredNETVersionMinor ||
111 | int.Parse(net_version[2]) < kRequiredNETVersionRevision)
112 | Logger.Log(LogLevel.Error, "Requires .NET 4.5.1 or above. Using " + net_version_str);
113 |
114 |
115 | Options.Instance.RemoteVersionSeen = remote.ToString();
116 | Options.Instance.Save();
117 | }
118 |
119 | if (Options.Instance.AutoUpdate)
120 | {
121 | var backgroundWorker = new BackgroundWorker();
122 | backgroundWorker.DoWork += (sender, args) => { UpdateData(); };
123 | backgroundWorker.RunWorkerAsync();
124 | }
125 | }
126 |
127 | public async Task UpdateData()
128 | {
129 | try
130 | {
131 | var request =
132 | WebRequest.Create("https://github.com/Liquidize/FFXIV_PercentilePlugin/raw/master/parsedata.bin");
133 | var response = await request.GetResponseAsync();
134 | if (response.GetResponseStream() != null)
135 | using (var bsonReader = new BsonReader(response.GetResponseStream()))
136 | {
137 | var serializer = new JsonSerializer();
138 | var data = serializer.Deserialize(bsonReader);
139 | if (data != null)
140 | {
141 | PercentileData = data;
142 | var file = new FileStream("PercentilePlugin/parsedata.bin", FileMode.OpenOrCreate);
143 | using (var writer = new BsonWriter(file))
144 | {
145 | serializer.Serialize(writer, data);
146 | }
147 |
148 | file.Close();
149 | Logger.Log(LogLevel.Info, "Percentile Data has been updated.");
150 | percentileUi.UpdateLabelText();
151 | }
152 | else
153 | {
154 | MessageBox.Show("Error in updating data.");
155 | }
156 | }
157 | }
158 | catch (WebException webEx)
159 | {
160 | Logger.Log(LogLevel.Error, "Error in updating data.");
161 | Logger.Log(LogLevel.Error, webEx.ToString());
162 | }
163 | catch (JsonException jsonEx)
164 | {
165 | Logger.Log(LogLevel.Error, "Error in updating data.");
166 | Logger.Log(LogLevel.Error, jsonEx.ToString());
167 | }
168 | }
169 |
170 | public void DeInitPlugin()
171 | {
172 | pluginLabel.Text = "Plugin Exited.";
173 | }
174 |
175 | public string GetTranslatedStrongest(string boss)
176 | {
177 | switch (boss)
178 | {
179 | case "カオス":
180 | return "Chaos";
181 | case "ミドガルズオルム":
182 | return "Midgardsormr";
183 | case "オメガ":
184 | return "Omega";
185 | case "朱雀":
186 | return "Suzaku";
187 | case "ツクヨミ":
188 | return "Tsukuyomi";
189 | case "白虎":
190 | return "Byakko";
191 | case "神龍":
192 | return "Shinryu";
193 | case "ラクシュミ":
194 | return "Lakshmi";
195 | case "スサノオ":
196 | return "Susano";
197 | case "背徳の皇帝マティウス":
198 | return "Mateus, the Corrupt";
199 | case "統制者ハシュマリム":
200 | return "Hashmal, Bringer of Order";
201 | case "人馬王ロフォカレ":
202 | return "Rofocale";
203 | case "冷血剣アルガス":
204 | return "Argath Thadalfus";
205 | case "暗黒の雲ファムフリート":
206 | return "Famfrit, the Darkening Cloud";
207 | case "魔人ベリアス":
208 | return "Belias, the Gigas";
209 | case "労働七号":
210 | return "Construct 7";
211 | case "鬼龍ヤズマット":
212 | return "Yiazmat";
213 | }
214 |
215 | return "";
216 | }
217 |
218 | public string GetBoss(EncounterData enc)
219 | {
220 | var strongest = enc.GetStrongestEnemy("YOU");
221 | var combatant = enc.GetCombatant(strongest);
222 | strongest = string.IsNullOrEmpty(GetTranslatedStrongest(strongest))
223 | ? strongest
224 | : GetTranslatedStrongest(strongest);
225 | if (enc.ZoneName.ToLower() == "the Weapon's Refrain (Ultimate)".ToLower()) return "The Ultima Weapon";
226 | if (enc.ZoneName.ToLower() == "the Unending Coil of Bahamut (Ultimate)".ToLower()) return "Bahamut Prime";
227 |
228 | if (enc.ZoneName.Contains("Alphascape (V4.0)")) strongest = "Omega-M and Omega-F";
229 |
230 | if (enc.ZoneName.Contains("Savage")) strongest = strongest + " (Savage)";
231 |
232 | if (enc.ZoneName.Contains("Alphascape") && enc.ZoneName.Contains("4.0") && enc.ZoneName.Contains("Savage"))
233 | {
234 | if (combatant.AllOut.ContainsKey("Target Analysis") || combatant.AllOut.ContainsKey("標的識別") ||
235 | combatant.AllOut.ContainsKey("Unknown_336C"))
236 | return "The Final Omega (Savage)";
237 | return "Omega-M and Omega-F (Savage)";
238 | }
239 |
240 | return strongest;
241 | }
242 |
243 | public double GetPercentile(CombatantData combatant)
244 | {
245 | var job = combatant.GetColumnByName("Job").ToUpper();
246 |
247 | if (combatant.Parent == null) return -1;
248 | if (string.IsNullOrEmpty(job)) return -1;
249 |
250 | var boss = GetBoss(combatant.Parent).ToLower();
251 |
252 | if (!string.IsNullOrEmpty(boss)) return GetRealPercentile(job, boss, combatant.EncDPS);
253 |
254 | return -1;
255 | }
256 |
257 |
258 | public static double GetRealPercentile(string job, string enc, double DPS)
259 | {
260 | if (PercentileData.Rankings.ContainsKey(enc) != true) return 0;
261 | var sequence = PercentileData.Rankings[enc][job].ToArray();
262 | if (sequence.Length == 0) return 100;
263 | Array.Sort(sequence);
264 | var l = 0;
265 | var r = sequence.Length - 1;
266 | var index = sequence.Length / 2;
267 |
268 | while (l <= r)
269 | {
270 | index = l + (r - l) / 2;
271 |
272 | if (sequence[index] < DPS)
273 | l = index + 1;
274 | else
275 | r = index - 1;
276 | }
277 |
278 | return (100 * index + sequence.Length) / sequence.Length;
279 | }
280 | }
281 | }
--------------------------------------------------------------------------------
/PercentilePlugin/PercentilePlugin/PercentilePlugin.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {BEA5D2A7-AA3C-4BC8-9227-03B80A848ADE}
8 | Library
9 | Properties
10 | PercentilePlugin
11 | PercentilePlugin
12 | v4.5.1
13 | 512
14 |
15 |
16 |
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 |
25 |
26 | pdbonly
27 | true
28 | bin\Release\
29 | TRACE
30 | prompt
31 | 4
32 |
33 |
34 |
35 | C:\Program Files (x86)\Advanced Combat Tracker\Advanced Combat Tracker.exe
36 |
37 |
38 | ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | UserControl
58 |
59 |
60 | PercentileUi.cs
61 |
62 |
63 |
64 |
65 |
66 |
67 | PercentileUi.cs
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 | {76c3faf6-98dd-48c1-808b-30a9f879e5a7}
77 | PercentilePlugin.Shared
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/PercentilePlugin/PercentilePlugin/PercentileUi.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace PercentilePlugin
2 | {
3 | partial class PercentileUi
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Component Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | this.logView = new System.Windows.Forms.ListView();
32 | this.timeCol = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
33 | this.levelCol = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
34 | this.messageCol = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
35 | this.lastCheckLbl = new System.Windows.Forms.Label();
36 | this.updateBtn = new System.Windows.Forms.Button();
37 | this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker();
38 | this.autoUpdateChbx = new System.Windows.Forms.CheckBox();
39 | this.SuspendLayout();
40 | //
41 | // logView
42 | //
43 | this.logView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
44 | this.timeCol,
45 | this.levelCol,
46 | this.messageCol});
47 | this.logView.Dock = System.Windows.Forms.DockStyle.Bottom;
48 | this.logView.Location = new System.Drawing.Point(0, 93);
49 | this.logView.Name = "logView";
50 | this.logView.Size = new System.Drawing.Size(906, 432);
51 | this.logView.TabIndex = 0;
52 | this.logView.UseCompatibleStateImageBehavior = false;
53 | this.logView.View = System.Windows.Forms.View.Details;
54 | this.logView.VirtualMode = true;
55 | this.logView.RetrieveVirtualItem += new System.Windows.Forms.RetrieveVirtualItemEventHandler(this.logView_RetrieveVirtualItem);
56 | //
57 | // timeCol
58 | //
59 | this.timeCol.Text = "Time";
60 | this.timeCol.Width = 106;
61 | //
62 | // levelCol
63 | //
64 | this.levelCol.Text = "Level";
65 | //
66 | // messageCol
67 | //
68 | this.messageCol.Text = "Message";
69 | this.messageCol.Width = 736;
70 | //
71 | // lastCheckLbl
72 | //
73 | this.lastCheckLbl.AutoSize = true;
74 | this.lastCheckLbl.Location = new System.Drawing.Point(3, 5);
75 | this.lastCheckLbl.Name = "lastCheckLbl";
76 | this.lastCheckLbl.Size = new System.Drawing.Size(160, 13);
77 | this.lastCheckLbl.TabIndex = 1;
78 | this.lastCheckLbl.Text = "Last Updated: NOT-AVAILABLE";
79 | //
80 | // updateBtn
81 | //
82 | this.updateBtn.Location = new System.Drawing.Point(3, 21);
83 | this.updateBtn.Name = "updateBtn";
84 | this.updateBtn.Size = new System.Drawing.Size(75, 23);
85 | this.updateBtn.TabIndex = 3;
86 | this.updateBtn.Text = "Update";
87 | this.updateBtn.UseVisualStyleBackColor = true;
88 | this.updateBtn.Click += new System.EventHandler(this.updateBtn_Click);
89 | //
90 | // autoUpdateChbx
91 | //
92 | this.autoUpdateChbx.AutoSize = true;
93 | this.autoUpdateChbx.Location = new System.Drawing.Point(85, 26);
94 | this.autoUpdateChbx.Name = "autoUpdateChbx";
95 | this.autoUpdateChbx.Size = new System.Drawing.Size(134, 17);
96 | this.autoUpdateChbx.TabIndex = 6;
97 | this.autoUpdateChbx.Text = "Auto Update On Start?";
98 | this.autoUpdateChbx.UseVisualStyleBackColor = true;
99 | this.autoUpdateChbx.CheckedChanged += new System.EventHandler(this.autoUpdateChbx_CheckedChanged);
100 | //
101 | // PercentileUi
102 | //
103 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
104 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
105 | this.AutoSize = true;
106 | this.Controls.Add(this.autoUpdateChbx);
107 | this.Controls.Add(this.updateBtn);
108 | this.Controls.Add(this.lastCheckLbl);
109 | this.Controls.Add(this.logView);
110 | this.Name = "PercentileUi";
111 | this.Size = new System.Drawing.Size(906, 525);
112 | this.ResumeLayout(false);
113 | this.PerformLayout();
114 |
115 | }
116 |
117 | #endregion
118 |
119 | private System.Windows.Forms.ListView logView;
120 | private System.Windows.Forms.ColumnHeader timeCol;
121 | private System.Windows.Forms.ColumnHeader levelCol;
122 | private System.Windows.Forms.ColumnHeader messageCol;
123 | private System.Windows.Forms.Button updateBtn;
124 | private System.ComponentModel.BackgroundWorker backgroundWorker1;
125 | public System.Windows.Forms.Label lastCheckLbl;
126 | private System.Windows.Forms.CheckBox autoUpdateChbx;
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/PercentilePlugin/PercentilePlugin/PercentileUi.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Drawing;
3 | using System.IO;
4 | using System.Net;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 | using System.Windows.Forms;
8 | using Newtonsoft.Json;
9 | using Newtonsoft.Json.Bson;
10 | using PercentilePlugin.Shared;
11 |
12 | namespace PercentilePlugin
13 | {
14 | public partial class PercentileUi : UserControl
15 | {
16 | private CancellationTokenSource cts;
17 | private Task updateTask;
18 |
19 | public PercentileUi()
20 | {
21 | InitializeComponent();
22 | logView.VirtualListSize = PercentilePlugin.Logger.Logs.Count;
23 | PercentilePlugin.Logger.Logs.ListChanged += (o, e) =>
24 | {
25 | logView.BeginUpdate();
26 | logView.VirtualListSize = PercentilePlugin.Logger.Logs.Count;
27 | logView.EnsureVisible(logView.VirtualListSize - 1);
28 | logView.EndUpdate();
29 | };
30 | autoUpdateChbx.Checked = Options.Instance.AutoUpdate;
31 | }
32 |
33 | private void logView_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
34 | {
35 | if (e.ItemIndex >= PercentilePlugin.Logger.Logs.Count)
36 | {
37 | e.Item = new ListViewItem();
38 | return;
39 | }
40 |
41 | var log = PercentilePlugin.Logger.Logs[e.ItemIndex];
42 | e.Item = new ListViewItem(log.Time.ToString());
43 | e.Item.UseItemStyleForSubItems = true;
44 | e.Item.SubItems.Add(log.Level.ToString());
45 | e.Item.SubItems.Add(log.Message);
46 | e.Item.ForeColor = Color.Black;
47 | if (log.Level == LogLevel.Warning)
48 | e.Item.BackColor = Color.LightYellow;
49 | else if (log.Level == LogLevel.Error)
50 | e.Item.BackColor = Color.LightPink;
51 | else
52 | e.Item.BackColor = Color.White;
53 | }
54 |
55 | public void UpdateLabelText()
56 | {
57 | lastCheckLbl.Text = "Last Updated: " + new DateTime(1970, 1, 1)
58 | .AddMilliseconds(PercentilePlugin.PercentileData.LastUpdated).ToLocalTime()
59 | .ToString("F");
60 | }
61 |
62 | private async void updateBtn_Click(object sender, EventArgs e)
63 | {
64 | updateBtn.Enabled = false;
65 | try
66 | {
67 | var request =
68 | WebRequest.Create("https://github.com/Liquidize/FFXIV_PercentilePlugin/raw/master/parsedata.bin");
69 | var response = await request.GetResponseAsync();
70 | if (response.GetResponseStream() != null)
71 | using (var bsonReader = new BsonReader(response.GetResponseStream()))
72 | {
73 | var serializer = new JsonSerializer();
74 | var data = serializer.Deserialize(bsonReader);
75 | if (data != null)
76 | {
77 | PercentilePlugin.PercentileData = data;
78 | var file = new FileStream("PercentilePlugin/parsedata.bin", FileMode.OpenOrCreate);
79 | using (var writer = new BsonWriter(file))
80 | {
81 | serializer.Serialize(writer, data);
82 | }
83 |
84 | file.Close();
85 | PercentilePlugin.Logger.Log(LogLevel.Info, "Percentile Data has been updated.");
86 | }
87 | else
88 | {
89 | MessageBox.Show("Error in updating data.");
90 | }
91 | }
92 | }
93 | catch (WebException webEx)
94 | {
95 | PercentilePlugin.Logger.Log(LogLevel.Error, "Error in updating data.");
96 | PercentilePlugin.Logger.Log(LogLevel.Error, webEx.ToString());
97 | }
98 | catch (JsonException jsonEx)
99 | {
100 | PercentilePlugin.Logger.Log(LogLevel.Error, "Error in updating data.");
101 | PercentilePlugin.Logger.Log(LogLevel.Error, jsonEx.ToString());
102 | }
103 |
104 | lastCheckLbl.Text = "Last Updated: " + new DateTime(1970, 1, 1)
105 | .AddMilliseconds(PercentilePlugin.PercentileData.LastUpdated).ToLocalTime()
106 | .ToString("F");
107 | updateBtn.Enabled = true;
108 | }
109 |
110 | private void autoUpdateChbx_CheckedChanged(object sender, EventArgs e)
111 | {
112 | Options.Instance.AutoUpdate = autoUpdateChbx.Checked;
113 | Options.Instance.Save();
114 | }
115 | }
116 | }
--------------------------------------------------------------------------------
/PercentilePlugin/PercentilePlugin/PercentileUi.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 | 17, 17
122 |
123 |
--------------------------------------------------------------------------------
/PercentilePlugin/PercentilePlugin/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("Percentile Plugin")]
9 | [assembly: AssemblyDescription("Allows overlays to display real time FFLogs percentiles.")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("PercentilePlugin")]
13 | [assembly: AssemblyCopyright("Copyright © Liquidize 2018")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("bea5d2a7-aa3c-4bc8-9227-03b80a848ade")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("0.2.5.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/PercentilePlugin/PercentilePlugin/VersionChecker.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Text.RegularExpressions;
6 | using System.Threading.Tasks;
7 | using Advanced_Combat_Tracker;
8 |
9 | namespace PercentilePlugin
10 | {
11 | // This class can determine the current plugin version, as well as the latest version
12 | // released of the plugin on GitHub. It is inspired by the work of anoyetta in
13 | // https://github.com/anoyetta/ACT.SpecialSpellTimer/blob/master/ACT.SpecialSpellTimer.Core/UpdateChecker.cs
14 | public class VersionChecker
15 | {
16 | public const string kReleaseUrl = "https://github.com/Liquidize/FFXIV_PercentilePlugin/releases/latest";
17 | public const string kIssueUrl = "https://github.com/Liquidize/FFXIV_PercentilePlugin/releases/issues";
18 |
19 | public VersionChecker()
20 | {
21 | }
22 |
23 | public Version GetLocalVersion()
24 | {
25 | return System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
26 | }
27 |
28 | public string GetLocalLocation()
29 | {
30 | return System.Reflection.Assembly.GetExecutingAssembly().Location;
31 | }
32 |
33 | public Version GetACTVersion()
34 | {
35 | return System.Reflection.Assembly.GetAssembly(typeof(Advanced_Combat_Tracker.ActGlobals)).GetName().Version;
36 | }
37 |
38 | public string GetACTLocation()
39 | {
40 | return System.Reflection.Assembly.GetAssembly(typeof(Advanced_Combat_Tracker.ActGlobals)).Location;
41 | }
42 |
43 | public Version GetRemoteVersion()
44 | {
45 | string html;
46 | try
47 | {
48 | var web = new System.Net.WebClient();
49 | System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Ssl3 | System.Net.SecurityProtocolType.Tls | System.Net.SecurityProtocolType.Tls11 | System.Net.SecurityProtocolType.Tls12;
50 | var page_stream = web.OpenRead(kReleaseUrl);
51 | var reader = new System.IO.StreamReader(page_stream);
52 | html = reader.ReadToEnd();
53 | }
54 | catch (Exception e)
55 | {
56 | PercentilePlugin.Logger.Log(LogLevel.Error,"Error fetching most recent github release: " + e.Message + "\n" + e.StackTrace);
57 | return new Version();
58 | }
59 |
60 | var pattern = @"href=""/liquidize/FFXIV_PercentilePlugin/releases/download/v?(?.*?)/";
61 | var regex = new Regex(pattern, RegexOptions.IgnoreCase | RegexOptions.Singleline);
62 | var match = regex.Match(html);
63 | if (!match.Success)
64 | {
65 | PercentilePlugin.Logger.Log(LogLevel.Error, "Error parsing most recent github release, no match found. Please report an issue at " + kIssueUrl);
66 | return new Version();
67 | }
68 |
69 | string version_string = match.Groups["Version"].Value;
70 |
71 | pattern = @"(?(?[0-9]+)\.(?[0-9]+)\.(?[0-9+]))";
72 | regex = new Regex(pattern);
73 | match = regex.Match(version_string);
74 | if (!match.Success)
75 | {
76 | PercentilePlugin.Logger.Log(LogLevel.Error, "Error parsing most recent github release, no version number found. Please report an issue at " + kIssueUrl);
77 | return new Version();
78 | }
79 |
80 | return new Version(int.Parse(match.Groups["Major"].Value), int.Parse(match.Groups["Minor"].Value), int.Parse(match.Groups["Revision"].Value));
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/PercentilePlugin/PercentilePlugin/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/PercentilePlugin/PercentilePlugin/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FFXIV_PercentilePlugin (beta)
2 | Percentile Plugin for ACT to allow overlays to display FFLogs percentile data in real time.
3 |
4 | The plugin works by using data obtained from FFLogs and calculating the current percentile in real time using your current DPS. The data used should be fairly accurate +/- 1% (give or take) of the Historical Percentile if you use the latest data. You can view the percentiles in real time via the "Percentile" column that is added to ACT or via a compatible overlay.
5 |
6 | Percentiles displayed in a modified Kagerou overlay (the Pct column):
7 | 
8 |
9 | **Note:** The plugin is still in development and while fully functional for displaying percentiles for known fights, future fights and some current fights may need to be adjusted.
10 |
11 | # How-To
12 |
13 | 1. Obtain the latest version (zip file) of the plugin from the [Releases](https://github.com/Liquidize/FFXIV_PercentilePlugin/releases)
14 | 2. Extract the zip file to where your "Advanced Combat Tracker.exe" is, usually something like "C:\Program Files (x86)\Advanced Combat Tracker"
15 | 3. Run ACT as administrator (incase of permissions errors for writing config files)
16 | 4. Go to the "Plugins" tab and the "PercentilePlugin.dll" as a plugin.
17 | 5. Go to the "PercentilePlugin" plugin tab, and click update.
18 | 6. Use a compatible overlay if you want to see the information in the overlay.
19 |
20 | **Note:** I've forked and added percentile functionality to the popular [Kagerou](https://github.com/hibiyasleep/kagerou) overlay, simply change the overlay URL to: **https://liquidize.github.io/kagerou/overlay/** and you should be able to add "Percentile" as a column in your tabs in the config. You will need to completely reconfigure your overlay though, unfortunately.
21 |
22 | Clicking the "Update" button will download the "parsedata.bin" file that is stored in this repository. This file contains all the data needed to calculate your parses, and is updated daily.
23 |
24 | # Compatible Overlays
25 |
26 | 1. [Kagerou pct](https://github.com/Liquidize/kagerou) - Forked by me, this is the latest version of Kagerou with the added ability to add "Percentile" as a column to your tabs. Just like Kagerou you can easily set this as your overlay by using the following url: https://liquidize.github.io/kagerou/overlay/
27 | 2. [Fancy Detail Overlay](https://i.imgur.com/HGSZoQ6.png) - By 라그린네, I am unsure of the original name of this overlay a user of the plugin asked me to add support for it. You can find the overlay in the Overlays folder on the repository.
28 | 3. [MopiMopi Pct](https://github.com/Liquidize/mopimopi) - A fork of [MopiMopi](https://github.com/HAERUHAERU/mopimopi) by [HaeruHaeru](https://github.com/HAERUHAERU/) with percentile support added by myself. This overlay requires ActWebSocket, follow the guide [HERE](https://docs.google.com/presentation/d/1U7-Vgv6UA2_EFdvw3m8BI-5-9T91WeKTflDuR7rEx-U/edit). Click [HERE](https://i.imgur.com/bPvNkIQ.png) for a preview. Use [https://liquidize.github.io/mopimopi/](https://liquidize.github.io/mopimopi/) as the URL instead of the one in the guide.
29 | 4. [Horizon Overlay](https://github.com/unfaiyted/horizoverlay) - The Horizon Overlay with Percentile support by [Unfaiyted](https://github.com/unfaiyted/).
30 |
31 | If you want your overlay listed here, please message me in game or on discord.
32 |
33 | * **Character in game:** Kaliya Y'mhitra (Goblin - NA)
34 | * **Discord:** Kaliya#0001
35 |
36 | # Credits
37 |
38 | * Liquidize / Kaliya Y'mhitra (Goblin - NA) - Developer/Plugin Creator
39 | * Kaliph Soren (Goblin - NA) - Co-developer
40 | * [Hibiyasleep](https://github.com/hibiyasleep) - Creator of the Kagerou overlay which I forked and added functionality to.
41 | * The update functionality of the plugin was taken from Cactbot an open source Raid helper found [here](https://github.com/quisquous/cactbot).
42 |
--------------------------------------------------------------------------------
/parsedata.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Liquidize/FFXIV_PercentilePlugin/611d147b8c828d3f45d1d84a9c88c322e51563c8/parsedata.bin
--------------------------------------------------------------------------------