Skip to content

Commit

Permalink
0.8.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Sans3108 committed Feb 21, 2025
1 parent 9f43b06 commit 2527315
Show file tree
Hide file tree
Showing 7 changed files with 47 additions and 40 deletions.
52 changes: 36 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,30 +155,51 @@ const map = await Map.new('Baby Aim 1.0');
console.log(`${map.finishes[0].players[0].name} ${map.finishes[0].timeString}`); // "Cireme 06:25"
```
### TW 0.6 Skin rendering
### Skin rendering
I tried my hand at making a renderer based on [TeeAssembler-2.0](https://github.com/AlexIsTheGuy/TeeAssembler-2.0) by [AlexIsTheGuy](https://github.com/AlexIsTheGuy), and I think I nailed it.
The lib also provides a way to render still images of any skin, in any way (without animated feet)
#### 0.6 (DDNet) skins
With inspiration from [TeeAssembler-2.0](https://github.com/AlexIsTheGuy/TeeAssembler-2.0) by [AlexIsTheGuy](https://github.com/AlexIsTheGuy) and help from [Patiga](https://github.com/Patiga)'s [Tee Rendering documentation](https://github.com/heinrich5991/libtw2/blob/master/doc/tee_rendering.md). It is able to render any 0.6 skin from either a skin name or buffer.
Example usage:
```ts
import { TeeSkin6 } from 'ddnet';

const mySkin = new TeeSkin6({ skinResource: 'CrystalCat' });
const rendered = await mySkin.render({ eyeVariant: 'eye-happy' });
// Do something with the rendered skin buffer, like sending it in a message on discord as an attachment
const mySkin = new TeeSkin6({ skinResource: '10Fox' });
const rendered = await mySkin.render(); // Do something with the rendered skin buffer

// Or optionally, save it to a file by providing a file path, like this:
await mySkin.render({ eyeVariant: 'eye-happy', saveFilePath: 'my-skin.png' }); // Still returns a buffer
// Or optionally, save it to a file by providing a file path
await mySkin.render({ saveFilePath: 'my-skin.png' }); // Still returns a buffer
```
_my-skin.png_
![Skin Render Output](https://raw.githubusercontent.com/Sans3108/DDNet/master/misc/my-skin.png)
### TW 0.7 Skin rendering
You can also customize the emote, and direction the tee is looking towards:
```ts
import { TeeSkin6, TeeSkinEyeVariant } from 'ddnet';

const mySkin = new TeeSkin6({ skinResource: '10Fox' });

await mySkin.render({
saveFilePath: 'my-skin-happy-left.png',
eyeVariant: TeeSkinEyeVariant.happy,
viewAngle: 180 // left, since 0 is right
});
```
_my-skin-happy-left.png_
![Skin Render Output](https://raw.githubusercontent.com/Sans3108/DDNet/master/misc/my-skin-happy-left.png)
#### 0.7 (Teeworlds) skins
I also tried creating a TW 0.7 skin renderer, which is similar to the 0.6 renderer but with a few changes.
Very similar to 0.6 skins, the only difference being how the instance is initialized.
Example usage:
Expand All @@ -197,28 +218,27 @@ const rendered = await skin.render({
markingTWcode: -485425166,
feetTWcode: 1102450,
eyesTWcode: 1441632
},
eyeVariant: 'eye-evil'
});
// Do something with the rendered skin buffer, like sending it in a message on discord as an attachment
}
}); // Do something with the rendered skin buffer

// Or optionally, save it to a file by providing a file path, like this:
// Or optionally, save it to a file by providing a file path
await skin.render({
customColors: {
bodyTWcode: 1102443,
markingTWcode: -485425166,
feetTWcode: 1102450,
eyesTWcode: 1441632
},
saveFilePath: 'fox.png',
eyeVariant: 'eye-evil'
saveFilePath: 'fox.png'
}); // Still returns a buffer
```
_fox.png_
![Skin Render Output](https://raw.githubusercontent.com/Sans3108/DDNet/master/misc/fox.png)
Other customizations remain the same between 0.6 and 0.7 skins such as the emote, eye direction etc.
## Cache
Most classes cache results, this is done using the [keyv](https://www.npmjs.com/package/keyv) and [@keyv/sqlite](https://www.npmjs.com/package/@keyv/sqlite) packages. Cache data is stored in the `ddnet_cache.sqlite` file.
Expand Down
Binary file modified misc/fox.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added misc/my-skin-happy-left.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified misc/my-skin.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 0 additions & 13 deletions src/DDNet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,16 +71,3 @@ export * from './schemas/players/query.js';
export * from './util.js';

export { findPlayer } from './Master.js';

// import { TeeSkinEyeVariant } from './classes/skins/TeeSkinUtils.js';
// import { findPlayer } from './Master.js';
// const me = (await findPlayer('Sans3108'))![0];

// console.log(me.self.skin);

// await me.renderSkin({
// saveFilePath: 'tee.png',
// eyeVariant: TeeSkinEyeVariant.normal,
// size: 256,
// viewAngle: 0
// });
18 changes: 9 additions & 9 deletions src/classes/skins/TeeSkin7.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,12 +223,12 @@ export class TeeSkin7 {
markingAsset
]);

// // Blink emote
// if (opts.eyeVariant === TeeSkinEyeVariant.blink) {
// selectedEyes = await sharp(selectedEyes.data)
// .resize(selectedEyes.info.width, Math.round(selectedEyes.info.height * 0.45))
// .toBuffer();
// }
// Blink emote
if (opts.eyeVariant === TeeSkinEyeVariant.blink) {
selectedEyes = await sharp(selectedEyes)
.resize(selectedEyesRegion.width, Math.round(selectedEyesRegion.height * 0.45))
.toBuffer();
}

// Coloring
[decorationShadow, decorationColorable, bodyColorable, marking, selectedEyes, footColorable] = await Promise.all([
Expand All @@ -254,8 +254,8 @@ export class TeeSkin7 {

const bodySize = spriteRegions.body.shadow.width;
const footSize = spriteRegions.foot.shadow.width;
const eyeHeight = spriteRegions.eyes[TeeSkinEyeVariant.normal].height;
const eyeWidth = spriteRegions.eyes[TeeSkinEyeVariant.normal].width;
const eyeHeight = opts.eyeVariant === TeeSkinEyeVariant.blink ? Math.round(selectedEyesRegion.height * 0.45) : selectedEyesRegion.height;
const eyeWidth = selectedEyesRegion.width;
const canvasSize = bodySize * 1.2; // Slight margin of empty space around the tee

const centerOffset = (4 / 64) * bodySize;
Expand All @@ -280,6 +280,7 @@ export class TeeSkin7 {

const overlays: sharp.OverlayOptions[] = [
{ input: footShadow, left: lFootX, top: footY }, // back foot
{ input: footShadow, left: rFootX, top: footY }, // front foot
{ input: bodyShadow, left: bodyX, top: bodyY },
decorationShadow ? { input: decorationShadow, left: bodyX, top: bodyY } : null,
{ input: footColorable, left: lFootX, top: footY }, // back foot
Expand All @@ -289,7 +290,6 @@ export class TeeSkin7 {
{ input: bodyAccent, left: bodyX, top: bodyY },
{ input: bodyOutline, left: bodyX, top: bodyY },
{ input: selectedEyes, left: eyeX, top: eyeY },
{ input: footShadow, left: rFootX, top: footY }, // front foot
{ input: footColorable, left: rFootX, top: footY } // front foot
].filter(Boolean) as sharp.OverlayOptions[];

Expand Down
4 changes: 2 additions & 2 deletions src/classes/skins/TeeSkinUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ export enum TeeSkinEyeVariant {
angry = 'eye-angry',
pain = 'eye-pain',
happy = 'eye-happy',
dead = 'eye-dead',
surprise = 'eye-surprise',
/**
* Unused, some 0.6 skins have them though
*/
dead = 'eye-dead',
surprise = 'eye-surprise',
blink = 'eye-blink'
}

Expand Down

0 comments on commit 2527315

Please sign in to comment.