Verbeter anatomie en plaatsing van avataraccessoires (v0.4.46-beta) #1

Open
bes-r wants to merge 192 commits from bes-r/avatar-realism into main AGit
4 changed files with 39 additions and 6 deletions
Showing only changes of commit 09a1326c7e - Show all commits

View file

@ -1 +1 @@
0.4.27-beta
0.4.28-beta

View file

@ -7,8 +7,8 @@
"use strict";
const AVATAR_SPECIES = ["dino", "monster", "animal"];
const AVATAR_HAIR = ["none", "spiky", "curly", "ponytail", "mohawk"];
const AVATAR_HEAD_ACC = ["none", "cap", "crown", "glasses", "bow"];
const AVATAR_BODY_ACC = ["none", "scarf", "cape", "badge"];
const AVATAR_HEAD_ACC = ["none", "cap", "crown", "glasses", "sunglasses", "bow", "headphones", "partyhat"];
const AVATAR_BODY_ACC = ["none", "scarf", "cape", "badge", "necklace", "backpack"];
const AVATAR_DEFAULT = Object.freeze({
species: "dino", bodyColor: "#3fae6a", hairStyle: "none", hairColor: "#5b3a29",
accessoryHead: "none", accessoryBody: "none",
@ -147,6 +147,27 @@ const AVATAR_HEAD_ACC_MARKUP = {
bow: () => `
<path d="M 68 14 Q 58 8 58 16 Q 58 22 68 18 Q 74 24 78 18 Q 80 12 74 10 Q 78 6 74 2 Q 68 2 68 14 Z" fill="#e05a8a"/>
<circle cx="69" cy="14" r="3" fill="#c23f6e"/>`,
sunglasses: () => `
<path d="M 27 20 Q 27 32 38 32 Q 49 32 48 22 Q 48 18 38 18 Q 27 18 27 20 Z" fill="#1a1d24"/>
<path d="M 52 22 Q 51 32 62 32 Q 73 32 73 20 Q 73 18 62 18 Q 52 18 52 22 Z" fill="#1a1d24"/>
<path d="M 48 22 Q 50 20 52 22" stroke="#1a1d24" stroke-width="2.4" fill="none"/>
<path d="M 27 21 L 20 19" stroke="#1a1d24" stroke-width="2.4" stroke-linecap="round"/>
<path d="M 73 21 L 80 19" stroke="#1a1d24" stroke-width="2.4" stroke-linecap="round"/>
<path d="M 31 22 Q 33 20 36 21" stroke="#fff" stroke-width="1.6" fill="none" opacity=".55" stroke-linecap="round"/>
<path d="M 56 22 Q 58 20 61 21" stroke="#fff" stroke-width="1.6" fill="none" opacity=".55" stroke-linecap="round"/>`,
headphones: () => `
<path d="M 20 20 Q 24 -6 50 -6 Q 76 -6 80 20" stroke="#333" stroke-width="4.5" fill="none" stroke-linecap="round"/>
<ellipse cx="19" cy="26" rx="7" ry="9" fill="#333"/>
<ellipse cx="81" cy="26" rx="7" ry="9" fill="#333"/>
<ellipse cx="19" cy="26" rx="3.6" ry="5.4" fill="#556"/>
<ellipse cx="81" cy="26" rx="3.6" ry="5.4" fill="#556"/>`,
partyhat: () => `
<path d="M 50 -6 L 60 21 Q 50 25 40 21 Z" fill="#e05a8a"/>
<path d="M 40.5 19 Q 50 23 59.5 19" stroke="#c23f6e" stroke-width="1.4" fill="none"/>
<circle cx="45.5" cy="8" r="1.8" fill="#f0b73a"/>
<circle cx="54" cy="12" r="1.8" fill="#3a8ee0"/>
<circle cx="47.5" cy="16" r="1.8" fill="#f0b73a"/>
<circle cx="50" cy="-7" r="3.6" fill="#f0b73a"/>`,
};
const AVATAR_BODY_ACC_MARKUP = {
@ -158,6 +179,14 @@ const AVATAR_BODY_ACC_MARKUP = {
<path d="M 30 55 Q 10 75 16 100 L 30 90 Q 26 70 40 58 Z" fill="#7a4bc7" opacity=".92"/>
<path d="M 70 55 Q 90 75 84 100 L 70 90 Q 74 70 60 58 Z" fill="#7a4bc7" opacity=".92"/>`,
badge: () => `<circle cx="50" cy="78" r="6" fill="#f0b73a" stroke="#fff" stroke-width="1.6"/>`,
necklace: () => `
<path d="M 34 58 Q 50 70 66 58" stroke="#f0b73a" stroke-width="2" fill="none" stroke-linecap="round"/>
<circle cx="50" cy="70" r="4.4" fill="#3a8ee0" stroke="#f0b73a" stroke-width="1.4"/>`,
backpack: () => `
<path d="M 30 58 Q 26 62 27 70" stroke="#5a4632" stroke-width="4.5" fill="none" stroke-linecap="round"/>
<path d="M 70 58 Q 74 62 73 70" stroke="#5a4632" stroke-width="4.5" fill="none" stroke-linecap="round"/>
<circle cx="27" cy="59" r="2" fill="#8a6a45"/>
<circle cx="73" cy="59" r="2" fill="#8a6a45"/>`,
};
/* een pet/kroon dekt het hele bovenhoofd af - loshangend haar eronder tonen

View file

@ -2,7 +2,7 @@
"use strict";
/* version shown until /api/version resolves (or if the fetch fails, e.g. offline).
Kept in sync by hand with the VERSION file at the repo root on every release. */
const VERSION = "0.4.27-beta";
const VERSION = "0.4.28-beta";
(function(){
const tag = document.getElementById("verTag");
tag.textContent = "v"+VERSION;
@ -169,7 +169,9 @@ const I18N = {
avatarHair_none:"Geen", avatarHair_spiky:"Piekerig", avatarHair_curly:"Krullen", avatarHair_ponytail:"Staartje", avatarHair_mohawk:"Hanenkam",
avatarAccHead:"Hoofdaccessoire", avatarAccBody:"Lichaamsaccessoire",
avatarAcc_none:"Geen", avatarAcc_cap:"Petje", avatarAcc_crown:"Kroon", avatarAcc_glasses:"Bril", avatarAcc_bow:"Strik",
avatarAcc_sunglasses:"Zonnebril", avatarAcc_headphones:"Koptelefoon", avatarAcc_partyhat:"Feesthoedje",
avatarAcc_scarf:"Sjaal", avatarAcc_cape:"Cape", avatarAcc_badge:"Insigne",
avatarAcc_necklace:"Ketting", avatarAcc_backpack:"Rugtas",
avatarSave:"Opslaan", avatarSaved:"Personage opgeslagen ✓",
clock24Hour:"24-uursklok",
widgetSettings:"Instellingen", clockWelcome:"Oefenen met de klok", clockWelcomeHint:"Kies bovenaan niveau 1, 2 of 3. Kies bij Instellingen hoe de opdracht wordt getoond en start daarna.", clockSetTitle:"Zet de wijzers op de goede tijd", clockReadTitle:"Bekijk de opdracht en maak de tijd", clockChooseTitle:"Kies het juiste tijdblok", clockYourTask:"Jouw opdracht", clockLinkHands:"Grote wijzer draait mee", clockTypeHint:"Voer de tijd in als UU:MM.", clockLevel1Hint:"Niveau 1: hele en halve uren", clockLevel2Hint:"Niveau 2: stappen van vijf minuten", clockLevel3Hint:"Niveau 3: alle minuten, met optioneel 24 uur en dagdelen", clockBuildHint:"Sleep vier cijfers naar de vakken en maak UU:MM.", clockDigitGhost:"?", clockDigit:"Cijfer {digit}", clockIncomplete:"Maak eerst de volledige tijd af.", clockCorrect:"Goed gedaan! Kies de volgende opdracht. 🌟", clockTryAgain:"Bijna! Probeer het nog een keer.", clockStartTask:"Start opdracht", clockNextTask:"Volgende opdracht", clockLevel:"Niveau", clockLevelHour:"Hele uren", clockLevelQuarter:"Kwartieren", clockLevelFive:"Vijf minuten", clockLevelMinute:"Iedere minuut", clockLearningStyle:"Opdracht tonen", clockStyleAnalogue:"Analoge klok", clockStyleDigital:"Grote digitale tijd", clockStyleWords:"In woorden", clockStyleBoth:"Klok, cijfers en woorden", clockDragHint:"Sleep de wijzers om de tijd te veranderen.", clockGame:"Spel", clockGameSet:"Klok zetten", clockGameRead:"Tijd aflezen", clockGameChoose:"Kies de tijd", clockStart:"Kies Nieuwe opdracht om te beginnen", clockRead:"Welke tijd zie je? Stel je antwoord hieronder in.", clockChoose:"Welke digitale tijd hoort bij deze klok?", clockTry:"Nog niet. Het goede antwoord is {time}.", clockScore:"Score: {score} · reeks: {streak}", clockWordsHour:"{hour} uur", clockWordsQuarterPast:"kwart over {hour}", clockWordsHalf:"half {hour}", clockWordsQuarterTo:"kwart voor {hour}", clockWordsPast:"{minutes} over {hour}", clockWordsTo:"{minutes} voor {hour}", clockMorning:"in de ochtend", clockAfternoon:"in de middag", clockEvening:"in de avond", clockNight:"in de nacht", fractionRows:"Rijen", fractionLabels:"Breuken tonen", fractionHint:"Tik in een rij tot waar je de breuk wilt kleuren.", fractionSelected:"Gekozen breuk: {fraction}", fractionClear:"Leegmaken", clockTask:"Oefenopdracht", clockSet:"Zet de klok op {time}", clockHour:"Uur", clockMinute:"Minuten", placeNumber:"Getal", placeGame:"Spel", placeGameExplore:"Verkennen", placeGameBuild:"Bouwen", placeLevel:"Niveau", placeLevel1Hint:"Niveau 1: getallen tot 100 (T en E)", placeLevel2Hint:"Niveau 2: getallen tot 1000 (H, T en E)", placeLevel3Hint:"Niveau 3: getallen tot 1.000.000 (HD, TD, D, H, T en E)", placeBuildTitle:"Bouw het getal", placeYourTask:"Jouw opdracht", placeBuildHint:"Sleep de cijfers naar de vakken en bouw het getal.", placeDigitGhost:"?", placeIncomplete:"Maak eerst het hele getal af.", placeCorrect:"Goed gedaan! Kies de volgende opdracht. 🌟", placeTryAgain:"Bijna! Probeer het nog een keer.", placeNextTask:"Volgende opdracht", placeScore:"Score: {score} · reeks: {streak}", moneyMake:"Maak € {amount}", wordtypeSentence:"Zin", wordtypeHint:"Tik: blauw zelfstandig naamwoord · groen werkwoord · geel bijvoeglijk naamwoord", scheduleActivity:"activiteit…", scheduleEmpty:"Voeg de eerste activiteit toe.", scheduleCount:"{count} momenten op de planning", pollReset:"Opnieuw", pollQuestion:"Vraag", pollOptions:"Antwoorden (één per regel)", pollTotal:"{count} stemmen", moodTotal:"{count} kinderen hebben gekozen", moodVote:"Kies {face}", galleryFavorites:"Favorieten", galleryFavorite:"Favoriet maken", galleryEmpty:"Geen widgets gevonden.", gallerySearch:"Zoek widgets…", templateMath:"Rekenles", templateLanguage:"Taalles", templateDay:"Dagstart", exportBoard:"Exporteren", importBoard:"Importeren", importError:"Dit is geen geldig Teach-bordbestand.", widgetDuplicate:"Dupliceren", widgetLock:"Vastzetten", widgetUnlock:"Losmaken", widgetPreview:"Voorbeeld zonder bediening", undo:"Ongedaan maken", redo:"Opnieuw",
@ -501,7 +503,9 @@ const I18N = {
avatarHair_none:"None", avatarHair_spiky:"Spiky", avatarHair_curly:"Curly", avatarHair_ponytail:"Ponytail", avatarHair_mohawk:"Mohawk",
avatarAccHead:"Head accessory", avatarAccBody:"Body accessory",
avatarAcc_none:"None", avatarAcc_cap:"Cap", avatarAcc_crown:"Crown", avatarAcc_glasses:"Glasses", avatarAcc_bow:"Bow",
avatarAcc_sunglasses:"Sunglasses", avatarAcc_headphones:"Headphones", avatarAcc_partyhat:"Party hat",
avatarAcc_scarf:"Scarf", avatarAcc_cape:"Cape", avatarAcc_badge:"Badge",
avatarAcc_necklace:"Necklace", avatarAcc_backpack:"Backpack",
avatarSave:"Save", avatarSaved:"Character saved ✓",
clock24Hour:"24-hour clock",
widgetSettings:"Settings", clockWelcome:"Practise telling time", clockWelcomeHint:"Choose level 1, 2 or 3 at the top. In Settings, choose how the task is shown, then start.", clockSetTitle:"Move the hands to the correct time", clockReadTitle:"View the task and make the time", clockChooseTitle:"Choose the correct time block", clockYourTask:"Your task", clockLinkHands:"Move the minute hand along", clockTypeHint:"Enter the time as HH:MM.", clockLevel1Hint:"Level 1: whole and half hours", clockLevel2Hint:"Level 2: five-minute steps", clockLevel3Hint:"Level 3: every minute, with optional 24-hour time and day parts", clockBuildHint:"Drag four digits into the slots to make HH:MM.", clockDigitGhost:"?", clockDigit:"Digit {digit}", clockIncomplete:"Complete the full time first.", clockCorrect:"Well done! Choose the next task. 🌟", clockTryAgain:"Almost! Try again.", clockStartTask:"Start task", clockNextTask:"Next task", clockLevel:"Level", clockLevelHour:"Whole hours", clockLevelQuarter:"Quarter hours", clockLevelFive:"Five minutes", clockLevelMinute:"Every minute", clockLearningStyle:"Show task as", clockStyleAnalogue:"Analogue clock", clockStyleDigital:"Large digital time", clockStyleWords:"In words", clockStyleBoth:"Clock, digits and words", clockDragHint:"Drag the hands to change the time.", clockGame:"Game", clockGameSet:"Set the clock", clockGameRead:"Read the time", clockGameChoose:"Choose the time", clockStart:"Choose New task to begin", clockRead:"What time is it? Set your answer below.", clockChoose:"Which digital time matches this clock?", clockTry:"Not yet. The correct answer is {time}.", clockScore:"Score: {score} · streak: {streak}", clockWordsHour:"{hour} o'clock", clockWordsQuarterPast:"quarter past {hour}", clockWordsHalf:"half past {hour}", clockWordsQuarterTo:"quarter to {hour}", clockWordsPast:"{minutes} past {hour}", clockWordsTo:"{minutes} to {hour}", clockMorning:"in the morning", clockAfternoon:"in the afternoon", clockEvening:"in the evening", clockNight:"at night", fractionRows:"Rows", fractionLabels:"Show fractions", fractionHint:"Tap a row up to the part you want to colour.", fractionSelected:"Selected fraction: {fraction}", fractionClear:"Clear", clockTask:"Practice task", clockSet:"Set the clock to {time}", clockHour:"Hour", clockMinute:"Minutes", placeNumber:"Number", placeGame:"Game", placeGameExplore:"Explore", placeGameBuild:"Build", placeLevel:"Level", placeLevel1Hint:"Level 1: numbers up to 100 (T and E)", placeLevel2Hint:"Level 2: numbers up to 1000 (H, T and E)", placeLevel3Hint:"Level 3: numbers up to 1,000,000 (HD, TD, D, H, T and E)", placeBuildTitle:"Build the number", placeYourTask:"Your task", placeBuildHint:"Drag the digits into the slots to build the number.", placeDigitGhost:"?", placeIncomplete:"Complete the whole number first.", placeCorrect:"Well done! Choose the next task. 🌟", placeTryAgain:"Almost! Try again.", placeNextTask:"Next task", placeScore:"Score: {score} · streak: {streak}", moneyMake:"Make € {amount}", wordtypeSentence:"Sentence", wordtypeHint:"Tap: blue noun · green verb · yellow adjective", scheduleActivity:"activity…", scheduleEmpty:"Add the first activity.", scheduleCount:"{count} moments in the schedule", pollReset:"Reset", pollQuestion:"Question", pollOptions:"Answers (one per line)", pollTotal:"{count} votes", moodTotal:"{count} children have chosen", moodVote:"Choose {face}", galleryFavorites:"Favourites", galleryFavorite:"Add favourite", galleryEmpty:"No widgets found.", gallerySearch:"Search widgets…", templateMath:"Maths lesson", templateLanguage:"Language lesson", templateDay:"Day start", exportBoard:"Export", importBoard:"Import", importError:"This is not a valid Teach board file.", widgetDuplicate:"Duplicate", widgetLock:"Lock", widgetUnlock:"Unlock", widgetPreview:"Preview without controls", undo:"Undo", redo:"Redo",

View file

@ -196,8 +196,8 @@ export default async function api(app) {
// geen rol-beperkte PERMISSIONS-sleutel nodig, altijd de eigen rij.
const AVATAR_SPECIES = ['dino', 'monster', 'animal'];
const AVATAR_HAIR = ['none', 'spiky', 'curly', 'ponytail', 'mohawk'];
const AVATAR_HEAD_ACC = ['none', 'cap', 'crown', 'glasses', 'bow'];
const AVATAR_BODY_ACC = ['none', 'scarf', 'cape', 'badge'];
const AVATAR_HEAD_ACC = ['none', 'cap', 'crown', 'glasses', 'sunglasses', 'bow', 'headphones', 'partyhat'];
const AVATAR_BODY_ACC = ['none', 'scarf', 'cape', 'badge', 'necklace', 'backpack'];
const isHexColor = (v) => typeof v === 'string' && /^#[0-9a-fA-F]{6}$/.test(v);
app.put('/me/avatar', async (req, reply) => {
need(req, reply);