Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

.setCosmetics() method #795

Merged
merged 4 commits into from
Dec 1, 2024
Merged
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
7 changes: 7 additions & 0 deletions resources/structs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -994,6 +994,13 @@ export interface Island {
};
}

export interface Cosmetics {
outfit?: { id: string; variants?: CosmeticVariant[]; enlightment?: CosmeticEnlightment };
backpack?: { id: string; variants?: CosmeticVariant[]; path?: string };
pickaxe?: { id: string; variants?: CosmeticVariant[]; path?: string };
shoes?: { id: string; path?: string };
}

export interface CosmeticVariant {
channel: string;
variant: string;
Expand Down
316 changes: 180 additions & 136 deletions src/structures/party/ClientPartyMember.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import PartyMember from './PartyMember';
import { AuthSessionStoreKey } from '../../../resources/enums';
import EpicgamesAPIError from '../../exceptions/EpicgamesAPIError';
import type {
CosmeticEnlightment, CosmeticsVariantMeta, CosmeticVariant, PartyMemberData, PartyMemberSchema, Schema,
CosmeticEnlightment, Cosmetics, CosmeticsVariantMeta, CosmeticVariant, PartyMemberData, PartyMemberSchema, Schema,
} from '../../../resources/structs';
import type Party from './Party';

Expand Down Expand Up @@ -188,64 +188,200 @@ class ClientPartyMember extends PartyMember {
}

/**
* Updates the client party member's outfit
* @param id The outfit's ID
* @param variants The outfit's variants
* @param enlightment The outfit's enlightment
* Updates multiple cosmetics for the client party member.
* If a cosmetic is set to `undefined` or any falsy value, it will be cleared, if possible.
* If a cosmetic is not provided, it will remain unchanged.
* @param cosmetics An object specifying the cosmetics to update, including outfit, backpack, pickaxe and shoes.
* @throws {EpicgamesAPIError}
*/
public async setOutfit(id: string, variants: CosmeticVariant[] = [], enlightment?: CosmeticEnlightment) {
let data = this.meta.get('Default:AthenaCosmeticLoadout_j');
let variantData = this.meta.get('Default:AthenaCosmeticLoadoutVariants_j');

public async setCosmetics(cosmetics: Cosmetics = {}) {
const {
outfit, backpack, pickaxe, shoes,
} = cosmetics;
const patches: Schema = {};

const parsedVariants: CosmeticsVariantMeta = {
athenaCharacter: {
i: variants.map((v) => ({
c: v.channel,
v: v.variant,
dE: v.dE || 0,
})),
},
};
if (outfit) {
let data = this.meta.get('Default:AthenaCosmeticLoadout_j');
let variantData = this.meta.get('Default:AthenaCosmeticLoadoutVariants_j');

const parsedVariants: CosmeticsVariantMeta = {
athenaCharacter: {
i: outfit.variants?.map((v) => ({
c: v.channel,
v: v.variant,
dE: v.dE || 0,
})) ?? [],
},
};

const scratchpad = [];
if (outfit.enlightment?.length === 2) {
scratchpad.push({
t: outfit.enlightment[0],
v: outfit.enlightment[1],
});
}

const scratchpad = [];
if (enlightment?.length === 2) {
scratchpad.push({
t: enlightment[0],
v: enlightment[1],
data = this.meta.set('Default:AthenaCosmeticLoadout_j', {
...data,
AthenaCosmeticLoadout: {
...data.AthenaCosmeticLoadout,
characterPrimaryAssetId: `AthenaCharacter:${outfit.id}`,
scratchpad,
},
});

patches['Default:AthenaCosmeticLoadout_j'] = data;

delete variantData.AthenaCosmeticLoadoutVariants.vL.AthenaCharacter;
if (parsedVariants.athenaCharacter?.i[0]) {
variantData = this.meta.set('Default:AthenaCosmeticLoadoutVariants_j', {
AthenaCosmeticLoadoutVariants: {
vL: {
...variantData.AthenaCosmeticLoadoutVariants.vL,
...parsedVariants,
},
},
});

patches['Default:AthenaCosmeticLoadoutVariants_j'] = variantData;
}
}

data = this.meta.set('Default:AthenaCosmeticLoadout_j', {
...data,
AthenaCosmeticLoadout: {
...data.AthenaCosmeticLoadout,
characterPrimaryAssetId: `AthenaCharacter:${id}`,
scratchpad,
},
});
if (Object.hasOwn(cosmetics, 'backpack')) {
if (!backpack) {
let data = this.meta.get('Default:AthenaCosmeticLoadout_j');

patches['Default:AthenaCosmeticLoadout_j'] = data;
data = this.meta.set('Default:AthenaCosmeticLoadout_j', {
...data,
AthenaCosmeticLoadout: {
...data.AthenaCosmeticLoadout,
backpackDef: '',
},
});

patches['Default:AthenaCosmeticLoadout_j'] = data;
} else {
let data = this.meta.get('Default:AthenaCosmeticLoadout_j');
let variantData = this.meta.get('Default:AthenaCosmeticLoadoutVariants_j');

const parsedVariants: CosmeticsVariantMeta = {
athenaBackpack: {
i: backpack.variants?.map((v) => ({
c: v.channel,
v: v.variant,
dE: v.dE || 0,
})) ?? [],
},
};

delete variantData.AthenaCosmeticLoadoutVariants.vL.AthenaCharacter;
if (parsedVariants.athenaCharacter?.i[0]) {
variantData = this.meta.set('Default:AthenaCosmeticLoadoutVariants_j', {
AthenaCosmeticLoadoutVariants: {
vL: {
...variantData.AthenaCosmeticLoadoutVariants.vL,
...parsedVariants,
data = this.meta.set('Default:AthenaCosmeticLoadout_j', {
...data,
AthenaCosmeticLoadout: {
...data.AthenaCosmeticLoadout,
backpackDef: `${backpack.path?.replace(/\/$/, '') ?? '/BRCosmetics/Athena/Items/Cosmetics/Backpacks'}/${backpack.id}.${backpack.id}`,
},
});

patches['Default:AthenaCosmeticLoadout_j'] = data;

delete variantData.AthenaCosmeticLoadoutVariants.vL.AthenaBackpack;
if (parsedVariants.athenaBackpack?.i[0]) {
variantData = this.meta.set('Default:AthenaCosmeticLoadoutVariants_j', {
AthenaCosmeticLoadoutVariants: {
vL: {
...variantData.AthenaCosmeticLoadoutVariants.vL,
...parsedVariants,
},
},
});

patches['Default:AthenaCosmeticLoadoutVariants_j'] = variantData;
}
}
}

if (pickaxe) {
let data = this.meta.get('Default:AthenaCosmeticLoadout_j');
let variantData = this.meta.get('Default:AthenaCosmeticLoadoutVariants_j');

const parsedVariants: CosmeticsVariantMeta = {
athenaPickaxe: {
i: pickaxe.variants?.map((v) => ({
c: v.channel,
v: v.variant,
dE: v.dE || 0,
})) ?? [],
},
};

data = this.meta.set('Default:AthenaCosmeticLoadout_j', {
...data,
AthenaCosmeticLoadout: {
...data.AthenaCosmeticLoadout,
pickaxeDef: `${pickaxe.path?.replace(/\/$/, '') ?? '/BRCosmetics/Athena/Items/Cosmetics/Pickaxes'}/${pickaxe.id}.${pickaxe.id}`,
},
});

patches['Default:AthenaCosmeticLoadoutVariants_j'] = variantData;
patches['Default:AthenaCosmeticLoadout_j'] = data;

delete variantData.AthenaCosmeticLoadoutVariants.vL.AthenaPickaxe;
if (parsedVariants.athenaPickaxe?.i[0]) {
variantData = this.meta.set('Default:AthenaCosmeticLoadoutVariants_j', {
AthenaCosmeticLoadoutVariants: {
vL: {
...variantData.AthenaCosmeticLoadoutVariants.vL,
...parsedVariants,
},
},
});

patches['Default:AthenaCosmeticLoadoutVariants_j'] = variantData;
}
}

if (Object.hasOwn(cosmetics, 'shoes')) {
if (!shoes) {
let data = this.meta.get('Default:AthenaCosmeticLoadout_j');

data = this.meta.set('Default:AthenaCosmeticLoadout_j', {
...data,
AthenaCosmeticLoadout: {
...data.AthenaCosmeticLoadout,
shoesDef: '',
},
});

patches['Default:AthenaCosmeticLoadout_j'] = data;
} else {
let data = this.meta.get('Default:AthenaCosmeticLoadout_j');

data = this.meta.set('Default:AthenaCosmeticLoadout_j', {
...data,
AthenaCosmeticLoadout: {
...data.AthenaCosmeticLoadout,
shoesDef: `${shoes.path?.replace(/\/$/, '') ?? '/CosmeticShoes/Assets/Items/Cosmetics'}/${shoes.id}.${shoes.id}`,
},
});

patches['Default:AthenaCosmeticLoadout_j'] = data;
}
}

await this.sendPatch(patches);
}

/**
* Updates the client party member's outfit
* @param id The outfit's ID
* @param variants The outfit's variants
* @param enlightment The outfit's enlightment
* @throws {EpicgamesAPIError}
*/
public async setOutfit(id: string, variants: CosmeticVariant[] = [], enlightment?: CosmeticEnlightment) {
return this.setCosmetics({ outfit: { id, variants, enlightment } });
}

/**
* Updates the client party member's backpack
* @param id The backpack's ID
Expand All @@ -254,46 +390,7 @@ class ClientPartyMember extends PartyMember {
* @throws {EpicgamesAPIError}
*/
public async setBackpack(id: string, variants: CosmeticVariant[] = [], path?: string) {
let data = this.meta.get('Default:AthenaCosmeticLoadout_j');
let variantData = this.meta.get('Default:AthenaCosmeticLoadoutVariants_j');

const patches: Schema = {};

const parsedVariants: CosmeticsVariantMeta = {
athenaBackpack: {
i: variants.map((v) => ({
c: v.channel,
v: v.variant,
dE: v.dE || 0,
})),
},
};

data = this.meta.set('Default:AthenaCosmeticLoadout_j', {
...data,
AthenaCosmeticLoadout: {
...data.AthenaCosmeticLoadout,
backpackDef: `${path?.replace(/\/$/, '') ?? '/BRCosmetics/Athena/Items/Cosmetics/Backpacks'}/${id}.${id}`,
},
});

patches['Default:AthenaCosmeticLoadout_j'] = data;

delete variantData.AthenaCosmeticLoadoutVariants.vL.AthenaBackpack;
if (parsedVariants.athenaBackpack?.i[0]) {
variantData = this.meta.set('Default:AthenaCosmeticLoadoutVariants_j', {
AthenaCosmeticLoadoutVariants: {
vL: {
...variantData.AthenaCosmeticLoadoutVariants.vL,
...parsedVariants,
},
},
});

patches['Default:AthenaCosmeticLoadoutVariants_j'] = variantData;
}

await this.sendPatch(patches);
return this.setCosmetics({ backpack: { id, variants, path } });
}

/**
Expand All @@ -303,7 +400,7 @@ class ClientPartyMember extends PartyMember {
* @param path The pet's path in the game files
*/
public async setPet(id: string, variants: CosmeticVariant[] = [], path?: string) {
return this.setBackpack(id, variants, path ?? '/BRCosmetics/Athena/Items/Cosmetics/PetCarriers');
return this.setCosmetics({ backpack: { id, variants, path: path ?? '/BRCosmetics/Athena/Items/Cosmetics/PetCarriers' } });
}

/**
Expand All @@ -314,46 +411,7 @@ class ClientPartyMember extends PartyMember {
* @throws {EpicgamesAPIError}
*/
public async setPickaxe(id: string, variants: CosmeticVariant[] = [], path?: string) {
let data = this.meta.get('Default:AthenaCosmeticLoadout_j');
let variantData = this.meta.get('Default:AthenaCosmeticLoadoutVariants_j');

const patches: Schema = {};

const parsedVariants: CosmeticsVariantMeta = {
athenaPickaxe: {
i: variants.map((v) => ({
c: v.channel,
v: v.variant,
dE: v.dE || 0,
})),
},
};

data = this.meta.set('Default:AthenaCosmeticLoadout_j', {
...data,
AthenaCosmeticLoadout: {
...data.AthenaCosmeticLoadout,
pickaxeDef: `${path?.replace(/\/$/, '') ?? '/BRCosmetics/Athena/Items/Cosmetics/Pickaxes'}/${id}.${id}`,
},
});

patches['Default:AthenaCosmeticLoadout_j'] = data;

delete variantData.AthenaCosmeticLoadoutVariants.vL.AthenaPickaxe;
if (parsedVariants.athenaPickaxe?.i[0]) {
variantData = this.meta.set('Default:AthenaCosmeticLoadoutVariants_j', {
AthenaCosmeticLoadoutVariants: {
vL: {
...variantData.AthenaCosmeticLoadoutVariants.vL,
...parsedVariants,
},
},
});

patches['Default:AthenaCosmeticLoadoutVariants_j'] = variantData;
}

await this.sendPatch(patches);
return this.setCosmetics({ pickaxe: { id, variants, path } });
}

/**
Expand All @@ -363,21 +421,7 @@ class ClientPartyMember extends PartyMember {
* @throws {EpicgamesAPIError}
*/
public async setShoes(id: string, path?: string) {
let data = this.meta.get('Default:AthenaCosmeticLoadout_j');

data = this.meta.set('Default:AthenaCosmeticLoadout_j', {
...data,
AthenaCosmeticLoadout: {
...data.AthenaCosmeticLoadout,
shoesDef: `${
path?.replace(/\/$/, '') ?? '/CosmeticShoes/Assets/Items/Cosmetics'
}/${id}.${id}`,
},
});

await this.sendPatch({
'Default:AthenaCosmeticLoadout_j': data,
});
return this.setCosmetics({ shoes: { id, path } });
}

/**
Expand Down