diff --git a/package-lock.json b/package-lock.json index faa7c64..b4feb23 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "devDependencies": { "@sveltejs/adapter-static": "^2.0.2", "@sveltejs/kit": "^1.20.5", + "@total-typescript/ts-reset": "^0.5.1", "@types/node": "^18.15.11", "@types/wicg-file-system-access": "^2020.9.6", "fast-xml-parser": "^4.2.0", @@ -583,6 +584,12 @@ "vite": "^4.0.0" } }, + "node_modules/@total-typescript/ts-reset": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@total-typescript/ts-reset/-/ts-reset-0.5.1.tgz", + "integrity": "sha512-AqlrT8YA1o7Ff5wPfMOL0pvL+1X+sw60NN6CcOCqs658emD6RfiXhF7Gu9QcfKBH7ELY2nInLhKSCWVoNL70MQ==", + "dev": true + }, "node_modules/@tsconfig/node10": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", diff --git a/package.json b/package.json index 8cab057..a270426 100644 --- a/package.json +++ b/package.json @@ -8,11 +8,12 @@ "preview": "vite preview", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", - "dump": "node --no-warnings --loader ts-node/esm src/dump.ts -O \"{'allowImportingTsExtensions': true,}\"" + "dump": "node --no-warnings --loader ts-node/esm src/dump.ts" }, "devDependencies": { "@sveltejs/adapter-static": "^2.0.2", "@sveltejs/kit": "^1.20.5", + "@total-typescript/ts-reset": "^0.5.1", "@types/node": "^18.15.11", "@types/wicg-file-system-access": "^2020.9.6", "fast-xml-parser": "^4.2.0", diff --git a/src/dump-1.5.ts b/src/dump-1.5.ts new file mode 100644 index 0000000..2620d22 --- /dev/null +++ b/src/dump-1.5.ts @@ -0,0 +1,292 @@ +import { copyFile, readFile, writeFile } from 'fs/promises'; +import type { BigCraftable, Boots, Clothing, ClothingType, Furniture, FurnitureType, Hat, MeleeWeapon, MeleeWeaponType, ObjectInformation, RangedWeapon, Tool } from './types/items/1.5'; +import { GetSprite } from './lib/Spritesheet'; +import type { TypeEnum } from './types/save/1.5'; + +const objects = JSON.parse(await readFile('./content/Data/ObjectInformation.json', 'utf-8')) as Record; +const objectsArray = Object.entries(objects).map(([key, value]) => { + const props = value.split('/'); + const [type, category] = props[3].split(' '); + + // There's a ton of "Stone" entries in objects, only use the one that has an id of 390 + if (props[0] === 'Stone' && Number(key) !== 390) return undefined; + + return { + _type: 'ObjectInformation', + Name: props[0], + Price: Number(props[1]), + Edibility: Number(props[2]), + Type: type as TypeEnum, + Category: Number(category), + DisplayName: props[4], + Description: props[5], + Misc1: props[6] ? Number(props[7]) : undefined, + Misc2: props[7] ? Number(props[8]) : undefined, + BuffDuration: props[8] ? Number(props[9]) : undefined, + Sprite: GetSprite('ObjectInformation', Number(key)), + ParentSheetIndex: Number(key), + } satisfies ObjectInformation; +}).filter((x) => x !== undefined) as ObjectInformation[]; + +const bigCraftables = JSON.parse(await readFile('./content/Data/BigCraftablesInformation.json', 'utf-8')) as Record; +const bigCraftablesArray = Object.entries(bigCraftables).map(([key, value]) => { + // Skip 22 and 23, weird table business + if (Number(key) === 22 || Number(key) === 23) return undefined; + + // Unobtainable items + if ([26, 27, 64].includes(Number(key))) return undefined; + + const props = value.split('/'); + return { + _type: 'BigCraftable', + Name: props[0], + Price: Number(props[1]), + Edibility: Number(props[2]), + Type: props[3].split(' ')[0] as TypeEnum, + Category: -9, + Description: props[4], + CanBePlacedOutdoors: props[5] === 'true', + CanBePlacedIutdoors: props[6] === 'true', + Fragility: Number(props[7]), + IsLamp: props[8] === 'true', + DisplayName: props[9], + Sprite: GetSprite('BigCraftable', Number(key)), + ParentSheetIndex: Number(key), + } satisfies BigCraftable; +}).filter((x) => x !== undefined) as BigCraftable[]; + +const boots = JSON.parse(await readFile('./content/Data/Boots.json', 'utf-8')) as Record; +const bootsArray = Object.entries(boots).map(([key, value]) => { + const props = value.split('/'); + return { + _type: 'Boots', + Name: props[0], + Description: props[1], + Price: Number(props[2]), + Defense: Number(props[3]), + Immunity: Number(props[4]), + ColorIndex: Number(props[5]), + DisplayName: props[6], + Sprite: GetSprite('Boots', Number(key)), + ParentSheetIndex: Number(key), + } satisfies Boots; +}); + +const clothing = JSON.parse(await readFile('./content/Data/ClothingInformation.json', 'utf-8')) as Record; +const clothingArray = Object.entries(clothing).map(([key, value]) => { + const props = value.split('/'); + const color = props[6].split(' '); + const type = props[8] as ClothingType; + const dyeable = props[7] === 'true'; + + let sprite; + let femaleSprite; + if (type === 'Shirt') { + sprite = GetSprite('Clothing', Number(props[3]), type, dyeable); + + if (props[4] !== '-1') { + femaleSprite = GetSprite('Clothing', Number(props[4]), type, dyeable); + } + } else { + sprite = GetSprite('Clothing', Number(key), type); + } + + return { + _type: 'Clothing', + Name: props[0], + DisplayName: props[1], + Description: props[2], + MaleIndex: Number(props[3]), + FemaleIndex: Number(props[4]), + Price: Number(props[5]), + DefaultColor: { + r: Number(color[0]), + g: Number(color[1]), + b: Number(color[2]), + }, + Dyeable: dyeable, + Type: type, + ExtraData: props[9], + Sprite: sprite, + FemaleSprite: femaleSprite, + ParentSheetIndex: Number(key), + } satisfies Clothing; +}); + +const furniture = JSON.parse(await readFile('./content/Data/Furniture.json', 'utf-8')) as Record; +const furnitureArray = Object.entries(furniture).map(([key, value]) => { + const props = value.split('/'); + const split2 = props[2].split(' '); + const split3 = props[3].split(' '); + + return { + _type: 'Furniture', + Name: props[0], + Type: props[1] as FurnitureType, + TilesheetSize: split2[0] === '-1' ? -1 : { width: Number(split2[0]), height: Number(split2[1]) }, + BoundingBoxSize: split3[0] === '-1' ? -1 : { width: Number(split3[0]), height: Number(split3[1]) }, + Rotations: Number(props[4]), + Price: Number(props[5]), + DisplayName: props[6], + PlacementRestriction: Number(props[7]), + Sprite: GetSprite('Furniture', Number(key)), + ParentSheetIndex: Number(key), + } satisfies Furniture; +}); + +const hats = JSON.parse(await readFile('./content/Data/Hats.json', 'utf-8')) as Record; +const hatsArray = Object.entries(hats).map(([key, value]) => { + const props = value.split('/'); + return { + _type: 'Hat', + Name: props[0], + Description: props[1], + ShowRealHair: props[2] === 'true', + SkipHairstyleOffset: props[3] === 'true', + DisplayName: props[4], + Sprite: GetSprite('Hat', Number(key)), + ParentSheetIndex: Number(key), + } satisfies Hat; +}); + +const weapons = JSON.parse(await readFile('./content/Data/Weapons.json', 'utf-8')) as Record; +const weaponsArray = Object.entries(weapons).map(([key, value]) => { + const props = value.split('/'); + return props[0].search('Slingshot') !== -1 ? { + _type: 'RangedWeapon', + Name: props[0], + Description: props[1], + MinDamage: Number(props[2]), + MaxDamage: Number(props[3]), + DisplayName: props[4], + Sprite: GetSprite('RangedWeapon', Number(key)), + ParentSheetIndex: Number(key), + } satisfies RangedWeapon : + { + _type: 'MeleeWeapon', + Name: props[0], + Description: props[1], + MinDamage: Number(props[2]), + MaxDamage: Number(props[3]), + Knockback: Number(props[4]), + Speed: Number(props[5]), + Precision: Number(props[6]), + Defense: Number(props[7]), + Type: Number(props[8]) as MeleeWeaponType, + MineBaseLevel: Number(props[9]), + MineMinLevel: Number(props[10]), + AreaOfEffect: Number(props[11]), + CritChance: Number(props[12]), + CritMultiplier: Number(props[13]), + DisplayName: props[14], + Sprite: GetSprite('MeleeWeapon', Number(key)), + ParentSheetIndex: Number(key), + } satisfies MeleeWeapon; +}); + +const tools = { + "6": "Milk Pail", + "7": "Shears", + "8": "Bamboo Pole", + "9": "Training Rod", + "10": "Fiberglass Rod", + "11": "Iridium Rod", + "12": "Copper Pan", + "47": "Hoe", + "54": "Copper Hoe", + "61": "Steel Hoe", + "89": "Gold Hoe", + "96": "Iridium Hoe", + "131": "Pickaxe", + "138": "Copper Pickaxe", + "145": "Steel Pickaxe", + "173": "Gold Pickaxe", + "180": "Iridium Pickaxe", + "215": "Axe", + "222": "Copper Axe", + "229": "Steel Axe", + "257": "Gold Axe", + "264": "Iridium Axe", + "296": "Watering Can", + "303": "Copper Watering Can", + "310": "Steel Watering Can", + "338": "Gold Watering Can", + "345": "Iridium Watering Can", +}; +const toolsArray = Object.entries(tools).map(([key, value]) => { + return { + _type: 'Tool', + Name: value, + Sprite: GetSprite('Tool', Number(key)), + ParentSheetIndex: Number(key), + } satisfies Tool; +}); + +const cookingRecipes = JSON.parse(await readFile('./content/Data/CookingRecipes.json', 'utf-8')) as Record; +const cookingRecipesArray = Object.entries(cookingRecipes).map(([key]) => key); +await writeFile('./static/1.5/cookingrecipes.json', JSON.stringify(cookingRecipesArray)); + +const craftingRecipes = JSON.parse(await readFile('./content/Data/CraftingRecipes.json', 'utf-8')) as Record; +const craftingRecipesArray = Object.entries(craftingRecipes).map(([key]) => key); +await writeFile('./static/1.5/craftingrecipes.json', JSON.stringify(craftingRecipesArray)); + +const writeToFile = JSON.stringify([...objectsArray, ...bigCraftablesArray, ...bootsArray, ...clothingArray, ...furnitureArray, ...hatsArray, ...weaponsArray, ...toolsArray].map(item => [item.Name, item])); +await writeFile('./static/1.5/iteminfo.json', writeToFile); + +// Copy all textures into assets folder +await copyFile(`./content/TileSheets/Craftables.png`, `./static/1.5/assets/Craftables.png`); +await copyFile(`./content/TileSheets/furniture.png`, `./static/1.5/assets/furniture.png`); +await copyFile(`./content/TileSheets/weapons.png`, `./static/1.5/assets/weapons.png`); +await copyFile(`./content/TileSheets/tools.png`, `./static/1.5/assets/tools.png`); +await copyFile(`./content/Characters/Farmer/pants.png`, `./static/1.5/assets/pants.png`); +await copyFile(`./content/Characters/Farmer/shirts.png`, `./static/1.5/assets/shirts.png`); +await copyFile(`./content/Characters/Farmer/accessories.png`, `./static/1.5/assets/accessories.png`); +// await copyFile(`./content/Characters/Farmer/farmer_base.png`, `./static/1.5/assets/farmer_base.png`); +// await copyFile(`./content/Characters/Farmer/farmer_base_bald.png`, `./static/1.5/assets/farmer_base_bald.png`); +// await copyFile(`./content/Characters/Farmer/farmer_girl_base.png`, `./static/1.5/assets/farmer_girl_base.png`); +// await copyFile(`./content/Characters/Farmer/farmer_girl_base_bald.png`, `./static/1.5/assets/farmer_girl_base_bald.png`); +await copyFile(`./content/Characters/Farmer/hairstyles.png`, `./static/1.5/assets/hairstyles.png`); +await copyFile(`./content/Characters/Farmer/hairstyles2.png`, `./static/1.5/assets/hairstyles2.png`); +await copyFile('./content/maps/springobjects.png', './static/1.5/assets/springobjects.png'); +await copyFile('./content/Characters/Farmer/hats.png', './static/1.5/assets/hats.png'); +await copyFile('./content/LooseSprites/daybg.png', './static/1.5/assets/daybg.png'); + +// Copy all portraits into assets folder +const chars = [ + 'Abigail', + 'Alex', + 'Caroline', + 'Clint', + 'Demetrius', + 'Dwarf', + 'Elliott', + 'Emily', + 'Evelyn', + 'George', + 'Gus', + 'Haley', + 'Harvey', + 'Jas', + 'Jodi', + 'Kent', + 'Krobus', + 'Leah', + 'Lewis', + 'Linus', + 'Marnie', + 'Maru', + 'Pam', + 'Penny', + 'Pierre', + 'Robin', + 'Sam', + 'Sandy', + 'Sebastian', + 'Shane', + 'Vincent', + 'Willy', + 'Wizard', +]; +for (const char of chars) { + await copyFile(`./content/Portraits/${char}.png`, `./static/1.5/assets/portraits/${char}.png`); +}; \ No newline at end of file diff --git a/src/dump.ts b/src/dump.ts index 66dc353..fa7e2b8 100644 --- a/src/dump.ts +++ b/src/dump.ts @@ -1,118 +1,39 @@ -import { copyFile, readFile, writeFile } from 'fs/promises'; -import type { BigCraftable, Boots, Clothing, ClothingType, Furniture, FurnitureType, Hat, MeleeWeapon, MeleeWeaponType, ObjectInformation, RangedWeapon, Tool } from './types/items'; -// @ts-expect-error -import { GetSprite } from './lib/Spritesheet.ts'; -import type { TypeEnum } from '$types/save/1.5'; - -const objects = JSON.parse(await readFile('./content/Data/ObjectInformation.json', 'utf-8')) as Record; -const objectsArray = Object.entries(objects).map(([key, value]) => { - const props = value.split('/'); - const [type, category] = props[3].split(' '); +import { copyFile, mkdir, readFile, writeFile } from 'fs/promises'; +import { GetSprite } from './lib/Spritesheet.js'; +import type { BigCraftable, Boots, Clothing, Furniture, FurnitureType, Hat, Object, Tool, Weapon } from './types/items/1.6.js'; +const objects = JSON.parse(await readFile('./content/Data/Objects.json', 'utf-8')) as Record; +const objectsArray = Object.values(objects).map((obj) => // There's a ton of "Stone" entries in objects, only use the one that has an id of 390 - if (props[0] === 'Stone' && Number(key) !== 390) return undefined; - - return { - _type: 'ObjectInformation', - name: props[0], - price: Number(props[1]), - edibility: Number(props[2]), - type: type as TypeEnum, - category: Number(category), - displayName: props[4], - description: props[5], - misc1: props[6] ? Number(props[7]) : undefined, - misc2: props[7] ? Number(props[8]) : undefined, - buffDuration: props[8] ? Number(props[9]) : undefined, - sprite: GetSprite('ObjectInformation', Number(key)), - parentSheetIndex: Number(key), - } satisfies ObjectInformation; -}).filter((x) => x !== undefined) as ObjectInformation[]; - -const bigCraftables = JSON.parse(await readFile('./content/Data/BigCraftablesInformation.json', 'utf-8')) as Record; -const bigCraftablesArray = Object.entries(bigCraftables).map(([key, value]) => { - // Skip 22 and 23, weird table business - if (Number(key) === 22 || Number(key) === 23) return undefined; + (obj.Name === 'Stone' && obj.SpriteIndex !== 390) + ? undefined + : ({ ...obj, _type: 'Object' })); - // Unobtainable items - if ([26, 27, 64].includes(Number(key))) return undefined; - - const props = value.split('/'); - return { - _type: 'BigCraftable', - name: props[0], - price: Number(props[1]), - edibility: Number(props[2]), - type: props[3].split(' ')[0] as TypeEnum, - category: -9, - description: props[4], - outdoors: props[5] === 'true', - indoors: props[6] === 'true', - fragility: Number(props[7]), - isLamp: props[8] === 'true', - displayName: props[9], - sprite: GetSprite('BigCraftable', Number(key)), - parentSheetIndex: Number(key), - } satisfies BigCraftable; -}).filter((x) => x !== undefined) as BigCraftable[]; +const bigCraftables = JSON.parse(await readFile('./content/Data/BigCraftables.json', 'utf-8')) as Record; +const bigCraftablesArray = Object.values(bigCraftables).map(obj => ({ ...obj, _type: 'BigCraftable' })); const boots = JSON.parse(await readFile('./content/Data/Boots.json', 'utf-8')) as Record; const bootsArray = Object.entries(boots).map(([key, value]) => { const props = value.split('/'); return { _type: 'Boots', - name: props[0], - description: props[1], - price: Number(props[2]), - addedDefense: Number(props[3]), - addedImmunity: Number(props[4]), - colorIndex: Number(props[5]), - displayName: props[6], - sprite: GetSprite('Boots', Number(key)), - parentSheetIndex: Number(key), + Name: props[0], + Description: props[1], + Price: Number(props[2]), + Defense: Number(props[3]), + Immunity: Number(props[4]), + ColorIndex: Number(props[5]), + DisplayName: props[6], + Sprite: GetSprite('Boots', Number(key)), + ParentSheetIndex: Number(key), } satisfies Boots; }); -const clothing = JSON.parse(await readFile('./content/Data/ClothingInformation.json', 'utf-8')) as Record; -const clothingArray = Object.entries(clothing).map(([key, value]) => { - const props = value.split('/'); - const color = props[6].split(' '); - const type = props[8] as ClothingType; - const dyeable = props[7] === 'true'; - - let sprite; - let femaleSprite; - if (type === 'Shirt') { - sprite = GetSprite('Clothing', Number(props[3]), type, dyeable); - - if (props[4] !== '-1') { - femaleSprite = GetSprite('Clothing', Number(props[4]), type, dyeable); - } - } else { - sprite = GetSprite('Clothing', Number(key), type); - } - - return { - _type: 'Clothing', - name: props[0], - displayName: props[1], - description: props[2], - maleIndex: Number(props[3]), - femaleIndex: Number(props[4]), - price: Number(props[5]), - defaultColor: { - r: Number(color[0]), - g: Number(color[1]), - b: Number(color[2]), - }, - dyeable: dyeable, - type: type, - extraData: props[9], - sprite: sprite, - femaleSprite: femaleSprite, - parentSheetIndex: Number(key), - } satisfies Clothing; -}); +const shirts = JSON.parse(await readFile('./content/Data/Shirts.json', 'utf-8')) as Record; +const pants = JSON.parse(await readFile('./content/Data/Pants.json', 'utf-8')) as Record; +const shirtsArray = Object.values(shirts).map((shirt, i) => ({ ...shirt, _type: 'Shirt', ParentSheetIndex: i, Type: 'Shirt' })); +const pantsArray = Object.values(pants).map((pant, i) => ({ ...pant, _type: 'Pants', ParentSheetIndex: i, Type: 'Pants' })); +const clothingArray = [...shirtsArray, ...pantsArray]; const furniture = JSON.parse(await readFile('./content/Data/Furniture.json', 'utf-8')) as Record; const furnitureArray = Object.entries(furniture).map(([key, value]) => { @@ -122,16 +43,16 @@ const furnitureArray = Object.entries(furniture).map(([key, value]) => { return { _type: 'Furniture', - name: props[0], - type: props[1] as FurnitureType, - tilesheetSize: split2[0] === '-1' ? -1 : { width: Number(split2[0]), height: Number(split2[1]) }, - boundingBoxSize: split3[0] === '-1' ? -1 : { width: Number(split3[0]), height: Number(split3[1]) }, - rotations: Number(props[4]), - price: Number(props[5]), - displayName: props[6], - placementRestriction: Number(props[7]), - sprite: GetSprite('Furniture', Number(key)), - parentSheetIndex: Number(key), + Name: props[0], + Type: props[1] as FurnitureType, + TilesheetSize: split2[0] === '-1' ? -1 : { width: Number(split2[0]), height: Number(split2[1]) }, + BoundingBoxSize: split3[0] === '-1' ? -1 : { width: Number(split3[0]), height: Number(split3[1]) }, + Rotations: Number(props[4]), + Price: Number(props[5]), + DisplayName: props[6], + PlacementRestriction: Number(props[7]), + Sprite: GetSprite('Furniture', Number(key)), + ParentSheetIndex: Number(key), } satisfies Furniture; }); @@ -140,88 +61,20 @@ const hatsArray = Object.entries(hats).map(([key, value]) => { const props = value.split('/'); return { _type: 'Hat', - name: props[0], - description: props[1], - showRealHair: props[2] === 'true', - skipHairstyleOffset: props[3] === 'true', - displayName: props[4], - sprite: GetSprite('Hat', Number(key)), - parentSheetIndex: Number(key), + Name: props[0], + Description: props[1], + ShowRealHair: props[2] === 'true', + SkipHairstyleOffset: props[3] === 'true', + DisplayName: props[4], + Sprite: GetSprite('Hat', Number(key)), + ParentSheetIndex: Number(key), } satisfies Hat; }); -const weapons = JSON.parse(await readFile('./content/Data/Weapons.json', 'utf-8')) as Record; -const weaponsArray = Object.entries(weapons).map(([key, value]) => { - const props = value.split('/'); - return props[0].search('Slingshot') !== -1 ? { - _type: 'RangedWeapon', - name: props[0], - description: props[1], - minDamage: Number(props[2]), - maxDamage: Number(props[3]), - displayName: props[4], - sprite: GetSprite('RangedWeapon', Number(key)), - parentSheetIndex: Number(key), - } satisfies RangedWeapon : - { - _type: 'MeleeWeapon', - name: props[0], - description: props[1], - minDamage: Number(props[2]), - maxDamage: Number(props[3]), - knockback: Number(props[4]), - speed: Number(props[5]), - addedPrecision: Number(props[6]), - addedDefense: Number(props[7]), - type: Number(props[8]) as MeleeWeaponType, - baseMineLevel: Number(props[9]), - minMineLevel: Number(props[10]), - addedAreaOfEffect: Number(props[11]), - criticalChance: Number(props[12]), - criticalDamage: Number(props[13]), - displayName: props[14], - sprite: GetSprite('MeleeWeapon', Number(key)), - parentSheetIndex: Number(key), - } satisfies MeleeWeapon; -}); - -const tools = { - "6": "Milk Pail", - "7": "Shears", - "8": "Bamboo Pole", - "9": "Training Rod", - "10": "Fiberglass Rod", - "11": "Iridium Rod", - "12": "Copper Pan", - "47": "Hoe", - "54": "Copper Hoe", - "61": "Steel Hoe", - "89": "Gold Hoe", - "96": "Iridium Hoe", - "131": "Pickaxe", - "138": "Copper Pickaxe", - "145": "Steel Pickaxe", - "173": "Gold Pickaxe", - "180": "Iridium Pickaxe", - "215": "Axe", - "222": "Copper Axe", - "229": "Steel Axe", - "257": "Gold Axe", - "264": "Iridium Axe", - "296": "Watering Can", - "303": "Copper Watering Can", - "310": "Steel Watering Can", - "338": "Gold Watering Can", - "345": "Iridium Watering Can", -}; -const toolsArray = Object.entries(tools).map(([key, value]) => { - return { - _type: 'Tool', - name: value, - sprite: GetSprite('Tool', Number(key)), - parentSheetIndex: Number(key), - } satisfies Tool; -}); +const weapons = JSON.parse(await readFile('./content/Data/Weapons.json', 'utf-8')) as Record; +const weaponsArray = Object.values(weapons).map(weapon => ({ ...weapon, _type: 'Weapon' })); +const tools = JSON.parse(await readFile('./content/Data/Tools.json', 'utf-8')) as Record; +const toolsArray = Object.values(tools).map(tool => ({ ...tool, _type: 'Tool' })); const cookingRecipes = JSON.parse(await readFile('./content/Data/CookingRecipes.json', 'utf-8')) as Record; const cookingRecipesArray = Object.entries(cookingRecipes).map(([key]) => key); @@ -231,7 +84,7 @@ const craftingRecipes = JSON.parse(await readFile('./content/Data/CraftingRecipe const craftingRecipesArray = Object.entries(craftingRecipes).map(([key]) => key); await writeFile('./static/craftingrecipes.json', JSON.stringify(craftingRecipesArray)); -const writeToFile = JSON.stringify([...objectsArray, ...bigCraftablesArray, ...bootsArray, ...clothingArray, ...furnitureArray, ...hatsArray, ...weaponsArray, ...toolsArray].map(item => [item.name, item])); +const writeToFile = JSON.stringify([...objectsArray, ...bigCraftablesArray, ...bootsArray, ...clothingArray, ...furnitureArray, ...hatsArray, ...weaponsArray, ...toolsArray].filter((o) => o).map(item => [item.Name, item])); await writeFile('./static/iteminfo.json', writeToFile); // Copy all textures into assets folder @@ -242,10 +95,10 @@ await copyFile(`./content/TileSheets/tools.png`, `./static/assets/tools.png`); await copyFile(`./content/Characters/Farmer/pants.png`, `./static/assets/pants.png`); await copyFile(`./content/Characters/Farmer/shirts.png`, `./static/assets/shirts.png`); await copyFile(`./content/Characters/Farmer/accessories.png`, `./static/assets/accessories.png`); -// await copyFile(`./content/Characters/Farmer/farmer_base.png`, `./static/assets/farmer_base.png`); -// await copyFile(`./content/Characters/Farmer/farmer_base_bald.png`, `./static/assets/farmer_base_bald.png`); -// await copyFile(`./content/Characters/Farmer/farmer_girl_base.png`, `./static/assets/farmer_girl_base.png`); -// await copyFile(`./content/Characters/Farmer/farmer_girl_base_bald.png`, `./static/assets/farmer_girl_base_bald.png`); +await copyFile(`./content/Characters/Farmer/farmer_base.png`, `./static/assets/farmer_base.png`); +await copyFile(`./content/Characters/Farmer/farmer_base_bald.png`, `./static/assets/farmer_base_bald.png`); +await copyFile(`./content/Characters/Farmer/farmer_girl_base.png`, `./static/assets/farmer_girl_base.png`); +await copyFile(`./content/Characters/Farmer/farmer_girl_base_bald.png`, `./static/assets/farmer_girl_base_bald.png`); await copyFile(`./content/Characters/Farmer/hairstyles.png`, `./static/assets/hairstyles.png`); await copyFile(`./content/Characters/Farmer/hairstyles2.png`, `./static/assets/hairstyles2.png`); await copyFile('./content/maps/springobjects.png', './static/assets/springobjects.png'); @@ -288,6 +141,12 @@ const chars = [ 'Willy', 'Wizard', ]; + +// Create portraits folder if it doesn't exist +try { + await mkdir('./static/assets/portraits'); +} catch (e) { } + for (const char of chars) { await copyFile(`./content/Portraits/${char}.png`, `./static/assets/portraits/${char}.png`); }; \ No newline at end of file diff --git a/src/lib/ItemData.ts b/src/lib/ItemData.ts index 3362ce9..e17116b 100644 --- a/src/lib/ItemData.ts +++ b/src/lib/ItemData.ts @@ -1,4 +1,4 @@ -import { FurnitureType, type Size } from "$types/items"; +import { FurnitureType, type Size } from "$types/items/1.6"; import { Category } from "$types/save/1.5"; export const HatWhichNumber = new Map([ diff --git a/src/lib/SaveFile.ts b/src/lib/SaveFile.ts index b0c49f4..a21a1ba 100644 --- a/src/lib/SaveFile.ts +++ b/src/lib/SaveFile.ts @@ -1,4 +1,4 @@ -import type { Player } from "$types/save/1.5"; +import type { Player } from "$types/save/1.6"; import { error } from "@sveltejs/kit"; import { XMLParser, XMLBuilder } from "fast-xml-parser"; import { get, writable, type Readable, type Writable } from "svelte/store"; @@ -120,7 +120,7 @@ export const SaveConverter = { if (!isSaveFile(json)) { throw error(400, "Invalid save file"); } const gameVersion = json.SaveGame.gameVersion as string | undefined; - if (!["1.5"].some((v) => gameVersion?.startsWith(v))) { + if (!["1.6"].some((v) => gameVersion?.startsWith(v))) { throw new Error(`Unsupported game version: ${gameVersion}`); } @@ -165,16 +165,16 @@ export const SaveConverter = { }); // Copy name to Name, and stack to Stack for every item in the inventory - players.forEach((player) => [player.hat, player.pantsItem, player.shirtItem, player.boots, player.leftRing, player.rightRing, ...player.items.Item].forEach((item) => { - if (item) { - // @ts-expect-error - item.Name = item.name; - // @ts-expect-error - item.Stack = item.stack; - // @ts-expect-error - item.Stackable = item.stackable; - } - })); + // players.forEach((player) => [player.hat, player.pantsItem, player.shirtItem, player.boots, player.leftRing, player.rightRing, ...player.items.Item].forEach((item) => { + // if (item) { + // // @ts-expect-error + // item.Name = item.name; + // // @ts-expect-error + // item.Stack = item.stack; + // // @ts-expect-error + // item.Stackable = item.stackable; + // } + // })); const builder = new XMLBuilder({ attributeNamePrefix: '@_', ignoreAttributes: false, suppressUnpairedNode: false, suppressEmptyNode: true, suppressBooleanAttributes: false }); const raw = builder.build(json) as string; diff --git a/src/lib/Spritesheet.ts b/src/lib/Spritesheet.ts index a36dfc8..518c4b6 100644 --- a/src/lib/Spritesheet.ts +++ b/src/lib/Spritesheet.ts @@ -1,4 +1,4 @@ -import type { Clothing, ItemInformation } from "$types/items"; +import type { Clothing, ItemInformation } from "$types/items/1.6"; import type { HairstyleColor } from "$types/save/1.5"; const ShirtsWithFemaleVariant = new Set([ @@ -18,41 +18,26 @@ const ShirtsWithMessedUpSpriteIndex = new Map export const GetSpritesheet = (lookupItem: ItemInformation): string => { let spritesheet = ''; switch (lookupItem?._type) { - case 'ObjectInformation': + case 'Object': case 'Boots': spritesheet = 'springobjects.png'; break; case 'BigCraftable': spritesheet = 'Craftables.png'; break; + case 'Pants': + spritesheet = 'pants.png'; + break; + case 'Shirt': + spritesheet = 'shirts.png'; + break; case 'Hat': spritesheet = 'hats.png'; - case 'Clothing': - switch (lookupItem._type) { - case 'Clothing': - switch (lookupItem.type) { - case 'Pants': - spritesheet = 'pants.png'; - break; - case 'Shirt': - spritesheet = 'shirts.png'; - break; - default: // Accessory - throw new Error('Not real clothing type'); - break; - } - break; - case 'Hat': - spritesheet = 'hats.png'; - break; - } - // TODO break; case 'Furniture': spritesheet = 'furniture.png'; break; - case 'RangedWeapon': - case 'MeleeWeapon': + case 'Weapon': spritesheet = 'weapons.png'; break; case 'Tool': @@ -73,34 +58,28 @@ export const IndexToSprite = (index: number, itemW: number, itemH: number, sheet return { x, y }; }; -export const GetSprite = (type: ItemInformation['_type'], index: number, clothingType: Clothing['type'] | undefined = undefined, dyeable: boolean | undefined = undefined): { x: number, y: number; } => { +export const GetSprite = (type: ItemInformation['_type'], index: number, dyeable: boolean | undefined = undefined): { x: number, y: number; } => { switch (type) { - case 'ObjectInformation': + case 'Object': return IndexToSprite(index, 16, 16, 384, 624); case 'BigCraftable': - return IndexToSprite(index, 16, 32, 128, 1152); + return IndexToSprite(index, 16, 32, 128, 1472); case 'Boots': return IndexToSprite(index, 16, 16, 384, 624); case 'Hat': - return IndexToSprite(index, 20, 80, 240, 640); - case 'Clothing': - switch (clothingType) { - case 'Pants': - // Special case, pants have 192x672 of animation frames, then the pants themselves are underneath on the left - const pantSprite = IndexToSprite(index, 192, 686, 1920, 1376); - return { x: pantSprite.x, y: pantSprite.y - 672 }; - case 'Shirt': - // let shirtSprite = ShirtsWithMessedUpSpriteIndex.get(index); - // if (shirtSprite) { return shirtSprite; } - let shirtSprite = IndexToSprite(index, 8, 32, 128, 608, 128); - return dyeable ? { x: shirtSprite.x - 128, y: shirtSprite.y } : shirtSprite; - default: - throw new Error('Not real clothing type'); - }; + return IndexToSprite(index, 20, 80, 240, 880); + case 'Pants': + // Special case, pants have 192x672 of animation frames, then the pants themselves are underneath on the left + const pantSprite = IndexToSprite(index, 192, 686, 1920, 1376); + return { x: pantSprite.x, y: pantSprite.y - 672 }; + case 'Shirt': + // let shirtSprite = ShirtsWithMessedUpSpriteIndex.get(index); + // if (shirtSprite) { return shirtSprite; } + let shirtSprite = IndexToSprite(index, 8, 32, 128, 608, 128); + return dyeable ? { x: shirtSprite.x - 128, y: shirtSprite.y } : shirtSprite; case 'Furniture': return IndexToSprite(index, 16, 16, 512, 1488); - case 'MeleeWeapon': - case 'RangedWeapon': + case 'Weapon': return IndexToSprite(index, 16, 16, 128, 144); case 'Tool': return IndexToSprite(index, 16, 16, 336, 384); diff --git a/src/reset.d.ts b/src/reset.d.ts new file mode 100644 index 0000000..e6307a6 --- /dev/null +++ b/src/reset.d.ts @@ -0,0 +1,2 @@ +// Do not add any other lines of code to this file! +import "@total-typescript/ts-reset"; \ No newline at end of file diff --git a/src/routes/(edit)/+layout.ts b/src/routes/(edit)/+layout.ts index e75bf48..60a5f75 100644 --- a/src/routes/(edit)/+layout.ts +++ b/src/routes/(edit)/+layout.ts @@ -1,5 +1,5 @@ import { base } from "$app/paths"; -import type { ItemInformation } from "$types/items"; +import type { ItemInformation } from "$types/items/1.6"; // Prefetch dumped data export const load = async ({ fetch }) => { diff --git a/src/routes/(edit)/appearance/+page.svelte b/src/routes/(edit)/appearance/+page.svelte index 15fa67c..f68865c 100644 --- a/src/routes/(edit)/appearance/+page.svelte +++ b/src/routes/(edit)/appearance/+page.svelte @@ -1,7 +1,7 @@ -
+
{#await loaded then} -
-
-
+
+
+
-
+
-
+
-
-
-
-
+
+
+
+
-
-
- - {#if character?.accessory !== -1} +
+
+ + {#if character?.accessory !== -1 && showAccessory}
+
{/if} {#if character?.hat} @@ -214,9 +268,24 @@ {/if} -
-
-
+
+
+
{/await}
diff --git a/src/routes/(edit)/character/+page.svelte b/src/routes/(edit)/character/+page.svelte index 6b12bc7..c704a48 100644 --- a/src/routes/(edit)/character/+page.svelte +++ b/src/routes/(edit)/character/+page.svelte @@ -1,7 +1,7 @@ {#if player} @@ -162,7 +162,10 @@ } .wallet { - box-shadow: 0 0 0 2px #b14e05, 0 0 0 4px #dc7b05, 0 0 0 6px #5b2b29; + box-shadow: + 0 0 0 2px #b14e05, + 0 0 0 4px #dc7b05, + 0 0 0 6px #5b2b29; margin: 0; margin-top: 24px; padding: 6px; diff --git a/src/routes/(edit)/inventory/+page.svelte b/src/routes/(edit)/inventory/+page.svelte index d448d47..2d23c84 100644 --- a/src/routes/(edit)/inventory/+page.svelte +++ b/src/routes/(edit)/inventory/+page.svelte @@ -4,14 +4,15 @@ import { CalculateEdibility, CalculatePrice } from '$lib/ItemQuality'; import { Character } from '$lib/SaveFile'; import { HexToRGB, RGBToHex } from '$lib/Spritesheet'; - import { FurnitureType, type ItemInformation } from '$types/items'; - import { Category, type Item, type Player } from '$types/save/1.5'; + import { FurnitureType, type ItemInformation } from '$types/items/1.6'; import { getContext } from 'svelte'; import Container from '../../Container.svelte'; import Preview from '../appearance/Preview.svelte'; import BigItem from './BigItem.svelte'; import QualitySelector from './QualitySelector.svelte'; import SmallItem from './SmallItem.svelte'; + import type { Item, Player, TypeEnum } from '$types/save/1.6'; + import { Category } from '$types/save/1.5'; const itemData = getContext>('itemData'); let selectedItemData: ItemInformation | undefined; @@ -53,17 +54,17 @@ // Check if the items price is the same as the default price // If so, we need to change the price whenever the quality changes // If not, we can assume the user has changed it, so just leave it alone - if ('price' in selectedItemData && selectedItem.price) { - const theoreticalOldPrice = CalculatePrice(selectedItemData.price, oldQuality ?? 0); + if ('Price' in selectedItemData && selectedItem.price) { + const theoreticalOldPrice = CalculatePrice(selectedItemData.Price, oldQuality ?? 0); if (theoreticalOldPrice === selectedItem.price) { - selectedItem.price = CalculatePrice(selectedItemData.price, selectedItem.quality); + selectedItem.price = CalculatePrice(selectedItemData.Price, selectedItem.quality); } } - if ('edibility' in selectedItemData && selectedItem.edibility) { - const theoreticalOldEdibility = CalculateEdibility(selectedItemData.edibility, oldQuality ?? 0); + if ('Edibility' in selectedItemData && selectedItem.edibility) { + const theoreticalOldEdibility = CalculateEdibility(selectedItemData.Edibility, oldQuality ?? 0); if (theoreticalOldEdibility === selectedItem.edibility) { - selectedItem.edibility = CalculateEdibility(selectedItemData.edibility, selectedItem.quality); + selectedItem.edibility = CalculateEdibility(selectedItemData.Edibility, selectedItem.quality); } } } finally { @@ -99,36 +100,15 @@ let category: number | undefined; switch (newItemData._type) { - case 'ObjectInformation': + case 'Object': + break; case 'BigCraftable': - category = newItemData.category; - - // This is reduntant and pointless in most cases, but we might as well try to guess the category if it's not specified - switch (newItemData.type) { - case 'Cooking': - category = Category.Cooking; - break; - case 'Seeds': - category = Category.Seeds; - break; - case 'Ring': - category = Category.Ring; - break; - case 'Arch': - case 'asdf': - case 'Quest': - case 'Basic': - case 'Crafting': - case 'Fish': - case 'Minerals': - // TODO - } - break; case 'Boots': category = Category.Boots; break; - case 'Clothing': + case 'Pants': + case 'Shirt': category = Category.Clothing; break; case 'Furniture': @@ -137,9 +117,7 @@ case 'Hat': category = Category.Hat; break; - case 'MeleeWeapon': - - case 'RangedWeapon': + case 'Weapon': category = Category.Weapon; break; case 'Tool': @@ -149,38 +127,35 @@ const newItem: Item = { name: newItemName, + itemId: 'ParentSheetIndex' in newItemData ? newItemData.ParentSheetIndex : 0, stack: 1, - parentSheetIndex: 'parentSheetIndex' in newItemData ? newItemData.parentSheetIndex : 0, - indexInTileSheet: 'parentSheetIndex' in newItemData ? newItemData.parentSheetIndex : 0, + quality: 0, + isRecipe: false, + parentSheetIndex: 'SpriteIndex' in newItemData ? newItemData.SpriteIndex : 0, + indexInTileSheet: 'SpriteIndex' in newItemData ? newItemData.SpriteIndex : 0, category: category, hasBeenInInventory: true, - hasBeenPickedUpByFarmer: true, - DisplayName: newItemData.name, SpecialVariable: 0, // TODO ? - indexInColorSheet: 0, // TODO isLostItem: false, specialItem: false, tileLocation: { X: 0, Y: 0 }, - boundingBox: { X: 0, Y: 0, Width: 64, Height: 64, Location: { X: 0, Y: 0 } }, + boundingBox: { X: 0, Y: 0, Width: 64, Height: 64, Size: { X: 64, Y: 64 }, Location: { X: 0, Y: 0 } }, canBeSetDown: true, canBeGrabbed: true, }; let type: string | undefined; switch (newItemData._type) { - case 'ObjectInformation': + case 'Object': case 'BigCraftable': type = 'Object'; break; case 'Furniture': type = 'Furniture'; break; - case 'MeleeWeapon': + case 'Weapon': type = 'MeleeWeapon'; break; - case 'RangedWeapon': - type = 'Weapon'; - break; case 'Tool': if (newItemName === 'Milk Pail') { type = 'MilkPail'; @@ -205,7 +180,7 @@ } if (type) { - // This is required for the game to recognize the item as the correct type, but isn't part of the XML structureS + // This is required for the game to recognize the item as the correct type, but isn't part of the XML structures // @ts-expect-error newItem['@_xsi:type'] = type; @@ -215,11 +190,11 @@ } } - if (newItemData._type === 'ObjectInformation') { + if (newItemData._type === 'Object') { newItem.price = 0; newItem.quality = 0; - if (newItemData.type === 'Ring') { + if (newItemData.Type === 'Ring') { const id = RingsUniqueID.get(newItemName); if (id) { newItem.uniqueID = id; @@ -228,48 +203,55 @@ } if (newItemData._type !== 'Tool') { - newItem.parentSheetIndex = newItemData.parentSheetIndex; + newItem.parentSheetIndex = 'SpriteIndex' in newItemData ? newItemData.SpriteIndex : 0; } if (newItem.name === 'Fishing Rod') { - newItem.BaseName = 'Fishing Rod'; - newItem.upgradeLevel = FishingRodUpgradeNumber.get(newItemData.name) ?? 0; + newItem.upgradeLevel = FishingRodUpgradeNumber.get(newItemData.Name) ?? 0; newItem.parentSheetIndex = 685; - newItem.initialParentTileIndex = FishingRodSpriteIndex.get(newItemData.name) ?? 0; + newItem.initialParentTileIndex = FishingRodSpriteIndex.get(newItemData.Name) ?? 0; newItem.indexOfMenuItemView = newItem.initialParentTileIndex; } if (newItemData._type === 'Hat') { - newItem.which = HatWhichNumber.get(newItemName) ?? 0; + newItem.which = ''; } if (newItemData._type === 'Furniture') { newItem.canBeGrabbed = true; - newItem.parentSheetIndex = newItemData.parentSheetIndex; - newItem.furniture_type = FurnitureTypeToNumber.get(newItemData.type); + newItem.parentSheetIndex = newItemData.ParentSheetIndex; + newItem.type = FurnitureTypeToNumber.get(newItemData.Type); // sourceRect is the sprite data, if I understand correctly - if (newItemData.tilesheetSize !== -1) { + if (newItemData.TilesheetSize !== -1) { newItem.sourceRect = { - X: newItemData.sprite.x, - Y: newItemData.sprite.y, - Width: newItemData.tilesheetSize.width, - Height: newItemData.tilesheetSize.height, + X: newItemData.Sprite.x, + Y: newItemData.Sprite.y, + Width: newItemData.TilesheetSize.width, + Height: newItemData.TilesheetSize.height, Location: { - X: newItemData.sprite.x, - Y: newItemData.sprite.y, + X: newItemData.Sprite.x, + Y: newItemData.Sprite.y, + }, + Size: { + X: newItemData.TilesheetSize.width, + Y: newItemData.TilesheetSize.height, }, }; newItem.defaultSourceRect = newItem.sourceRect; } // Bounding box is the hitbox/placement box - if (newItemData.boundingBoxSize !== -1) { + if (newItemData.BoundingBoxSize !== -1) { newItem.boundingBox = { X: 0, Y: 0, - Width: newItemData.boundingBoxSize.width, - Height: newItemData.boundingBoxSize.height, + Width: newItemData.BoundingBoxSize.width, + Height: newItemData.BoundingBoxSize.height, + Size: { + X: newItemData.BoundingBoxSize.width, + Y: newItemData.BoundingBoxSize.height, + }, Location: { X: 0, Y: 0, @@ -280,31 +262,29 @@ } // If the item is a lamp, enable the lamp property - if (newItemData.type === FurnitureType.Lamp) { + if (newItemData.Type === FurnitureType.Lamp) { newItem.isLamp = true; } } - if (newItemData._type === 'Clothing' && newItemData.dyeable) { + if ('CanBeDyed' in newItemData && newItemData.CanBeDyed) { newItem.clothesColor = { R: 255, G: 255, B: 255, A: 255, PackedValue: 0 }; } - if (newItemData._type === 'ObjectInformation' || newItemData._type === 'BigCraftable') { - newItem.type = newItemData.type; - switch (newItemData.type) { - case 'Ring': - // @ts-expect-error - newItem['@_xsi:type'] = 'Ring'; - break; + if (newItemData._type === 'Object') { + newItem.type = newItemData.Type as TypeEnum; + if (newItemData._type === 'Object' && newItemData.Type === 'Ring') { + // @ts-expect-error + newItem['@_xsi:type'] = 'Ring'; } } - if ('edibility' in newItemData) { - newItem.edibility = newItemData.edibility ?? -300; + if ('Edibility' in newItemData) { + newItem.Price = newItemData.Edibility ?? -300; } - if ('price' in newItemData) { - newItem.price = newItemData.price ?? 0; + if ('Price' in newItemData) { + newItem.Price = newItemData.Price ?? 0; } if (typeof symbol === 'number') { @@ -314,10 +294,12 @@ } if (symbol === 'hat' && newItem.name === 'Copper Pan') { + // @ts-expect-error This is exlusive to the copper pan newItem.ignoreHairstyleOffset = true; newItem.parentSheetIndex = 71; newItem.indexInTileSheet = 71; newItem.category = Category.Hat; + // @ts-expect-error This is exlusive to the copper pan newItem.which = 71; } @@ -366,7 +348,7 @@
- +
@@ -405,16 +387,17 @@ {#if selectedItem} {#if selectedItemData} - {#if (selectedItem.stackable === undefined || selectedItem.stackable === true) && !['Clothing', 'Boots', 'Hat'].includes(selectedItemData._type)} + + {#if !['Clothing', 'Boots', 'Hat', 'Weapon', 'Pants', 'Shirt'].includes(selectedItemData._type)} {/if} - {#if selectedItemData._type === 'MeleeWeapon'} + {#if selectedItemData._type === 'Weapon'} - {:else if selectedItemData._type === 'RangedWeapon'} - {:else if selectedItemData._type === 'Tool'} {:else if selectedItemData._type === 'BigCraftable'} @@ -482,11 +463,11 @@ {:else if selectedItemData._type === 'Boots'} - {:else if selectedItemData._type === 'Clothing'} - {#if selectedItemData.dyeable} + {:else if selectedItemData._type === 'Shirt'} + {#if selectedItemData.CanBeDyed}
diff --git a/src/routes/(edit)/inventory/QualitySelector.svelte b/src/routes/(edit)/inventory/QualitySelector.svelte index 2df6e6e..ab6d7d3 100644 --- a/src/routes/(edit)/inventory/QualitySelector.svelte +++ b/src/routes/(edit)/inventory/QualitySelector.svelte @@ -1,10 +1,10 @@
@@ -56,17 +56,23 @@ } .container label:nth-child(2) { - text-shadow: 0 0 0 #dadfe5, 0 0 2px #aaadb2; + text-shadow: + 0 0 0 #dadfe5, + 0 0 2px #aaadb2; color: transparent; } .container label:nth-child(3) { - text-shadow: 0 0 0 #ffff18, 0 0 2px #cccc13; + text-shadow: + 0 0 0 #ffff18, + 0 0 2px #cccc13; color: transparent; } .container label:nth-child(4) { - text-shadow: 0 0 0 #db67c4, 0 0 2px #a74f96; + text-shadow: + 0 0 0 #db67c4, + 0 0 2px #a74f96; color: transparent; } diff --git a/src/routes/(edit)/inventory/SmallItem.svelte b/src/routes/(edit)/inventory/SmallItem.svelte index 734fdd7..5874b43 100644 --- a/src/routes/(edit)/inventory/SmallItem.svelte +++ b/src/routes/(edit)/inventory/SmallItem.svelte @@ -1,12 +1,12 @@ -
+