refactor: ♻️ restructure character data files, move shared functionality into library
This commit is contained in:
parent
23c06bcd3a
commit
d7d00046b1
9 changed files with 239 additions and 312 deletions
73
src/includes/util.js
Normal file
73
src/includes/util.js
Normal file
|
@ -0,0 +1,73 @@
|
|||
const getClientLocale = () => {
|
||||
return navigator.languages.length > 0 ? navigator.languages[0] : 'en-US';
|
||||
};
|
||||
|
||||
const getAge = (dateOfBirth) => {
|
||||
const today = new Date();
|
||||
|
||||
const thisYear = today.getFullYear();
|
||||
const thisMonth = today.getMonth();
|
||||
const thisDay = today.getDate();
|
||||
|
||||
const dobYear = dateOfBirth.getFullYear();
|
||||
const dobMonth = dateOfBirth.getMonth();
|
||||
const dobDay = dateOfBirth.getDate();
|
||||
|
||||
let age = thisYear - dobYear;
|
||||
|
||||
if (thisMonth < dobMonth) age--;
|
||||
if (thisMonth === dobMonth && thisDay < dobDay) age--;
|
||||
|
||||
return age;
|
||||
};
|
||||
|
||||
const toImperial = (cm) => {
|
||||
const realFeet = (cm * 0.3937) / 12;
|
||||
const feet = Math.floor(realFeet);
|
||||
const inches = Math.round((realFeet - feet) * 12);
|
||||
|
||||
return `${feet}'${inches}"`;
|
||||
};
|
||||
|
||||
const toInch = (cm) => {
|
||||
return `${Math.round(cm / 2.45)} in`;
|
||||
};
|
||||
|
||||
const toLbs = (kg) => {
|
||||
const nearExact = kg / 0.45359237;
|
||||
const lbs = Math.floor(nearExact);
|
||||
|
||||
return lbs;
|
||||
};
|
||||
|
||||
const toFahrenheit = (celsius) => {
|
||||
return celsius * 1.8 + 32;
|
||||
};
|
||||
|
||||
const dateFormat = new Intl.DateTimeFormat(getClientLocale(), {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: '2-digit'
|
||||
});
|
||||
|
||||
const getFullName = (...names) => names.join(' ');
|
||||
const getDateOfBirth = (dob) => `${dateFormat.format(dob)} (${getAge(dob)})`;
|
||||
const getHeight = (height) => `${height} cm (${toImperial(height)})`;
|
||||
const getWeight = (weight) => `${weight} kg (${toLbs(weight)} lbs)`;
|
||||
const getTailLength = (length) => `${length / 100} m (${toImperial(length)})`;
|
||||
const getWingspan = (wingspan) => `${wingspan / 100} m (${toImperial(wingspan)})`;
|
||||
|
||||
export {
|
||||
getAge,
|
||||
toImperial,
|
||||
toInch,
|
||||
toLbs,
|
||||
toFahrenheit,
|
||||
dateFormat,
|
||||
getFullName,
|
||||
getDateOfBirth,
|
||||
getHeight,
|
||||
getWeight,
|
||||
getTailLength,
|
||||
getWingspan
|
||||
};
|
|
@ -1,7 +1,7 @@
|
|||
<script webc:setup>
|
||||
const characters = Object.keys($data.collections).filter((coll) => coll !== 'all');
|
||||
|
||||
const getCharacterName = (name) => $data.collections[name][0].data.getFullName();
|
||||
const getCharacterName = (name) => $data.collections[name][0].data.fullName;
|
||||
const getDescription = (name) => $data.collections[name][0].data.description;
|
||||
const getAvatar = (name) => `src/img/${name}/avatar.png`;
|
||||
const getAltText = (name) => name.charAt(0).toUpperCase() + name.slice(1) + ' Avatar';
|
||||
|
|
|
@ -6,4 +6,4 @@ eleventyNavigation:
|
|||
|
||||
Jarek's color palette:
|
||||
|
||||
<colors :@colors="$data.getColors()"></colors>
|
||||
<colors :@colors="$data.colors"></colors>
|
||||
|
|
|
@ -1,17 +1,20 @@
|
|||
import { getFullName, getHeight, getWeight, getTailLength } from '../includes/util.js';
|
||||
|
||||
const firstName = 'Jarek',
|
||||
lastName = 'Vakhtang',
|
||||
fullName = getFullName(firstName, lastName),
|
||||
species = 'Krondir',
|
||||
gender = 'male',
|
||||
height = 217,
|
||||
weight = 159,
|
||||
tailLength = 112,
|
||||
colors = {
|
||||
primary: '#C5914E',
|
||||
hair: '#827427',
|
||||
eyes: '#B2B59D',
|
||||
horn: '#DABA8F',
|
||||
penis: '#B39B38'
|
||||
},
|
||||
colors = [
|
||||
{ name: 'Main Fur', value: '#C5914E' },
|
||||
{ name: 'Hair', value: '#827427' },
|
||||
{ name: 'Eyes', value: '#B2B59D' },
|
||||
{ name: 'Horn', value: '#DABA8F' },
|
||||
{ name: 'Penis', value: '#B39B38' }
|
||||
],
|
||||
penis = {
|
||||
shape: 'canid',
|
||||
type: 'grower',
|
||||
|
@ -21,47 +24,21 @@ const firstName = 'Jarek',
|
|||
},
|
||||
description = "Don't encroach on his turf, or you'll be sorry!";
|
||||
|
||||
const toImperial = (cm) => {
|
||||
const realFeet = (cm * 0.3937) / 12;
|
||||
const feet = Math.floor(realFeet);
|
||||
const inches = Math.round((realFeet - feet) * 12);
|
||||
|
||||
return `${feet}'${inches}"`;
|
||||
};
|
||||
|
||||
const toLbs = (kg) => {
|
||||
const nearExact = kg / 0.45359237;
|
||||
const lbs = Math.floor(nearExact);
|
||||
|
||||
return lbs;
|
||||
};
|
||||
|
||||
const getFullName = () => `${firstName} ${lastName}`;
|
||||
const getHeight = () => `${height} cm (${toImperial(height)})`;
|
||||
const getWeight = () => `${weight} kg (${toLbs(weight)} lbs)`;
|
||||
const getTailLength = () => `${tailLength / 100} m (${toImperial(tailLength)})`;
|
||||
|
||||
const getTraits = () => [
|
||||
// { icon: 'fa6-solid:cake-candles', type: 'Date of Birth', text: getDateOfBirth() },
|
||||
{ icon: 'fa6-solid:mars', type: 'Sex/Gender', text: gender },
|
||||
{ icon: 'fa6-solid:ruler', type: 'Height', text: getHeight() },
|
||||
{ icon: 'fa6-solid:weight-hanging', type: 'Weight', text: getWeight() },
|
||||
{ icon: 'fa6-solid:ruler', type: 'Tail Length', text: getTailLength() }
|
||||
];
|
||||
|
||||
const getColors = () => [
|
||||
{ name: 'Main Fur', value: colors.primary },
|
||||
{ name: 'Hair', value: colors.hair },
|
||||
{ name: 'Eyes', value: colors.eyes },
|
||||
{ name: 'Horn', value: colors.horn }
|
||||
{ icon: 'fa6-solid:ruler', type: 'Height', text: getHeight(height) },
|
||||
{ icon: 'fa6-solid:weight-hanging', type: 'Weight', text: getWeight(weight) },
|
||||
{ icon: 'fa6-solid:ruler', type: 'Tail Length', text: getTailLength(tailLength) }
|
||||
];
|
||||
|
||||
export default {
|
||||
firstName,
|
||||
fullName,
|
||||
species,
|
||||
gender,
|
||||
colors,
|
||||
description,
|
||||
getFullName,
|
||||
getTraits,
|
||||
getColors
|
||||
getTraits
|
||||
};
|
||||
|
|
|
@ -15,10 +15,10 @@ eleventyNavigation:
|
|||
:@nsfw="true"
|
||||
></ref-img>
|
||||
|
||||
<colors :@colors="$data.getColors()"></colors>
|
||||
<colors :@colors="$data.colors"></colors>
|
||||
|
||||
<quick-info>
|
||||
<traits :@traits="$data.getTraits('general')"></traits>
|
||||
<traits :@traits="$data.getTraits('anatomy')"></traits>
|
||||
</quick-info>
|
||||
|
||||
Sebin is the offspring of a human and a dragon. Like a human, he is a bipedal plantigrade. Most of his body is covered with red scales. Yellow scales run from the underside of his jaw over his chest and legs to the underside of the tip of his tail. These scales are particularly hard and function like plate armor to better protect vital organs.
|
||||
|
|
|
@ -1,6 +1,17 @@
|
|||
import {
|
||||
getDateOfBirth,
|
||||
getFullName,
|
||||
getHeight,
|
||||
getTailLength,
|
||||
getWeight,
|
||||
getWingspan,
|
||||
toInch
|
||||
} from '../includes/util.js';
|
||||
|
||||
const firstName = 'Sebin',
|
||||
middleName = 'Antario',
|
||||
lastName = 'Nyshkim',
|
||||
fullName = getFullName(firstName, middleName, lastName),
|
||||
species = 'Anthro Dragon',
|
||||
dateOfBirth = new Date('1993-10-17'),
|
||||
gender = 'male',
|
||||
|
@ -11,19 +22,14 @@ const firstName = 'Sebin',
|
|||
weight = 124, // kg
|
||||
tailLength = 104, // cm
|
||||
wingspan = 417, // cm
|
||||
colors = {
|
||||
hairPrimary: '#4b608f',
|
||||
hairSecondary: '#6684c0',
|
||||
eyes: '#31c215',
|
||||
scalesPrimary: '#c64c35',
|
||||
scalesSecondary: '#eda958',
|
||||
eyebrows: '#eda958',
|
||||
tailspikes: '#7f4539',
|
||||
horns: '#413a3a',
|
||||
claws: '#413a3a',
|
||||
nipples: '#413a3a',
|
||||
penis: '#413a3a'
|
||||
},
|
||||
colors = [
|
||||
{ name: 'Scales', value: '#c64c35' },
|
||||
{ name: 'Chest, Membranes, Facial Spikes', value: '#eda958' },
|
||||
{ name: 'Hair', value: '#4b608f' },
|
||||
{ name: 'Eyes', value: '#31c215' },
|
||||
{ name: 'Horns, Claws, Nipples', value: '#413a3a' },
|
||||
{ name: 'Tail Spikes', value: '#7f4539' }
|
||||
],
|
||||
penis = {
|
||||
shape: 'humanoid',
|
||||
type: 'grower',
|
||||
|
@ -32,7 +38,55 @@ const firstName = 'Sebin',
|
|||
girth: 5 // cm
|
||||
},
|
||||
description = 'Learn all about that derg!',
|
||||
profile = [
|
||||
{ icon: 'fa6-solid:cake-candles', type: 'Date of Birth', text: getDateOfBirth(dateOfBirth) },
|
||||
{ icon: 'fa6-solid:mars', type: 'Sex/Gender', text: `${gender} (${pronouns})` },
|
||||
{ icon: 'fa6-solid:ruler', type: 'Height', text: getHeight(height) },
|
||||
{ icon: 'fa6-solid:weight-hanging', type: 'Weight', text: getWeight(weight) }
|
||||
],
|
||||
hobbies = [
|
||||
{ icon: 'fa6-solid:dumbbell', type: 'Hobby', text: 'working out' },
|
||||
{ icon: 'fa6-solid:plane', type: 'Hobby', text: 'traveling' },
|
||||
{ icon: 'fa6-solid:campground', type: 'Hobby', text: 'camping' },
|
||||
{ icon: 'fa6-solid:gamepad', type: 'Hobby', text: 'video games' },
|
||||
{ icon: 'fa6-solid:laptop-code', type: 'Hobby', text: 'tech' }
|
||||
],
|
||||
anatomy = [
|
||||
{ icon: 'fa6-solid:ruler', type: 'Tail Length', text: getTailLength(tailLength) },
|
||||
{ icon: 'fa6-solid:shoe-prints', type: 'Walk', text: 'plantigrade' },
|
||||
{ icon: 'fa6-solid:info', type: 'Claws', text: 'sharp, hands & feet' },
|
||||
{ icon: 'fa6-solid:info', type: 'Nipples', text: 'yes' }
|
||||
],
|
||||
wings = [
|
||||
{ icon: 'fa6-solid:ruler', type: 'Wingspan', text: getWingspan(wingspan) },
|
||||
{ icon: 'fa6-solid:feather', type: 'Arms', text: 2 },
|
||||
{ icon: 'fa6-solid:feather', type: 'Fingers', text: 3 },
|
||||
{ icon: 'fa6-solid:star', type: 'Special Features', text: 'talon on top' }
|
||||
],
|
||||
head = [
|
||||
{ icon: 'fa6-solid:eye', type: 'Pupils', text: 'round' },
|
||||
{ icon: 'fa6-solid:eye', type: 'Eyebrows', text: 'yellow spikes' },
|
||||
{ icon: 'fa6-solid:info', type: 'Cheeks', text: 'yellow spikes' },
|
||||
{ icon: 'fa6-solid:dragon', type: 'Horns', text: 'curved' },
|
||||
{ icon: 'fa6-solid:info', type: 'Hair', text: 'mid-long, mullet' },
|
||||
{ icon: 'fa6-solid:ear-listen', type: 'Ears', text: 'long, pointy, movable' },
|
||||
{ icon: 'fa6-solid:tooth', type: 'Teeth', text: 'sharp fangs' },
|
||||
{ icon: 'fa6-solid:face-grin-tongue', type: 'Tongue', text: 'pointy tip' }
|
||||
],
|
||||
muscle = [
|
||||
{ icon: 'fa6-solid:dumbbell', type: 'Build', text: 'muscular' },
|
||||
{ icon: 'fa6-solid:dumbbell', type: 'Pecs', text: 'big' },
|
||||
{ icon: 'fa6-solid:dumbbell', type: 'abs', text: 'defined' }
|
||||
],
|
||||
naughty = [
|
||||
{ icon: 'fa6-solid:ruler', type: 'Length', text: `${penis.size} cm (${toInch(penis.size)})` },
|
||||
{ icon: 'fa6-solid:ruler', type: 'Girth', text: `${penis.girth} cm (${toInch(penis.girth)})` },
|
||||
{ icon: 'fa6-solid:shapes', type: 'Shape', text: penis.shape },
|
||||
{ icon: 'fa6-solid:maximize', type: 'Type', text: penis.type },
|
||||
{ icon: 'fa6-solid:star', type: 'Special Features', text: penis.special }
|
||||
],
|
||||
kinks = [
|
||||
// 0 = No, 1 = Maybe, 2 = Yes, 3 = Love
|
||||
{ name: 'Absorption', rating: 0 },
|
||||
{ name: 'Anal', rating: 3, receive: true, give: true },
|
||||
{ name: 'Auto-Fellatio', rating: 2 },
|
||||
|
@ -61,11 +115,12 @@ const firstName = 'Sebin',
|
|||
{ name: 'Growth', rating: 3, receive: true },
|
||||
{ name: 'Handjobs', rating: 2, receive: true, give: true },
|
||||
{ name: 'Hotdogging', rating: 2, give: true },
|
||||
{ name: 'Hyper', rating: 3 },
|
||||
{ name: 'Kissing', rating: 2, receive: true, give: true },
|
||||
{ name: 'Macro', rating: 3 },
|
||||
{ name: 'Milking', rating: 2 },
|
||||
{ name: 'Muscle Growth', rating: 3, receive: true, give: true },
|
||||
{ name: 'Muscle Worship', rating: 2, receive: true, give: true },
|
||||
{ name: 'Muscle Worship', rating: 3, receive: true, give: true },
|
||||
{ name: 'Nipple Play', rating: 2, receive: true, give: true },
|
||||
{ name: 'Oral', rating: 3, receive: true, give: true },
|
||||
{ name: 'Rough', rating: 2, receive: true, give: true },
|
||||
|
@ -81,149 +136,35 @@ const firstName = 'Sebin',
|
|||
{ name: 'Vore', rating: 0 }
|
||||
];
|
||||
|
||||
const getClientLocale = () => {
|
||||
return navigator.languages.length > 0 ? navigator.languages[0] : 'en-US';
|
||||
};
|
||||
|
||||
const getAge = (dateOfBirth) => {
|
||||
const today = new Date();
|
||||
|
||||
const thisYear = today.getFullYear();
|
||||
const thisMonth = today.getMonth();
|
||||
const thisDay = today.getDate();
|
||||
|
||||
const dobYear = dateOfBirth.getFullYear();
|
||||
const dobMonth = dateOfBirth.getMonth();
|
||||
const dobDay = dateOfBirth.getDate();
|
||||
|
||||
let age = thisYear - dobYear;
|
||||
|
||||
if (thisMonth < dobMonth) age--;
|
||||
if (thisMonth === dobMonth && thisDay < dobDay) age--;
|
||||
|
||||
return age;
|
||||
};
|
||||
|
||||
const toImperial = (cm) => {
|
||||
const realFeet = (cm * 0.3937) / 12;
|
||||
const feet = Math.floor(realFeet);
|
||||
const inches = Math.round((realFeet - feet) * 12);
|
||||
|
||||
return `${feet}'${inches}"`;
|
||||
};
|
||||
|
||||
const toInch = (cm) => {
|
||||
return `${Math.round(cm / 2.45)} in`;
|
||||
};
|
||||
|
||||
const toLbs = (kg) => {
|
||||
const nearExact = kg / 0.45359237;
|
||||
const lbs = Math.floor(nearExact);
|
||||
|
||||
return lbs;
|
||||
};
|
||||
|
||||
const toFahrenheit = (celsius) => {
|
||||
return celsius * 1.8 + 32;
|
||||
};
|
||||
|
||||
const dateFormat = new Intl.DateTimeFormat(getClientLocale(), {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: '2-digit'
|
||||
});
|
||||
|
||||
const getFullName = () => `${firstName} ${middleName} ${lastName}`;
|
||||
const getDateOfBirth = () => `${dateFormat.format(dateOfBirth)} (${getAge(dateOfBirth)})`;
|
||||
const getHeight = () => `${height} cm (${toImperial(height)})`;
|
||||
const getWeight = () => `${weight} kg (${toLbs(weight)} lbs)`;
|
||||
const getTailLength = () => `${tailLength / 100} m (${toImperial(tailLength)})`;
|
||||
const getWingspan = () => `${wingspan / 100} m (${toImperial(wingspan)})`;
|
||||
|
||||
const getTraits = (type) => {
|
||||
switch (type) {
|
||||
case 'hobbies':
|
||||
return [
|
||||
{ icon: 'fa6-solid:dumbbell', type: 'Hobby', text: 'working out' },
|
||||
{ icon: 'fa6-solid:plane', type: 'Hobby', text: 'traveling' },
|
||||
{ icon: 'fa6-solid:campground', type: 'Hobby', text: 'camping' },
|
||||
{ icon: 'fa6-solid:gamepad', type: 'Hobby', text: 'video games' },
|
||||
{ icon: 'fa6-solid:laptop-code', type: 'Hobby', text: 'tech' }
|
||||
];
|
||||
|
||||
case 'general':
|
||||
return [
|
||||
{ icon: 'fa6-solid:ruler', type: 'Tail Length', text: getTailLength() },
|
||||
{ icon: 'fa6-solid:shoe-prints', type: 'Walk', text: 'plantigrade' },
|
||||
{ icon: 'fa6-solid:info', type: 'Claws', text: 'sharp, hands & feet' },
|
||||
{ icon: 'fa6-solid:info', type: 'Nipples', text: 'yes' }
|
||||
];
|
||||
|
||||
return hobbies;
|
||||
case 'anatomy':
|
||||
return anatomy;
|
||||
case 'wings':
|
||||
return [
|
||||
{ icon: 'fa6-solid:ruler', type: 'Wingspan', text: getWingspan() },
|
||||
{ icon: 'fa6-solid:feather', type: 'Arms', text: 2 },
|
||||
{ icon: 'fa6-solid:feather', type: 'Fingers', text: 3 },
|
||||
{ icon: 'fa6-solid:star', type: 'Special Features', text: 'talon on top' }
|
||||
];
|
||||
|
||||
return wings;
|
||||
case 'head':
|
||||
return [
|
||||
{ icon: 'fa6-solid:eye', type: 'Pupils', text: 'round' },
|
||||
{ icon: 'fa6-solid:eye', type: 'Eyebrows', text: 'yellow spikes' },
|
||||
{ icon: 'fa6-solid:info', type: 'Cheeks', text: 'yellow spikes' },
|
||||
{ icon: 'fa6-solid:dragon', type: 'Horns', text: 'curved' },
|
||||
{ icon: 'fa6-solid:info', type: 'Hair', text: 'mid-long, mullet' },
|
||||
{ icon: 'fa6-solid:ear-listen', type: 'Ears', text: 'long, pointy, movable' },
|
||||
{ icon: 'fa6-solid:tooth', type: 'Teeth', text: 'sharp fangs' },
|
||||
{ icon: 'fa6-solid:face-grin-tongue', type: 'Tongue', text: 'pointy tip' }
|
||||
];
|
||||
|
||||
return head;
|
||||
case 'muscle':
|
||||
return [
|
||||
{ icon: 'fa6-solid:dumbbell', type: 'Build', text: 'muscular' },
|
||||
{ icon: 'fa6-solid:dumbbell', type: 'Pecs', text: 'big' },
|
||||
{ icon: 'fa6-solid:dumbbell', type: 'abs', text: 'defined' }
|
||||
];
|
||||
|
||||
return muscle;
|
||||
case 'naughty':
|
||||
return [
|
||||
{ icon: 'fa6-solid:ruler', type: 'Length', text: `${penis.size} cm (${toInch(penis.size)})` },
|
||||
{ icon: 'fa6-solid:ruler', type: 'Girth', text: `${penis.girth} cm (${toInch(penis.girth)})` },
|
||||
{ icon: 'fa6-solid:shapes', type: 'Shape', text: penis.shape },
|
||||
{ icon: 'fa6-solid:maximize', type: 'Type', text: penis.type },
|
||||
{ icon: 'fa6-solid:star', type: 'Special Features', text: penis.special }
|
||||
];
|
||||
|
||||
return naughty;
|
||||
default:
|
||||
return [
|
||||
{ icon: 'fa6-solid:cake-candles', type: 'Date of Birth', text: getDateOfBirth() },
|
||||
{ icon: 'fa6-solid:mars', type: 'Sex/Gender', text: `${gender} (${pronouns})` },
|
||||
{ icon: 'fa6-solid:ruler', type: 'Height', text: getHeight() },
|
||||
{ icon: 'fa6-solid:weight-hanging', type: 'Weight', text: getWeight() }
|
||||
];
|
||||
return profile;
|
||||
}
|
||||
};
|
||||
|
||||
const getColors = () => [
|
||||
{ name: 'Scales', value: colors.scalesPrimary },
|
||||
{ name: 'Chest, Membranes, Facial Spikes', value: colors.scalesSecondary },
|
||||
{ name: 'Hair', value: colors.hairPrimary },
|
||||
{ name: 'Eyes', value: colors.eyes },
|
||||
{ name: 'Horns, Claws, Nipples', value: colors.horns },
|
||||
{ name: 'Tail Spikes', value: colors.tailspikes }
|
||||
];
|
||||
|
||||
export default {
|
||||
firstName,
|
||||
fullName,
|
||||
species,
|
||||
gender,
|
||||
pronouns,
|
||||
orientation,
|
||||
position,
|
||||
colors,
|
||||
kinks,
|
||||
description,
|
||||
getFullName,
|
||||
getTraits,
|
||||
getColors
|
||||
getTraits
|
||||
};
|
||||
|
|
|
@ -14,7 +14,7 @@ eleventyNavigation:
|
|||
:@nsfw="true"
|
||||
></ref-img>
|
||||
|
||||
<colors :@colors="$data.getColors()"></colors>
|
||||
<colors :@colors="$data.colors"></colors>
|
||||
|
||||
Viktor is a bipedal plantigrade Ankylosaurus. His skin is mostly bicolored, with several shades of brown.
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
import { getDateOfBirth, getFullName, getHeight, getWeight } from '../includes/util.js';
|
||||
|
||||
const firstName = 'Viktor',
|
||||
lastName = 'Kraastav',
|
||||
fullName = getFullName(firstName, lastName),
|
||||
species = 'Ankylosaurus',
|
||||
dateOfBirth = new Date('1987-12-08'),
|
||||
gender = 'male',
|
||||
|
@ -8,151 +11,47 @@ const firstName = 'Viktor',
|
|||
role = 'bottom',
|
||||
height = 227, // cm
|
||||
weight = 175, // kg
|
||||
color = {
|
||||
front: '#e7c7b1',
|
||||
limbs: '#493428',
|
||||
back: '#422322',
|
||||
spine: '#341c1c',
|
||||
tissue: '#6bb9db',
|
||||
spikes: '#f8ebdd',
|
||||
eyesPrimary: '#a7eef1',
|
||||
eyesSecondary: '#6dabd1'
|
||||
},
|
||||
colors = [
|
||||
{ name: 'Front', value: '#e7c7b1' },
|
||||
{ name: 'Arms, legs', value: '#493428' },
|
||||
{ name: 'Back Main', value: '#422322' },
|
||||
{ name: 'Back Spine', value: '#341c1c' },
|
||||
{ name: 'Freckles, tissue', value: '#6bb9db' },
|
||||
{ name: 'Spikes, tail club', value: '#f8ebdd' },
|
||||
{ name: 'Eyes primary', value: '#a7eef1' },
|
||||
{ name: 'Eyes secondary', value: '#6dabd1' }
|
||||
],
|
||||
description = 'Hardened-up, but far from fossilized!',
|
||||
jobs = [
|
||||
{
|
||||
title: 'Bartender',
|
||||
icon: 'fa6-solid:whiskey-glass',
|
||||
desc: "Viktor's professional career began as a bartender at a pub in his hometown. There he often doubled as a bouncer when a few guests got too drunk and started making a fuss. A defining moment of that job was when someone climbed over the counter and threatened him with a broken bottle. Wrestled to the ground and with a broken bottle in front of his face, he had to make a split-second decision. With a powerful swing of his tail bone, he knocked the attacker down. This experience taught him the importance of effectively defending himself against unpleasant fellows and to eliminate threats before they get close to him ever again."
|
||||
},
|
||||
{
|
||||
title: 'Lumberjack',
|
||||
icon: 'fa6-solid:tree',
|
||||
desc: 'After leaving his hometown, Viktor started working as a lumberjack. The club at the end of his tail came very much in handy as a counterweight for each swing of his axe, allowing him to strike efficiently and powerfully, felling even the largest trees with relative ease. His naturally tough scales protected him from splinters from the felled wood. The long-lasting hard physical work toughened his body over the years. Before taking the cut logs to the sawmill, Viktor always took a break to rest in the peace and quiet of the forest. Although he enjoyed the seclusion of the country life for a while, he was longing for the sociability of city life again.'
|
||||
},
|
||||
{
|
||||
title: 'Car Mechanic',
|
||||
icon: 'fa6-solid:car',
|
||||
desc: 'Moving into a suburban town, Viktor applied at an auto repair shop, where he learned the ins and outs of fixing cars. He became really good at it and enjoyed breathing new mechanical life into broken down vehicles. However, as time went on, the repair shop faced financial troubles, as it became increasingly difficult to come by spare parts as auto makers would only deal them to certified repair partners and certifications were prohibitively expensive. Viktor had to watch business slowly deteriorate, as skilled coworkers kept getting laid off, until the repair shop closed down for good.'
|
||||
},
|
||||
{
|
||||
title: 'Construction Worker',
|
||||
icon: 'fa6-solid:helmet-safety',
|
||||
desc: 'Having taken a liking in physically demanding work, Viktor took on a job as a construction worker. Hard hat perched atop his head and belt tool slung around his waist, he was always ready to lend a hand (or tail if walls or boulders needed a good teardown). Since he had more than enough strength training from his previous jobs, he was often assigned carrying jobs to clear the site and haul building materials. After work was done, Viktor enjoyed the company of his colleagues over an after-work beer until late in the evenings. On one of these evenings, he got a little reckless with a colleague under the veil of the night and both were caught in the act of fooling around with each other on the construction site. In the following weeks, the colleagues kept their distance from Viktor and the evenings together also dissolved very quickly when he joined them. The continued ostracization ultimately prompted him to look for something new.'
|
||||
},
|
||||
{
|
||||
title: 'Welder',
|
||||
icon: 'fa6-solid:industry',
|
||||
desc: 'In his job as a welder, Viktor spent his days working shifts in a workshop. In addition to special tools and plasma welders, he also used his powerful tail club to hammer metal parts into shape. He acquired a wide variety of welding techniques to join or repair metal structures. He worked with a wide variety of alloys such as steel, aluminum and titanium. Viktor showed extreme skill in his work with great precision and attention to detail. However, this dedication was a thorn in the side of a jealous colleague who saw Viktor as a rival and schemed against him, sabotaging his work, which ultimately ended in his termination after a customer sued the welding shop for botching the job. Viktor never found out who the culprit was, and the loss of that job still hangs over him.'
|
||||
},
|
||||
{
|
||||
title: 'Delivery Driver',
|
||||
icon: 'fa6-solid:truck',
|
||||
desc: "When Viktor was strapped for cash, he took on a job in the gig economy as a driver delivering packages for a large online delivery service. His previous physically demanding jobs allowed him to haul even bulky deliveries to their destination with relative ease. If the shipping center managers hadn't been breathing down his neck constantly, he might have held this job even longer. But after one of the managers tried to show him up in front of the whole team, he snapped and broke their leg with his tail club. Of course, he didn't have to report for duty the next day."
|
||||
},
|
||||
{
|
||||
title: 'Docks Werehouse Worker',
|
||||
icon: 'fa6-solid:boxes-stacked',
|
||||
desc: "Currently Viktor works at the docks in the port town he moved to. His main responsibilities include loading and unloading cargo from ships and transporting it to and from warehousing. The hustle and bustle of the port sometimes gets on his nerves. Especially when the crew of docking ships come ashore again after a long time and make it clear that they don't have much contact with \"landlubbers\". Viktor doesn't get particularly impressed by this and foul mouths them right back if he gets the impression they're looking for trouble."
|
||||
}
|
||||
naughty = [
|
||||
{ icon: 'fa6-solid:heart', type: 'Orientation', text: orientation },
|
||||
{ icon: 'fa6-solid:arrows-up-down', type: 'Role', text: role }
|
||||
],
|
||||
anatomy = [
|
||||
{ icon: 'fa6-solid:cake-candles', type: 'Date of Birth', text: getDateOfBirth(dateOfBirth) },
|
||||
{ icon: 'fa6-solid:mars', type: 'Sex/Gender', text: `${gender} (${pronouns})` },
|
||||
{ icon: 'fa6-solid:ruler', type: 'Height', text: getHeight(height) },
|
||||
{ icon: 'fa6-solid:weight-hanging', type: 'Weight', text: getWeight(weight) }
|
||||
];
|
||||
|
||||
const getClientLocale = () => {
|
||||
return navigator.languages.length > 0 ? navigator.languages[0] : 'en-US';
|
||||
};
|
||||
|
||||
const getAge = (dateOfBirth) => {
|
||||
const today = new Date();
|
||||
|
||||
const thisYear = today.getFullYear();
|
||||
const thisMonth = today.getMonth();
|
||||
const thisDay = today.getDate();
|
||||
|
||||
const dobYear = dateOfBirth.getFullYear();
|
||||
const dobMonth = dateOfBirth.getMonth();
|
||||
const dobDay = dateOfBirth.getDate();
|
||||
|
||||
let age = thisYear - dobYear;
|
||||
|
||||
if (thisMonth < dobMonth) age--;
|
||||
if (thisMonth === dobMonth && thisDay < dobDay) age--;
|
||||
|
||||
return age;
|
||||
};
|
||||
|
||||
const toImperial = (cm) => {
|
||||
const realFeet = (cm * 0.3937) / 12;
|
||||
const feet = Math.floor(realFeet);
|
||||
const inches = Math.round((realFeet - feet) * 12);
|
||||
|
||||
return `${feet}'${inches}"`;
|
||||
};
|
||||
|
||||
const toInch = (cm) => {
|
||||
return `${Math.round(cm / 2.45)} in`;
|
||||
};
|
||||
|
||||
const toLbs = (kg) => {
|
||||
const nearExact = kg / 0.45359237;
|
||||
const lbs = Math.floor(nearExact);
|
||||
|
||||
return lbs;
|
||||
};
|
||||
|
||||
const toFahrenheit = (celsius) => {
|
||||
return celsius * 1.8 + 32;
|
||||
};
|
||||
|
||||
const dateFormat = new Intl.DateTimeFormat(getClientLocale(), {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: '2-digit'
|
||||
});
|
||||
|
||||
const getFullName = () => `${firstName} ${lastName}`;
|
||||
const getDateOfBirth = () => `${dateFormat.format(dateOfBirth)} (${getAge(dateOfBirth)})`;
|
||||
const getHeight = () => `${height} cm (${toImperial(height)})`;
|
||||
const getWeight = () => `${weight} kg (${toLbs(weight)} lbs)`;
|
||||
|
||||
const getTraits = (type) => {
|
||||
switch (type) {
|
||||
case 'naughty':
|
||||
return [
|
||||
{ icon: 'fa6-solid:heart', type: 'Orientation', text: orientation },
|
||||
{ icon: 'fa6-solid:arrows-up-down', type: 'Role', text: role }
|
||||
];
|
||||
return naughty;
|
||||
|
||||
default:
|
||||
return [
|
||||
{ icon: 'fa6-solid:cake-candles', type: 'Date of Birth', text: getDateOfBirth() },
|
||||
{ icon: 'fa6-solid:mars', type: 'Sex/Gender', text: `${gender} (${pronouns})` },
|
||||
{ icon: 'fa6-solid:ruler', type: 'Height', text: getHeight() },
|
||||
{ icon: 'fa6-solid:weight-hanging', type: 'Weight', text: getWeight() }
|
||||
];
|
||||
return anatomy;
|
||||
}
|
||||
};
|
||||
|
||||
const getColors = () => [
|
||||
{ name: 'Front', value: color.front },
|
||||
{ name: 'Arms, legs', value: color.limbs },
|
||||
{ name: 'Back Main', value: color.back },
|
||||
{ name: 'Back Spine', value: color.spine },
|
||||
{ name: 'Highlight scales, tissue', value: color.tissue },
|
||||
{ name: 'Spikes, tail club', value: color.spikes },
|
||||
{ name: 'Eyes primary', value: color.eyesPrimary },
|
||||
{ name: 'Eyes secondary', value: color.eyesSecondary }
|
||||
];
|
||||
|
||||
export default {
|
||||
firstName,
|
||||
fullName,
|
||||
species,
|
||||
gender,
|
||||
pronouns,
|
||||
colors,
|
||||
orientation,
|
||||
role,
|
||||
description,
|
||||
jobs,
|
||||
getFullName,
|
||||
getTraits,
|
||||
getColors
|
||||
getTraits
|
||||
};
|
||||
|
|
|
@ -1,3 +1,40 @@
|
|||
{
|
||||
"layout": "character.webc"
|
||||
"layout": "character.webc",
|
||||
"jobs": [
|
||||
{
|
||||
"title": "Bartender",
|
||||
"icon": "fa6-solid:whiskey-glass",
|
||||
"desc": "Viktor's professional career began as a bartender at a pub in his hometown. There he often doubled as a bouncer when a few guests got too drunk and started making a fuss. A defining moment of that job was when someone climbed over the counter and threatened him with a broken bottle. Wrestled to the ground and with a broken bottle in front of his face, he had to make a split-second decision. With a powerful swing of his tail bone, he knocked the attacker down. This experience taught him the importance of effectively defending himself against unpleasant fellows and to eliminate threats before they get close to him ever again."
|
||||
},
|
||||
{
|
||||
"title": "Lumberjack",
|
||||
"icon": "fa6-solid:tree",
|
||||
"desc": "After leaving his hometown, Viktor started working as a lumberjack. The club at the end of his tail came very much in handy as a counterweight for each swing of his axe, allowing him to strike efficiently and powerfully, felling even the largest trees with relative ease. His naturally tough scales protected him from splinters from the felled wood. The long-lasting hard physical work toughened his body over the years. Before taking the cut logs to the sawmill, Viktor always took a break to rest in the peace and quiet of the forest. Although he enjoyed the seclusion of the country life for a while, he was longing for the sociability of city life again."
|
||||
},
|
||||
{
|
||||
"title": "Car Mechanic",
|
||||
"icon": "fa6-solid:car",
|
||||
"desc": "Moving into a suburban town, Viktor applied at an auto repair shop, where he learned the ins and outs of fixing cars. He became really good at it and enjoyed breathing new mechanical life into broken down vehicles. However, as time went on, the repair shop faced financial troubles, as it became increasingly difficult to come by spare parts as auto makers would only deal them to certified repair partners and certifications were prohibitively expensive. Viktor had to watch business slowly deteriorate, as skilled coworkers kept getting laid off, until the repair shop closed down for good."
|
||||
},
|
||||
{
|
||||
"title": "Construction Worker",
|
||||
"icon": "fa6-solid:helmet-safety",
|
||||
"desc": "Having taken a liking in physically demanding work, Viktor took on a job as a construction worker. Hard hat perched atop his head and belt tool slung around his waist, he was always ready to lend a hand (or tail if walls or boulders needed a good teardown). Since he had more than enough strength training from his previous jobs, he was often assigned carrying jobs to clear the site and haul building materials. After work was done, Viktor enjoyed the company of his colleagues over an after-work beer until late in the evenings. On one of these evenings, he got a little reckless with a colleague under the veil of the night and both were caught in the act of fooling around with each other on the construction site. In the following weeks, the colleagues kept their distance from Viktor and the evenings together also dissolved very quickly when he joined them. The continued ostracization ultimately prompted him to look for something new."
|
||||
},
|
||||
{
|
||||
"title": "Welder",
|
||||
"icon": "fa6-solid:industry",
|
||||
"desc": "In his job as a welder, Viktor spent his days working shifts in a workshop. In addition to special tools and plasma welders, he also used his powerful tail club to hammer metal parts into shape. He acquired a wide variety of welding techniques to join or repair metal structures. He worked with a wide variety of alloys such as steel, aluminum and titanium. Viktor showed extreme skill in his work with great precision and attention to detail. However, this dedication was a thorn in the side of a jealous colleague who saw Viktor as a rival and schemed against him, sabotaging his work, which ultimately ended in his termination after a customer sued the welding shop for botching the job. Viktor never found out who the culprit was, and the loss of that job still hangs over him."
|
||||
},
|
||||
{
|
||||
"title": "Delivery Driver",
|
||||
"icon": "fa6-solid:truck",
|
||||
"desc": "When Viktor was strapped for cash, he took on a job in the gig economy as a driver delivering packages for a large online delivery service. His previous physically demanding jobs allowed him to haul even bulky deliveries to their destination with relative ease. If the shipping center managers hadn't been breathing down his neck constantly, he might have held this job even longer. But after one of the managers tried to show him up in front of the whole team, he snapped and broke their leg with his tail club. Of course, he didn't have to report for duty the next day."
|
||||
},
|
||||
{
|
||||
"title": "Docks Werehouse Worker",
|
||||
"icon": "fa6-solid:boxes-stacked",
|
||||
"desc": "Currently Viktor works at the docks in the port town he moved to. His main responsibilities include loading and unloading cargo from ships and transporting it to and from warehousing. The hustle and bustle of the port sometimes gets on his nerves. Especially when the crew of docking ships come ashore again after a long time and make it clear that they don't have much contact with \"landlubbers\". Viktor doesn't get particularly impressed by this and foul mouths them right back if he gets the impression they're looking for trouble."
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue