diff --git a/src/Chf/Dna.ts b/src/Chf/Dna.ts
index ec0bfcb..452db95 100644
--- a/src/Chf/Dna.ts
+++ b/src/Chf/Dna.ts
@@ -35,7 +35,6 @@ export type DnaFacePart =
'crown'
export interface DnaBlend {
- part: DnaFacePart
headId: number
percent: number
}
@@ -61,17 +60,55 @@ export interface Dna {
blends: DnaBlends
}
-export function readDna(parentReader: BufferReader, bodyType: BodyType): Dna {
- const isMale = bodyType === 'male'
+export function readDna(parentReader: BufferReader): Dna {
parentReader.expectUint64(dnaSize)
const bytes = parentReader.readBytes(dnaSize)
const dnaString = toHexStr(bytes)
- const reader = new BufferReader(bytes.buffer)
+ return dnaFromString(dnaString)
+}
+
+export function writeDna(writer: BufferWriter, dna: Dna, _bodyType: BodyType) {
+ const bytes = fromHexStr(dna.dnaString)
+ if (bytes.length !== dnaSize)
+ throw new Error(`DNA string must be ${dnaSize} bytes long`)
+
+ writer.writeUint64(dnaSize)
+ writer.writeBytes(bytes)
+
+ // TODO: this is probably broken. Replace the above with this when fixed
+ // const isMale = bodyType === 'male'
+ // writer.writeUint32(0xFCD09394)
+ // writer.writeUint32(isMale ? 0xDD6C67F6 : 0x9EF4EB54)
+ // writer.writeUint32(isMale ? 0x65E740D3 : 0x65D75204)
+ // writer.writeUint32(0)
+ // writer.writeByte(0x0C)
+ // writer.writeByte(0x0)
+ // writer.writeByte(0x04)
+ // writer.writeByte(0x0)
+ // writer.writeByte(0x4)
+ // writer.writeByte(0x0)
+ // writer.writeByte(dna.childCount)
+ // writer.writeByte(0x0)
+
+ // for (let i = 0; i < partCount; i++) {
+ // const part = i % 12
+ // const blends = dna.blends.get(part) || []
+ // const blend = blends[i % blends.length]
+
+ // const percentShort = blend.percent / 100 * 0xFFFF
+ // writer.writeUint16(percentShort)
+ // writer.writeByte(blend.headId)
+ // writer.writeByte(0)
+ // }
+}
+
+export function dnaFromString(dnaString: string): Dna {
+ const reader = new BufferReader(fromHexStr(dnaString).buffer)
reader.expectUint32(0xFCD09394)
- reader.expectUint32(isMale ? 0xDD6C67F6 : 0x9EF4EB54)
- reader.expectUint32(isMale ? 0x65E740D3 : 0x65D75204)
+ reader.readUint32()// skip keys. bad idea?
+ reader.readUint32()
reader.expectUint32(0)
reader.expectByte(0x0C)
reader.expectByte(0x0)
@@ -80,7 +117,7 @@ export function readDna(parentReader: BufferReader, bodyType: BodyType): Dna {
reader.expectByte(0x4)
reader.expectByte(0x0)
const childCount = reader.readByte()
- reader.expectByte(0x0)
+ reader.readByte()// might be 0 or ff for some reason?
const map: DnaBlends = {
eyebrowLeft: [],
@@ -104,9 +141,8 @@ export function readDna(parentReader: BufferReader, bodyType: BodyType): Dna {
reader.expectByte(0)
const percent = percentShort / 0xFFFF * 100
- const blend = { headId, percent, part: idxPartRecord[part] }
- map[idxPartRecord[part]].push(blend)
+ map[idxPartRecord[part]].push({ headId, percent })
}
return {
@@ -115,38 +151,3 @@ export function readDna(parentReader: BufferReader, bodyType: BodyType): Dna {
blends: map,
}
}
-
-export function writeDna(writer: BufferWriter, dna: Dna, _bodyType: BodyType) {
- const bytes = fromHexStr(dna.dnaString)
- if (bytes.length !== dnaSize)
- throw new Error(`DNA string must be ${dnaSize} bytes long`)
-
- writer.writeUint64(dnaSize)
- writer.writeBytes(bytes)
-
- // TODO: this is probably broken. Replace the above with this when fixed
- // const isMale = bodyType === 'male'
- // writer.writeUint32(0xFCD09394)
- // writer.writeUint32(isMale ? 0xDD6C67F6 : 0x9EF4EB54)
- // writer.writeUint32(isMale ? 0x65E740D3 : 0x65D75204)
- // writer.writeUint32(0)
- // writer.writeByte(0x0C)
- // writer.writeByte(0x0)
- // writer.writeByte(0x04)
- // writer.writeByte(0x0)
- // writer.writeByte(0x4)
- // writer.writeByte(0x0)
- // writer.writeByte(dna.childCount)
- // writer.writeByte(0x0)
-
- // for (let i = 0; i < partCount; i++) {
- // const part = i % 12
- // const blends = dna.blends.get(part) || []
- // const blend = blends[i % blends.length]
-
- // const percentShort = blend.percent / 100 * 0xFFFF
- // writer.writeUint16(percentShort)
- // writer.writeByte(blend.headId)
- // writer.writeByte(0)
- // }
-}
diff --git a/src/Components/CharacterEditor.tsx b/src/Components/CharacterEditor.tsx
index e7abe91..87f42e5 100644
--- a/src/Components/CharacterEditor.tsx
+++ b/src/Components/CharacterEditor.tsx
@@ -1,6 +1,7 @@
import { Affix, Button, Center, Group, Stack } from '@mantine/core'
import { IconDownload } from '@tabler/icons-react'
import { useCallback } from 'react'
+import { useLocalStorage } from '@mantine/hooks'
import { useCharacter } from '../Context/CharacterContext'
import { createChf } from '../Chf/ChfFile'
import SkinColorPicker from './SkinColorPicker'
@@ -8,7 +9,10 @@ import { CharacterJsonDisplay } from './CharacterJsonDisplay'
import { DnaPanel } from './DnaPanel'
function CharacterEditor() {
- const isDev = import.meta.env.DEV
+ const [isDev] = useLocalStorage({
+ key: 'isDev',
+ defaultValue: false,
+ })
const [character] = useCharacter()
const exportCharacter = useCallback(() => {
const buffer = createChf(character)
@@ -25,13 +29,9 @@ function CharacterEditor() {
-
-
-
- {isDev && (
-
- )}
+ {isDev && }
+ {isDev && }
diff --git a/src/Components/DnaPanel.tsx b/src/Components/DnaPanel.tsx
index 733dea0..d3f7550 100644
--- a/src/Components/DnaPanel.tsx
+++ b/src/Components/DnaPanel.tsx
@@ -1,45 +1,115 @@
-import { Fieldset, Group, NumberInput, Slider, Stack } from '@mantine/core'
-import { useCallback } from 'react'
+import { Button, Center, Fieldset, Group, Input, Modal, NumberInput, Slider, Stack, Text } from '@mantine/core'
+import { useCallback, useState } from 'react'
import type { NumberFormatValues } from 'react-number-format'
+import { useDisclosure } from '@mantine/hooks'
+import { notifications } from '@mantine/notifications'
import { useCharacter } from '../Context/CharacterContext'
-import type { DnaFacePart } from '../Chf/Dna'
+import { type DnaFacePart, dnaFromString } from '../Chf/Dna'
export function DnaPanel() {
+ const [opened, { toggle, close }] = useDisclosure(false)
+ const [dnaString, setDnaString] = useState('')
+ const [character, updateCharacter] = useCharacter()
+
+ const importDna = useCallback(() => {
+ updateCharacter((d) => {
+ d.dna = dnaFromString(dnaString)
+ })
+ close()
+ }, [updateCharacter, close, dnaString])
+
+ const dnaStringOpen = useCallback(() => {
+ setDnaString('')
+ toggle()
+ }, [toggle, setDnaString])
+
+ const dnaStringClipboard = useCallback(() => {
+ navigator.clipboard.writeText(character.dna.dnaString)
+ notifications.show({
+ title: 'DNA String copied to clipboard',
+ message: 'You can now share it with others or import it into another character.',
+ autoClose: 2000,
+ })
+ }, [character])
+
return (
-
+ <>
+
+
+
+
+ Enter the DNA string of the character you want to import. These can be shared between players or imported from NPCs from the game files.
+
+ setDnaString(event.currentTarget.value)}
+ size="xl"
+ w="100%"
+ placeholder="9493D0FC...."
+ />
+
+
+
+
+
+ >
)
}
function DnaPart({ label, part }: { label: string, part: DnaFacePart }) {
- const [character] = useCharacter()
+ const [character, updateCharacter] = useCharacter()
const blend = character.dna.blends[part]
// make this work properly. We want the sum of all the blend percentages to be 100
// and the others to be updated accordingly when one is changed
- // const updateBlend = useCallback((sliderIndex: number, newValue: number) => {
-
- // }, [blend, character, part, updateCharacter])
+ const updateBlend = useCallback((sliderIndex: number, newValue: number) => {
+ console.log('updateBlend', sliderIndex, newValue)
+ }, [blend, character, part, updateCharacter])
const isValidHeadId = useCallback((headId: NumberFormatValues) => {
const id = headId.floatValue ?? 0
@@ -47,51 +117,36 @@ function DnaPart({ label, part }: { label: string, part: DnaFacePart }) {
return id >= 0 && id <= 26 && Number.isInteger(id)
}, [])
- function HeadPicker({ index }: { index: number }) {
- return (
- updateCharacter((d) => { d.dna.blends[part][index].headId = values.floatValue ?? 0 })}
- />
- )
- }
-
- function MySlider({ index }: { index: number }) {
+ function DnaBlend({ index }: { index: number }) {
return (
- updateBlend(index, value)}
- />
+
+ updateCharacter((d) => { d.dna.blends[part][index].headId = values.floatValue ?? 0 })}
+ />
+ updateBlend(index, value)}
+ />
+
)
}
return (
)
}