mirror of
https://github.com/xivapi/ffxiv-datamining.git
synced 2026-07-24 02:49:10 +00:00
Big clean up of structure and contents
This commit is contained in:
+2
-49
@@ -1,18 +1,6 @@
|
||||
# Research
|
||||
|
||||
This folder contains various research on the FFXIV database structure, file structure and various formulas and the craziness that comes with FFXIV :)
|
||||
|
||||
### Achievements
|
||||
|
||||
- Requirement Types: https://github.com/viion/ffxiv-datamining/blob/master/research/achievements_requirement_type.md
|
||||
|
||||
### Collectables
|
||||
|
||||
- https://github.com/viion/ffxiv-datamining/blob/master/research/collectable_reward_info.txt
|
||||
|
||||
### Emotes
|
||||
|
||||
- https://github.com/viion/ffxiv-datamining/blob/master/research/emotes.md
|
||||
Random stuff, research, useful files, etc
|
||||
|
||||
### DevNPCs
|
||||
|
||||
@@ -20,42 +8,7 @@ This really needs maintaining, there is a bunch of NPC's which are "dev" specifi
|
||||
|
||||
- https://github.com/viion/ffxiv-datamining/blob/master/research/devnpcs.csv
|
||||
|
||||
|
||||
### F.A.T.Es
|
||||
|
||||
- EXP: https://github.com/viion/ffxiv-datamining/blob/master/research/fate_exp.md
|
||||
- Levels: https://github.com/viion/ffxiv-datamining/blob/master/research/fate_levels.md
|
||||
|
||||
### Icons
|
||||
|
||||
- https://github.com/viion/ffxiv-datamining/blob/master/research/icon_paths.md
|
||||
|
||||
### Items
|
||||
|
||||
- Stats: https://github.com/viion/ffxiv-datamining/blob/master/research/item_stats.md
|
||||
|
||||
### Leves
|
||||
|
||||
- Leve to NPC: https://github.com/viion/ffxiv-datamining/blob/master/research/leve_to_npc.md
|
||||
|
||||
### Maps
|
||||
|
||||
- Scales: https://github.com/viion/ffxiv-datamining/blob/master/research/map_scales.md
|
||||
- Coordinate conversion: https://github.com/viion/ffxiv-datamining/blob/master/research/map_coordinates.md
|
||||
|
||||
### Quests
|
||||
|
||||
- EXP Formula: https://github.com/viion/ffxiv-datamining/blob/master/research/quest_exp_reward.md
|
||||
|
||||
### Weather
|
||||
|
||||
- https://github.com/viion/ffxiv-datamining/blob/master/research/weather.md
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
# File structure research
|
||||
### File structure research
|
||||
|
||||
**Huge thank you to FFXIV Explorer Ioncannon for providing these**
|
||||
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
In Patch 4.4, SE changed how colours work in tooltips in preperation of new UI "Skins". The logic for displaying colours changed quite drastically but was figured out quickly, here is the process and how it works now:
|
||||
|
||||
## (ID: 174) Bane tooltip
|
||||
|
||||

|
||||
|
||||
**Old tooltip logic**
|
||||
```
|
||||
Spread a target's <Color(-154)>Bio</Color> and <Color(-154)>Miasma</Color>
|
||||
effects (except <Color(-154)>Miasma II</Color>) to nearby enemies.
|
||||
```
|
||||
|
||||
This is now:
|
||||
```
|
||||
Spread a target's <72>F201FA</72><73>F201FB</73>Bio<73>01</73><72>01</72>
|
||||
and <72>F201FA</72><73>F201FB</73>Miasma<73>01</73><72>01</72> effects
|
||||
(except <72>F201FA</72><73>F201FB</73>Miasma II<73>01</73><72>01</72>) to nearby enemies.
|
||||
```
|
||||
|
||||
And this is how it should be when converted:
|
||||
```
|
||||
Spread a target's <span style="color:#ffff66;">Bio</span> and
|
||||
<span style="color:#ffff66;">Miasma</span> effects (except
|
||||
<span style="color:#ffff66;">Miasma II</span>) to nearby enemies.
|
||||
```
|
||||
|
||||
Taking a small snippet:
|
||||
|
||||
- Before: `<span style="color:#ffff66;">Bio</span>`
|
||||
- After: `<72>F201FA</72><73>F201FB</73>Bio<73>01</73><72>01</72>`
|
||||
|
||||
> At this time we're not sure what 73 is for.
|
||||
|
||||
Focusing on these values, **we need to remove the first byte: F2**
|
||||
|
||||
- `<72>01FA</72><73>01FB</73>Bio<73>01</73><72>01</72>`
|
||||
|
||||
**After that, convert the HEX values to DEC:**
|
||||
|
||||
- `<72>506</72><73>507</73>Bio<73>01</73><72>01</72>`
|
||||
|
||||
In Patch 4.4 a new file was added: [UIColor.csv](https://github.com/viion/ffxiv-datamining/blob/master/csv/UIColor.csv)
|
||||
|
||||
If our guess of Foreground/Background is correct, then in `UIColor`: Col 1 = Foreground and Col 2 = Background, grabbing the rows related to our decimal values and the correct column we get:
|
||||
|
||||
- `<72>4294928127</72><73>4294174719</73>Bio<73>01</73><72>01</72>`
|
||||
|
||||
**And we can convert these values from DEC to HEX:**
|
||||
|
||||
- `<72>FFFF66FF</72><73>FFF3E7FF</73>Bio<73>01</73><72>01</72>`
|
||||
|
||||
The hex can contain the alpha transparency, so if the value is a 8 character hex, **we only care about the first 6.**
|
||||
|
||||
- `<72>FFFF66</72><73>FFF3E7</73>Bio<73>01</73><72>01</72>`
|
||||
|
||||
The 01 are always closing colours (resetting back to default text colors) so we can just replace those with generic closing tags and replace the opening tags to be HTML-like:
|
||||
|
||||
- `<72 = FFFF66><73 = FFF3E7>Bio</73></72>`
|
||||
|
||||
And now it's an easy convert to html:
|
||||
|
||||
- `<span style="color:#FFFF66;"><span style="color:#FFF3E7;">Bio</span></span>`
|
||||
|
||||
Remove 73 as it doesn't seem to do much right now:
|
||||
|
||||
- Before: `<span style="color:#ffff66;">Bio</span>`
|
||||
- After: `<span style="color:#FFFF66;">Bio</span>`
|
||||
|
||||
*And there we have it!*
|
||||
|
||||
---
|
||||
|
||||
Some testing html:
|
||||
|
||||
```
|
||||
<main>
|
||||
<div>
|
||||
default <span style="color:#FFFF66;"><span style="color:#FFF3E7;">Bio</span></span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
No 73 (correct look) <span style="color:#FFFF66;">Bio</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
No 72 <span style="color:#FFF3E7;">Bio</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
72 = fore, 73 = back <span style="color:#FFFF66;"><span style="background-color:#FFF3E7;">Bio</span></span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
72 = fore, 73 = glow <span style="color:#FFFF66;"><span style="text-shadow: 0 0 3px #FFF3E7;">Bio</span></span>
|
||||
</div>
|
||||
</main>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Test Data
|
||||
|
||||
```
|
||||
Spreads a target's <72>F201FA</72>Bio<72>01</72> and <72>F201FA</72>Miasma<72>01</72> effects to nearby enemies.</If><Else/>Spreads a target's <72>F201FA</72>Bio<72>01</72> and <72>F201FA</72>Miasma<72>01</72> effects to nearby enemies.</If> Potency is reduced by 20% for the second enemy, 40% for the third, 60% for the fourth, and 80% for all remaining enemies. <72>F201F8</72>Duration:<72>01</72> Time remaining on original effect <72>F201F8</72>Additional Effect:<72>01</72> 15% chance that <72>F201FA</72>Bio<72>01</72> and <72>F201FA</72>Miasma<72>01</72> duration resets if shorter than original effect duration <If(GreaterThanOrEqualTo(PlayerParameter(72),58))><If(Equal(PlayerParameter(68),27))><72>F201F8</72>Additional Effect:<72>01</72> <72>F201FA</72>Aethertrail Attunement<72>01</72> <Else/></If><Else/></If><If(GreaterThanOrEqualTo(PlayerParameter(72),70))><If(Equal(PlayerParameter(68),28))><72>F201F8</72>Additional Effect:<72>01</72> Increases <72>F201F4</72>Faerie Gauge<72>01</72> by 10 <Else/></If><Else/></If><72>F201F8</72>Aetherflow Gauge Cost:<72>01</72> 1
|
||||
|
||||
Spread a target's <Color(-154)>Bio</Color> and <Color(-154)>Miasma</Color> effects (except <Color(-154)>Miasma II</Color>) to nearby enemies.<Else/>Spreads a target's <Color(-154)>Bio</Color> and <Color(-154)>Miasma</Color> effects to nearby enemies.</If><Else/>Spreads a target's <Color(-154)>Bio</Color> and <Color(-154)>Miasma</Color> effects to nearby enemies.</If>
|
||||
Potency is reduced by 20% for the second enemy, 40% for the third, 60% for the fourth, and 80% for all remaining enemies.
|
||||
<Color(52258)>Duration:</Color> Time remaining on original effect
|
||||
<Color(52258)>Additional Effect:</Color> 15% chance that <Color(-154)>Bio</Color> and <Color(-154)>Miasma</Color> duration resets if shorter than original effect duration
|
||||
<If(GreaterThanOrEqualTo(PlayerParameter(72),58))><If(Equal(PlayerParameter(68),27))><Color(52258)>Additional Effect:</Color> <Color(-154)>Aethertrail Attunement</Color>
|
||||
<Else/></If><Else/></If><If(GreaterThanOrEqualTo(PlayerParameter(72),70))><If(Equal(PlayerParameter(68),28))><Color(52258)>Additional Effect:</Color> Increases <Color(-34022)>Faerie Gauge</Color> by 10
|
||||
<Else/></If><Else/></If><Color(52258)>Aetherflow Gauge Cost:</Color> 1
|
||||
|
||||
Spread a target's <span style="color:#ffff66;">Bio</span> and <span style="color:#ffff66;">Miasma</span> effects (except <span style="color:#ffff66;">Miasma II</span>) to nearby enemies. Potency is reduced by 20% for the second enemy, 40% for the third, 60% for the fourth, and 80% for all remaining enemies.n<span style="color:#00cc22;">Duration:</span> Time remaining on original effectn<span style="color:#00cc22;">Additional Effect:</span> 15% chance that <span style="color:#ffff66;">Bio</span> and <span style="color:#ffff66;">Miasma</span> duration resets if shorter than original effect duration <span style="color:#00cc22;">Additional Effect:</span> <span style="color:#ffff66;">Aethertrail Attunement</span> <span style="color:#00cc22;">Additional Effect:</span> Increases <span style="color:#ff7b1a;">Faerie Gauge</span> by 10 <span style="color:#00cc22;">Aetherflow Gauge Cost:</span> 1
|
||||
```
|
||||
@@ -1,30 +0,0 @@
|
||||
Achievements link to a "pre-requisite type" that defines it as completed. The list of Types are:
|
||||
|
||||
```
|
||||
0: complete specific Legacy thing
|
||||
1: do n things
|
||||
2: complete n other achievements
|
||||
3: achieve n levels as class
|
||||
4: affix n materia to the same piece of gear
|
||||
5: complete all n of these requirement_2 - 9 things
|
||||
6: complete a specific quest
|
||||
7: complete all specific hunting log entries
|
||||
8: discover every location within...
|
||||
9: complete any of these requirement_2 - 9 quests (?)
|
||||
10: level your companion chocobo to rank n
|
||||
11: achieve PVP rank n with a specific Grand Company
|
||||
12: participate in n matches in The Feast
|
||||
13: triumph in n matches in The Feast
|
||||
14: complete a specific Trial
|
||||
15: achieve rank n with a specific Beast Tribe
|
||||
16: there is no type 16
|
||||
17: participate in n Frontline matches
|
||||
18: guide a specific Grand Company to n Frontline victories
|
||||
19: triumph in n Frontline matches
|
||||
20: attune to all aether currents in a specific area
|
||||
21: obtain n minions
|
||||
22: there is no type 22
|
||||
23: complete all Verminion challenges
|
||||
24: obtain a variety of anima weapon
|
||||
25: finish top 10 in a Regional Feast Championship
|
||||
```
|
||||
@@ -1,44 +0,0 @@
|
||||
MasterpieceSupplyDuty is where all of the Collectable information is kept. Unknown how scrips are calculated for base value,
|
||||
but Bonus / High Bonus is calculated very easily, for both EXP and Scrips.
|
||||
|
||||
(rounded down) Base * 1.1 = Bonus
|
||||
(rounded down) Base * 1.2 = High Bonus
|
||||
|
||||
Items which are 'in demand' (a yellow star next to their name in the turn-in window) are multipled by 20% as well (*1.2),
|
||||
so if an item is in demand then its base value is what the 'High Bonus' is normally, while Bonus/High Bonus are multiplied by
|
||||
1.1 and 1.2!
|
||||
|
||||
Example:
|
||||
|
||||
Base Item: Level 57 Hardsilver Hatchet. 46 Blue Scrips, 119808 EXP.
|
||||
Bonus Scrips Value (46*1.1) = 50.6 (round down to 50).
|
||||
High Scrips Value (46*1.2) = 55.2 (round down to 55).
|
||||
Bonus EXP Value (119808*1.1)= 131788.8 (rounded down to 131788).
|
||||
High EXP Value (119808*1.2)= 143769.6 (rounded down to 143769).
|
||||
|
||||
If Hardsilver Hatchet were in demand, then the base value would change:
|
||||
Scrips: 55
|
||||
EXP : 143769
|
||||
Bonus Scrips: (55*1.1 round down) = 60
|
||||
High Scrips: (55*1.2 round down) = 66
|
||||
Bonus EXP : (143769*1.1 round down) = 158145
|
||||
High EXP : (143769*1.2 round down) = 172522
|
||||
|
||||
Collectability ranges are in the MasterpieceSupplyDuty file, so all that's needed is to figure out how scrips and exp are
|
||||
actually calculated. EXP might be set in stone somewhere, as I've noticed all of the CRAFTING Collectables have the same EXP
|
||||
rewards across all classes for the exact same level (assuming an item isn't in demand, of course. But if an item is in demand,
|
||||
you can divide it by 1.2 and round up to get the info!) Gathering collectables I'd presume share a similar EXP systme, but I
|
||||
haven't mapped out those values yet.
|
||||
|
||||
Level = EXP
|
||||
------------------
|
||||
Level 51 = 52920
|
||||
Level 52 = 63360
|
||||
Level 53 = 77760
|
||||
Level 54 = 93600
|
||||
Level 55 = 110880
|
||||
Level 56 = 103680
|
||||
Level 57 = 119808
|
||||
Level 58 = 164505
|
||||
Level 59 = 155520
|
||||
Level 60 = 175564
|
||||
@@ -1,239 +0,0 @@
|
||||
# FFXIV Companion App
|
||||
- https://eu.finalfantasyxiv.com/companion/
|
||||
|
||||
The FINAL FANTASY XIV companion app has the ability to query the game servers for a specific resource to pull information (market info, retainer items, player items, chat messages, etc). This document will list research and provide logic for obtaining data out of the app as well as quering the companion API.
|
||||
|
||||
What we know:
|
||||
|
||||
- The Companion App Queries a PHP 7 server.
|
||||
- Queries are not performed in real-time but instead a request adds to a queue and then the servers process the request and feed the result back to the same request ID when queried again. This means you have to query several times to the same url (using the same request-id) in order to finally get a result on the nth try. This is usually around 2-3 seconds.
|
||||
- Tokens last 24 hours before being expired. Unknown if using the companion app will "increase" the duration a token however using the API does not extend the token duration.
|
||||
- You can download the APK anywhere and rename it to `.zip` to extract the contents, there are some nice icons and a very basic `sqlite` file, unfortunately this does not contain any useful info like Libra did (eg no dungeon loot tables anymore...)
|
||||
|
||||
## Current tools
|
||||
|
||||
Minoost has done some fantastic work reverse engineering the auth of the app, you can find his work here: https://github.com/Minoost/libpompom-sharp
|
||||
|
||||
## Getting data
|
||||
|
||||
This segment will provide a small amount of PHP as that is what I am familiar with, however because I've not translated the auth logic into PHP you will need a valid token
|
||||
|
||||
### Getting a token
|
||||
|
||||
This is quite easy if you setup some software. I use: https://www.telerik.com/fiddler
|
||||
- I use my iPhone X, you do not need to jail break: https://docs.telerik.com/fiddler/Configure-Fiddler/Tasks/ConfigureForiOS
|
||||
- If you need help with setting up Fiddle to talk to your iphone, just ping Vekien in xivapi.com Discord, it is very easy.
|
||||
- If you have an android, it is probably even simplier, there will be lots of guides.
|
||||
|
||||
Once you have a proxy to fiddler, go to filters and add the url `companion-eu.finalfantasyxiv.com`, change `-eu` to your data center, or remove it completely to see login/auth queries. If you use the app from here you can begin to see the different endpoints and how they work.
|
||||
|
||||
Once you have a valid token, you can provide it to any endpoint and everything will work. The following script in PHP requires you to install guzzle `composer req guzzlehttp/guzzle` (you could just use curl internally if you prefer)
|
||||
|
||||
**Some header info:**
|
||||
- `token` - Your token from the app, find via Fiddler
|
||||
- `request-id` - Any kind of string can be here, the app uses UUID however it can be anything, a string, text, numbers, whatever, a poem...
|
||||
> **Important** if you make the `request-id` unique PER REQUEST you will have a huge delay, around 2 seconds per call, if you use the same string for each request, you will get responses instantly from the API (often first query). It is more beneficial to hard code your request id, but this is not intentional by SE i believe, this is not what the app does...
|
||||
> **Important update: 27th August 2018** - This "exploit" seems to have been patched, there looks to be `request-id` rate limiting, a very basic NGINX with bursting. Need more testing. The request-id can still be anything but if you use the same one you'll get rate-limited by the app.
|
||||
|
||||
- `Content-Type` - `application/json;charset=utf-8`
|
||||
- `Accept` - `*/*'`
|
||||
- `domain-type` - `global`
|
||||
- `User-Agent` - `ffxivcomapp-e/1.0.1.0 CFNetwork/974.2.1 Darwin/18.0.0`
|
||||
|
||||
**PHP code to query the API**
|
||||
|
||||
```php
|
||||
<?php
|
||||
require __DIR__ .'/vendor/autoload.php';
|
||||
use GuzzleHttp\Client;
|
||||
|
||||
$headers = [
|
||||
// Your token, you would need to watch your traffic (eg I use Fiddler)
|
||||
// for this or use: https://github.com/Minoost/libpompom-sharp
|
||||
'token' => '<put your token here>',
|
||||
'request-id' => "lol whatever you want here",
|
||||
|
||||
// other random blab
|
||||
'Content-Type' => 'application/json;charset=utf-8',
|
||||
'Accept' => '*/*',
|
||||
'domain-type' => 'global',
|
||||
'User-Agent' => 'ffxivcomapp-e/1.0.1.0 CFNetwork/974.2.1 Darwin/18.0.0',
|
||||
];
|
||||
|
||||
$client = new Client([
|
||||
'base_uri' => 'https://companion-eu.finalfantasyxiv.com/', //'https://companion.finalfantasyxiv.com',
|
||||
'timeout' => 10,
|
||||
]);
|
||||
|
||||
/**
|
||||
* base_uri = https://companion.finalfantasyxiv.com
|
||||
* /points/status
|
||||
* /login/character
|
||||
* /login/characters
|
||||
* /login/region
|
||||
*
|
||||
* base_uri = https://companion-eu.finalfantasyxiv.com
|
||||
* ^^ can be na or ja (i think data center?)
|
||||
*
|
||||
* These require the correct endpoint, eg:
|
||||
* - login/characters/{character_id}, you need to query /login/character or /login/characters to
|
||||
* get your character id.
|
||||
*
|
||||
* It returns a region, which will be the new 'base_uri'
|
||||
*
|
||||
* /character/login-status
|
||||
* /items/character
|
||||
* /market/items/catalog/18189 (<-- item id , aka "catalogId")
|
||||
*/
|
||||
|
||||
// You have to keep hitting the API until you get a 200,
|
||||
// as it queues requests and processes them in the background
|
||||
// This will attempt 15 times delaying for 250ms
|
||||
foreach (range(0, 15) as $i) {
|
||||
// get market info for item id 18189
|
||||
$response = $client->get('/sight-v060/sight/market/items/catalog/18189', [
|
||||
\GuzzleHttp\RequestOptions::HEADERS => $headers,
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() == 200) {
|
||||
// this is the API JSON data
|
||||
$data = (string)$response->getBody();
|
||||
|
||||
// this is the json response from the server, do something with it.
|
||||
print_r($data);
|
||||
break;
|
||||
}
|
||||
|
||||
// delay for 250 milliseconds
|
||||
usleep(250000);
|
||||
}
|
||||
```
|
||||
|
||||
To run this code:
|
||||
- Update the `token`
|
||||
- install dependencies using composer: `composer req guzzlehttp/guzzle`
|
||||
- Copy the code to a file, eg: `companion.php`
|
||||
- Call: `php companion.php`
|
||||
- the variable `$data` will be json, you could do `json_decode($data, true)` to get the response as an array of data.
|
||||
|
||||
### Market Response info
|
||||
- `stack` Quantity for sale
|
||||
- `catalogId` ItemID you see in the game files
|
||||
- `signatureName` the person who crafted the item
|
||||
- `isCrafted` obvious
|
||||
- `hq` hq or not, (see how SE don't do `isHq`, consistency!)
|
||||
- `stain` the stain values, can't remember how this is formatted
|
||||
- `sellPrice` obvious the gil price
|
||||
- `sellRetainerName` the name of the retainer
|
||||
- `town` the id of the town, this matches identically to Town.csv you get from the data files
|
||||
- `materia` this is an array of materia:
|
||||
- `key` = ID of the materia in the materia.csv file
|
||||
- `grade` = the grade of the materia, which if you see in the materia.csv is just a numeric column number, starts from 0
|
||||
|
||||
### Minor notes
|
||||
|
||||
- Market endpoint ids match ItemSearchCategory.csv
|
||||
- Items that are not sellable can be queried, you will get no listings however you will get lodestone ID's which can be useful (HQ Icons!)
|
||||
- There doesn't seem to be any restrictions on spam or concurrent queries, I've been able to setup 4 terminals and query every item on the market board within 2 hours. I am going to test higher concurrent queries and see if there is a rate-limit, or I get banned lol.
|
||||
- At this time if you are looking to query the market board for every server you would need to have an account per server this is due to how the app locks you into 1 character for an account, if you try to switch characters it would reset your token. You could in theory use 1 account for many servers however you could not do this in real time and due to the nature of the API taking 2-3 seconds a response, you're looking at about 8 hours to query every possible item for 1 server. I'll let someone else handle the logistics of this (*note: Not tested if a token is reset when switching character, just assuming by how its designed*)
|
||||
- There are 66 servers, cheapest acc: $12.99 (trail accounts do not work), so looking at $858/month for 1 char per server :D
|
||||
- No known way to query Korean or Chinese market boards at this time.
|
||||
|
||||
### All endpoints:
|
||||
|
||||
##### Payments
|
||||
|
||||
- POST("points/kupo-nuts")
|
||||
- PUT("points/mog-coins/android")
|
||||
- GET("purchase/charge")
|
||||
- GET("purchase/cesa-limit")
|
||||
- GET("purchase/user-birth")
|
||||
- GET("points/history")
|
||||
- GET("points/status")
|
||||
- GET("points/products")
|
||||
- POST("purchase/user-birth")
|
||||
- POST("points/interrupted-process")
|
||||
- POST("purchase/transaction")
|
||||
|
||||
##### Market
|
||||
|
||||
- DELETE("market/retainers/{cid}/rack/{itemId}")
|
||||
- PATCH("market/retainers/{cid}/rack/{itemId}")
|
||||
- GET("market/items/catalog/{catalogId}/hq")
|
||||
- `catalogId` = ItemID in game files
|
||||
- GET("market/items/catalog/{catalogId}")
|
||||
- GET("market/items/category/{categoryId}")
|
||||
- `categoryId` = ItemSearchCategory.csv in game files
|
||||
- GET("market/retainers/{cid}")
|
||||
- GET("market/items/history/catalog/{catalogId}")
|
||||
- POST("market/item")
|
||||
- POST("market/retainers/{cid}/rack")
|
||||
- POST("market/retainers/{cid}")
|
||||
- POST("market/payment/transaction")
|
||||
- DELETE("market/retainers/{cid}")
|
||||
|
||||
##### Login
|
||||
|
||||
- DELETE("login/auth") - Headers({"domain-type: global"})
|
||||
- GET("login/character")
|
||||
- GET("login/characters") - Headers({"domain-type: global"})
|
||||
- GET("login/region") - Headers({"domain-type: global"})
|
||||
- POST("login/auth") - Headers({"domain-type: global"})
|
||||
- POST("login/characters/{cid}") - Headers({"domain-type: global"})
|
||||
- `{cid}` is an internal id and not what you see on lodestone
|
||||
- POST("login/token") - Headers({"domain-type: global"})
|
||||
- POST("login/advertising-id")
|
||||
- POST("login/fcm-token")
|
||||
|
||||
##### Item
|
||||
|
||||
- GET("items/character")
|
||||
- GET("character/login-status")
|
||||
- GET("items/retainers/{retainerCid}")
|
||||
- GET("retainers")
|
||||
- PUT("items/{type}/{cid}/gil")
|
||||
- PUT("items/{type}/{cid}/{storage}")
|
||||
- DELETE("items/{type}/{cid}/{storage}/{itemId}")
|
||||
- PUT("items/recycle/{itemId}")
|
||||
|
||||
##### ChatRoom
|
||||
|
||||
- POST("chatrooms/{rid}/members")
|
||||
- DELETE("chatrooms/{rid}")
|
||||
- DELETE("chatrooms/{rid}/messages/{seqNum}")
|
||||
- GET("chatrooms/{rid}")
|
||||
- GET("chatrooms")
|
||||
- GET("chatrooms/{rid}/messages")
|
||||
- POST("chatrooms")
|
||||
- POST("chatrooms/{rid}/messages")
|
||||
- POST("chatrooms/{rid}/push-notification")
|
||||
- PATCH("chatrooms/{rid}/messages/last-chat")
|
||||
- PATCH("chatrooms/{rid}/setting")
|
||||
|
||||
##### Address Book
|
||||
|
||||
- HTTP(hasBody=true, method="DELETE", path="address-book/blocklist")
|
||||
- GET("address-book")
|
||||
- GET("address-book/{cid}/profile")
|
||||
- POST("address-book/blocklist")
|
||||
|
||||
##### Schedule
|
||||
|
||||
- PATCH("schedules/{sid}/cancel")
|
||||
- PATCH("schedules/{sid}/close")
|
||||
- DELETE("schedules/{sid}")
|
||||
- PATCH("schedules/{sid}")
|
||||
- GET("schedules/chatrooms/{rid}")
|
||||
- GET("schedules/{sid}")
|
||||
- GET("schedules/history")
|
||||
- GET("schedules")
|
||||
- POST("schedules")
|
||||
- POST("schedules/{sid}/push-notification")
|
||||
- PATCH("schedules/{sid}/role")
|
||||
|
||||
##### Report
|
||||
|
||||
- POST("report/chatrooms/{rid}/message")
|
||||
- POST("report/chatrooms/{rid}")
|
||||
- POST("report/schedules/{sid}")
|
||||
- POST("report/schedules/{sid}/comment")
|
||||
@@ -1,87 +0,0 @@
|
||||
100 Standard Emotes 1 0 False
|
||||
|
||||
|
||||
<Clickable(
|
||||
<If(Equal(ObjectParameter(1),ObjectParameter(2)))>
|
||||
you
|
||||
<Else/>
|
||||
<If(PlayerParameter(7))>
|
||||
<SheetEn(ObjStr,2,PlayerParameter(7),1,1)/>
|
||||
<Else/>
|
||||
ObjectParameter(2)
|
||||
</If>
|
||||
</If>)/>
|
||||
|
||||
<If(Equal(ObjectParameter(1),ObjectParameter(2)))>
|
||||
look
|
||||
<Else/>
|
||||
looks
|
||||
</If>
|
||||
|
||||
at
|
||||
|
||||
<If(Equal(ObjectParameter(1),ObjectParameter(3)))>
|
||||
<If(PlayerParameter(8))>
|
||||
<SheetEn(ObjStr,2,PlayerParameter(8),1,1)/>
|
||||
<Else/>
|
||||
you
|
||||
</If>
|
||||
<Else/>
|
||||
<If(PlayerParameter(8))>
|
||||
<SheetEn(ObjStr,2,PlayerParameter(8),1,1)/>
|
||||
<Else/>
|
||||
ObjectParameter(3)
|
||||
</If>
|
||||
</If>
|
||||
|
||||
in surprise.
|
||||
|
||||
|
||||
Un-targeted
|
||||
Solo: [You] look surprised!
|
||||
Someone else: [Siyukan Virtue] looks surprised!
|
||||
|
||||
Targeted
|
||||
targeting other: [You] [look] at [the housing enthusiast] in surprise.
|
||||
player targeting you: [Siyukan Virtue] [looks] at [you] in surprise.
|
||||
player targeting other: [Siyukan Virtue] [looks] at [the housing enthusiast] in surprise.
|
||||
|
||||
|
||||
|
||||
JSON:
|
||||
|
||||
"en": [
|
||||
"?? missing character choice ",
|
||||
{
|
||||
"condition": {
|
||||
"left_operand": "target",
|
||||
"operator": "==",
|
||||
"right_operand": "not_self"
|
||||
},
|
||||
"no": "looks",
|
||||
"yes": "look"
|
||||
},
|
||||
" at ",
|
||||
{
|
||||
"condition": {
|
||||
"left_operand": "target",
|
||||
"operator": "==",
|
||||
"right_operand": "self"
|
||||
},
|
||||
"no": [
|
||||
{
|
||||
"condition": "is_player",
|
||||
"no": "self",
|
||||
"yes": "<SheetEn(ObjStr,2,PlayerParameter(8),1,1)/>"
|
||||
}
|
||||
],
|
||||
"yes": [
|
||||
{
|
||||
"condition": "is_player",
|
||||
"no": "you",
|
||||
"yes": "<SheetEn(ObjStr,2,PlayerParameter(8),1,1)/>"
|
||||
}
|
||||
]
|
||||
},
|
||||
" in surprise."
|
||||
],
|
||||
@@ -1,73 +0,0 @@
|
||||
Fate EXP is still a mystery, research so far:
|
||||
|
||||
Equation: ` fate offset 22 * paramgrow offset 8 + paramgrow offset 8 = exp`
|
||||
|
||||
# Where it has worked
|
||||
|
||||
**Example: Whiteout, level 36 fate, level 36 char, 18900 exp (9450 w/ no bonus)**
|
||||
- Whiteout - fate id: `465`, offset `22` = `41`
|
||||
- paramgrow (id `36` = your level??? or fate level...), offset `8` = `225 `
|
||||
- Equation: `41 * 225 + 225 = 9450`
|
||||
|
||||
---
|
||||
|
||||
**Example:A Log Up, level 35 fate, level 35 char, 18040 exp (9020 w/ no bonus)***
|
||||
- A Log Up - fate id: 466, offset `22` = `40`
|
||||
- paramgrow (id `35` = your level??? or fate level...), offset `8` = `220 `
|
||||
- Equation: `40 * 220 + 220 = 9020`
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
# Where it has not worked
|
||||
|
||||
**Eye in the Sky, level 50 fate, level 50 char. 32400 (+50% exp..) - 21600 without bonus**
|
||||
- Eye in the Sky - fate id: 787, offset 22 = 54
|
||||
- paramgrow (id 50), offset 8 = 300
|
||||
|
||||
Equation: `54 * 300 + 300 = 16500`
|
||||
|
||||
---
|
||||
|
||||
Baa Baa Black Sheep: id = 463
|
||||
|
||||
Offset 22 = 41
|
||||
Offset 8 = 220 for paramgrow 36 (fate level).
|
||||
|
||||
41 * 225 + 225 = 9450
|
||||
|
||||
level 32 fate, level 31 mch (so an under leveled mch), 15016 exp (+100% armoury bonus) should be 7508 without
|
||||
|
||||
|
||||
---
|
||||
|
||||
Jewels-of-the-Isle: id = 280
|
||||
Complete level: 32
|
||||
|
||||
fate - offset 22 = 37
|
||||
paramgrow - offset 8 = 200
|
||||
|
||||
37 * 200 + 200
|
||||
|
||||
|
||||
---
|
||||
|
||||
Follow the Light, level 33 fate on a level 31 char. 14678 exp (+100%), should be 7339 without bonus
|
||||
|
||||
Fate ID: 286
|
||||
|
||||
fate - offset 22 = 38
|
||||
paramgrow offset 8 = 200 (33 = 210)
|
||||
|
||||
---
|
||||
|
||||
|
||||
<Hezkezl>: Just a Matter of Rut, level 31 FATE. Level 31 mch, got 15200 exp (7600 w/out bonus)
|
||||
|
||||
fate id: 287
|
||||
|
||||
fate - offset 22 = 36
|
||||
paramgrow offset 8 = 200
|
||||
|
||||
36 * 200 + 200
|
||||
@@ -1,6 +0,0 @@
|
||||
The sync level for fates is always +5 of the fate level.
|
||||
|
||||
## Test Data
|
||||
- level 9 fate, level 60 char syncs down to 14
|
||||
- level 39 fate, level 60 char syncs down to 44
|
||||
- level 36 fate, level 60 char syncs down to 41
|
||||
@@ -1,42 +0,0 @@
|
||||
# FFXIV Icon Paths
|
||||
|
||||
The path for icons are very funky, depending on the icon number you need to format the path very specifically, here is a function that does it in PHP which should be easy to translate into other languages:
|
||||
|
||||
```php
|
||||
// Is the number 6 or more characters?
|
||||
$extended = (strlen($number) >= 6);
|
||||
|
||||
// get icon based on number size, prepend 0 if its below 6 characters.
|
||||
$icon = $extended
|
||||
? str_pad($number, 5, "0", STR_PAD_LEFT)
|
||||
: '0' . str_pad($number, 5, "0", STR_PAD_LEFT);
|
||||
|
||||
// build path, take values [0, 1, 2] from the number, add 000 for extended
|
||||
// for non extended start with 0 and add [1,2] + 000 for path.
|
||||
$path = $extended
|
||||
? $icon[0] . $icon[1] . $icon[2] .'000'
|
||||
: '0'. $icon[1] . $icon[2] .'000';
|
||||
```
|
||||
|
||||
The result would be:
|
||||
|
||||
- `26039`
|
||||
- Is less than 5? **yes**
|
||||
- Folder: `0 26 000` (the 0 we prepended)
|
||||
- Filename: `026039`
|
||||
- Result: /026000/026039.png 
|
||||
|
||||
XIVDB Icons can be obtained via: `https://secure.xivdb.com/img/game/{id path}`
|
||||
|
||||
# XIVDB Icons
|
||||
|
||||
> Items only.
|
||||
|
||||
It's possible to obtain icons based on their in-game ID, for example:
|
||||
|
||||
- Word of the Magnate Replica = `16923`
|
||||
- Take first number for folder: `1`
|
||||
- Path: `/img/game_local/ 1 / 16923 .jpg`
|
||||
- Result: 
|
||||
|
||||
The XIVDB icons are not always available, when a new patch comes out it is difficult to get the latest icons.
|
||||
@@ -1,164 +0,0 @@
|
||||
### 1013
|
||||
Mount Barding (data_0 = BuddyEquip)
|
||||
|
||||
### 1053
|
||||
Company Issue Expectorant, Company Issue Tonic, Over-Aspected Cluster, Phoenix Down, all "revive" of some sort
|
||||
|
||||
### 1054
|
||||
Attribute Reset, no longer used
|
||||
|
||||
### 1055
|
||||
Cordials, eg: Watered Cordial, hi cordial - Restores GP
|
||||
- `data_0` = max GP to restore
|
||||
|
||||
### 1058
|
||||
Used to upgrade an Animus weapon to a Novus weapon
|
||||
|
||||
### 1322
|
||||
Unlock Mount
|
||||
- `data_0` = `Mount`
|
||||
|
||||
### 1326
|
||||
Fantasia + Retainer Fantasia, data_1 is a bool for "is for retainer"
|
||||
|
||||
### 1767
|
||||
Wings, eg: Icarus Wing, restores TP
|
||||
- `data_0` = max TP to restore
|
||||
|
||||
### 1769
|
||||
*not used*
|
||||
|
||||
### 2120
|
||||
Cards and Eggs + Starlite giftbox, nothing in data_0, unsure the meaning
|
||||
|
||||
### 2136
|
||||
Unlocks master recipes, (eg Master Carpenter I),
|
||||
- `data_0` = `SecretRecipeBook`
|
||||
|
||||
### 2483
|
||||
Chocobo Food. Unsure what data_0 is, it's ids 1,2,3,4,5,6,7
|
||||
|
||||
### 2633
|
||||
Unlocks Ballroom emotes, speed boost for mounts on specific maps as well as hair style..
|
||||
- `data_1` = `LogMessage`
|
||||
|
||||
### 2634
|
||||
Reagan Pepper, Resets chocobos SP
|
||||
|
||||
### 2645
|
||||
Realm Reborn Red, celebrate!
|
||||
|
||||
### 2894
|
||||
Eternity Ring, teleport to partner
|
||||
|
||||
### 3237
|
||||
XXX for beginners, eg: Armoring for Beginners,
|
||||
- `data_0` = `ClassJob`
|
||||
|
||||
### 3240
|
||||
Blank Invitation, creates a Ceremony Invitation with your wedding date on it
|
||||
|
||||
### 3292
|
||||
Thavnairian Onion - Chocobo food to level it past level 10+ (so every level past 10)
|
||||
|
||||
### 3357
|
||||
Unlock use of Triple Triad Cards
|
||||
- `data_0` = `TripleTriadCard`
|
||||
|
||||
### 3800
|
||||
Items that grant MGP
|
||||
- `data_0` = the amount of MGP to reward
|
||||
|
||||
### 4107
|
||||
eg: Tome of Ichthyological Folklore
|
||||
- `data_0` = `GatheringSubCategory`
|
||||
|
||||
### 4647
|
||||
Unlocking coffers, Wrapped Presents or using Elemental Ward Potions
|
||||
|
||||
### 5136
|
||||
Celebratory fireworks, eg: Heavenscracker
|
||||
|
||||
### 5564
|
||||
Tales of Adventure, boosts to level 60, unsure how the 11->24 links (not class job), 11 = something Paladin related
|
||||
|
||||
### 5845
|
||||
Unlocks a Orchestrion Roll
|
||||
- `data_0` = `Orchestrion`
|
||||
|
||||
### 816
|
||||
Company Manuals + Squadron Gear Maintenance Manual, unsure what it links to
|
||||
|
||||
### 841
|
||||
Using potions, eg: Paralyzing Potion. Not sure how data_0 links (also has data_hq_0)
|
||||
|
||||
### 842
|
||||
Debuff removal items, eg: Echo Drops, Antitiode, etc.
|
||||
- `Data_0-8` = `Status`
|
||||
|
||||
### 843
|
||||
Grants ability to discover concealed nodes, eg: Traders' Favor (Dravanian Forelands), unsure how data_1 links
|
||||
|
||||
### 844
|
||||
Battle Food,
|
||||
- `data_0` (all 48, maybe an animation)??
|
||||
- `data_1` = `ItemFood`
|
||||
- `data_2` = Duration in seconds
|
||||
|
||||
### 845
|
||||
Crafting + Gathering Food
|
||||
- `data_0` (all 48, maybe an animation)??
|
||||
- `data_1` = `ItemFood`
|
||||
- `data_2` = Duration in seconds
|
||||
|
||||
### 846
|
||||
Atrtribute Potions, eg: X-Potion of Dexterity
|
||||
|
||||
### 847
|
||||
Health potions, eg: X-Potion
|
||||
- `data_0` = %
|
||||
- `data_1` = max value
|
||||
|
||||
### 848
|
||||
Ether MP potions, eg: X-Ether
|
||||
- `data_0` = %
|
||||
- `data_1` = max
|
||||
|
||||
### 849
|
||||
Elixir potions,
|
||||
- HP
|
||||
- `data_0` = %
|
||||
- `data_1` = max value
|
||||
- MP
|
||||
- `data_3`
|
||||
- `data_4`
|
||||
|
||||
### 850
|
||||
Sanction effect
|
||||
|
||||
### 851
|
||||
Gysahl Greens, Summon Chocobo
|
||||
|
||||
### 852
|
||||
Consumable effects such as sparklers, etc
|
||||
|
||||
### 853
|
||||
Obtain a minion
|
||||
- `data_0` = Minion
|
||||
|
||||
### 8667
|
||||
Sustaining Potion, unsure how data_0 links
|
||||
|
||||
### 9343
|
||||
Fate Action.. Stygian Ash, Removes the shield from Ixion
|
||||
|
||||
### 944
|
||||
Magicked Prism
|
||||
|
||||
### 9993
|
||||
"High-grade Company-issue Tonic...", Revives?
|
||||
|
||||
### 9994
|
||||
Teleport ticket to a grand company
|
||||
- `data_0` = `GrandCompany`
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
Most of these are kind of obvious, but will add here for safe keeping!
|
||||
|
||||
# Attack speed
|
||||
|
||||
Equation: `(attack speed / 1000 `
|
||||
|
||||
# Auto Attack
|
||||
|
||||
Equation: `(damage / 3 * attack speed)`
|
||||
|
||||
Remember to use HQ damage when determining Auto-Attack for HQ items.
|
||||
|
||||
# DPS
|
||||
|
||||
Equation: `(damage / 3)`
|
||||
|
||||
# Finding HQ values
|
||||
|
||||
HQ values are just the difference, the "additional number", if an item is Accuracy: 32, and HQ Accuracy: 35, the HQ value is listed as 3.
|
||||
|
||||
An example can be found at offsets: [34, 50, 75 = value, param, hq](https://github.com/viion/XIV-Datamining/blob/master/offsets/Items/items_csv_xivdb_unverified.txt#L15-L17)
|
||||
|
||||
# Max meld values
|
||||
|
||||
```csharp
|
||||
class ItemLevel {
|
||||
public int GetMaximum(BaseParam baseParam) {
|
||||
const int Offset = -1;
|
||||
return baseParam.Key == 0 ? 0 : Convert.ToInt32(this[Offset + baseParam.Key]);
|
||||
}
|
||||
}
|
||||
|
||||
class BaseParam {
|
||||
public int GetMaximum(EquipSlotCategory category) {
|
||||
const int Offset = 2;
|
||||
return category.Key == 0 ? 0 : Convert.ToInt32(this[Offset + category.Key]);
|
||||
}
|
||||
|
||||
public int GetModifier(int role) {
|
||||
const int Offset = 24;
|
||||
const int Maximum = 12;
|
||||
if (role < 0 || role > Maximum)
|
||||
return 0;
|
||||
return Convert.ToInt32(this[Offset + role]);
|
||||
}
|
||||
}
|
||||
|
||||
class Equipment {
|
||||
public int GetMaximumParamValue(BaseParam baseParam)
|
||||
// Base value for the param based on the item's level
|
||||
var maxBase = ItemLevel.GetMaximum(baseParam);
|
||||
// Factor, in percent, for the param when applied to the item's equip slot
|
||||
var slotFactor = baseParam.GetMaximum(EquipSlotCategory);
|
||||
// Factor, in percent, for the param when used for the item's role
|
||||
var roleModifier = baseParam.GetModifier(BaseParamModifier);
|
||||
|
||||
// Rounding appears to use AwayFromZero. Tested with:
|
||||
// Velveteen Work Gloves (#3601) for gathering (34.5 -> 35)
|
||||
// Gryphonskin Ring (#4526) for wind resistance (4.5 -> 5)
|
||||
// Fingerless Goatskin Gloves of Gathering (#3578) for GP (2.5 -> 3)
|
||||
return (int)Math.Round(maxBase * slotFactor * roleModifier / 10000.0, MidpointRounding.AwayFromZero);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# Overall Item Level
|
||||
|
||||
This one is really for gearset applications.
|
||||
|
||||
Item level is `*2` when involving two handed weapons, this is to compensate for off hand. You can test this by equipping only a Sword in game, then equip your Shield of the same item level and record the two values. Then equip a two handed weapon, your character will be the same overal item level as a Sword+Shield.
|
||||
|
||||
# Category to Slot ID
|
||||
```
|
||||
// slot id => cat id
|
||||
[
|
||||
// main hand
|
||||
738 => [
|
||||
1,2,3,4,5,6,7,8,9,10,84,87,88,89,
|
||||
12,14,16,18,20,22,24,26,28,30,32,
|
||||
],
|
||||
|
||||
// off hand
|
||||
739 => [
|
||||
13,15,17,10,21,23,25,27,29,31,11
|
||||
],
|
||||
|
||||
// head, body, hands, waist, legs, feet, ears, neck, wrists, ring
|
||||
740 => [34],
|
||||
741 => [35],
|
||||
742 => [37],
|
||||
743 => [39],
|
||||
744 => [36],
|
||||
745 => [38],
|
||||
746 => [41],
|
||||
747 => [40],
|
||||
748 => [42],
|
||||
748 => [43],
|
||||
];
|
||||
```
|
||||
@@ -1,15 +0,0 @@
|
||||
# Leves to NPC
|
||||
|
||||
Leves are linked to an NPC via the levels_csv, this is also the same for the starting position of the Leve.
|
||||
|
||||
|
||||
## Example:
|
||||
- Leve ID: `906` (Necrologos: His Treasure Forhelen (L))
|
||||
- NPC ID: `1011208` (Eloin)
|
||||
|
||||
npc: `1011208` in levels = id: `5581915`
|
||||
|
||||
Then find 5581915 in leves CSV.
|
||||
|
||||
- Start npc: Offset `8`
|
||||
- Start Position: Offset `9`
|
||||
@@ -1,117 +0,0 @@
|
||||
# Conversions to 2D Map
|
||||
|
||||
*Code samples in C#*
|
||||
|
||||
Conversion of a Level coordinate into a 2D map coordinate:
|
||||
```csharp
|
||||
private double ToMapCoordinate(double val) {
|
||||
var c = Map.SizeFactor / 100.0;
|
||||
|
||||
val *= c;
|
||||
return ((41.0 / c) * ((val + 1024.0) / 2048.0)) + 1;
|
||||
}
|
||||
```
|
||||
|
||||
Conversion of a FishingSpot coordinate into a 2D map coordinate:
|
||||
```csharp
|
||||
private double ToMapCoordinate(double val) {
|
||||
var c = TerritoryType.Map.SizeFactor / 100.0;
|
||||
|
||||
return (41.0 / c) * ((val) / 2048.0) + 1;
|
||||
}
|
||||
```
|
||||
|
||||
# Conversions to Long/Lat (eg Leaflets/Google Maps)
|
||||
|
||||
*Code samples in PHP*
|
||||
|
||||
You can translate the `levels.exd` values to Long/Lat using the `x`, `y` and **scale** would be the maps [size_factor](https://github.com/viion/XIV-Datamining/blob/master/offsets/3.1_list.txt#L679)
|
||||
|
||||
```php
|
||||
//
|
||||
// Used due to spherical maps, world is round irl :P
|
||||
//
|
||||
function mapsRadiansToDegrees($rad)
|
||||
{
|
||||
return $rad / (pi() / 180);
|
||||
}
|
||||
|
||||
//
|
||||
// This code will take the X, Y and map scale from the levels CSV and convert it into a LAT/LONG
|
||||
// which can be used on leaflet or Google maps or just about any map tool
|
||||
//
|
||||
public function mapsTranslateXYZtoLatLong($x, $y, $scale = 100)
|
||||
{
|
||||
if ($x == 0 || $y == 0 || $scale == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$scale = $scale / 100;
|
||||
|
||||
$tilesize = 2048 / $scale;
|
||||
|
||||
$pixelsPerLonDegree = $tilesize / 360;
|
||||
$pixelsPerLonRadian = $tilesize / (2 * pi());
|
||||
|
||||
$lng = $x / $pixelsPerLonDegree;
|
||||
$latRadians = $y / -$pixelsPerLonRadian;
|
||||
$lat = $this->mapsRadiansToDegrees(2 * atan(exp($latRadians)) - pi() / 2);
|
||||
|
||||
$coords = [
|
||||
'x' => $lng,
|
||||
'y' => $lat,
|
||||
];
|
||||
|
||||
return $coords;
|
||||
}
|
||||
```
|
||||
|
||||
To translate a `levels.exd` position to an in-game X/Y:
|
||||
|
||||
```php
|
||||
//
|
||||
// This takes the X/Y, scale and tilescale from levels and converts it to
|
||||
// and in-game X/Y position, I have not found tile scale in the files
|
||||
// and it's never not been 50 (50px per tile), its not 100% accurate,
|
||||
// tilescale is actually something like 49.951219... (2048/41)
|
||||
//
|
||||
public function mapsTranslateXYZToGame($x, $y, $scale, $tilescale = 50)
|
||||
{
|
||||
if ($x == 0 || $y == 0 || $scale == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$map = 2048 / ($scale / 100);
|
||||
|
||||
$tilecount = $map / $tilescale;
|
||||
|
||||
$x = ceil(round(($x / $tilescale) + ($tilecount / 2),1));
|
||||
$y = ceil(round(($y / $tilescale) + ($tilecount / 2),1));
|
||||
|
||||
return [
|
||||
'x' => $x,
|
||||
'y' => $y,
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
And finally, to translate an in game X/Y to a `levels.exd` position which can then be converted to a 2D or Long/Lat:
|
||||
|
||||
```php
|
||||
//
|
||||
// Translates a X/Y from in-game to a levels output (which can then be translate dto lat/long
|
||||
// this is quite accurate, not found it to be off at all. Requires map scale
|
||||
//
|
||||
public function mapsTranslateGameToXYZ($x, $y, $scale)
|
||||
{
|
||||
$scale = $scale / 100;
|
||||
|
||||
$x = ($x*50)-25-(1024 / $scale);
|
||||
$y = ($y*50)-25-(1024 / $scale);
|
||||
|
||||
return [
|
||||
'x' => $x,
|
||||
'y' => $y,
|
||||
];
|
||||
}
|
||||
```
|
||||
@@ -1,11 +0,0 @@
|
||||
31 = 100 = Lower La Noscea = 41.9
|
||||
|
||||
52 = 200 = New Gridania = 21.4
|
||||
|
||||
58 = 300 = The Tam-Tara Deepcroft = 14.6
|
||||
|
||||
359 = 400 = The Navel = 8.6
|
||||
|
||||
733 = 800 = Mizzenmast Inn = 6.0
|
||||
|
||||
2200 = 95 = Coerthas Western Highlands = 44
|
||||
@@ -1,60 +0,0 @@
|
||||
Quest experience points are generated using a small equation.
|
||||
Why this is done I am not sure as the values always seem static.
|
||||
|
||||
### Formula updated: 28th May, 2018
|
||||
|
||||
|
||||
**CORE FORMULA**
|
||||
> `EXP = Quest.ExpFactor * ParamGrow.QuestExpModifier * (45 + (5 * Quest.ClassJobLevel_0)) / 100`
|
||||
|
||||
All formula's use this as their starting point and ADD onto it.
|
||||
|
||||
**QUEST LEVEL 50**
|
||||
> `EXP = CORE + ((400 * (Quest.ExpFactor / 100)) + ((Quest.ClassJobLevel_0-52) * (400 * (Quest.ExpFactor/100))))`
|
||||
|
||||
**QUEST LEVEL 51**
|
||||
> `EXP = CORE + ((800 * (Quest.ExpFactor / 100)) + ((Quest.ClassJobLevel_0-52) * (800 * (Quest.ExpFactor/100))))`
|
||||
|
||||
**QUEST LEVEL 52-59**
|
||||
> `EXP = CORE + ((2000 * (Quest.ExpFactor / 100)) + ((Quest.ClassJobLevel_0-52) * (2000 * (Quest.ExpFactor/100))))`
|
||||
|
||||
**QUEST LEVEL 60-69**
|
||||
> `EXP =CORE + ((37125 * (Quest.ExpFactor / 100)) + ((Quest.ClassJobLevel_0-60) * (3375 * (Quest.ExpFactor/100))))`
|
||||
|
||||
|
||||
-----
|
||||
|
||||
#### Formula in PHP:
|
||||
|
||||
This is example code, it requires getting the `quest` and `paramGrow` prior
|
||||
|
||||
```php
|
||||
$quest = $service->get("Quest_<id>");
|
||||
$paramGrow = $service->get("xiv_ParamGrow_{$quest->ClassJobLevel_0}");
|
||||
|
||||
// CORE = Quest.ExpFactor * ParamGrow.QuestExpModifier * (45 + (5 * Quest.ClassJobLevel_0)) / 100
|
||||
$EXP = $quest->ExpFactor * $paramGrow->QuestExpModifier * (45 + (5 * $quest->ClassJobLevel_0)) / 100;
|
||||
|
||||
// CORE + ((400 * (Quest.ExpFactor / 100)) + ((Quest.ClassJobLevel_0-52) * (400 * (Quest.ExpFactor/100))))
|
||||
if (in_array($quest->ClassJobLevel_0, [50])) {
|
||||
$EXP = $EXP + ((400 * ($quest->ExpFactor / 100)) + (($quest->ClassJobLevel_0 - 50) * (400 * ($quest->ExpFactor / 100))));
|
||||
}
|
||||
|
||||
// CORE + ((800 * (Quest.ExpFactor / 100)) + ((Quest.ClassJobLevel_0-52) * (800 * (Quest.ExpFactor/100))))
|
||||
else if (in_array($quest->ClassJobLevel_0, [51])) {
|
||||
$EXP = $EXP + ((800 * ($quest->ExpFactor / 100)) + (($quest->ClassJobLevel_0 - 50) * (400 * ($quest->ExpFactor / 100))));
|
||||
}
|
||||
|
||||
// CORE + ((2000 * (Quest.ExpFactor / 100)) + ((Quest.ClassJobLevel_0-52) * (2000 * (Quest.ExpFactor/100))))
|
||||
else if (in_array($quest->ClassJobLevel_0, [52,53,54,55,56,57,58,59])) {
|
||||
$EXP = $EXP + ((2000 * ($quest->ExpFactor / 100)) + (($quest->ClassJobLevel_0 - 52) * (2000 * ($quest->ExpFactor / 100))));
|
||||
}
|
||||
|
||||
// CORE + ((37125 * (Quest.ExpFactor / 100)) + ((Quest.ClassJobLevel_0-60) * (3375 * (Quest.ExpFactor/100))))
|
||||
else if (in_array($quest->ClassJobLevel_0, [60,61,62,63,64,65,66,67,68,69])) {
|
||||
$EXP = $EXP + ((37125 * ($quest->ExpFactor / 100)) + (($quest->ClassJobLevel_0 - 60) * (3375 * ($quest->ExpFactor / 100))));
|
||||
}
|
||||
|
||||
// Set
|
||||
$quest->ExperiencePoints = $EXP;
|
||||
```
|
||||
@@ -1,32 +0,0 @@
|
||||
The current weather of a zone is based on the current unix epoch, and a zone weather table (WeatherRate).
|
||||
A forecast target integer is first calculated. This value to the frequency of the zone's weather.
|
||||
When the number is less than the cumulative rate at which a weather pattern occurs, that weather will appear in the zone.
|
||||
For example, in Gridania it's raining 20% of the time, and foggy 10%. It rains when the number is < 20, and foggy when it's >= 20 and < 30, and so on.
|
||||
|
||||
````js
|
||||
calculateForecastTarget: function(lDate) {
|
||||
// Thanks to Rogueadyn's SaintCoinach library for this calculation.
|
||||
// lDate is the current local time.
|
||||
|
||||
var unixSeconds = parseInt(lDate.getTime() / 1000);
|
||||
// Get Eorzea hour for weather start
|
||||
var bell = unixSeconds / 175;
|
||||
|
||||
// Do the magic 'cause for calculations 16:00 is 0, 00:00 is 8 and 08:00 is 16
|
||||
var increment = (bell + 8 - (bell % 8)) % 24;
|
||||
|
||||
// Take Eorzea days since unix epoch
|
||||
var totalDays = unixSeconds / 4200;
|
||||
totalDays = (totalDays << 32) >>> 0; // Convert to uint
|
||||
|
||||
// 0x64 = 100
|
||||
var calcBase = totalDays * 100 + increment;
|
||||
|
||||
// 0xB = 11
|
||||
var step1 = (calcBase << 11) ^ calcBase;
|
||||
var step2 = (step1 >>> 8) ^ step1;
|
||||
|
||||
// 0x64 = 100
|
||||
return step2 % 100;
|
||||
}
|
||||
````
|
||||
Reference in New Issue
Block a user