Public
Edited
Oct 2, 2024
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
const quests = [...questData.values()]
// const questsWithNpcs = quests.filter(it=>it.scriptedNpcs.size > 0).map(it=>{
// const multiLocNpcs = [...it.scriptedNpcs.values()].filter(npc=>{
// return npcIDtoLocationsMap.get(npc) === undefined
// })
// return {
// id: it.id,
// name: it.name,
// multiLocNpcs,
// }
// })
const questsWithListeners = quests.filter(it=>it.listeners.size > 0).map(it=>{
const npcs = [...it.listeners.values()]
const npcsWithName = npcs.filter(npcID=>npcData.get(npcID).name !== '')
const multiLocNpcs = npcsWithName.filter(npcID=>{
const locs = npcIDtoLocationsMap.get(npcID)
return locs !== undefined && locs.length > 1
}).map(it=>{
return {
id: it,
name: npcData.get(it).name,
diffrentLocNum: npcIDtoLocationsMap.get(it).length
}
})
return {
id: it.id,
name: it.name,
npcs: npcs.map(it=>{
return {
id: it,
name: npcData.get(it).name,
}
}),
npcsWithName: npcsWithName.map(it=>{
return {
id: it,
name: npcData.get(it).name,
}
}),
multiLocNpcs,
}
}).filter(it=>it.multiLocNpcs.length > 0)
return questsWithListeners
}
Insert cell
questData = {
const map = new Map()
questCSV.forEach(row=>{
const questID = row[0]
const locations = []
const todoStart = 1222
const todoLength = 24
const todoArraySize = 8
for (let i = 0; i < todoLength; i++) {
const array = []
for (let j = 0; j < todoArraySize; j++) {
const locID = row[todoStart + j*24 + i]
if (locID !== '0') {
const location = levelData.get(locID)
let npcName = ''
if (location.object !== '0') npcName = npcData.get(location.object)?.name
array.push({
...location,
npcName,
})
}
}
locations.push(array)
}
const issuer = row[40]
const issuerLocation = row[41]
const target = row[43]
// related npc from script and listeners
const scriptArgStart = 100
const scriptArgLength = 50
const scriptedNpcs = new Set()
for (let i = 0; i < scriptArgLength; i++) {
const id = row[scriptArgStart + i]
if (id === '0') continue
if (npcData.has(id) && npcData.get(id).name !== '') {
scriptedNpcs.add(id)
}
}
const listeners = new Set()
const listenerStart = 278
const listenerLength = 64
for (let i = 0; i < listenerLength; i++) {
const id = row[listenerStart + i]
if (id === '0') continue
if (npcData.has(id)) {
listeners.add(id)
}
}
map.set(questID, {
id: questID,
name: row[1],
issuer,
issuerLocation,
target,
locations,
scriptedNpcs,
listeners,
raw: row,
})
})
return map
}
Insert cell
npcData = {
const map = new Map()
eNpcResidentCSV.forEach(row=>{
map.set(row[0], {
name: row[1],
raw: row,
})
})
return map
}
Insert cell
[...npcIDtoLocationsMap.entries()].filter(([k,v])=>v.length > 1 && v.some(it=>it.type === '8'))
Insert cell
Insert cell
// 0,1 ,2 ,3 ,4 ,5 ,6,7 ,8 ,9 ,10 ,11
// #,Singular,Adjective,Plural,PossessivePronoun,StartsWithVowel, ,Pronoun,Article,Title,Map,
eNpcResidentCSV = {
const en = 'https://raw.githubusercontent.com/xivapi/ffxiv-datamining/master/csv/ENpcResident.csv'
const zh = 'https://raw.githubusercontent.com/thewakingsands/ffxiv-datamining-cn/master/ENpcResident.csv'
const target = serverType === 'zh' ? zh : en
return parseCSV(await (await fetch(target)).text())
}
Insert cell
[...levelData.values()].filter(it=>!npcData.get(it.object))
Insert cell
levelData = {
const map = new Map()
levelCSV.forEach(row=>{
map.set(row[0], {
id: row[0],
x: row[1],
y: row[2],
z: row[3],
yaw: row[4],
radius: row[5],
type: row[6],
object: row[7],
map: row[8],
eventID: row[9],
territory: row[10],
})
})
return map
}
Insert cell
// 0 ,1,2,3,4 ,5 ,6 ,7 ,8 ,9 ,10
// # ,X,Y,Z,Yaw,Radius,Type,Object,Map,EventId,Territory
levelCSV = {
const en = 'https://raw.githubusercontent.com/xivapi/ffxiv-datamining/master/csv/Level.csv'
const zh = 'https://raw.githubusercontent.com/thewakingsands/ffxiv-datamining-cn/master/Level.csv'
const target = serverType === 'zh' ? zh : en
return parseCSV(await (await fetch(target)).text())
}
Insert cell
// https://docs.google.com/spreadsheets/d/14R7dPKGwBQr7lA8YjtZJDfc_HbrAw1aeUeHSoN5PK3w/edit?usp=sharing
questCSV = {
const en = 'https://raw.githubusercontent.com/xivapi/ffxiv-datamining/master/csv/Quest.csv'
const zh = 'https://raw.githubusercontent.com/thewakingsands/ffxiv-datamining-cn/master/Quest.csv'
const target = serverType === 'zh' ? zh : en
return parseCSV(await (await fetch(target)).text())
}
Insert cell
function parseCSV(text) {
const lineend0 = text.indexOf('\n') // key,0,1 ...
const lineend1 = text.indexOf('\n', lineend0 + 1) // #, Name, ...
const lineend2 = text.indexOf('\n', lineend1 + 1) // int,str, ...
// const lineend3 = text.indexOf('\n', lineend2 + 1) // 0, '', ... , some files don't start with 0
const idxes = text.slice(0, lineend0).split(',')
const labels = text.slice(lineend0 + 1, lineend1).split(',')
return d3.csvParseRows(text.slice(lineend2 + 1))
}
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more