diff --git a/backToTheFutureFrontend/src/App.css b/backToTheFutureFrontend/src/App.css index 60693b0..ea12022 100644 --- a/backToTheFutureFrontend/src/App.css +++ b/backToTheFutureFrontend/src/App.css @@ -90,6 +90,9 @@ background-size: contain; margin-bottom: 1vmin; } +.App-button-start:hover { + opacity: 85%; +} .App-teams-button-back { background-image: url('assets/back_button.svg'); @@ -104,4 +107,204 @@ height: inherit; } -/****************BASE***************/ \ No newline at end of file +/****************BASE***************/ + +.App-button-back { + background-image: url('assets/back_button.svg'); + background-size: contain; + width: 20vmin; + height: 5vmin; + position: absolute; + top: 1vmin; + right: 5vmin; +} +.App-button-back:hover { + background-image: url('assets/back_button_hover.svg'); +} + + +.App-functions-container { + align-items: center; + justify-content: center; + display: flex; + flex-direction: row; +} + + +.App-button-action { + background-size: contain; + height: 4vmin; + width: 10vmin; + margin: 0 0.25vmin 0.25vmin; +} + +.App-button-eat { + background-image: url('assets/buttons/eat-button.svg'); +} +.App-button-eat:hover { + background-image: url('assets/buttons/eat-button-hover.svg'); +} + +.App-button-sleep { + background-image: url('assets/buttons/sleep-button.svg'); +} +.App-button-sleep:hover { + background-image: url('assets/buttons/sleep-button-hover.svg'); +} + +.App-button-play { + background-image: url('assets/buttons/play-button.svg'); +} +.App-button-play:hover { + background-image: url('assets/buttons/play-button-hover.svg'); +} + +.App-button-clean { + background-image: url('assets/buttons/clean-button.svg'); +} +.App-button-clean:hover { + background-image: url('assets/buttons/clean-button-hover.svg'); +} + +.App-tamagotchi-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + background-size: contain; + background-image: url('assets/tamagotchi_bg.svg'); + background-repeat: no-repeat; + width: 26vmin; + height: 32vmin; + margin-top: 4vmin; + margin-bottom: 4vmin; +} + +.App-tamagotchi-image-container { + width: 5.5vmin; + height: 8vmin; + margin-top: -3vmin; + margin-bottom: 1vmin; +} + +.App-tamagotchi-image { + background-size: contain; + background-repeat: no-repeat; + height: 100%; + width: 100%; +} + +.App-tamagotchi-image-base { + background-image: url('assets/actions/default.svg'); +} + +@keyframes blink { + from { + opacity:1 + } + to { + opacity:0 + } +} + +.App-tamagotchi-image-cleaning { + background-image: url('assets/actions/clean.svg'); + animation:blink 1s steps(3,end) infinite alternate-reverse; +} + +.App-tamagotchi-image-eating { + background-image: url('assets/actions/eat.svg'); + margin-top: 50%; + animation:blink 1s steps(3,end) infinite alternate-reverse; +} + +.App-tamagotchi-image-sleeping { + background-image: url('assets/actions/sleep.svg'); + animation:blink 1s steps(3,end) infinite alternate-reverse; +} + +.App-tamagotchi-image-playing { + background-image: url('assets/actions/play.svg'); + margin-top: 25%; + animation:blink 1s steps(3,end) infinite alternate-reverse; +} + + +.App-scales-container { + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: left; + + margin-bottom: -5vmin; +} +.App-scales-container-row { + display: flex; + flex-direction: row; + + width: 15vmin; +} + +.App-scales-container-row-top { + margin-bottom: 1vmin; +} + +.App-scale-container { + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: left; + + color: #474C41; + font-size: 1vmin; + width: 50%; + padding-left: 1vmin; + padding-right: 1vmin; +} + +.App-scale-container-value { + margin-top: 0.3vmin; + + height: 0.3vmin; + background: #474C41; +} + +.App-commands-container { + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: left; + height: 16vmin; +} +.App-commands-container-row { + display: flex; + flex-direction: row; + height: 50%; + margin-top: 1vmin; + margin-bottom: 1vmin; +} + +.App-command-container { + background-size: contain; + background-repeat: no-repeat; + width: 6vmin; + + margin-left: 0.3vmin; + margin-right: 0.3vmin; +} + +.App-command-container-clean { + background-image: url('assets/commands/small-clean.svg'); +} + +.App-command-container-eat { + background-image: url('assets/commands/small-eat.svg'); +} + +.App-command-container-play { + background-image: url('assets/commands/small-play.svg'); +} + +.App-command-container-sleep { + background-image: url('assets/commands/small-sleep.svg'); +} diff --git a/backToTheFutureFrontend/src/assets/actions/clean.svg b/backToTheFutureFrontend/src/assets/actions/clean.svg new file mode 100644 index 0000000..72fa0a1 --- /dev/null +++ b/backToTheFutureFrontend/src/assets/actions/clean.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backToTheFutureFrontend/src/assets/actions/default.svg b/backToTheFutureFrontend/src/assets/actions/default.svg new file mode 100644 index 0000000..9b464c6 --- /dev/null +++ b/backToTheFutureFrontend/src/assets/actions/default.svg @@ -0,0 +1,204 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backToTheFutureFrontend/src/assets/actions/eat.svg b/backToTheFutureFrontend/src/assets/actions/eat.svg new file mode 100644 index 0000000..fcdd4bc --- /dev/null +++ b/backToTheFutureFrontend/src/assets/actions/eat.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backToTheFutureFrontend/src/assets/actions/korgie.svg b/backToTheFutureFrontend/src/assets/actions/korgie.svg new file mode 100644 index 0000000..7aeabdd --- /dev/null +++ b/backToTheFutureFrontend/src/assets/actions/korgie.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backToTheFutureFrontend/src/assets/actions/play.svg b/backToTheFutureFrontend/src/assets/actions/play.svg new file mode 100644 index 0000000..cf14c59 --- /dev/null +++ b/backToTheFutureFrontend/src/assets/actions/play.svg @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backToTheFutureFrontend/src/assets/actions/sleep.svg b/backToTheFutureFrontend/src/assets/actions/sleep.svg new file mode 100644 index 0000000..1d2ee10 --- /dev/null +++ b/backToTheFutureFrontend/src/assets/actions/sleep.svg @@ -0,0 +1,177 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backToTheFutureFrontend/src/assets/back_button_hover.svg b/backToTheFutureFrontend/src/assets/back_button_hover.svg new file mode 100644 index 0000000..ea135a7 --- /dev/null +++ b/backToTheFutureFrontend/src/assets/back_button_hover.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/backToTheFutureFrontend/src/assets/buttons/clean-button-hover.svg b/backToTheFutureFrontend/src/assets/buttons/clean-button-hover.svg new file mode 100644 index 0000000..ee05724 --- /dev/null +++ b/backToTheFutureFrontend/src/assets/buttons/clean-button-hover.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/backToTheFutureFrontend/src/assets/buttons/clean-button.svg b/backToTheFutureFrontend/src/assets/buttons/clean-button.svg new file mode 100644 index 0000000..e34e3c7 --- /dev/null +++ b/backToTheFutureFrontend/src/assets/buttons/clean-button.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/backToTheFutureFrontend/src/assets/buttons/eat-button-hover.svg b/backToTheFutureFrontend/src/assets/buttons/eat-button-hover.svg new file mode 100644 index 0000000..b1f1362 --- /dev/null +++ b/backToTheFutureFrontend/src/assets/buttons/eat-button-hover.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/backToTheFutureFrontend/src/assets/buttons/eat-button.svg b/backToTheFutureFrontend/src/assets/buttons/eat-button.svg new file mode 100644 index 0000000..e5762cb --- /dev/null +++ b/backToTheFutureFrontend/src/assets/buttons/eat-button.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/backToTheFutureFrontend/src/assets/buttons/play-button-hover.svg b/backToTheFutureFrontend/src/assets/buttons/play-button-hover.svg new file mode 100644 index 0000000..ac48f8c --- /dev/null +++ b/backToTheFutureFrontend/src/assets/buttons/play-button-hover.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/backToTheFutureFrontend/src/assets/buttons/play-button.svg b/backToTheFutureFrontend/src/assets/buttons/play-button.svg new file mode 100644 index 0000000..27de1fb --- /dev/null +++ b/backToTheFutureFrontend/src/assets/buttons/play-button.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/backToTheFutureFrontend/src/assets/buttons/sleep-button-hover.svg b/backToTheFutureFrontend/src/assets/buttons/sleep-button-hover.svg new file mode 100644 index 0000000..d9bd2e5 --- /dev/null +++ b/backToTheFutureFrontend/src/assets/buttons/sleep-button-hover.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/backToTheFutureFrontend/src/assets/buttons/sleep-button.svg b/backToTheFutureFrontend/src/assets/buttons/sleep-button.svg new file mode 100644 index 0000000..461dc3f --- /dev/null +++ b/backToTheFutureFrontend/src/assets/buttons/sleep-button.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/backToTheFutureFrontend/src/assets/commands/small-clean.svg b/backToTheFutureFrontend/src/assets/commands/small-clean.svg new file mode 100644 index 0000000..5872afd --- /dev/null +++ b/backToTheFutureFrontend/src/assets/commands/small-clean.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backToTheFutureFrontend/src/assets/commands/small-eat.svg b/backToTheFutureFrontend/src/assets/commands/small-eat.svg new file mode 100644 index 0000000..b74c959 --- /dev/null +++ b/backToTheFutureFrontend/src/assets/commands/small-eat.svg @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backToTheFutureFrontend/src/assets/commands/small-play.svg b/backToTheFutureFrontend/src/assets/commands/small-play.svg new file mode 100644 index 0000000..b84033c --- /dev/null +++ b/backToTheFutureFrontend/src/assets/commands/small-play.svg @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backToTheFutureFrontend/src/assets/commands/small-sleep.svg b/backToTheFutureFrontend/src/assets/commands/small-sleep.svg new file mode 100644 index 0000000..bf08f53 --- /dev/null +++ b/backToTheFutureFrontend/src/assets/commands/small-sleep.svg @@ -0,0 +1,179 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backToTheFutureFrontend/src/assets/tamagotchi_bg.svg b/backToTheFutureFrontend/src/assets/tamagotchi_bg.svg new file mode 100644 index 0000000..b794394 --- /dev/null +++ b/backToTheFutureFrontend/src/assets/tamagotchi_bg.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/backToTheFutureFrontend/src/components/CommandRow.tsx b/backToTheFutureFrontend/src/components/CommandRow.tsx new file mode 100644 index 0000000..c9dafd2 --- /dev/null +++ b/backToTheFutureFrontend/src/components/CommandRow.tsx @@ -0,0 +1,35 @@ +import {Command} from "../models/Command"; + +type CommandRowProps = { + commands: Array +} + +export default function CommandRowProps({commands}: CommandRowProps) { + function getCommandClass(command: Command) { + switch (command) { + case Command.Clean: { + return "App-command-container-clean" + } + case Command.Eat: { + return "App-command-container-eat" + } + case Command.Play: { + return "App-command-container-play" + } + case Command.Sleep: { + return "App-command-container-sleep" + } + } + } + + return( +
+ { + commands.map((command) => ( +
+ ) + ) + } +
+ ) +} diff --git a/backToTheFutureFrontend/src/components/GameScreen.tsx b/backToTheFutureFrontend/src/components/GameScreen.tsx index 5642cda..f0bd8f8 100644 --- a/backToTheFutureFrontend/src/components/GameScreen.tsx +++ b/backToTheFutureFrontend/src/components/GameScreen.tsx @@ -1,9 +1,11 @@ import StartScreen from "./screens/StartScreen"; import React from "react"; +import MainGameScreen from "./screens/MainGameScreen"; window.React = React export enum GameState { START, + GAME } export type GameScreenProps = { @@ -16,5 +18,8 @@ export default function GameScreen({state, gameStateSetter}: GameScreenProps) { case GameState.START: { return } + case GameState.GAME: { + return + } } } diff --git a/backToTheFutureFrontend/src/components/Scale.tsx b/backToTheFutureFrontend/src/components/Scale.tsx new file mode 100644 index 0000000..f8f5711 --- /dev/null +++ b/backToTheFutureFrontend/src/components/Scale.tsx @@ -0,0 +1,15 @@ +type ScaleProps = { + text: string, + value: number +} + +export default function Scale({text, value}: ScaleProps) { + const percentage = (value <= 5 ? 5 : value) + '%'; + + return ( +
+
{text}
+
+
+ ) +} diff --git a/backToTheFutureFrontend/src/components/Tamagotchi.tsx b/backToTheFutureFrontend/src/components/Tamagotchi.tsx new file mode 100644 index 0000000..eafed22 --- /dev/null +++ b/backToTheFutureFrontend/src/components/Tamagotchi.tsx @@ -0,0 +1,46 @@ +import {State} from "../models/TamagotchiState"; +import Scale from "./Scale"; + +type TamagotchiProps = { + eat: number, + sleep: number, + play: number, + clean: number, + state: State +} + +export default function Tamagotchi({eat, sleep, play, clean, state}: TamagotchiProps) { + function getImageClass() { + if (state == State.Cleaning) { + return "App-tamagotchi-image-cleaning" + } + if (state == State.Eating) { + return "App-tamagotchi-image-eating" + } + if (state == State.Sleeping) { + return "App-tamagotchi-image-sleeping" + } + if (state == State.Playing) { + return "App-tamagotchi-image-playing" + } + return "App-tamagotchi-image-base" + } + + return ( +
+
+
+
+
+
+ + +
+
+ + +
+
+
+ ) +} \ No newline at end of file diff --git a/backToTheFutureFrontend/src/components/screens/MainGameScreen.tsx b/backToTheFutureFrontend/src/components/screens/MainGameScreen.tsx new file mode 100644 index 0000000..9a1c6b8 --- /dev/null +++ b/backToTheFutureFrontend/src/components/screens/MainGameScreen.tsx @@ -0,0 +1,221 @@ +import {GameState} from "../GameScreen"; +import Tamagotchi from "../Tamagotchi"; +import {useEffect, useState} from "react"; +import {State} from "../../models/TamagotchiState"; +import {Command} from "../../models/Command"; +import CommandRowProps from "../CommandRow"; + +type MainGameScreenProps = { + gameStateSetter: (gs: GameState) => void +} + +const MAX_SIZE = 16 + +export default function MainGameScreen({gameStateSetter}: MainGameScreenProps) { + let [state, stateSetter] = useState(State.Base) + let [commands, commandsSetter] = useState>([]) + + let [eatScale, eatScaleSetter] = useState(100); + let [cleanScale, cleanScaleSetter] = useState(100); + let [playScale, playScaleSetter] = useState(100); + let [sleepScale, sleepScaleSetter] = useState(100); + + let [isInProgress, isInProgressSetter] = useState(false) + + const [damages, setDamages] = useState(0) + + function randomIntFromInterval(min: number, max: number) { // min and max included + return Math.floor(Math.random() * (max - min + 1) + min) + } + + function setCommands(newCommands: Array) { + const clonedCommands: Array = [] + newCommands.forEach(command => clonedCommands.push(command)) + commandsSetter(clonedCommands) + } + + function removeFromQueue() { + const command = commands[0] + setCommands(commands.slice(1, MAX_SIZE)) + performCommand(command) + } + + function performCommand(command: Command) { + switch (command) { + case Command.Clean: { + clean() + return + } + case Command.Eat: { + eat() + return; + } + case Command.Play: { + play() + return; + } + case Command.Sleep: { + sleep() + return; + } + } + } + + useEffect(() => { + if (commands.length > 0 && !isInProgress) { + removeFromQueue() + } + }, [isInProgress]) + + useEffect(() => { + const timer = setTimeout(() => { + setDamages(damages + 1) + // TODO: get from server + const states = [State.Playing, State.Cleaning, State.Eating, State.Sleeping] + + const randomState = states[randomIntFromInterval(0, states.length - 1)] + const randomDamage = randomIntFromInterval(10, 20) + + decreaseValues(randomState, randomDamage) + }, 3000) + return () => clearTimeout(timer) + }, [damages]) + + // useEffect(() => { + // // TODO: get from server + // const states = [State.Playing, State.Cleaning, State.Eating, State.Sleeping] + // + // const randomState = states[randomIntFromInterval(0, states.length - 1)] + // const randomDamage = randomIntFromInterval(0, 5) + // + // decreaseValues(randomState, randomDamage) + // }, []) + + function putIntoQueue(command: Command) { + if (commands.length >= MAX_SIZE) { + alert("You cannot add more actions! The queue is full!") + } else { + // TODO: send a query to the server + if (!isInProgress) { + performCommand(command) + } else { + setCommands(commands.concat([command])) + } + } + } + + function decreaseValue(value: number, amountToDecrease: number) { + const newValue = value - amountToDecrease + if (newValue < 0) { + return 0 + } + return newValue + } + + function increaseValue(value: number, amountToIncrease: number) { + const newValue = value + amountToIncrease + if (newValue > 100) { + return 100 + } + return newValue + } + + function increaseValues(state: State, amountToIncrease: number) { + switch (state) { + case State.Eating: { + eatScaleSetter(increaseValue(eatScale, amountToIncrease)) + return + } + case State.Cleaning: { + cleanScaleSetter(increaseValue(cleanScale, amountToIncrease)) + return; + } + case State.Sleeping: { + sleepScaleSetter(increaseValue(sleepScale, amountToIncrease)) + return; + } + case State.Playing: { + playScaleSetter(increaseValue(playScale, amountToIncrease)) + } + } + } + + function decreaseValues(state: State, amountToDecrease: number) { + switch (state) { + case State.Eating: { + eatScaleSetter(decreaseValue(eatScale, amountToDecrease)) + return + } + case State.Cleaning: { + cleanScaleSetter(decreaseValue(cleanScale, amountToDecrease)) + return; + } + case State.Sleeping: { + sleepScaleSetter(decreaseValue(sleepScale, amountToDecrease)) + return; + } + case State.Playing: { + playScaleSetter(decreaseValue(playScale, amountToDecrease)) + } + } + } + + function runAction(newState: State) { + isInProgressSetter(true) + if (newState == State.Base) { + isInProgressSetter(false) + return + } + stateSetter(newState) + setTimeout(() => { + // TODO: decrease values + stateSetter(State.Base); + increaseValues(newState, 30) + isInProgressSetter(false) + }, + 3500); + } + + function eat() { + runAction(State.Eating) + } + + function sleep() { + runAction(State.Sleeping) + } + + function clean() { + runAction(State.Cleaning) + } + + function play() { + runAction(State.Playing) + } + + return ( +
+
+ +
+
+ + + + +
+ +
+ + +
+
+ ) +} diff --git a/backToTheFutureFrontend/src/components/screens/StartScreen.tsx b/backToTheFutureFrontend/src/components/screens/StartScreen.tsx index ea11347..c449c59 100644 --- a/backToTheFutureFrontend/src/components/screens/StartScreen.tsx +++ b/backToTheFutureFrontend/src/components/screens/StartScreen.tsx @@ -12,7 +12,10 @@ export default function StartScreen({gameStateSetter}: StartScreenProps) {

Back To The Future

by Kotlin Course

- +
); diff --git a/backToTheFutureFrontend/src/models/Command.tsx b/backToTheFutureFrontend/src/models/Command.tsx new file mode 100644 index 0000000..50a6542 --- /dev/null +++ b/backToTheFutureFrontend/src/models/Command.tsx @@ -0,0 +1,6 @@ +export enum Command { + Eat, + Sleep, + Clean, + Play +} diff --git a/backToTheFutureFrontend/src/models/TamagotchiState.tsx b/backToTheFutureFrontend/src/models/TamagotchiState.tsx new file mode 100644 index 0000000..9433b52 --- /dev/null +++ b/backToTheFutureFrontend/src/models/TamagotchiState.tsx @@ -0,0 +1,8 @@ +export enum State +{ + Base, + Eating, + Sleeping, + Cleaning, + Playing +} diff --git a/oldSchoolFrontend/src/components/screens/MainActionsScreen.tsx b/oldSchoolFrontend/src/components/screens/MainActionsScreen.tsx index 99acf93..72fbecc 100644 --- a/oldSchoolFrontend/src/components/screens/MainActionsScreen.tsx +++ b/oldSchoolFrontend/src/components/screens/MainActionsScreen.tsx @@ -90,7 +90,8 @@ export default function MainActionsScreen({gameStateSetter}: MainActionsScreenPr for (let index in photos) { if (photos[index].name.toLowerCase() === response.data) { indexToHighLightSetter(+index) - infoTextSetter("Only the first photo with " + color + "\nbackground color has been found!") + let article = color == "orange" ? "an" : "a" + infoTextSetter("Only the first photo with " + article + " " + color + "\nbackground color has been found!") return } } @@ -136,10 +137,10 @@ export default function MainActionsScreen({gameStateSetter}: MainActionsScreenPr } if (type == "background color") { sendGroupByRequest("/functions/groupByByColor") - infoTextSetter("Photos with the same background color\nwere grouped.") + infoTextSetter("Photos have been grouped according to\nthe background color.") } else if (type == "hair type and hat") { sendGroupByRequest("/functions/groupByPhotosByHairAndHat") - infoTextSetter("Photos with the same hair tone (dark or light) were grouped.\nInside each group photos were grouped by a hat presence.") + infoTextSetter("Photos have been grouped according to the hair tone (dark or light).\nInside each group, photos are grouped according to the absence/presence of a hat.") } }