Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/MooLite/core/MooLite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { EquipmentBuffsUpdatedParser } from "src/MooLite/core/server/messages/Eq
import { ItemsUpdatedParser } from "src/MooLite/core/server/messages/ItemsUpdated";
import { LootOpenedParser } from "src/MooLite/core/server/messages/LootOpened";
import { AbilitiesUpdatedParser } from "src/MooLite/core/server/messages/AbilitiesUpdated";
import { InitClientInfoParser } from "./server/messages/InitClientInfo";
import { unsafeWindow } from "$";

export class MooLite {
Expand All @@ -33,6 +34,7 @@ export class MooLite {
messageParsers: MessageParser[] = [
// Server messages
new InitCharacterInfoParser(),
new InitClientInfoParser(),
new PongParser(),

new ActionCompletedParser(),
Expand Down Expand Up @@ -171,8 +173,7 @@ export class MooLite {
});
if (!parser) {
if (!isClientMessage) {
console.warn(`Unhandled message type ${message.type}`);
console.log(message);
console.warn(`Unhandled message type ${message.type}`, message);
Comment thread
seashairo marked this conversation as resolved.
}
return;
}
Expand Down
29 changes: 29 additions & 0 deletions src/MooLite/core/inventory/items/decodeItemHash.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { ItemHrid } from "../ItemHrid";
import { ItemLocationHrid } from "../ItemLocationHrid";

export interface ItemHashParts {
characterId: number;
itemLocationHrid: ItemLocationHrid;
itemHrid: ItemHrid;
enhancementLevel: number;
}

/**
* An item hash looks like
* `123456::/item_locations/inventory::/items/cheese_enhancer::0`
* This function will break it down into its component parts so they can be
* used elsewhere, like when tracking what items are being used for enhancing.
*
* @param itemHash - the hash to deconstruct, should be in the above format
* @returns a deconstructed item hash
*/
export function decodeItemHash(itemHash: string): ItemHashParts {
const parts = itemHash.split("::");

return {
characterId: parseInt(parts[0]),
itemLocationHrid: parts[1] as any as ItemLocationHrid,
itemHrid: parts[2] as any as ItemHrid,
enhancementLevel: parseInt(parts[3]),
};
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export enum ClientMessageType {
Ping = "/character_tasks/ping",
Ping = "ping",
Comment thread
seashairo marked this conversation as resolved.
}
130 changes: 130 additions & 0 deletions src/MooLite/plugins/EnhancingTracker/EnhancingTrackerPlugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import { MooLitePlugin } from "src/MooLite/core/plugins/MooLitePlugin";
import { MooLiteTab } from "src/MooLite/core/plugins/MooLiteTab";
import EnhancingTrackerPluginDisplay from "src/MooLite/plugins/EnhancingTracker/EnhancingTrackerPluginDisplay.vue";
import { markRaw } from "vue";
import { PluginAuthorCredits } from "src/MooLite/core/plugins/PluginAuthorCredits";
import { ItemDetail } from "src/MooLite/core/inventory/items/ItemDetail";
import { ItemHrid } from "src/MooLite/core/inventory/ItemHrid";
import { CharacterAction } from "src/MooLite/core/actions/CharacterAction";
import { ItemHashParts, decodeItemHash } from "src/MooLite/core/inventory/items/decodeItemHash";
import { ItemAmount } from "src/MooLite/core/inventory/items/ItemAmount";
import { ActionHrid } from "src/MooLite/core/actions/ActionHrid";

export class EnhancingTrackerPlugin extends MooLitePlugin {
name: string = "Enhancing Tracker";
key = "enhancing-tracker";
description: string = "Tracks materials consumed during enhancing.";

credits: PluginAuthorCredits = {
author: "seashairo",
maintainer: "seashairo",
};

tab: MooLiteTab = {
icon: "⭐",
pluginName: this.name,
componentName: "EnhancingTrackerPluginDisplay",
component: markRaw(EnhancingTrackerPluginDisplay),
};

public get itemDetailMap(): Record<ItemHrid, ItemDetail> {
return this._game.inventory.itemDetailMap;
}

itemBeingEnhancedHrid: ItemHrid | null = null;
itemsConsumed: Record<ItemHrid, number> = {};
bestEnhancementLevel: number = 0;
successes: number = 0;
failures: number = 0;
blessedTeaProcs: number = 0;
itemState: ItemHashParts | null = null;
previousAction: CharacterAction | null = null;

public get successRate(): number {
const attempts = this.successes + this.failures;
return attempts === 0 ? 0 : (this.successes / attempts) * 100;
}

onActionQueueUpdated(queue: CharacterAction[]): void {
const currentAction = queue.length ? queue[0] : null;

const enhancing = "/actions/enhancing/enhance" as unknown as ActionHrid;
const currentActionIsEnhancing = currentAction?.actionHrid === enhancing;
const previousActionIsEnhancing = this.previousAction?.actionHrid === enhancing;

if (currentActionIsEnhancing) {
previousActionIsEnhancing
? this.handleEnhancing(currentAction)
: this.handleStartedEnhancing(currentAction);
}

this.previousAction = currentAction;
}

handleStartedEnhancing(currentAction: CharacterAction): void {
const itemHashParts = decodeItemHash(currentAction.upgradeItemHash);
this.itemState = itemHashParts;
this.itemBeingEnhancedHrid = itemHashParts.itemHrid;
this.bestEnhancementLevel = itemHashParts.enhancementLevel;
this.itemsConsumed = {};
}

addConsumedItem(itemAmount: ItemAmount): void {
itemAmount.itemHrid in this.itemsConsumed
? (this.itemsConsumed[itemAmount.itemHrid] += itemAmount.count)
: (this.itemsConsumed[itemAmount.itemHrid] = itemAmount.count);
}

handleEnhancing(currentAction: CharacterAction): void {
const currentItemState = decodeItemHash(currentAction.upgradeItemHash);
const itemBeingEnhancedHrid = currentItemState.itemHrid;

// If the item being enhanced has changed hrid, then we're enhancing
// something new.
//
// TODO: Handle this more gracefully. Maybe store a dict of enhancing
// data against hrid to display a table?
if (this.itemBeingEnhancedHrid !== itemBeingEnhancedHrid) {
return this.handleStartedEnhancing(currentAction);
}

// Instead of watching the items gained and lost, we want to use the
// item's enhancement cost. This is because players using the market
// will hit the onItemGained handler and leave the count inaccurate.
//
// TODO: When Blessed Tea procs, do you use 1 or 2 sets of consumables?
// If it's 1 then this is accurate, but if it's 2 then this logic would
// need to be part of the level check below
const enhancingCosts = this._game.inventory.itemDetailMap[itemBeingEnhancedHrid].enhancementCosts;
enhancingCosts!.forEach((itemAmount) => this.addConsumedItem(itemAmount));

const itemState = this.itemState!;
if (currentItemState.enhancementLevel > itemState.enhancementLevel) {
const diff = currentItemState.enhancementLevel - itemState.enhancementLevel;
this.successes += 1;
// I'm pretty sure the level diff will only ever be 1 or 2, but just
// in case blessed tea can multi-proc we'll check for anything
// greater than 1. I'm also assuming (but haven't confirmed) that a
// blessed tea proc will still be in a single action update and not
// two separate ones.
if (diff > 1) {
this.blessedTeaProcs += 1;
}
} else {
this.failures += 1;

// Protections don't get counted in the regular consumed items
// report, so if we drop an enhancement level then we need to
// manually count a lost protection.
const protectionHash = currentAction.enhancingProtectionItemHash;
if (protectionHash !== "" && itemState.enhancementLevel >= currentAction.enhancingProtectionItemMinLevel) {
const itemHrid = decodeItemHash(protectionHash).itemHrid;
this.addConsumedItem({ itemHrid, count: 1 });
}
}

this.bestEnhancementLevel = Math.max(currentItemState.enhancementLevel, this.bestEnhancementLevel);

this.itemState = currentItemState;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<script setup lang="ts">
import { Ref, ref } from "vue";
import { EnhancingTrackerPlugin } from "src/MooLite/plugins/EnhancingTracker/EnhancingTrackerPlugin";
import { ItemAmount } from "src/MooLite/core/inventory/items/ItemAmount";
import MooDivider from "src/components/atoms/MooDivider.vue";
import ItemIcon from "src/components/atoms/ItemIcon.vue";

const props = defineProps<{
plugin: EnhancingTrackerPlugin;
}>();

const loot: Ref<ItemAmount[]> = ref([]);
</script>

<template>
<div class="flex flex-col gap-2">
<span class="text-center">{{ props.plugin.name }}</span>

<MooDivider />

<div v-if="plugin.itemBeingEnhancedHrid" class="flex flex-col gap-2">
<div class="flex items-center justify-between">
<p>Enhancing {{ plugin.itemDetailMap[plugin.itemBeingEnhancedHrid].name }}</p>
<ItemIcon :item="plugin.itemBeingEnhancedHrid" class="inline" />
</div>

<hr />

<div>
<p>Items consumed</p>
<p v-if="Object.keys(plugin.itemsConsumed).length === 0">None!</p>
<div v-for="(value, key) in plugin.itemsConsumed" class="flex items-center justify-between">
<span>
<ItemIcon :item="key" class="inline" />
{{ plugin.itemDetailMap[key].name }}
</span>
<p>{{ value }}</p>
</div>
</div>

<p>Best enhancement level: {{ plugin.bestEnhancementLevel }}</p>
<p>
Success rate: {{ plugin.successRate.toFixed(2) }}% ({{ plugin.successes }} /
{{ plugin.successes + plugin.failures }})
</p>
</div>

<div v-if="!plugin.itemBeingEnhancedHrid" class="flex flex-col gap-2">
<p>It doesn't look like you're enhancing anything at the moment. Try it out!</p>
</div>
</div>
</template>
2 changes: 1 addition & 1 deletion src/components/atoms/ItemIcon.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const imgUrl = computed(() => {
const split = (props.item as unknown as string).split("/");
const itemPostfix = split[split.length - 1];
// TODO(@Isha): Research how these CDN resource urls are generated
return `/static/media/items_sprite.951ef1ec.svg#${itemPostfix}`;
return `/static/media/items_sprite.018a3c6e.svg#${itemPostfix}`;
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you figured out how these work? :p

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No idea :D
It does get icons to render again for a while though

});
</script>

Expand Down
2 changes: 2 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { ConsumableNotifierPlugin } from "./MooLite/plugins/ConsumableNotifier/C
import { EquipmentExporterPlugin } from "src/MooLite/plugins/EquipmentExporter/EquipmentExporterPlugin";
import { WhisperManagerPlugin } from "./MooLite/plugins/WhisperManager/WhisperManagerPlugin";
import { ThemesPlugin } from "src/MooLite/plugins/Themes/ThemesPlugin";
import { EnhancingTrackerPlugin } from "./MooLite/plugins/EnhancingTracker/EnhancingTrackerPlugin";

declare global {
interface Window {
Expand Down Expand Up @@ -61,6 +62,7 @@ const launchMooLite = () => {
new EquipmentExporterPlugin(),
new WhisperManagerPlugin(),
new ThemesPlugin(),
new EnhancingTrackerPlugin(),
]) as unknown as MooLitePlugin[];
const pluginManager = reactive<PluginManager>(new PluginManager(game, plugins)) as PluginManager;

Expand Down