/************************************************************** * LInE - Free Education, Private Data - http://www.usp.br/line * * This file holds all global elements to the game. * * Generating game levels in menu: * ..................................................... * ...............square....................circle...... } = gameShape * .........../...........\....................|........ } = gameType (game) * ........One.............Two................One....... } * ......./...\.........../...\............./....\...... * ......A.....B.........A.....B...........A......B..... = gameMode (game mode) * .(floor)..(stack)..(top)..(bottom)..(floor)..(stack). * .......\./.............\./................\./........ * ........|...............|..................|......... * ......./.\..............|................/.|.\....... * ...Plus...Minus.......Equals........Plus.Minus.Mixed. = gameOperation (game math operation) * .......\./..............|................\.|./....... * ........|...............|..................|......... * ......1,2,3.........1,2,3,4,5..........1,2,3,4,5..... = gameDifficulty (difficulty level) * ..................................................... * * About levels in map: * * ..................(game.levels)...................... * ......................__|__.......................... * .....................|.|.|.|......................... * ...................0,1,2,3,4,5....................... = mapPosition (map positions) * ...................|.........|....................... * ................(start)....(end)..................... **************************************************************/ /** * Turns console messages ON/OFF (for debug purposes only) * @type {boolean} */ const debugMode = false; /** FOR MOODLE
* * iFractions can run on a server or inside moodle through iAssign.
* This variable should be set according to where it is suposed to run:
* - if true, on moodle
* - if false, on a server */ const moodle = false; /** * Name of the selected game.
* Can be: 'squareOne', 'squareTwo' or 'circleOne'. * * @type {string} */ let gameType; /** * Used for text and game information.
* Shape that makes the name of the game - e.g in 'squareOne' it is 'square'.
* Can be: 'circle' or 'square'. * * @type {string} */ let gameShape; /** * Holds selected game mode.
* In squareOne/circleOne can be: 'A' (click on the floor) or 'B' (click on the amount to go/stacked figures).
* In squareTwo can be: 'A' (more subdivisions on top) or 'B' (more subdivisions on bottom). * * @type {string} */ let gameMode; /** * Holds game math operation.
* In squareOne can be: 'Plus' (green tractor goes right) or 'Minus' (red tractor goes left).
* In circleOne can be: 'Plus' (green tractor goes right), 'Minus' (red tractor goes left) or 'Mixed' (green tractor goes both sides).
* In squareTwo can be: 'Equals' (compares two rectangle subdivisions). * * @type {string} */ let gameOperation; /** * Holds game overall difficulty. 1 (easier) -> n (harder).
* In squareOne can be: 1..3.
* In circleOne/squareTwo can be: 1..5. * * @type {number} */ let gameDifficulty; /** * Turns displaying the fraction labels on levels ON/OFF * @type {boolean} */ let fractionLabel = true; /** * When true, the character can move to next position in the map * @type {boolean} */ let mapMove; /** * Character position on the map, aka game levels (1..4: valid; 5: end) * @type {number} */ let mapPosition; /** * Number of finished levels in the map * @type {number} */ let completedLevels; /** * Turns game audio ON/OFF * @type {boolean} */ let audioStatus = false; /** * Player's name * @type {string} */ let playerName; /** * String that contains the selected language.
* It is the name of the language file. * @type {string} */ let langString; /** * Holds the current state.
* Is used as if it was a 'this' inside state functions. * @type {object} */ let self; const medSrc = 'assets/img/'; // Base directory for media /** * Metadata for all games * @type {object} */ const info = { all: { squareOne: { gameShape: 'square', gameType: 'squareOne', gameTypeUrl: 'game0', gameMode: ['A', 'B'], gameModeUrl: ['mode0', 'mode1'], gameOperation: ['Plus', 'Minus'], gameOperationUrl: ['operation_plus', 'operation_minus'], gameDifficulty: 3, }, circleOne: { gameShape: 'circle', gameType: 'circleOne', gameTypeUrl: 'game1', gameMode: ['A', 'B'], gameModeUrl: ['mode2', 'mode3'], gameOperation: ['Plus', 'Minus', 'Mixed'], gameOperationUrl: [ 'operation_plus', 'operation_minus', 'operation_mixed', ], gameDifficulty: 5, }, squareTwo: { gameShape: 'square', gameType: 'squareTwo', gameTypeUrl: 'game2', gameMode: ['A', 'B'], gameModeUrl: ['mode4', 'mode5'], gameOperation: ['Equals'], gameOperationUrl: ['operation_equals'], gameDifficulty: 5, }, }, gameShape: [], gameType: [], gameTypeUrl: [], gameMode: [], gameModeUrl: [], gameOperation: [], gameOperationUrl: [], gameDifficulty: [], }; // Called once when the game starts (function () { for (key in info.all) { info.gameShape.push(info.all[key].gameShape); info.gameType.push(info.all[key].gameType); info.gameTypeUrl.push(info.all[key].gameTypeUrl); info.gameMode.push(info.all[key].gameMode); info.gameModeUrl.push(info.all[key].gameMode); info.gameOperation.push(info.all[key].gameOperation); info.gameOperationUrl.push(info.all[key].gameOperationUrl); info.gameDifficulty.push(info.all[key].gameDifficulty); } })(); /** * Preset colors for graphic elements. * @type {object} */ const colors = { // Blues blue: '#003cb3', // Subtitle blueDark: '#183780', // Line color that indicates right and fraction numbers blueBg: '#cce5ff', // Background color blueBgOff: '#adc8e6', blueBgInsideLevel: '#a8c0e6', // Background color in squareOne (used for floor gap) blueMenuLine: '#b7cdf4', // Reds red: '#b30000', // Linecolor that indicates left redLight: '#d27979', // squareTwo figures redDark: '#330000', // squareTwo figures and some titles // Greens green: '#00804d', // Title greenLight: '#83afaf', // squareTwo figures greenDark: '#1e2f2f', // squareTwo figures greenNeon: '#00d600', // Basics white: '#efeff5', gray: '#708090', black: '#000', yellow: '#ffef1f', }; /** * Preset text styles for game text.
* Contains: font, size, text color and text align. * @type {object} */ const textStyles = { h1_green: { font: '32px Arial,sans-serif', fill: colors.green, align: 'center', }, // Menu title h2_green: { font: '26px Arial,sans-serif', fill: colors.green, align: 'center', }, // Flag labels (langState) h1_white: { font: '32px Arial,sans-serif', fill: colors.white, align: 'center', }, // Ok button (nameState) h2_white: { font: '26px Arial,sans-serif', fill: colors.white, align: 'center', }, // Difficulty buttons (menuState) h3__white: { font: '23px Arial,sans-serif', fill: colors.white, align: 'center', }, // Difficulty numbers (menuState) h4_white: { font: '20px Arial,sans-serif', fill: colors.white, align: 'center', }, // Difficulty numbers (menuState) p_white: { font: '14px Arial,sans-serif', fill: colors.white, align: 'center', }, // Enter button (menuState) h2_brown: { font: '26px Arial,sans-serif', fill: colors.redDark, align: 'center', }, // Map difficulty label h4_brown: { font: '20px Arial,sans-serif', fill: colors.redDark, align: 'center', }, // Menu overtitle p_brown: { font: '14px Arial,sans-serif', fill: colors.redDark, align: 'center', }, // Map difficulty label h2_blue: { font: '26px Arial,sans-serif', fill: colors.blue, align: 'center', }, // Menu subtitle h4_blue: { font: '20px Arial,sans-serif', fill: colors.blue, align: 'center', }, // Menu subtitle h2_blueDark: { font: '26px Arial,sans-serif', fill: colors.blueDark, align: 'center', }, // Fractions h4_blueDark: { font: '20px Arial,sans-serif', fill: colors.blueDark, align: 'center', }, // Fractions p_blueDark: { font: '14px Arial,sans-serif', fill: colors.blueDark, align: 'center', }, // Fractions }; /** * List of URL for all media in the game * divided 1st by the 'state' that loads the media * and 2nd by the 'media type' for that state. * * @type {object} */ const url = { /** * url. * where can be: boot, menu, squareOne, squareTwo, circleOne. */ boot: { /** * url.. * where can be: image, sprite, audio

* * image: [ [name, source], ... ]
* sprite: [ [name, source, number of frames], ... ]
* audio: [ [name, [source, alternative source] ], ... ] */ image: [ // Scene ['bgimage', medSrc + 'scene/bg.jpg'], ['bgmap', medSrc + 'scene/bg_map.png'], ['broken_sign', medSrc + 'scene/broken_sign.png'], ['bush', medSrc + 'scene/bush.png'], ['cloud', medSrc + 'scene/cloud.png'], ['floor', medSrc + 'scene/floor.png'], ['place_off', medSrc + 'scene/place_off.png'], ['place_on', medSrc + 'scene/place_on.png'], ['rock', medSrc + 'scene/rock.png'], ['road', medSrc + 'scene/road.png'], ['sign', medSrc + 'scene/sign.png'], ['tree1', medSrc + 'scene/tree.png'], ['tree2', medSrc + 'scene/tree2.png'], ['tree3', medSrc + 'scene/tree3.png'], ['tree4', medSrc + 'scene/tree4.png'], // Flags ['flag_BR', medSrc + 'flag/br.png'], ['flag_FR', medSrc + 'flag/fr.png'], ['flag_IT', medSrc + 'flag/it.png'], ['flag_PE', medSrc + 'flag/pe.png'], ['flag_US', medSrc + 'flag/us.png'], // Navigation icons on the top of the page ['back', medSrc + 'navig_icon/back.png'], ['help', medSrc + 'navig_icon/help.png'], ['home', medSrc + 'navig_icon/home.png'], ['language', medSrc + 'navig_icon/language.png'], ['menu', medSrc + 'navig_icon/menu.png'], // Interactive icons ['arrow_down', medSrc + 'interac_icon/down.png'], ['close', medSrc + 'interac_icon/close.png'], ['error', medSrc + 'interac_icon/error.png'], ['help_pointer', medSrc + 'interac_icon/pointer.png'], ['info', medSrc + 'interac_icon/info.png'], ['ok', medSrc + 'interac_icon/ok.png'], // Menu icons - Games ['game0', medSrc + 'levels/squareOne.png'], // Square I ['game1', medSrc + 'levels/circleOne.png'], // Circle I ['game2', medSrc + 'levels/squareTwo.png'], // Square II // Menu icons - Info box ['c1-A', medSrc + 'info_box/c1-A.png'], ['c1-A-h', medSrc + 'info_box/c1-A-h.png'], ['c1-B-h', medSrc + 'info_box/c1-B-h.png'], ['c1-diff-1', medSrc + 'info_box/c1-diff-1.png'], ['c1-diff-5', medSrc + 'info_box/c1-diff-5.png'], ['c1-label', medSrc + 'info_box/c1-label.png'], ['map-c1s2', medSrc + 'info_box/map-c1s2.png'], ['map-s1', medSrc + 'info_box/map-s1.png'], ['s1-A', medSrc + 'info_box/s1-A.png'], ['s1-A-h', medSrc + 'info_box/s1-A-h.png'], ['s1-B-h', medSrc + 'info_box/s1-B-h.png'], ['s1-diff-1', medSrc + 'info_box/s1-diff-1.png'], ['s1-diff-3', medSrc + 'info_box/s1-diff-3.png'], ['s1-label', medSrc + 'info_box/s1-label.png'], ['s2', medSrc + 'info_box/s2.png'], ['s2-A-h', medSrc + 'info_box/s2-A-h.png'], ['s2-B-h', medSrc + 'info_box/s2-B-h.png'], ['s2-diff-1', medSrc + 'info_box/s2-diff-1.png'], ['s2-diff-5', medSrc + 'info_box/s2-diff-5.png'], ['s2-label', medSrc + 'info_box/s2-label.png'], ['operation_plus', medSrc + 'info_box/operation_plus.png'], ['operation_minus', medSrc + 'info_box/operation_minus.png'], ['operation_mixed', medSrc + 'info_box/operation_mixed.png'], ['operation_equals', medSrc + 'info_box/operation_equals.png'], ], sprite: [ // Game Sprites ['kid_walk', medSrc + 'character/kid/walk.png', 26], // Navigation icons on the top of the page ['audio', medSrc + 'navig_icon/audio.png', 2], // Interactive icons ['select', medSrc + 'interac_icon/selectionBox.png', 2], // Menu icons - Game modes ['mode0', medSrc + 'levels/squareOne_1.png', 2], // Square I : A ['mode1', medSrc + 'levels/squareOne_2.png', 2], // Square I : B ['mode2', medSrc + 'levels/circleOne_1.png', 2], // Circle I : A ['mode3', medSrc + 'levels/circleOne_2.png', 2], // Circle I : B ['mode4', medSrc + 'levels/squareTwo_1.png', 2], // Square II : A ['mode5', medSrc + 'levels/squareTwo_2.png', 2], // Square II : B // Menu icons - Math operations ['operation_plus', medSrc + 'levels/operation_plus.png', 2], // Square/circle I : right ['operation_minus', medSrc + 'levels/operation_minus.png', 2], // Square/circle I : left ['operation_mixed', medSrc + 'levels/operation_mixed.png', 2], // Circle I : mixed ['operation_equals', medSrc + 'levels/operation_equals.png', 2], // Square II : equals ], audio: [ // Sound effects ['beepSound', ['assets/audio/beep.ogg', 'assets/audio/beep.mp3']], ['okSound', ['assets/audio/ok.ogg', 'assets/audio/ok.mp3']], ['errorSound', ['assets/audio/error.ogg', 'assets/audio/error.mp3']], ['popSound', ['', 'assets/audio/pop.wav']], ], }, squareOne: { image: [ // Map buildings ['farm', medSrc + 'scene/farm.png'], ['garage', medSrc + 'scene/garage.png'], ], sprite: [ // Game sprites ['tractor', medSrc + 'character/tractor/tractor.png', 15], ], audio: [], }, squareTwo: { image: [ // Map buildings ['house', medSrc + 'scene/house.png'], ['school', medSrc + 'scene/school.png'], ], sprite: [ // Game sprites ['kid_standing', medSrc + 'character/kid/lost.png', 6], ['kid_run', medSrc + 'character/kid/run.png', 12], ], audio: [], }, circleOne: { image: [ // Map buildings ['house', medSrc + 'scene/house.png'], ['school', medSrc + 'scene/school.png'], // Game images ['balloon', medSrc + 'character/balloon/airballoon_upper.png'], ['balloon_basket', medSrc + 'character/balloon/airballoon_base.png'], ], sprite: [ // Game sprites ['kid_run', medSrc + 'character/kid/run.png', 12], ], audio: [], }, }; /** * Manages navigation icons on the top of the screen * @namespace */ const navigationIcons = { /** * Add navigation icons.
* * The icons on the left are ordered from left to right.
* * The icons on the right are ordered from right to left. * * @param {boolean} leftIcon0 1st left icon (back) * @param {boolean} leftIcon1 2nd left icon (main menu) * @param {boolean} leftIcon2 3rd left icon (solve game) * @param {boolean} rightIcon0 1st right icon (audio) * @param {boolean} rightIcon1 2nd right icon (lang) * @param {undefined|string} state state to be called by the 'back' button (must exist if param 'leftIcon0' is true) * @param {undefined|function} help function in the current game state that display correct answer */ add: function ( leftIcon0, leftIcon1, leftIcon2, rightIcon0, rightIcon1, state, help ) { let left_x = 10; let right_x = context.canvas.width - 50 - 10; this.iconsList = []; // 'Descriptive labels' for the navigation icons this.left_text = game.add.text(left_x, 73, '', textStyles.h4_brown); this.left_text.align = 'left'; this.right_text = game.add.text(right_x + 50, 73, '', textStyles.h4_brown); this.right_text.align = 'right'; // Left icons if (leftIcon0) { // Return to previous screen if (!state) { console.error( "Game error: You tried to add a 'back' icon without the 'state' parameter." ); } else { this.state = state; this.iconsList.push(game.add.image(left_x, 10, 'back')); left_x += 50; } } if (leftIcon1) { // Return to main menu screen this.iconsList.push(game.add.image(left_x, 10, 'menu')); left_x += 50; } if (leftIcon2) { // Shows solution to the game if (!help) { console.error( "Game error: You tried to add a 'game solution' icon without the 'help' parameter." ); } else { this.help = help; this.iconsList.push(game.add.image(left_x, 10, 'help')); left_x += 50; } } // Right icons if (rightIcon0) { // Turns game audio on/off this.audioIcon = game.add.sprite(right_x, 10, 'audio', 1); this.audioIcon.curFrame = audioStatus ? 0 : 1; this.iconsList.push(this.audioIcon); right_x -= 50; } if (rightIcon1) { // Return to select language screen this.iconsList.push(game.add.image(right_x, 10, 'language')); right_x -= 50; } }, /** * When 'back' icon is clicked go to this state * * @param {string} state name of the next state */ callState: function (state) { if (audioStatus) game.audio.popSound.play(); game.event.clear(self); game.state.start(state); }, /** * Called by mouse click event * * @param {number} x contains the mouse x coordinate * @param {number} y contains the mouse y coordinate */ onInputDown: function (x, y) { navigationIcons.iconsList.forEach((cur) => { if (game.math.isOverIcon(x, y, cur)) { const name = cur.name; switch (name) { case 'back': navigationIcons.callState(navigationIcons.state); break; case 'menu': navigationIcons.callState('menu'); break; case 'help': navigationIcons.help(); break; case 'language': navigationIcons.callState('lang'); break; case 'audio': if (audioStatus) { audioStatus = false; navigationIcons.audioIcon.curFrame = 1; } else { audioStatus = true; if (audioStatus) game.audio.popSound.play(); navigationIcons.audioIcon.curFrame = 0; } game.render.all(); break; default: console.error('Game error: error in navigation icon'); } } }); }, /** * Called by mouse move event * * @param {number} x contains the mouse x coordinate * @param {number} y contains the mouse y coordinate */ onInputOver: function (x, y) { let flag = false; navigationIcons.iconsList.forEach((cur) => { if (game.math.isOverIcon(x, y, cur)) { flag = true; let name = cur.name; switch (name) { case 'back': navigationIcons.left_text.name = game.lang.nav_back; break; case 'menu': navigationIcons.left_text.name = game.lang.nav_menu; break; case 'help': navigationIcons.left_text.name = game.lang.nav_help; break; case 'language': navigationIcons.right_text.name = game.lang.nav_lang; break; case 'audio': navigationIcons.right_text.name = game.lang.audio; break; } } }); if (!flag) { navigationIcons.left_text.name = ''; navigationIcons.right_text.name = ''; } else { document.body.style.cursor = 'pointer'; } }, }; /** * Sends game information to database * * @param {string} extraData player information for the current game */ const sendToDB = function (extraData) { // FOR MOODLE if (moodle) { if (self.result) moodleVar.hits[mapPosition - 1]++; else moodleVar.errors[mapPosition - 1]++; moodleVar.time[mapPosition - 1] += game.timer.elapsed; const url = iLMparameters.iLM_PARAM_ServerToGetAnswerURL; const grade = '' + getEvaluation(); const report = getAnswer(); const data = 'return_get_answer=1' + '&iLM_PARAM_ActivityEvaluation=' + encodeURIComponent(grade) + '&iLM_PARAM_ArchiveContent=' + encodeURIComponent(report); const init = { method: 'POST', body: data, headers: { 'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8', }, }; fetch(url, init) .then((response) => { if (response.ok) { if (debugMode) console.log('Processing...'); } else { console.error('Game error: Network response was not ok.'); } }) .catch((error) => { console.error( 'Game error: problem with fetch operation - ' + error.message + '.' ); }); } else { // Create some variables we need to send to our PHP file // Attention: this names must be compactible to data table (MySQL server) // @see php/save.php const data = 'line_ip=143.107.45.11' + // INSERT database server IP '&line_name=' + playerName + '&line_lang=' + langString + extraData; const url = 'php/save.php'; const init = { method: 'POST', body: data, headers: { 'Content-type': 'application/x-www-form-urlencoded' }, }; fetch(url, init) .then((response) => { if (response.ok) { if (debugMode) console.log('Processing...'); response.text().then((text) => { if (debugMode) { console.log(text); } }); } else { console.error('Game error: Network response was not ok.'); } }) .catch((error) => { console.error( 'Game error: problem with fetch operation - ' + error.message + '.' ); }); } }; let gameFrame = function () { let x = (y = 200); let width = context.canvas.width - 2 * x; let height = context.canvas.height - 2 * y; let rect = function () { game.add.geom.rect(x, y, width, height, colors.red, 2); }; let point = function (offsetW, offsetH) { for (let i = 0, y1 = y; i < 4; i++) { x1 = x; for (let j = 0; j < 7; j++) { game.add.geom.rect(x1, y1, 20, 20, undefined, 0, colors.red, 1); x1 += offsetW; } y1 += offsetH; } }; return { x, y, width, height, rect, point }; }; // For debug const debug = { grid: function () { const grid = 2; const h = 1920 / (grid + 0.5); const v = 1080 / (grid + 0.5); for (let i = 0; i < grid; i++) { game.add.geom.rect( h / 2 + i * h, 0, h / 2, 1080, '', 0, colors.blue, 0.3 ); game.add.geom.rect( 0, v / 2 + i * v, 1920, v / 2, '', 0, colors.blue, 0.3 ); } }, };