Skip to content

Commit

Permalink
add: tower-quests script
Browse files Browse the repository at this point in the history
  • Loading branch information
Mitsunee committed Oct 17, 2024
1 parent 1c796d7 commit 72ccf31
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 4 deletions.
19 changes: 15 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,26 @@ pnpm exp-calc

This script can be configured with a level range and filter function to determine the mobs for exp comparisons (similar to Exp Compare)

### Tower Quests

```
pnpm tsx src/tower-quests.ts
```

Simple script that finds all Forsaken Tower quests, organizes them by floor and outputs a list with how much exp is earned at each possible level for those floors, as well as how much penya is earned and how many quests are available.

## Cache

Instead of constantly redownloading the same data the `pnpm prepare-cache` script downloads all needed data at once and stores it in `./data`:

```
data
├── monsters.json (list of all monster IDs)
└── monster
├── {id}.json (individual monster data)
└── skill
└── {id}.json (individual skill data)
├── quests.json (list of all quest IDs)
├── monster
| ├── {id}.json (individual monster data)
| └── skill
| └── {id}.json (individual skill data)
└── quests
└── {id}.json (individual quest data)
```
80 changes: 80 additions & 0 deletions src/tower-quests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { getQuestData } from "./utils/get-quest-data";
import { getQuestList } from "./utils/get-quest-list";

//const [, , ...args] = process.argv;

interface TowerQuestData extends QuestData {
beginNPC: 11734;
minLevel: number;
maxLevel: number;
endReceiveExperience: NonNullable<QuestData["endReceiveExperience"]>;
endReceiveGold: NonNullable<QuestData["endReceiveGold"]>;
}

interface TowerFloor {
num: number;
minLevel: number;
maxLevel: number;
quests: Array<TowerQuestData>;
}

function isTowerQuest(quest: QuestData): quest is TowerQuestData {
if (quest.beginNPC !== 11734) return false;
return true;
}

async function main() {
const fullList = await getQuestList();
const towerQuests = new Array<TowerQuestData>();
const towerFloors = new Array<TowerFloor>();

// find all tower quests
for (const id of fullList) {
const data = await getQuestData(id);
if (!isTowerQuest(data)) continue;
towerQuests.push(data);
}

// sort by minimum level and get list of max levels
towerQuests.sort((a, b) => a.minLevel - b.minLevel);
const maxLevels = Array.from(
new Set(towerQuests.map(quest => quest.maxLevel))
).sort((a, b) => a - b);

// sort by floor
for (let i = 0; i < maxLevels.length; i++) {
const num = i + 1;
const quests = towerQuests.filter(quest => quest.maxLevel == maxLevels[i]);
const minLevel = Math.min(...quests.map(quest => quest.minLevel));
towerFloors.push({ num, minLevel, maxLevel: maxLevels[i], quests });
}

console.log(`Found ${towerFloors.length} Floors`);

for (const { num, minLevel, maxLevel, quests } of towerFloors) {
console.log(`\nFloor F${num}: ${minLevel}~${maxLevel}`);
const table = Object.fromEntries(
Array.from({ length: maxLevel - minLevel + 1 }, (_, i) => {
const lv = minLevel + i;
const validQuests = quests.filter(quest => quest.minLevel <= lv);
const expDirty = validQuests.reduce((sum, quest) => {
return sum + quest.endReceiveExperience[lv - 1];
}, 0);
const exp = Math.round(expDirty * 10000) / 10000;
const penya = validQuests.reduce((sum, quest) => {
return sum + quest.endReceiveGold;
}, 0);

return [lv, { exp, penya, quests: validQuests.length }] as const;
})
);
console.table(table);
}
}

main()
.then(() => console.log("Completed"))
.catch(e => {
console.error(e);
process.exit(1);
});
25 changes: 25 additions & 0 deletions src/utils/get-quest-data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { isFile, readFile } from "@foxkit/node-util/fs";
import { PATHS } from "~/paths";

const cache = new Map<number, QuestData>();

/**
* Reads and parses cached Quest Data
* @param id Quest Id
* @returns Data of Quest
*/
export async function getQuestData(id: number): Promise<QuestData> {
const cached = cache.get(id);
if (cached) return cached;

const filePath = PATHS.quest(id);
const exists = await isFile(filePath);
if (!exists) throw new Error(`Quest id ${id} does not exist`);

const file = await readFile(filePath);
if (!file) throw new Error(`Error when readinf file '${filePath}'`);

const data: QuestData = JSON.parse(file);
cache.set(id, data);
return data;
}
17 changes: 17 additions & 0 deletions src/utils/get-quest-list.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { readFile } from "@foxkit/node-util/fs";
import { PATHS } from "~/paths";

let cache: number[];

/**
* Reads and parses the cached quest id list
* @returns Array of Quest IDs (as number)
*/
export async function getQuestList(): Promise<number[]> {
if (cache) return cache;
const file = await readFile(PATHS.questList);
if (!file) throw new Error("Error while reading quests list file");
const list = JSON.parse(file);
cache = list;
return list;
}

0 comments on commit 72ccf31

Please sign in to comment.