diff --git a/arithmetic_en.html b/arithmetic_en.html index 87cee60..c1f8cf2 100644 --- a/arithmetic_en.html +++ b/arithmetic_en.html @@ -45,7 +45,10 @@ "phet-io" ], "simFeatures": { - "supportsSound": true + "supportsSound": true, + "supportedRegionsAndCultures": [ + "usa" + ] }, "simulation": true, "phet-io": { diff --git a/js/arithmetic-main.js b/js/arithmetic-main.js index 7f5cecc..638b910 100644 --- a/js/arithmetic-main.js +++ b/js/arithmetic-main.js @@ -6,10 +6,12 @@ * @author John Blanco, Andrey Zelenkov (MLearner) */ +import PreferencesModel from '../../joist/js/preferences/PreferencesModel.js'; import Sim from '../../joist/js/Sim.js'; import simLauncher from '../../joist/js/simLauncher.js'; import Tandem from '../../tandem/js/Tandem.js'; import ArithmeticStrings from './ArithmeticStrings.js'; +import BoxPlayerImages from './common/view/BoxPlayerImages.js'; import DivideScreen from './divide/DivideScreen.js'; import FactorScreen from './factor/FactorScreen.js'; import MultiplyScreen from './multiply/MultiplyScreen.js'; @@ -20,7 +22,14 @@ const arithmeticTitleStringProperty = ArithmeticStrings.arithmetic.titleStringPr // constants const tandem = Tandem.ROOT; +const preferencesModel = new PreferencesModel( { + localizationOptions: { + characterSets: BoxPlayerImages.BOX_PLAYER_CHARACTER_SETS + } +} ); + const simOptions = { + preferencesModel: preferencesModel, credits: { leadDesign: 'Michael Dubson, Amanda McGarry', softwareDevelopment: 'John Blanco, Michael Dubson', @@ -33,8 +42,8 @@ const simOptions = { simLauncher.launch( () => { // Create and start the sim new Sim( arithmeticTitleStringProperty, [ - new MultiplyScreen( { tandem: tandem.createTandem( 'multiplyScreen' ) } ), - new FactorScreen( { tandem: tandem.createTandem( 'factorScreen' ) } ), - new DivideScreen( { tandem: tandem.createTandem( 'divideScreen' ) } ) + new MultiplyScreen( preferencesModel, { tandem: tandem.createTandem( 'multiplyScreen' ) } ), + new FactorScreen( preferencesModel, { tandem: tandem.createTandem( 'factorScreen' ) } ), + new DivideScreen( preferencesModel, { tandem: tandem.createTandem( 'divideScreen' ) } ) ], simOptions ).start(); } ); \ No newline at end of file diff --git a/js/common/model/ArithmeticModel.js b/js/common/model/ArithmeticModel.js index 5a4d0be..447b6c6 100644 --- a/js/common/model/ArithmeticModel.js +++ b/js/common/model/ArithmeticModel.js @@ -31,7 +31,7 @@ class ArithmeticModel { /** * Constructor for ArithmeticModel */ - constructor( tandem, options ) { + constructor( preferencesModel, tandem, options ) { // @private - for PhET-iO this.checkAnswerEmitter = new Emitter( { @@ -92,6 +92,12 @@ class ArithmeticModel { this.activeLevelModel.displayScoreProperty.set( this.activeLevelModel.currentScoreProperty.get() ); } } ); + + /** + * @public + * @type {Property} + */ + this.regionAndCulturePortrayalProperty = preferencesModel.localizationModel.regionAndCulturePortrayalProperty; } // @protected - get the current level model, use this to make the code more readable diff --git a/js/common/view/BoxPlayerCharacterSet.js b/js/common/view/BoxPlayerCharacterSet.js new file mode 100644 index 0000000..12446d9 --- /dev/null +++ b/js/common/view/BoxPlayerCharacterSet.js @@ -0,0 +1,48 @@ +// Copyright 2023, University of Colorado Boulder + +/** + * The BoxPlayerCharacterSet defines what is needed for each character set in Arithmetic. + * + * @author Luisa Vargas + * + */ + +import RegionAndCulturePortrayal from '../../../../joist/js/preferences/RegionAndCulturePortrayal.js'; +import arithmetic from '../../arithmetic.js'; + +export default class BoxPlayerCharacterSet extends RegionAndCulturePortrayal { + + /** + * @param label { LocalizedStringProperty } + * @param multiplyLevel1 { HTMLImageElement } + * @param multiplyLevel2 { HTMLImageElement } + * @param multiplyLevel3 { HTMLImageElement } + * @param factorLevel1 { HTMLImageElement } + * @param factorLevel2 { HTMLImageElement } + * @param factorLevel3 { HTMLImageElement } + * @param divideLevel1 { HTMLImageElement } + * @param divideLevel2 { HTMLImageElement } + * @param divideLevel3 { HTMLImageElement } + * @param queryParameterValue { string } + */ + constructor( label, + multiplyLevel1, multiplyLevel2, multiplyLevel3, + factorLevel1, factorLevel2, factorLevel3, + divideLevel1, divideLevel2, divideLevel3, + queryParameterValue ) { + + super( label, queryParameterValue, {} ); + + this.multiplyLevel1 = multiplyLevel1; + this.multiplyLevel2 = multiplyLevel2; + this.multiplyLevel3 = multiplyLevel3; + this.factorLevel1 = factorLevel1; + this.factorLevel2 = factorLevel2; + this.factorLevel3 = factorLevel3; + this.divideLevel1 = divideLevel1; + this.divideLevel2 = divideLevel2; + this.divideLevel3 = divideLevel3; + } +} + +arithmetic.register( 'BoxPlayerCharacterSet', BoxPlayerCharacterSet ); \ No newline at end of file diff --git a/js/common/view/BoxPlayerCharacterSetUSA.js b/js/common/view/BoxPlayerCharacterSetUSA.js new file mode 100644 index 0000000..19a3dec --- /dev/null +++ b/js/common/view/BoxPlayerCharacterSetUSA.js @@ -0,0 +1,37 @@ +// Copyright 2023, University of Colorado Boulder + +/** + * This file instantiates the USA region and culture portrayals. + * + * @author Luisa Vargas + * + */ + +import JoistStrings from '../../../../joist/js/JoistStrings.js'; +import { USA_REGION_AND_CULTURE_ID } from '../../../../joist/js/preferences/RegionAndCulturePortrayal.js'; +import divideLevel1Icon_png from '../../../mipmaps/divideLevel1Icon_png.js'; +import divideLevel2Icon_png from '../../../mipmaps/divideLevel2Icon_png.js'; +import divideLevel3Icon_png from '../../../mipmaps/divideLevel3Icon_png.js'; +import factorLevel1Icon_png from '../../../mipmaps/factorLevel1Icon_png.js'; +import factorLevel2Icon_png from '../../../mipmaps/factorLevel2Icon_png.js'; +import factorLevel3Icon_png from '../../../mipmaps/factorLevel3Icon_png.js'; +import multiplyLevel1Icon_png from '../../../mipmaps/multiplyLevel1Icon_png.js'; +import multiplyLevel2Icon_png from '../../../mipmaps/multiplyLevel2Icon_png.js'; +import multiplyLevel3Icon_png from '../../../mipmaps/multiplyLevel3Icon_png.js'; +import BoxPlayerCharacterSet from './BoxPlayerCharacterSet.js'; + +const ExplorerCharacterSetUSA = new BoxPlayerCharacterSet( + JoistStrings.preferences.tabs.localization.regionAndCulture.portrayalSets.unitedStatesOfAmericaStringProperty, + multiplyLevel1Icon_png, + multiplyLevel2Icon_png, + multiplyLevel3Icon_png, + factorLevel1Icon_png, + factorLevel2Icon_png, + factorLevel3Icon_png, + divideLevel1Icon_png, + divideLevel2Icon_png, + divideLevel3Icon_png, + USA_REGION_AND_CULTURE_ID +); + +export default ExplorerCharacterSetUSA; \ No newline at end of file diff --git a/js/common/view/BoxPlayerController.js b/js/common/view/BoxPlayerController.js new file mode 100644 index 0000000..09f0875 --- /dev/null +++ b/js/common/view/BoxPlayerController.js @@ -0,0 +1,105 @@ +// Copyright 2023, University of Colorado Boulder + +/** + * The BoxPlayerController creates the images of each version of the 'level' box player ( multiply level 1, + * multiply level 2, multiply level 3, factor level 1, factor level 2, factor level 3, divide level 1, divide level 2, + * and divide level 3), as well as defines the visibility of each individual image based on the + * regionAndCulturePortrayalProperty. + * + * @author Luisa Vargas + * + */ + +import DerivedProperty from '../../../../axon/js/DerivedProperty.js'; +import { Image, Node } from '../../../../scenery/js/imports.js'; +import arithmetic from '../../arithmetic.js'; +import BoxPlayerImages from './BoxPlayerImages.js'; + +export default class BoxPlayerController { + + /** + * @param { ArithmeticModel } sceneModel + */ + constructor( sceneModel ) { + + const boxPlayerSets = BoxPlayerImages.BOX_PLAYER_CHARACTER_SETS; + const regionAndCulturePortrayalProperty = sceneModel.regionAndCulturePortrayalProperty; + + const createVisibleProperty = set => { + return new DerivedProperty( [ regionAndCulturePortrayalProperty ], portrayal => { + return portrayal === set; + } ); + }; + + const multiplyLevel1Images = boxPlayerSets.map( set => new Image( set.multiplyLevel1, + { + visibleProperty: createVisibleProperty( set ) + } ) ); + const multiplyLevel2Images = boxPlayerSets.map( set => new Image( set.multiplyLevel2, + { + visibleProperty: createVisibleProperty( set ) + } ) ); + const multiplyLevel3Images = boxPlayerSets.map( set => new Image( set.multiplyLevel3, + { + visibleProperty: createVisibleProperty( set ) + } ) ); + const factorLevel1Images = boxPlayerSets.map( set => new Image( set.factorLevel1, + { + visibleProperty: createVisibleProperty( set ) + } ) ); + const factorLevel2Images = boxPlayerSets.map( set => new Image( set.factorLevel2, + { + visibleProperty: createVisibleProperty( set ) + } ) ); + const factorLevel3Images = boxPlayerSets.map( set => new Image( set.factorLevel3, + { + visibleProperty: createVisibleProperty( set ) + } ) ); + const divideLevel1Images = boxPlayerSets.map( set => new Image( set.divideLevel1, + { + visibleProperty: createVisibleProperty( set ) + } ) ); + const divideLevel2Images = boxPlayerSets.map( set => new Image( set.divideLevel2, + { + visibleProperty: createVisibleProperty( set ) + } ) ); + const divideLevel3Images = boxPlayerSets.map( set => new Image( set.divideLevel3, + { + visibleProperty: createVisibleProperty( set ) + } ) ); + + const multiplyLevel1Node = new Node( { children: multiplyLevel1Images } ); + const multiplyLevel2Node = new Node( { children: multiplyLevel2Images } ); + const multiplyLevel3Node = new Node( { children: multiplyLevel3Images } ); + const factorLevel1Node = new Node( { children: factorLevel1Images } ); + const factorLevel2Node = new Node( { children: factorLevel2Images } ); + const factorLevel3Node = new Node( { children: factorLevel3Images } ); + const divideLevel1Node = new Node( { children: divideLevel1Images } ); + const divideLevel2Node = new Node( { children: divideLevel2Images } ); + const divideLevel3Node = new Node( { children: divideLevel3Images } ); + + /** + * @public + * @type {{divide: Node[], multiply: Node[], factor: Node[]}} + */ + this.boxPlayerNodes = { + multiply: [ + multiplyLevel1Node, + multiplyLevel2Node, + multiplyLevel3Node + ], + factor: [ + factorLevel1Node, + factorLevel2Node, + factorLevel3Node + ], + divide: [ + divideLevel1Node, + divideLevel2Node, + divideLevel3Node + ] + }; + } +} + +arithmetic.register( 'BoxPlayerController', BoxPlayerController ); \ No newline at end of file diff --git a/js/common/view/BoxPlayerImages.js b/js/common/view/BoxPlayerImages.js new file mode 100644 index 0000000..a278a36 --- /dev/null +++ b/js/common/view/BoxPlayerImages.js @@ -0,0 +1,20 @@ +// Copyright 2023, University of Colorado Boulder +/** + * BoxPlayer images contains an array of character sets, each representing a different region/culture. + * + * @author Luisa Vargas + * + */ + +import arithmetic from '../../arithmetic.js'; +import BoxPlayerCharacterSetUSA from './BoxPlayerCharacterSetUSA.js'; + + +const BoxPlayerImages = { + BOX_PLAYER_CHARACTER_SETS: [ + BoxPlayerCharacterSetUSA + ] +}; + +arithmetic.register( 'BoxPlayerImages', BoxPlayerImages ); +export default BoxPlayerImages; \ No newline at end of file diff --git a/js/common/view/LevelSelectionNode.js b/js/common/view/LevelSelectionNode.js index e19cd2d..40ead2b 100644 --- a/js/common/view/LevelSelectionNode.js +++ b/js/common/view/LevelSelectionNode.js @@ -11,47 +11,20 @@ import merge from '../../../../phet-core/js/merge.js'; import ResetAllButton from '../../../../scenery-phet/js/buttons/ResetAllButton.js'; import TimerToggleButton from '../../../../scenery-phet/js/buttons/TimerToggleButton.js'; import PhetFont from '../../../../scenery-phet/js/PhetFont.js'; -import { HBox, Image, Node, Text, VBox } from '../../../../scenery/js/imports.js'; +import { HBox, Node, Text, VBox } from '../../../../scenery/js/imports.js'; import LevelSelectionButton from '../../../../vegas/js/LevelSelectionButton.js'; import ScoreDisplayStars from '../../../../vegas/js/ScoreDisplayStars.js'; -import divideLevel1Icon_png from '../../../mipmaps/divideLevel1Icon_png.js'; -import divideLevel2Icon_png from '../../../mipmaps/divideLevel2Icon_png.js'; -import divideLevel3Icon_png from '../../../mipmaps/divideLevel3Icon_png.js'; -import factorLevel1Icon_png from '../../../mipmaps/factorLevel1Icon_png.js'; -import factorLevel2Icon_png from '../../../mipmaps/factorLevel2Icon_png.js'; -import factorLevel3Icon_png from '../../../mipmaps/factorLevel3Icon_png.js'; -import multiplyLevel1Icon_png from '../../../mipmaps/multiplyLevel1Icon_png.js'; -import multiplyLevel2Icon_png from '../../../mipmaps/multiplyLevel2Icon_png.js'; -import multiplyLevel3Icon_png from '../../../mipmaps/multiplyLevel3Icon_png.js'; import arithmetic from '../../arithmetic.js'; import ArithmeticStrings from '../../ArithmeticStrings.js'; import ArithmeticConstants from '../ArithmeticConstants.js'; import ArithmeticGlobals from '../ArithmeticGlobals.js'; +import BoxPlayerController from './BoxPlayerController.js'; // constants const CHOOSE_LEVEL_TITLE_FONT = new PhetFont( { size: 24 } ); const TAB_TITLE_FONT = new PhetFont( { size: 54 } ); const BUTTON_LENGTH = 150; -// icon sets, used to place on the buttons -const ICON_SETS = { - multiply: [ - multiplyLevel1Icon_png, - multiplyLevel2Icon_png, - multiplyLevel3Icon_png - ], - factor: [ - factorLevel1Icon_png, - factorLevel2Icon_png, - factorLevel3Icon_png - ], - divide: [ - divideLevel1Icon_png, - divideLevel2Icon_png, - divideLevel3Icon_png - ] -}; - const chooseYourLevelString = ArithmeticStrings.chooseYourLevel; class LevelSelectionNode extends Node { @@ -88,10 +61,16 @@ class LevelSelectionNode extends Node { } ); this.addChild( chooseLevelTitle ); + + const boxPlayerController = new BoxPlayerController( model ); + + // icon sets, used to place on the buttons + const iconSets = boxPlayerController.boxPlayerNodes; + // add select level buttons - assert && assert( model.levelModels.length === ICON_SETS[ options.iconSet ].length, 'Number of icons doesn\'t match number of levels' ); + assert && assert( model.levelModels.length === iconSets[ options.iconSet ].length, 'Number of icons doesn\'t match number of levels' ); const levelSelectButtons = model.levelModels.map( ( level, levelIndex ) => new LevelSelectionButton( - new Image( ICON_SETS[ options.iconSet ][ levelIndex ] ), + iconSets[ options.iconSet ][ levelIndex ], model.levelModels[ levelIndex ].displayScoreProperty, { buttonWidth: BUTTON_LENGTH, diff --git a/js/divide/DivideScreen.js b/js/divide/DivideScreen.js index c0dc4dd..cb0c743 100644 --- a/js/divide/DivideScreen.js +++ b/js/divide/DivideScreen.js @@ -22,9 +22,10 @@ import DivideView from './view/DivideView.js'; class DivideScreen extends Screen { /** + * @param { PreferencesModel } preferencesModel * @param {Object} [options] */ - constructor( options ) { + constructor( preferencesModel, options ) { options = merge( { name: ArithmeticStrings.divideStringProperty, @@ -37,7 +38,7 @@ class DivideScreen extends Screen { }, options ); super( - () => new DivideModel( options.tandem.createTandem( 'model' ) ), + () => new DivideModel( preferencesModel, options.tandem.createTandem( 'model' ) ), model => new DivideView( model ), options ); diff --git a/js/divide/model/DivideModel.js b/js/divide/model/DivideModel.js index e911c60..5847c20 100644 --- a/js/divide/model/DivideModel.js +++ b/js/divide/model/DivideModel.js @@ -16,10 +16,11 @@ import GameState from '../../common/model/GameState.js'; class DivideModel extends ArithmeticModel { /** - * @param {Tandem} tandem + * @param { PreferencesModel } preferencesModel + * @param { Tandem } tandem */ - constructor( tandem ) { - super( tandem, { + constructor( preferencesModel, tandem ) { + super( preferencesModel, tandem, { fillEquation: () => { // Convert any strings entered by the user into numerical values. diff --git a/js/factor/FactorScreen.js b/js/factor/FactorScreen.js index 7676224..8cbc4b0 100644 --- a/js/factor/FactorScreen.js +++ b/js/factor/FactorScreen.js @@ -21,9 +21,10 @@ import FactorView from './view/FactorView.js'; class FactorScreen extends Screen { /** + * @param { PreferencesModel } preferencesModel * @param {Object} [options] */ - constructor( options ) { + constructor( preferencesModel, options ) { options = merge( { name: ArithmeticStrings.factorStringProperty, @@ -36,7 +37,7 @@ class FactorScreen extends Screen { }, options ); super( - () => new FactorModel( options.tandem.createTandem( 'model' ) ), + () => new FactorModel( preferencesModel, options.tandem.createTandem( 'model' ) ), model => new FactorView( model ), options ); diff --git a/js/factor/model/FactorModel.js b/js/factor/model/FactorModel.js index fd1e8c4..9d7a13e 100644 --- a/js/factor/model/FactorModel.js +++ b/js/factor/model/FactorModel.js @@ -15,10 +15,11 @@ import GameState from '../../common/model/GameState.js'; class FactorModel extends ArithmeticModel { /** + * @param { PreferencesModel } preferencesModel * @param {Tandem} tandem */ - constructor( tandem ) { - super( tandem ); + constructor( preferencesModel, tandem ) { + super( preferencesModel, tandem ); } // @public diff --git a/js/multiply/MultiplyScreen.js b/js/multiply/MultiplyScreen.js index 197e10e..a5e3382 100644 --- a/js/multiply/MultiplyScreen.js +++ b/js/multiply/MultiplyScreen.js @@ -21,9 +21,10 @@ import MultiplyView from './view/MultiplyView.js'; class MultiplyScreen extends Screen { /** + * @param { PreferencesModel } preferencesModel * @param {Object} [options] */ - constructor( options ) { + constructor( preferencesModel, options ) { options = merge( { name: ArithmeticStrings.multiplyStringProperty, @@ -36,7 +37,7 @@ class MultiplyScreen extends Screen { }, options ); super( - () => new MultiplyModel( options.tandem.createTandem( 'model' ) ), + () => new MultiplyModel( preferencesModel, options.tandem.createTandem( 'model' ) ), model => new MultiplyView( model ), options ); diff --git a/js/multiply/model/MultiplyModel.js b/js/multiply/model/MultiplyModel.js index ae1562b..f9ca390 100644 --- a/js/multiply/model/MultiplyModel.js +++ b/js/multiply/model/MultiplyModel.js @@ -14,9 +14,13 @@ import GameState from '../../common/model/GameState.js'; class MultiplyModel extends ArithmeticModel { - constructor( tandem ) { + /** + * @param { PreferencesModel } preferencesModel + * @param { Tandem } tandem + */ + constructor( preferencesModel, tandem ) { - super( tandem, { + super( preferencesModel, tandem, { fillEquation: () => { this.problemModel.productProperty.set( Number( this.inputProperty.get() ) ); this.submitAnswer(); diff --git a/package.json b/package.json index c04c367..080a42f 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,10 @@ "phet-io" ], "simFeatures": { - "supportsSound": true + "supportsSound": true, + "supportedRegionsAndCultures": [ + "usa" + ] }, "simulation": true, "phet-io": {