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

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more