├── es ├── .bookignore ├── book.json ├── 02-javascript │ ├── 0201-poo │ │ ├── images │ │ │ ├── lotr-notes.jpg │ │ │ ├── space-invaders.jpg │ │ │ ├── batman-storyboard.jpg │ │ │ ├── mario-level-design.png │ │ │ ├── space-invaders-types.png │ │ │ ├── monobehaviour-flowchart.png │ │ │ ├── space-invaders-objects.png │ │ │ ├── space-invaders-enemy-api.png │ │ │ ├── space-invaders-hierarchy.png │ │ │ ├── space-invaders-constructors.png │ │ │ ├── space-invaders-enemy-state.png │ │ │ ├── space-invaders-object-diagram.png │ │ │ ├── space-invaders-constructor-detail.png │ │ │ ├── space-invaders-constructor-example.png │ │ │ └── space-invaders-hierarchy-constructor.png │ │ └── index.md │ ├── 02-practica │ │ ├── start-here │ │ │ ├── src │ │ │ │ ├── dice.js │ │ │ │ ├── utils.js │ │ │ │ ├── Options.js │ │ │ │ ├── TurnList.js │ │ │ │ ├── OptionsStack.js │ │ │ │ ├── items.js │ │ │ │ ├── Character.js │ │ │ │ ├── entities.js │ │ │ │ ├── CharactersView.js │ │ │ │ └── Battle.js │ │ │ ├── .eslintrc.json │ │ │ ├── gulpfile.js │ │ │ ├── package.json │ │ │ ├── spec │ │ │ │ ├── utils.js │ │ │ │ ├── samplelib.js │ │ │ │ ├── Options.js │ │ │ │ ├── TurnList.js │ │ │ │ ├── OptionsStack.js │ │ │ │ ├── CharacterView.js │ │ │ │ └── entities.js │ │ │ └── index.js │ │ ├── GUIDE.md │ │ ├── TDD.md │ │ └── index.md │ ├── 0202-modelo-de-datos │ │ └── images │ │ │ ├── space-invaders-enemy-api.png │ │ │ ├── space-invaders-enemy-state.png │ │ │ ├── space-invaders-hierarchy.png │ │ │ ├── space-invaders-constructor-example.png │ │ │ └── space-invaders-hierarchy-constructor.png │ ├── 02-ejercicios │ │ └── index.md │ └── 0203-modelo-de-ejecucion │ │ └── index.md ├── 03-javascript-en-el-navegador │ ├── 0301-dom │ │ └── images │ │ │ ├── search.png │ │ │ ├── dom_section.png │ │ │ ├── request_dance_step1.png │ │ │ └── request_dance_step2.png │ ├── 03-practica │ │ ├── images │ │ │ ├── death.png │ │ │ ├── end_game.png │ │ │ ├── parties.png │ │ │ ├── attack_info.png │ │ │ ├── defend_info.png │ │ │ ├── info_panel.png │ │ │ ├── screenshot.png │ │ │ ├── actions_menu.png │ │ │ ├── targets_menu.png │ │ │ ├── disabled_spells.png │ │ │ ├── render_chara_bug.png │ │ │ └── highlight_current_chara.png │ │ ├── start-here │ │ │ ├── index.html │ │ │ ├── styles.css │ │ │ └── js │ │ │ │ └── main.js │ │ ├── index.md │ │ └── guia.md │ ├── 03-ejercicios │ │ └── images │ │ │ ├── console.png │ │ │ ├── exercise01.png │ │ │ ├── exercise02.png │ │ │ ├── exercise03_step02.png │ │ │ ├── exercise03_inspector01.png │ │ │ ├── exercise03_inspector02.png │ │ │ ├── exercise03_querystring.png │ │ │ ├── exercise03_strikethrough.png │ │ │ ├── exercise03_selected_value.png │ │ │ └── exercise03_inspector_data_attr.png │ └── 0302-canvas │ │ ├── images │ │ ├── canvas_ex01.png │ │ ├── canvas_ex02.png │ │ ├── canvas_ex03.png │ │ └── canvas_ex04.png │ │ └── index.md ├── SUMMARY.md ├── README.md └── 01-intro │ └── index.md ├── LANGS.md ├── .gitignore ├── styles └── website.css ├── root └── index.html ├── README.md ├── scripts └── generate-zips.js ├── package.json └── LICENSE /es/.bookignore: -------------------------------------------------------------------------------- 1 | start-here 2 | -------------------------------------------------------------------------------- /es/book.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "es" 3 | } 4 | -------------------------------------------------------------------------------- /LANGS.md: -------------------------------------------------------------------------------- 1 | # Languages 2 | 3 | - [Castellano](es/) 4 | - [English](en/) 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | _book 4 | .DS_Store 5 | es/styles 6 | en/styles 7 | dist 8 | *.zip 9 | -------------------------------------------------------------------------------- /styles/website.css: -------------------------------------------------------------------------------- 1 | /*.chapter span { 2 | font-style: italic; 3 | }*/ 4 | .chapter a { 5 | text-decoration: underline; 6 | } 7 | -------------------------------------------------------------------------------- /es/02-javascript/0201-poo/images/lotr-notes.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/02-javascript/0201-poo/images/lotr-notes.jpg -------------------------------------------------------------------------------- /es/02-javascript/0201-poo/images/space-invaders.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/02-javascript/0201-poo/images/space-invaders.jpg -------------------------------------------------------------------------------- /es/02-javascript/0201-poo/images/batman-storyboard.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/02-javascript/0201-poo/images/batman-storyboard.jpg -------------------------------------------------------------------------------- /es/02-javascript/0201-poo/images/mario-level-design.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/02-javascript/0201-poo/images/mario-level-design.png -------------------------------------------------------------------------------- /es/02-javascript/02-practica/start-here/src/dice.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports.d100 = function () { 4 | return Math.floor(Math.random() * 100) + 1; 5 | }; 6 | -------------------------------------------------------------------------------- /es/02-javascript/0201-poo/images/space-invaders-types.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/02-javascript/0201-poo/images/space-invaders-types.png -------------------------------------------------------------------------------- /es/02-javascript/0201-poo/images/monobehaviour-flowchart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/02-javascript/0201-poo/images/monobehaviour-flowchart.png -------------------------------------------------------------------------------- /es/02-javascript/0201-poo/images/space-invaders-objects.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/02-javascript/0201-poo/images/space-invaders-objects.png -------------------------------------------------------------------------------- /es/03-javascript-en-el-navegador/0301-dom/images/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/03-javascript-en-el-navegador/0301-dom/images/search.png -------------------------------------------------------------------------------- /es/02-javascript/0201-poo/images/space-invaders-enemy-api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/02-javascript/0201-poo/images/space-invaders-enemy-api.png -------------------------------------------------------------------------------- /es/02-javascript/0201-poo/images/space-invaders-hierarchy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/02-javascript/0201-poo/images/space-invaders-hierarchy.png -------------------------------------------------------------------------------- /es/03-javascript-en-el-navegador/03-practica/images/death.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/03-javascript-en-el-navegador/03-practica/images/death.png -------------------------------------------------------------------------------- /es/02-javascript/0201-poo/images/space-invaders-constructors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/02-javascript/0201-poo/images/space-invaders-constructors.png -------------------------------------------------------------------------------- /es/02-javascript/0201-poo/images/space-invaders-enemy-state.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/02-javascript/0201-poo/images/space-invaders-enemy-state.png -------------------------------------------------------------------------------- /es/03-javascript-en-el-navegador/03-ejercicios/images/console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/03-javascript-en-el-navegador/03-ejercicios/images/console.png -------------------------------------------------------------------------------- /es/03-javascript-en-el-navegador/03-practica/images/end_game.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/03-javascript-en-el-navegador/03-practica/images/end_game.png -------------------------------------------------------------------------------- /es/03-javascript-en-el-navegador/03-practica/images/parties.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/03-javascript-en-el-navegador/03-practica/images/parties.png -------------------------------------------------------------------------------- /es/03-javascript-en-el-navegador/0301-dom/images/dom_section.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/03-javascript-en-el-navegador/0301-dom/images/dom_section.png -------------------------------------------------------------------------------- /es/02-javascript/0201-poo/images/space-invaders-object-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/02-javascript/0201-poo/images/space-invaders-object-diagram.png -------------------------------------------------------------------------------- /es/03-javascript-en-el-navegador/03-practica/images/attack_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/03-javascript-en-el-navegador/03-practica/images/attack_info.png -------------------------------------------------------------------------------- /es/03-javascript-en-el-navegador/03-practica/images/defend_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/03-javascript-en-el-navegador/03-practica/images/defend_info.png -------------------------------------------------------------------------------- /es/03-javascript-en-el-navegador/03-practica/images/info_panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/03-javascript-en-el-navegador/03-practica/images/info_panel.png -------------------------------------------------------------------------------- /es/03-javascript-en-el-navegador/03-practica/images/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/03-javascript-en-el-navegador/03-practica/images/screenshot.png -------------------------------------------------------------------------------- /es/03-javascript-en-el-navegador/0302-canvas/images/canvas_ex01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/03-javascript-en-el-navegador/0302-canvas/images/canvas_ex01.png -------------------------------------------------------------------------------- /es/03-javascript-en-el-navegador/0302-canvas/images/canvas_ex02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/03-javascript-en-el-navegador/0302-canvas/images/canvas_ex02.png -------------------------------------------------------------------------------- /es/03-javascript-en-el-navegador/0302-canvas/images/canvas_ex03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/03-javascript-en-el-navegador/0302-canvas/images/canvas_ex03.png -------------------------------------------------------------------------------- /es/03-javascript-en-el-navegador/0302-canvas/images/canvas_ex04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/03-javascript-en-el-navegador/0302-canvas/images/canvas_ex04.png -------------------------------------------------------------------------------- /es/02-javascript/0201-poo/images/space-invaders-constructor-detail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/02-javascript/0201-poo/images/space-invaders-constructor-detail.png -------------------------------------------------------------------------------- /es/03-javascript-en-el-navegador/03-ejercicios/images/exercise01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/03-javascript-en-el-navegador/03-ejercicios/images/exercise01.png -------------------------------------------------------------------------------- /es/03-javascript-en-el-navegador/03-ejercicios/images/exercise02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/03-javascript-en-el-navegador/03-ejercicios/images/exercise02.png -------------------------------------------------------------------------------- /es/03-javascript-en-el-navegador/03-practica/images/actions_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/03-javascript-en-el-navegador/03-practica/images/actions_menu.png -------------------------------------------------------------------------------- /es/03-javascript-en-el-navegador/03-practica/images/targets_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/03-javascript-en-el-navegador/03-practica/images/targets_menu.png -------------------------------------------------------------------------------- /es/02-javascript/0201-poo/images/space-invaders-constructor-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/02-javascript/0201-poo/images/space-invaders-constructor-example.png -------------------------------------------------------------------------------- /es/03-javascript-en-el-navegador/03-practica/images/disabled_spells.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/03-javascript-en-el-navegador/03-practica/images/disabled_spells.png -------------------------------------------------------------------------------- /es/03-javascript-en-el-navegador/03-practica/images/render_chara_bug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/03-javascript-en-el-navegador/03-practica/images/render_chara_bug.png -------------------------------------------------------------------------------- /es/03-javascript-en-el-navegador/0301-dom/images/request_dance_step1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/03-javascript-en-el-navegador/0301-dom/images/request_dance_step1.png -------------------------------------------------------------------------------- /es/03-javascript-en-el-navegador/0301-dom/images/request_dance_step2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/03-javascript-en-el-navegador/0301-dom/images/request_dance_step2.png -------------------------------------------------------------------------------- /es/02-javascript/0201-poo/images/space-invaders-hierarchy-constructor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/02-javascript/0201-poo/images/space-invaders-hierarchy-constructor.png -------------------------------------------------------------------------------- /es/02-javascript/0202-modelo-de-datos/images/space-invaders-enemy-api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/02-javascript/0202-modelo-de-datos/images/space-invaders-enemy-api.png -------------------------------------------------------------------------------- /es/02-javascript/0202-modelo-de-datos/images/space-invaders-enemy-state.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/02-javascript/0202-modelo-de-datos/images/space-invaders-enemy-state.png -------------------------------------------------------------------------------- /es/02-javascript/0202-modelo-de-datos/images/space-invaders-hierarchy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/02-javascript/0202-modelo-de-datos/images/space-invaders-hierarchy.png -------------------------------------------------------------------------------- /es/03-javascript-en-el-navegador/03-ejercicios/images/exercise03_step02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/03-javascript-en-el-navegador/03-ejercicios/images/exercise03_step02.png -------------------------------------------------------------------------------- /es/03-javascript-en-el-navegador/03-ejercicios/images/exercise03_inspector01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/03-javascript-en-el-navegador/03-ejercicios/images/exercise03_inspector01.png -------------------------------------------------------------------------------- /es/03-javascript-en-el-navegador/03-ejercicios/images/exercise03_inspector02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/03-javascript-en-el-navegador/03-ejercicios/images/exercise03_inspector02.png -------------------------------------------------------------------------------- /es/03-javascript-en-el-navegador/03-ejercicios/images/exercise03_querystring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/03-javascript-en-el-navegador/03-ejercicios/images/exercise03_querystring.png -------------------------------------------------------------------------------- /es/03-javascript-en-el-navegador/03-practica/images/highlight_current_chara.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/03-javascript-en-el-navegador/03-practica/images/highlight_current_chara.png -------------------------------------------------------------------------------- /es/03-javascript-en-el-navegador/03-ejercicios/images/exercise03_strikethrough.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/03-javascript-en-el-navegador/03-ejercicios/images/exercise03_strikethrough.png -------------------------------------------------------------------------------- /es/02-javascript/0202-modelo-de-datos/images/space-invaders-constructor-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/02-javascript/0202-modelo-de-datos/images/space-invaders-constructor-example.png -------------------------------------------------------------------------------- /es/02-javascript/0202-modelo-de-datos/images/space-invaders-hierarchy-constructor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/02-javascript/0202-modelo-de-datos/images/space-invaders-hierarchy-constructor.png -------------------------------------------------------------------------------- /es/03-javascript-en-el-navegador/03-ejercicios/images/exercise03_selected_value.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/03-javascript-en-el-navegador/03-ejercicios/images/exercise03_selected_value.png -------------------------------------------------------------------------------- /es/03-javascript-en-el-navegador/03-ejercicios/images/exercise03_inspector_data_attr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozdevs/js-for-gamedev/HEAD/es/03-javascript-en-el-navegador/03-ejercicios/images/exercise03_inspector_data_attr.png -------------------------------------------------------------------------------- /es/02-javascript/02-practica/start-here/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true 4 | }, 5 | "extends": "eslint:recommended", 6 | "rules": { 7 | "quotes": [ 8 | "error", 9 | "single" 10 | ], 11 | "max-len": ["error", 100], 12 | "max-statements": ["error", 40] 13 | } 14 | } -------------------------------------------------------------------------------- /es/02-javascript/02-practica/start-here/src/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | listToMap: function (list, getIndex) { 5 | return list.reduce(function (map, item) { 6 | map[getIndex(item)] = item; 7 | return map; 8 | }, {}); 9 | }, 10 | 11 | mapValues: function (map) { 12 | return Object.keys(map).map(function (key) { 13 | return map[key]; 14 | }); 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /root/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | JavaScript for game development 5 | 6 | 7 | 8 | 9 | 10 |

JavaScript for game development

11 | 12 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /es/02-javascript/02-practica/start-here/gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var jasmine = require('gulp-jasmine'); 3 | var eslint = require('gulp-eslint'); 4 | 5 | gulp.task('watch', ['test'], function () { 6 | gulp.watch('./src/**/*', ['test']); 7 | gulp.watch('./spec/**/*', ['test']); 8 | }); 9 | 10 | gulp.task('lint', function () { 11 | gulp.src('./src/**/*') 12 | .pipe(eslint()) 13 | .pipe(eslint.format()); 14 | }); 15 | 16 | gulp.task('test', ['lint'], function () { 17 | gulp.src('./spec/**/*') 18 | .pipe(jasmine({ includeStackTrace: true })); 19 | }); 20 | 21 | 22 | gulp.task('default', ['test']); 23 | -------------------------------------------------------------------------------- /es/02-javascript/02-practica/start-here/src/Options.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var EventEmitter = require('events').EventEmitter; 4 | 5 | function Options(group) { 6 | EventEmitter.call(this); 7 | this._group = typeof group === 'object' ? group : {}; 8 | } 9 | Options.prototype = Object.create(EventEmitter.prototype); 10 | Options.prototype.constructor = Options; 11 | 12 | Options.prototype.list = function () { 13 | return Object.keys(this._group); 14 | }; 15 | 16 | Options.prototype.get = function (id) { 17 | return this._group[id]; 18 | }; 19 | 20 | Options.prototype.select = function (id) { 21 | // Haz que se emita un evento cuando seleccionamos una opción. 22 | }; 23 | 24 | module.exports = Options; 25 | -------------------------------------------------------------------------------- /es/02-javascript/02-practica/start-here/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pvli2017-rpg-battle", 3 | "version": "1.0.0", 4 | "description": "Skeleton for practise 0 of PVLI course 2017", 5 | "main": "index.js", 6 | "scripts": { 7 | "bundle": "browserify export.js --standalone RPG > rpg.js", 8 | "test": "node ./node_modules/gulp/bin/gulp.js test", 9 | "watch": "node ./node_modules/gulp/bin/gulp.js watch" 10 | }, 11 | "author": "Salvador de la Puente González ", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "browserify": "^13.1.0", 15 | "gulp": "^3.9.1", 16 | "gulp-eslint": "^3.0.1", 17 | "gulp-jasmine": "^2.4.2", 18 | "mockery": "^2.0.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /es/02-javascript/02-practica/start-here/src/TurnList.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function TurnList() {} 4 | 5 | TurnList.prototype.reset = function (charactersById) { 6 | this._charactersById = charactersById; 7 | 8 | this._turnIndex = -1; 9 | this.turnNumber = 0; 10 | this.activeCharacterId = null; 11 | this.list = this._sortByInitiative(); 12 | }; 13 | 14 | TurnList.prototype.next = function () { 15 | // Haz que calcule el siguiente turno y devuelva el resultado 16 | // según la especificación. Recuerda que debe saltar los personajes 17 | // muertos. 18 | }; 19 | 20 | TurnList.prototype._sortByInitiative = function () { 21 | // Utiliza la función Array.sort(). ¡No te implementes tu propia 22 | // función de ordenación! 23 | }; 24 | 25 | module.exports = TurnList; 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JavaScript for game development 2 | 3 | A compilation of materials to learn JavaScript and make HTML5 games. 4 | You can also [find it online](https://mozdevs.github.io/js-for-gamedev/es/). 5 | 6 | ## Build the ebook 7 | 8 | This project uses **Node** and **npm scripts** to build and perform some tasks. You will need to have both Node and npm installed in your system. 9 | 10 | 1. Install third-party dependencies 11 | 12 | ```sh 13 | npm install 14 | ``` 15 | 2. Build the HTML ebooks in all available languages with: 16 | 17 | ```sh 18 | npm run build 19 | ``` 20 | 21 | ## Other tasks 22 | 23 | You can clean up the files generated by the build process with: 24 | 25 | ```sh 26 | npm run clean 27 | ``` 28 | 29 | You can publish the HTML ebooks via Github Pages with: 30 | 31 | ```sh 32 | npm run deploy 33 | ``` 34 | -------------------------------------------------------------------------------- /scripts/generate-zips.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var archiver = require('archiver'); 4 | var path = require('path'); 5 | var fs = require('fs'); 6 | 7 | // add here all the dirs that will be zipped 8 | const dirs = [ 9 | 'es/02-javascript/02-practica/start-here', 10 | 'es/03-javascript-en-el-navegador/03-practica/start-here' 11 | ]; 12 | 13 | dirs.forEach(function (src) { 14 | let zipFile = archiver('zip'); 15 | let filename = path.join(path.dirname(src), `${path.basename(src)}.zip`); 16 | 17 | let output = fs.createWriteStream(filename); 18 | output.on('close', function () { 19 | console.log(`${filename} - ${zipFile.pointer()} total bytes`); 20 | }); 21 | 22 | zipFile.pipe(output); 23 | zipFile.glob('**/*', { 24 | expand: true, 25 | cwd: src, 26 | dot: true 27 | }); 28 | zipFile.finalize(); 29 | }); 30 | -------------------------------------------------------------------------------- /es/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Índice 2 | 3 | 4 | - [El entorno de trabajo](01-intro/index.md) 5 | 6 | - JavaScript básico 7 | - [Programación orientada a objetos](02-javascript/0201-poo/index.md) 8 | - [El modelo de datos de JavaScript](02-javascript/0202-modelo-de-datos/index.md) 9 | - [El modelo de ejecución de JavaScript](02-javascript/0203-modelo-de-ejecucion/index.md) 10 | - [Ejercicios guiados](02-javascript/02-ejercicios/index.md) 11 | - [Práctica](02-javascript/02-practica/index.md) 12 | - [Guía](02-javascript/02-practica/GUIDE.md) 13 | - [TDD](02-javascript/02-practica/TDD.md) 14 | 15 | - JavaScript en el navegador 16 | 17 | - [Navegador y DOM](03-javascript-en-el-navegador/0301-dom/index.md) 18 | - [Canvas](03-javascript-en-el-navegador/0302-canvas/index.md) 19 | - [Ejercicios guiados](03-javascript-en-el-navegador/03-ejercicios/index.md) 20 | - [Práctica](03-javascript-en-el-navegador/03-practica/index.md) 21 | - [Guía](03-javascript-en-el-navegador/03-practica/guia.md) 22 | -------------------------------------------------------------------------------- /es/02-javascript/02-practica/start-here/src/OptionsStack.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Options = require('./Options'); 3 | 4 | function OptionsStack() { 5 | this._stack = []; 6 | Object.defineProperty(this, 'current', { 7 | get: function () { 8 | return this._stack[this._stack.length - 1]; 9 | }, 10 | set: function (v) { 11 | if (!(v instanceof Options)) { 12 | v = new Options(v); 13 | } 14 | return this._stack.push(v); 15 | } 16 | }); 17 | } 18 | 19 | OptionsStack.prototype.select = function (id) { 20 | // Redirige el comando al último de la pila. 21 | }; 22 | 23 | OptionsStack.prototype.list = function () { 24 | // Redirige el comando al último de la pila. 25 | }; 26 | 27 | OptionsStack.prototype.get = function (id) { 28 | return this.current.get(id); 29 | }; 30 | 31 | OptionsStack.prototype.cancel = function () { 32 | this._stack.pop(); 33 | }; 34 | 35 | OptionsStack.prototype.clear = function () { 36 | this._stack = []; 37 | }; 38 | 39 | module.exports = OptionsStack; 40 | -------------------------------------------------------------------------------- /es/02-javascript/02-practica/start-here/spec/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Utils module', function () { 4 | var utils = require('../src/utils'); 5 | 6 | it('has a listToMap() to convert from a list to an object choosing the key. ', 7 | function () { 8 | var list = [ 9 | { name: 'a', hp: 1 }, 10 | { name: 'b', hp: 2 }, 11 | { name: 'c', hp: 3 } 12 | ]; 13 | expect(utils.listToMap(list, useName)).toEqual({ 14 | a: { name: 'a', hp: 1 }, 15 | b: { name: 'b', hp: 2 }, 16 | c: { name: 'c', hp: 3 } 17 | }); 18 | 19 | function useName(item) { return item.name; } 20 | }); 21 | 22 | it('has mapValues() returning a list of the values of a map', function () { 23 | var map = { 24 | a: { name: 'a', hp: 1 }, 25 | b: { name: 'b', hp: 2 }, 26 | c: { name: 'c', hp: 3 } 27 | }; 28 | expect(utils.mapValues(map)).toEqual([ 29 | { name: 'a', hp: 1 }, 30 | { name: 'b', hp: 2 }, 31 | { name: 'c', hp: 3 } 32 | ]); 33 | }); 34 | 35 | }); 36 | -------------------------------------------------------------------------------- /es/02-javascript/02-practica/start-here/src/items.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function Item(name, effect) { 4 | this.name = name; 5 | this.effect = effect; 6 | } 7 | 8 | function Weapon(name, damage, extraEffect) { 9 | extraEffect = extraEffect || new Effect({}); 10 | // Haz que Weapon sea subtipo de Item haciendo que llame al constructor de 11 | // de Item. 12 | } 13 | // Termina de implementar la herencia haciendo que la propiedad prototype de 14 | // Item sea el prototipo de Weapon.prototype y recuerda ajustar el constructor. 15 | 16 | function Scroll(name, cost, effect) { 17 | Item.call(this, name, effect); 18 | this.cost = cost; 19 | } 20 | Scroll.prototype = Object.create(Item.prototype); 21 | Scroll.prototype.constructor = Scroll; 22 | 23 | Scroll.prototype.canBeUsed = function (mp) { 24 | // El pergamino puede usarse si los puntos de maná son superiores o iguales 25 | // al coste del hechizo. 26 | }; 27 | 28 | function Effect(variations) { 29 | // Copia las propiedades que se encuentran en variations como propiedades de 30 | // este objeto. 31 | } 32 | 33 | module.exports = { 34 | Item: Item, 35 | Weapon: Weapon, 36 | Scroll: Scroll, 37 | Effect: Effect 38 | }; 39 | -------------------------------------------------------------------------------- /es/02-javascript/02-practica/start-here/src/Character.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var dice = require('./dice'); 3 | 4 | function Character(name, features) { 5 | features = features || {}; 6 | this.name = name; 7 | // Extrae del parámetro features cada característica y alamacénala en 8 | // una propiedad. 9 | this._mp = features.mp || 0; 10 | this.maxMp = features.maxMp || this._mp; 11 | } 12 | 13 | Character.prototype._immuneToEffect = ['name', 'weapon']; 14 | 15 | Character.prototype.isDead = function () { 16 | // Rellena el cuerpo de esta función 17 | }; 18 | 19 | Character.prototype.applyEffect = function (effect, isAlly) { 20 | // Implementa las reglas de aplicación de efecto para modificar las 21 | // características del personaje. Recuerda devolver true o false según 22 | // si el efecto se ha aplicado o no. 23 | }; 24 | 25 | Object.defineProperty(Character.prototype, 'mp', { 26 | get: function () { 27 | return this._mp; 28 | }, 29 | set: function (newValue) { 30 | this._mp = Math.max(0, Math.min(newValue, this.maxMp)); 31 | } 32 | }); 33 | 34 | Object.defineProperty(Character.prototype, 'hp', { 35 | // Puedes usar la mísma ténica que antes para mantener el valor de hp en el 36 | // rango correcto. 37 | }); 38 | 39 | // Puedes hacer algo similar a lo anterior para mantener la defensa entre 0 y 40 | // 100. 41 | 42 | module.exports = Character; 43 | -------------------------------------------------------------------------------- /es/02-javascript/02-practica/start-here/src/entities.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var items = require('./items'); 3 | var Character = require('./Character'); 4 | 5 | var Effect = items.Effect; 6 | 7 | 8 | var lib = module.exports = { 9 | Item: items.Item, 10 | Weapon: items.Weapon, 11 | Scroll: items.Scroll, 12 | Effect: Effect, 13 | Character: Character, 14 | 15 | weapons: { 16 | get sword() { 17 | return new items.Weapon('sword', 25); 18 | }, 19 | get wand() { 20 | return new items.Weapon('wand', 5); 21 | }, 22 | // Implementa los colmillos y el pseudópodo 23 | }, 24 | 25 | characters: { 26 | 27 | get heroTank() { 28 | return new Character('Tank', { 29 | initiative: 10, 30 | weapon: lib.weapons.sword, 31 | defense: 70, 32 | hp: 80, 33 | mp: 30 34 | }); 35 | }, 36 | 37 | // Implementa el mago 38 | 39 | get monsterSkeleton() { 40 | return new Character('skeleton', { 41 | initiative: 9, 42 | defense: 50, 43 | weapon: lib.weapons.sword, 44 | hp: 100, 45 | mp: 0 46 | }); 47 | }, 48 | 49 | // Implementa el limo y el murciélago 50 | }, 51 | 52 | scrolls: { 53 | 54 | get health() { 55 | return new items.Scroll('health', 10, new Effect({ hp: 25 })); 56 | }, 57 | 58 | // Implementa la bola de fuego 59 | 60 | } 61 | }; 62 | -------------------------------------------------------------------------------- /es/02-javascript/02-practica/start-here/spec/samplelib.js: -------------------------------------------------------------------------------- 1 | var entities = require('../src/entities'); 2 | 3 | var Character = entities.Character; 4 | var Weapon = entities.Weapon; 5 | var Scroll = entities.Scroll; 6 | var Effect = entities.Effect; 7 | 8 | var lib = module.exports = { 9 | weapons: { 10 | get sword() { return new Weapon('Iron sword', 25); }, 11 | get wand() { return new Weapon('Wood wand', 5, { mp: -5 }); }, 12 | get claws() { return new Weapon('Claws', 15); } 13 | }, 14 | 15 | characters: { 16 | get heroTank() { 17 | return new Character('Tank', { 18 | initiative: 10, 19 | weapon: lib.weapons.sword, 20 | defense: 70, 21 | hp: 80, 22 | maxHp: 80, 23 | mp: 0, 24 | maxMp: 0 25 | }); 26 | }, 27 | 28 | get heroWizard() { 29 | return new Character('Wizz', { 30 | initiative: 4, 31 | weapon: lib.weapons.wand, 32 | defense: 50, 33 | hp: 40, 34 | maxHp: 40, 35 | mp: 100, 36 | maxMp: 100 37 | }); 38 | }, 39 | 40 | get fastEnemy() { 41 | return new Character('Fasty', { 42 | initiative: 30, 43 | weapon: lib.weapons.claws, 44 | defense: 40, 45 | hp: 30, 46 | maxHp: 30, 47 | mp: 100, 48 | maxMp: 100 49 | }); 50 | } 51 | }, 52 | 53 | scrolls: { 54 | get health() { 55 | return new Scroll('Health', 10, new Effect({ hp: 25 })); 56 | }, 57 | get fire() { 58 | return new Scroll('Fire', 30, new Effect({ hp: -25 })); 59 | } 60 | } 61 | }; 62 | -------------------------------------------------------------------------------- /es/02-javascript/02-practica/start-here/spec/Options.js: -------------------------------------------------------------------------------- 1 | describe('Options type', function () { 2 | 'use strict'; 3 | 4 | var Options = require('../src/Options'); 5 | 6 | var options; 7 | 8 | var dataFor = { 9 | itemA: {}, 10 | itemB: {}, 11 | itemC: {} 12 | }; 13 | 14 | var items = { 15 | itemA: dataFor.itemA, 16 | itemB: dataFor.itemB, 17 | itemC: dataFor.itemC 18 | }; 19 | 20 | beforeEach(function () { 21 | options = new Options(items); 22 | }); 23 | 24 | it('allows the empty menu.', function () { 25 | options = new Options(); 26 | expect(options.list()).toEqual([]); 27 | }); 28 | 29 | it('list all the options.', function () { 30 | expect(options.list()) 31 | .toEqual(jasmine.arrayContaining(Object.keys(items))); 32 | }); 33 | 34 | it('recovers data associated to the menu item.', function () { 35 | expect(options.get('itemA')).toBe(dataFor['itemA']); 36 | }); 37 | 38 | xit('emits an event when selecting an entry.', function (done) { 39 | var entryId = 'itemA'; 40 | options.on('chose', function (id, data) { 41 | expect(id).toBe(entryId); 42 | expect(data).toBe(dataFor[entryId]); 43 | done(); 44 | }); 45 | options.select(entryId); 46 | }); 47 | 48 | xit('emits an error event when the entry does not exist.', function (done) { 49 | var entryId = 'xxxx'; 50 | options.on('choseError', function (reason, id) { 51 | expect(reason).toBe('option-does-not-exist'); 52 | expect(id).toBe(entryId); 53 | done(); 54 | }); 55 | options.select(entryId); 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "js-for-gamedev", 3 | "version": "1.0.0", 4 | "description": "Learning guide about JavaScript for game development", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "styles": "mkdirp ./es/styles && cpr styles/website.css es/styles/ -o", 9 | "serve:es": "npm run styles && gitbook serve ./es", 10 | "zip": "node scripts/generate-zips.js", 11 | "ebook:es": "mkdirp dist/es/ebook && npm run ebook:es:pdf && npm run ebook:es:epub", 12 | "ebook:es:pdf": "gitbook pdf ./es ./dist/es/ebook/js-for-gamedev.pdf", 13 | "ebook:es:epub": "gitbook epub ./es ./dist/es/ebook/js-for-gamedev.epub", 14 | "gitbook:es": "gitbook build ./es dist/es", 15 | "build:es": "npm run styles && npm run gitbook:es", 16 | "build:root": "mkdirp dist && cpr root dist -u -o", 17 | "build": "npm run zip && npm run build:root && npm run build:es", 18 | "clean": "rimraf dist ./es/styles es/**/*.zip", 19 | "deploy": "gh-pages -d dist" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "git+https://github.com/mozdevs/js-for-gamedev.git" 24 | }, 25 | "keywords": [ 26 | "learning", 27 | "tutorial", 28 | "javascript", 29 | "game", 30 | "dev" 31 | ], 32 | "license": "SEE LICENSE IN LICENSE.md", 33 | "bugs": { 34 | "url": "https://github.com/mozdevs/js-for-gamedev/issues" 35 | }, 36 | "homepage": "https://github.com/mozdevs/js-for-gamedev#readme", 37 | "private": true, 38 | "dependencies": { 39 | "archiver": "^1.2.0", 40 | "cpr": "^2.0.0", 41 | "gh-pages": "^0.12.0", 42 | "gitbook-cli": "^2.3.0", 43 | "mkdirp": "^0.5.1", 44 | "rimraf": "^2.5.4" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /es/02-javascript/02-practica/start-here/src/CharactersView.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function CharactersView() { 4 | this._views = {}; 5 | } 6 | 7 | CharactersView.prototype._visibleFeatures = [ 8 | 'name', 9 | 'party', 10 | 'initiative', 11 | 'defense', 12 | 'hp', 13 | 'mp', 14 | 'maxHp', 15 | 'maxMp' 16 | ]; 17 | 18 | CharactersView.prototype.all = function () { 19 | return Object.keys(this._views).reduce(function (copy, id) { 20 | copy[id] = this._views[id]; 21 | return copy; 22 | }.bind(this), {}); 23 | }; 24 | 25 | CharactersView.prototype.allFrom = function (party) { 26 | return Object.keys(this._views).reduce(function (copy, id) { 27 | if (this._views[id].party === party) { 28 | copy[id] = this._views[id]; 29 | } 30 | return copy; 31 | }.bind(this), {}); 32 | }; 33 | 34 | CharactersView.prototype.get = function (id) { 35 | return this._views[id] || null; 36 | }; 37 | 38 | CharactersView.prototype.set = function (characters) { 39 | this._views = Object.keys(characters).reduce(function (views, id) { 40 | views[id] = this._getViewFor(characters[id]); 41 | return views; 42 | }.bind(this), {}); 43 | }; 44 | 45 | CharactersView.prototype._getViewFor = function (character) { 46 | var view = {}; 47 | // Usa la lista de características visibles y Object.defineProperty() para 48 | // devolver un objeto de JavaScript con las características visibles pero 49 | // no modificables. 50 | Object.defineProperty(view, 'cada feature', { 51 | get: function () { 52 | // ¿Cómo sería este getter para reflejar la propiedad del personaje? 53 | }, 54 | set: function (value) { 55 | // ¿Y este setter para ignorar cualquier acción? 56 | }, 57 | enumerable: true 58 | }); 59 | // Acuérdate de devolver el objeto. 60 | }; 61 | 62 | module.exports = CharactersView; 63 | -------------------------------------------------------------------------------- /es/03-javascript-en-el-navegador/03-practica/start-here/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Batalla RPG 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |

RPG Battle

14 |
15 |
16 |
17 |

Heroes

18 |
    19 |
    20 |
    21 |

    Monsters

    22 |
      23 |
      24 |
      25 | 26 |
      27 |

      Fight!

      28 |
      29 | 30 |
      31 |
      32 |
        33 | 34 |

        35 |
        36 | 37 |
        38 |
          39 | 40 |

          or 41 | Cancel 42 |

          43 |
          44 | 45 |
          46 |
            47 | 48 |

            or 49 | Cancel 50 |

            51 |
            52 |
            53 |
            54 | 55 | 56 | -------------------------------------------------------------------------------- /es/03-javascript-en-el-navegador/03-practica/start-here/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | line-height: 1.2; 3 | font-family: monospace; 4 | font-size: 18px; 5 | } 6 | 7 | main { 8 | max-width: 900px; 9 | margin-left: auto; 10 | margin-right: auto; 11 | } 12 | 13 | body > h1 { 14 | text-align: center; 15 | } 16 | 17 | p, li { 18 | margin: 0.5em 0; 19 | } 20 | 21 | fieldset ul { 22 | padding-left: 0; 23 | } 24 | 25 | fieldset li { 26 | list-style-type: none; 27 | } 28 | 29 | button, input, select { 30 | font-family: monospace; 31 | font-size: 18px; 32 | } 33 | 34 | .parties { 35 | display: flex; 36 | justify-content: space-between; 37 | } 38 | 39 | .party { 40 | width: calc(50% - 2em); 41 | margin-left: 2em; 42 | } 43 | 44 | .party:first-child { 45 | margin-left: 0; 46 | } 47 | 48 | .party h1 { 49 | border-bottom: 1px solid #000; 50 | } 51 | 52 | .party li { 53 | list-style: none; 54 | text-transform: capitalize; 55 | position: relative; 56 | } 57 | 58 | .party ul { 59 | padding-left: 0; 60 | } 61 | 62 | .dead { 63 | text-decoration: line-through; 64 | } 65 | 66 | .active { 67 | background: rgba(255, 255, 0, 0.5); 68 | } 69 | 70 | .active::before { 71 | content: "► "; 72 | display: block; 73 | position: absolute; 74 | left: -1em; 75 | } 76 | 77 | .info { 78 | padding: 1em; 79 | background: #efefef; 80 | box-sizing: border-box; 81 | } 82 | 83 | .info strong { 84 | text-transform: capitalize; 85 | } 86 | 87 | .battle-menu .choices { 88 | text-transform: capitalize; 89 | padding-left: 0; 90 | } 91 | 92 | .battle-menu .choices li { 93 | list-style-type: none; 94 | } 95 | 96 | .battle-menu button { 97 | box-sizing: border-box; 98 | padding: 1em; 99 | } 100 | -------------------------------------------------------------------------------- /es/02-javascript/02-practica/start-here/spec/TurnList.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | xdescribe('The TurnList type', function () { 4 | var TurnList = require('../src/TurnList'); 5 | var turnList; 6 | var characters; 7 | 8 | function FakeCharacter(party, inititative, isDead) { 9 | this.party = party; 10 | this.initiative = inititative; 11 | this._isDead = isDead; 12 | } 13 | FakeCharacter.prototype.isDead = function () { 14 | return this._isDead; 15 | }; 16 | 17 | beforeEach(function () { 18 | characters = { 19 | a: new FakeCharacter('heroes', 1), 20 | b: new FakeCharacter('heroes', 5), 21 | c: new FakeCharacter('monsters', 10) 22 | }; 23 | 24 | turnList = new TurnList(); 25 | turnList.reset(characters); 26 | }); 27 | 28 | it('accepts a set of characters and sort them by inititative.', function () { 29 | expect(turnList.turnNumber).toBe(0); 30 | expect(turnList.activeCharacterId).toBe(null); 31 | expect(turnList.list).toEqual(['c', 'b', 'a']); 32 | }); 33 | 34 | it('accepts a set of characters and sort them by inititative.', function () { 35 | var turn = turnList.next(); 36 | 37 | expect(turn.number).toBe(1); 38 | expect(turn.party).toBe(characters.c.party); 39 | expect(turn.activeCharacterId).toBe('c'); 40 | 41 | expect(turnList.turnNumber).toBe(1); 42 | expect(turnList.activeCharacterId).toBe('c'); 43 | }); 44 | 45 | it('ignore all dead characters', function () { 46 | characters.c._isDead = true; 47 | characters.b._isDead = true; 48 | var turn = turnList.next(); 49 | 50 | expect(turn.number).toBe(1); 51 | expect(turn.party).toBe(characters.a.party); 52 | expect(turn.activeCharacterId).toBe('a'); 53 | 54 | expect(turnList.turnNumber).toBe(1); 55 | expect(turnList.activeCharacterId).toBe('a'); 56 | }); 57 | 58 | it('starts over when reaching the end of the list.', function () { 59 | turnList.next(); 60 | turnList.next(); 61 | turnList.next(); 62 | var turn = turnList.next(); 63 | 64 | expect(turn.number).toBe(4); 65 | expect(turn.party).toBe(characters.c.party); 66 | expect(turn.activeCharacterId).toBe('c'); 67 | 68 | expect(turnList.turnNumber).toBe(4); 69 | expect(turnList.activeCharacterId).toBe('c'); 70 | }); 71 | }); 72 | -------------------------------------------------------------------------------- /es/README.md: -------------------------------------------------------------------------------- 1 | # JavaScript para el desarrollo de videojuegos 2 | 3 | Esta es una guía de introducción a **JavaScript**, y está orientada al 4 | desarrollo de **videojuegos HTML5**. 5 | 6 | Esta basada en unos materiales que desarrollamos en una colaboración con la 7 | Universidad Complutense de Madrid para la asignatura de _Programación de 8 | videojuegos con lenguajes interpretados_. Puedes acceder a los materiales 9 | originales de la asignatura a través de 10 | [este repositorio en Github](https://github.com/clnznr/pvli2017). 11 | 12 | El código fuente de esta guía también está 13 | [publicado en Github](https://github.com/mozdevs/js-for-gamedev/). Si encuentras 14 | una errata o quieres sugerir algún cambio, por favor háznoslo saber 15 | [abriendo un ticket](https://github.com/mozdevs/js-for-gamedev/issues). 16 | 17 | ## Videojuegos en la Web 18 | 19 | La llegada de **HTML5** y sus tecnologías asociadas expandió enormemente las 20 | capacidades de la Web como **plataforma de videojuegos**. Hasta entonces, la 21 | mayoría de juegos web requerían un plugin externo –como Flash o Unity Player–, 22 | pero hoy ya no es necesario y los juegos HTML5 se ejecutan en el navegador de 23 | forma transparente. 24 | 25 | La Web nos ofrece **API** de gráficos 2D y 3D (esta última, basada en el estándar 26 | OpenGL ES), de reproducción y sintetización de audio, de acceso a múltiples 27 | métodos de entrada (_gamepads_, eventos de _touch_, giroscopios…), etc. En 28 | definitiva, todo lo que necesitamos para desarrollar videojuegos. 29 | 30 | Existen multitud de **motores y herramientas** para crear videojuegos HTML5. 31 | Algunos de los motores más populares, como Unity, Unreal o Game Maker, ya 32 | incluyen un exportador HTML5. También existen motores o frameworks específicos 33 | para la web, en los que podemos desarrollar con JavaScript, como Phaser o 34 | PlayCanvas. 35 | 36 | El objetivo de esta guía es proporcionar una base de conocimientos JavaScript 37 | para que puedas desarrollar videojuegos web utilizando librerías o motores web 38 | existentes. 39 | 40 | ## ¿A quién está dirigida esta guía? 41 | 42 | - A cualquiera con interés en el desarrollo de videojuegos y que ya tenga unos 43 | **conocimientos mínimos de programación** (cualquier lenguaje sirve, como Lua, 44 | C o Python): variables, bucles, funciones, condiciones, etc. 45 | 46 | - A programadores de videojuegos que quieran desarrollar videojuegos web con 47 | JavaScript. 48 | 49 | - A desarrolladores web que quieran aprender los fundamentos de la programación 50 | orientada a objetos con JavaScript. 51 | 52 | --- 53 | 54 | **Importante**: 55 | 56 | Se recomienda leer todos los artículos de una unidad, así como hacer los ejercicios guiados _antes_ de realizar la práctica propuesta. 57 | -------------------------------------------------------------------------------- /es/02-javascript/02-practica/start-here/spec/OptionsStack.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var mockery = require('mockery'); 4 | 5 | xdescribe('OptionsStack type', function () { 6 | var OptionsStack; 7 | var optionsStack; 8 | 9 | var MockOptions = jasmine.createSpy('MockOptions'); 10 | MockOptions.prototype.select = function() {}; 11 | MockOptions.prototype.list = function() {}; 12 | MockOptions.prototype.get = function() {}; 13 | 14 | beforeAll(function () { 15 | mockery.registerMock('./Options', MockOptions); 16 | mockery.enable({ 17 | useCleanCache: true, 18 | warnOnUnregistered: false 19 | }); 20 | 21 | OptionsStack = require('../src/OptionsStack'); 22 | }); 23 | 24 | afterAll(function () { 25 | mockery.disable(); 26 | mockery.deregisterMock('./Options'); 27 | }); 28 | 29 | beforeEach(function () { 30 | spyOn(MockOptions.prototype, 'select'); 31 | spyOn(MockOptions.prototype, 'list'); 32 | spyOn(MockOptions.prototype, 'get'); 33 | optionsStack = new OptionsStack(); 34 | }); 35 | 36 | it('adds an options group by assigning a group to current.', function () { 37 | var group = new MockOptions(); 38 | optionsStack.current = group; 39 | expect(optionsStack.current).toBe(group); 40 | }); 41 | 42 | it('adds an options group by assigning a object to current.', function () { 43 | var group = { a: 1, b: 2}; 44 | optionsStack.current = group; 45 | expect(MockOptions).toHaveBeenCalledWith(group); 46 | expect(optionsStack.current).toEqual(jasmine.any(MockOptions)); 47 | }); 48 | 49 | it('returns to the previous options group when calling cancel().', 50 | function () { 51 | var group = new MockOptions(); 52 | var group2 = new MockOptions(); 53 | optionsStack.current = group; 54 | optionsStack.current = group2; 55 | expect(optionsStack.current).toBe(group2); 56 | optionsStack.cancel(); 57 | expect(optionsStack.current).toBe(group); 58 | }); 59 | 60 | it('proxies get() to the latest options group.', function () { 61 | var group = new MockOptions(); 62 | optionsStack.current = group; 63 | optionsStack.get(); 64 | expect(MockOptions.prototype.get).toHaveBeenCalled(); 65 | }); 66 | 67 | xit('proxies select() to the latest options group.', function () { 68 | var group = new MockOptions(); 69 | optionsStack.current = group; 70 | optionsStack.select('x'); 71 | expect(MockOptions.prototype.select).toHaveBeenCalledWith('x'); 72 | }); 73 | 74 | xit('proxies list() to the latest options group.', function () { 75 | var group = new MockOptions(); 76 | optionsStack.current = group; 77 | optionsStack.list(); 78 | expect(MockOptions.prototype.list).toHaveBeenCalled(); 79 | }); 80 | }); 81 | -------------------------------------------------------------------------------- /es/02-javascript/02-practica/start-here/spec/CharacterView.js: -------------------------------------------------------------------------------- 1 | var samples = require('./samplelib'); 2 | 3 | xdescribe('CharactesView type', function () { 4 | 'use strict'; 5 | 6 | var CharactersView = require('../src/CharactersView'); 7 | 8 | var charactersView; 9 | 10 | var heroTank = samples.characters.heroTank; 11 | var heroWizard = samples.characters.heroWizard; 12 | 13 | var visibleFeatures = [ 14 | 'name', 15 | 'party', 16 | 'initiative', 17 | 'defense', 18 | 'hp', 19 | 'mp', 20 | 'maxHp', 21 | 'maxMp' 22 | ]; 23 | 24 | beforeAll(function () { 25 | heroTank.party = 'teamA'; 26 | heroWizard.party = 'teamB'; 27 | }); 28 | 29 | beforeEach(function () { 30 | charactersView = new CharactersView(); 31 | charactersView.set({ 32 | Tank: heroTank, 33 | Wizz: heroWizard 34 | }); 35 | }); 36 | 37 | it('shows only the visible features and includes id.', function () { 38 | var heroTankView = charactersView.get('Tank'); 39 | var featuresCount = Object.keys(heroTankView).length ; 40 | 41 | expect(featuresCount).toBe(visibleFeatures.length); 42 | visibleFeatures.forEach(function (feature) { 43 | expect(heroTankView[feature]).toEqual(heroTank[feature]); 44 | }); 45 | }); 46 | 47 | it('list all characters.', function () { 48 | var heroTankView = charactersView.get('Tank'); 49 | var heroWizardView = charactersView.get('Wizz'); 50 | expect(charactersView.all()) 51 | .toEqual({ 52 | Tank: heroTankView, 53 | Wizz: heroWizardView 54 | }); 55 | }); 56 | 57 | it('list all characters by party', function () { 58 | var heroTankView = charactersView.get('Tank'); 59 | var heroWizardView = charactersView.get('Wizz'); 60 | expect(charactersView.allFrom('teamA')) 61 | .toEqual({ 62 | Tank: heroTankView 63 | }); 64 | expect(charactersView.allFrom('teamB')) 65 | .toEqual({ 66 | Wizz: heroWizardView 67 | }); 68 | }); 69 | 70 | it('does not allow to modify character\'s features', function () { 71 | var heroTankView = charactersView.get('Tank'); 72 | 73 | visibleFeatures.forEach(function (feature) { 74 | var expectedValue = heroTankView[feature]; 75 | heroTankView[feature] = expectedValue + 1; 76 | expect(heroTankView[feature]).toEqual(expectedValue); 77 | }); 78 | }); 79 | 80 | it('does not modify the original character.', function () { 81 | var heroTankView = charactersView.get('Tank'); 82 | 83 | var expectedValues = visibleFeatures.reduce(function (values, feature) { 84 | values[feature] = heroTank[feature]; 85 | return values; 86 | }, {}); 87 | 88 | visibleFeatures.forEach(function (feature) { 89 | heroTankView[feature] += 1; 90 | expect(heroTank[feature]).toEqual(expectedValues[feature]); 91 | }); 92 | }); 93 | }); 94 | -------------------------------------------------------------------------------- /es/01-intro/index.md: -------------------------------------------------------------------------------- 1 | # El entorno de trabajo 2 | 3 | ## La línea de comandos 4 | 5 | La línea de comandos es una aplicación para comunicarse con el sistema operativo 6 | a través de comandos escritos. Gran parte de las herramientas empleadas en el 7 | desarrollo web son aplicaciones de consola, es decir, aplicaciones cuya interfaz 8 | está pensada para usarse a través de una línea de comandos. 9 | 10 | En general, no necesitas instalar líneas de comandos de otros desarrolladores 11 | porque los sistemas operativos incluyen las suyas. Consulta una de estas guías 12 | acerca de cómo lanzar estas aplicaciones de acuerdo a tu sistema operativo: 13 | 14 | - En caso de trabajar sobre [Windows](http://www.howtogeek.com/235101/10-ways-to-open-the-command-prompt-in-windows-10/). 15 | 16 | - En caso de trabajar sobre [Mac OS](http://blog.teamtreehouse.com/introduction-to-the-mac-os-x-command-line). 17 | 18 | - En caso de trabajar sobre [Linux](http://askubuntu.com/questions/183775/how-do-i-open-a-terminal). 19 | 20 | Conviene familiarizarse con los comandos que ofrecen los distintos sistemas 21 | operativos. Linux y Mac OS ofrecen un conjunto de utilidades muy similar basado 22 | en el estándar [POSIX](https://en.wikipedia.org/wiki/POSIX). Sin embargo, 23 | el conjunto de comandos de Windows es radicalmente distinto. 24 | 25 | De acuerdo a tu sistema operativo, utiliza una de las siguientes guías para 26 | aprender los comandos más normales en cada entorno: 27 | 28 | - Si tu sistema operativo es Windows, utiliza la guía de http://dosprompt.info/ 29 | 30 | - Si es Mac OS o Linux, utiliza la guía de [_The Linux Information 31 | Project_](http://www.linfo.org/command_line_lesson_1.html) 32 | 33 | También puedes hacer que Windows ofrezca las mismas utilidades que Linux o Mac 34 | OS instalando [Cygwin](https://www.cygwin.com/). 35 | 36 | ## Node 37 | 38 | JavaScript es el lenguaje de programación de la Web y se ejecuta, 39 | principalmente, dentro de una máquina virtual integrada en un navegador. 40 | No obstante, este no es el único entorno en el que podemos utilizar JavaScript. 41 | 42 | Node es un intérprete JavaScript basado en V8, la máquina virtual del navegador 43 | Chrome, de Google. Node es ampliamente utilizado en el desarrollo de 44 | aplicaciones en el lado del servidor y será en Node que aprenderemos JavaScript 45 | para enfatizar su independencia del navegador. 46 | 47 | Para realizar los ejercicios y prácticas propuestos en esta guía, recomendamos 48 | [instalar cualquier versión de Node 6](https://nodejs.org/en/). 49 | 50 | ## El editor de texto 51 | 52 | Conviene tener en cuenta que para comenzar a programar en JavaScript sólo 53 | necesitas un editor de texto plano. Sin embargo, es recomendable elegir un 54 | editor con algunas características avanzadas que nos permita ser más 55 | productivos. 56 | 57 | **Nota**: es importante no confundir un editor de texto plano (como Notepad), 58 | con un procesador de texto (como Microsoft Word). 59 | 60 | El editor [Atom](https://atom.io/) es muy popular entre desarrolladores web. Es 61 | una buena opción por ser gratuito, de código abierto y extensible (y, además, 62 | ha sido programado con JavaScript). 63 | 64 | Si optas por Atom, te recomendamos que instales el siguiente _add-on_: 65 | [`linter-jshint`](https://github.com/AtomLinter/linter-jshint). Es un _linter_, 66 | un software que analizará tu código JavaScript. No sólo te corregirá el estilo 67 | (por ejemplo, que las líneas no superen cierto número de caracteres), sino que 68 | te avisará de posibles malas prácticas que podrían causar _bugs_ (como, 69 | por ejemplo, usar una variable sin haberla declarado antes). 70 | 71 | 72 | ### Editores de consola 73 | 74 | Es conveniente conocer al menos un editor de consola, puesto que no siempre 75 | trabajarás con entornos de escritorio. Hay varios muy buenos, entre ellos 76 | [Vim](http://www.vim.org/). 77 | 78 | De todas formas, si nunca has trabajado con un editor de consola, _recomendamos 79 | encarecidamente que uses Atom_ o cualquier otro editor con una interfaz visual. 80 | Es la forma más rápida de comenzar, puesto que la curva de aprendizaje de Vim 81 | es muy pronunciada. 82 | 83 | Si, pese a nuestras advertencias, optas por utilizar Vim, no olvides jugar a 84 | [Vim Adventures](http://vim-adventures.com/) y completar el tutorial de 85 | http://www.openvim.com, que te ayudarán a familiarizarte con el editor. 86 | Vim es también altamente configurable y existen varias guías sobre cómo mejorar 87 | la experiencia de programación. 88 | 89 | ## El navegador 90 | 91 | **Cualquier navegador moderno** sirve y, de hecho, recomendamos que instales 92 | varios para que puedas comprobar que el juego sea compatible entre navegadores. 93 | 94 | Debes aprender cómo activar las **herramientas de desarrollador** en tu 95 | navegador, ya que te permitirán depurar tu juego, analizar su rendimiento, etc. 96 | En esta guía mostraremos 97 | [Firefox Developer Edition](https://www.mozilla.org/firefox/developer/), que 98 | es una versión de Firefox especial para desarrolladores web e incluye 99 | herramientas y opciones no disponibles en la versión normal de Firefox. 100 | -------------------------------------------------------------------------------- /es/02-javascript/02-practica/GUIDE.md: -------------------------------------------------------------------------------- 1 | # Batalla RPG - Guía de la práctica 2 | 3 | La guía de la práctica te sugiere un orden para completar con éxito la 4 | implementación de la funcionalidad de la práctica. Prepárate, eso sí, para 5 | **leer mucho JavaScript** y ten a mano Google, la MDN y StackOverflow. 6 | 7 | ## 1. El modelo de datos 8 | 9 | En el juego existen muchos tipos de entidades, algunos relacionados y otros no. 10 | 11 | La especificación de tales entidades puede encontrarse en `spec/entities.js` y 12 | la implementación en `src/entities.js`, `src/Character.js` y `src/items.js`. 13 | 14 | ### Efectos 15 | 16 | Quizá el tipo más sencillo sea el tipo `Effect` en `src/items.js` y especificado 17 | en `spec/entities.js`. Comienza por aquí. 18 | 19 | ### Personajes 20 | 21 | Continúa activando los tests relacionados con el tipo `Character` y desactiva 22 | los demás. 23 | 24 | A continuación abre `src/Character.js` e implementa las partes que faltan. 25 | 26 | ### Elementos 27 | 28 | Continúa con el módulo `src/items.js` activando paulatinamente las _suites_ para 29 | los tipos `Item`, `Weapon` y `Scroll` que encontrarás en `spec/entities.js`. 30 | 31 | ### Entidades por defecto 32 | 33 | Tienes que crear algunos personajes, armas y pergaminos por defecto para que 34 | otras prácticas puedan usarlos. La _suite_ _built-in entities_ en 35 | `spec/entities.js` incluye todas las expectativas de estas entidades. 36 | 37 | Ve al archivo `src/entities.js` y completa las que falten. Fíjate que las 38 | propiedades son _getters_ para que cada acceso a las propiedades te devuelvan 39 | un nuevo personaje. 40 | 41 | ## 2. La lista de turnos 42 | 43 | Esta es fácil. La especificación se encuentra en `spec/TurnList.js` y la 44 | implementación en `src/TurnList.js`. Tan sólo rellena los huecos. Es pura 45 | algoritmia. Quizá tengas que mirarte cómo funciona la función `Array.sort()` 46 | para no implementarte tu propia función de ordenamiento. 47 | 48 | ## 3. La vista del personaje 49 | 50 | La vista del personaje es una representación de sólo lectura de las estadísticas 51 | del mismo. Su especificación está en `spec/CharactersView.js` y su 52 | implementación en `src/CharactersView.js`. Puedes continuar por ahí. 53 | 54 | ## 4. El grupo de opciones 55 | 56 | El grupo de opciones representa las opciones que se pueden elegir en un momento 57 | dado. La especificación está en `spec/Options.js` y la implementación, 58 | casi completa, en `src/Options.js`. Fíjate cómo el tipo `Options` 59 | extiende `EventEmitter`. Tu misión será implementar el método `.select()` para 60 | que al llamarlo se emita un evento acorde con la especificación. 61 | 62 | ## 5. La pila de opciones 63 | 64 | Un RPG se compone normalmente de varios menús apilados. Por ejemplo, el menú 65 | de acciones da paso al menú de hechizos que da paso al menú de objetivos. En 66 | cualquier momento podemos regresar al menú anterior. La pila de opciones en 67 | `src/OptionsStack.js` y especificada en `spec/OptionsStack.js` refleja este 68 | comportamiento. 69 | 70 | La API es la misma que el grupo de opciones pero los métodos realmente sólo 71 | se deben redirigir al último menú apilado. Fíjate en cómo se apilan y desapilan 72 | menús nuevos. 73 | 74 | ## 6. Utilidades 75 | 76 | Tranqui. No tienes que hacer nada aquí, tan sólo ten en cuenta que tienes el 77 | módulo `src/utils.js` donde puedes colocar más utilidades si encuentras que 78 | andas repitiendo el mismo código en muchas partes. Te aconsejo que escribas 79 | algún test. Puedes inspirarte en los que ya hay en `spec/utils.js`. 80 | 81 | ## 7. La batalla 82 | 83 | Has llegado al plato fuerte de la práctica. Hasta aquí era todo preparar los 84 | tipos en los que se apoya el tipo `Battle`. Ahora tendrás que implementar 85 | la máquina de estados que controla las acciones de batalla: defender, atacar 86 | y lanzar un hechizo. 87 | 88 | Fíjate que los combatientes, sus armas y los hechizos **no son los que vienen 89 | por defecto en `src/entities.js`** sino los que se encuentran en 90 | `spec/samplelib.js`. 91 | 92 | La especificación de la batalla está en `spec/Battle.js` y la implementación en 93 | `src/Battle.js`. Para abordar esta implementación con éxito es necesario que 94 | todos los tests hasta ahora pasen. 95 | 96 | En esta parte de la práctica no hay recomendaciones sobre qué tests activar 97 | primero. Tendrás que experimentar. 98 | 99 | Repasa bien el código, muchos de los ejercicios consisten en dar las 100 | implementaciones de **funciones auxiliares**. Es el caso de: 101 | + `assignParty` 102 | + `useUniqueNames` 103 | + `isAlive` 104 | + `getCommonParty` 105 | 106 | Fíjate entonces en la función `_showAction` que hará que las acciones de 107 | batalla estén disponibles en el atributo `options`. 108 | 109 | Ahora concéntrate en las acciones. La implementación de `_defend` está casi 110 | hecha. Sólo tendrás que completar las funciones `_improveDefense` y 111 | `_restoreDefense` para el cálculo de la defensa mejorada. 112 | 113 | La acción _defend_ rellena la estructura `this._action` con el nombre de la 114 | acción, los identificadores del personaje activo y del objetivo, el efecto 115 | y la nueva defensa. Todos menos la defensa son necesarios para poder llamar 116 | a la función `_executeAction` que ejecutará la acción e informará del 117 | resultado. 118 | 119 | Durante el proceso de implementación de las acciones, tendrás que implementar 120 | también `_showTargets` y `_showScrolls` de manera similar, de acuerdo a la 121 | especificación y al [enunciado](index.md). 122 | 123 | ## 8. Calidad del código 124 | Cuando termines y todos tus tests estén en verde habrás terminado la práctica. 125 | 126 | Rotula la rama con un _tag_ o cambia de rama antes de mejorar la calidad del 127 | código. 128 | 129 | Lee ahora los errores de estilo que el comando de tests pueda proporcionar y 130 | pasa los tests cada vez que realices una modificación para asegurarte que no 131 | has roto nada. 132 | 133 | ## Fin 134 | 135 | ¡Enhorabuena! Has completado la práctica. 136 | -------------------------------------------------------------------------------- /es/02-javascript/02-practica/TDD.md: -------------------------------------------------------------------------------- 1 | # TDD: Desarrollo dirigido por tests 2 | 3 | Practicando TDD, iremos escribiendo código de forma que los tests pasen. Los 4 | tests vienen dados pero están desactivados. La [guía de la práctica]( 5 | ./GUIDE.md) recomienda en qué orden activar los tests para completar la práctica 6 | poco a poco. 7 | 8 | ### Tests y suites 9 | 10 | En esta práctica usamos [**Jasmine**](http://jasmine.github.io) como framework 11 | para tests. En Jasmine escribimos suites y tests. Las suites se pueden anidar 12 | y pueden llevar código de inicialización. En general, la API de Jasmine es muy 13 | clara y no necesita mayor explicación. De todas formas, aquí tienes un ejemplo: 14 | 15 | ```js 16 | describe('Las suites en Jasmine', function () { 17 | 18 | describe('pueden anidarse', function () { 19 | 20 | it('y encierran tests con expectativas', function () { 21 | expect(2 + 2).toBe(4); 22 | }); 23 | 24 | }); 25 | 26 | }); 27 | ``` 28 | 29 | Se llama test a un fragmento de código que pone a prueba una funcionalidad 30 | específica. El test puede pasar o fallar. En caso de fallo, la consola mostrará 31 | por qué ha fallado en la forma de una traza: 32 | 33 | ```js 34 | 15.2) Expected 'b' to be 'c'. 35 | Error: Expected 'b' to be 'c'. 36 | at stack (/Users/salva/workspace/pvli2017-rpg-battle/node_modules/jasmine-core/lib/jasmine-core/jasmine.js:1640:17) 37 | at buildExpectationResult (/Users/salva/workspace/pvli2017-rpg-battle/node_modules/jasmine-core/lib/jasmine-core/jasmine.js:1610:14) 38 | at Spec.expectationResultFactory (/Users/salva/workspace/pvli2017-rpg-battle/node_modules/jasmine-core/lib/jasmine-core/jasmine.js:655:18) 39 | at Spec.addExpectationResult (/Users/salva/workspace/pvli2017-rpg-battle/node_modules/jasmine-core/lib/jasmine-core/jasmine.js:342:34) 40 | at Expectation.addExpectationResult (/Users/salva/workspace/pvli2017-rpg-battle/node_modules/jasmine-core/lib/jasmine-core/jasmine.js:599:21) 41 | at Expectation.toBe (/Users/salva/workspace/pvli2017-rpg-battle/node_modules/jasmine-core/lib/jasmine-core/jasmine.js:1564:12) 42 | at Object. (/Users/salva/workspace/pvli2017-rpg-battle/spec/TurnList.js:39:36) 43 | at attemptSync (/Users/salva/workspace/pvli2017-rpg-battle/node_modules/jasmine-core/lib/jasmine-core/jasmine.js:1950:24) 44 | at QueueRunner.run (/Users/salva/workspace/pvli2017-rpg-battle/node_modules/jasmine-core/lib/jasmine-core/jasmine.js:1938:9) 45 | at QueueRunner.execute (/Users/salva/workspace/pvli2017-rpg-battle/node_modules/jasmine-core/lib/jasmine-core/jasmine.js:1923:10) 46 | ``` 47 | 48 | La traza contiene el fallo y dónde se ha producido en el conjunto de llamadas 49 | desde la más reciente hasta la más vieja. A veces los fallos son producto de 50 | implementaciones que no cumplen las expectativas, otras veces serán fallos en 51 | tiempo de ejecución y otras serán fallos de sintaxis. 52 | 53 | Acostúmbrate a fallar y a encontrar en la traza el punto exacto del código que 54 | está bajo tu control para solucionarlo. Para ello busca las carpetas `spec` y 55 | `src` entre la traza. El primer número tras la ruta es la línea del fallo. 56 | 57 | ### Activando y desactivando tests 58 | 59 | Los tests y las suites pueden desactivarse añadiendo el prefijo `x`. Por 60 | ejemplo: 61 | 62 | ```js 63 | describe('Las suites en Jasmine', function () { 64 | 65 | describe('pueden anidarse', function () { 66 | 67 | it('y encierran tests con expectativas', function () { 68 | expect(2 + 2).toBe(4); 69 | }); 70 | 71 | xit('este test está desactivado', function () { 72 | 73 | }); 74 | 75 | }); 76 | 77 | xdescribe('la suite y todos sus tests están desactivados.', function () { 78 | 79 | }); 80 | 81 | }); 82 | ``` 83 | 84 | Los tests desactivados no comprueban las expectativas pero Jasmine te 85 | informa de que están desactivados. 86 | 87 | ### El ciclo de desarrollo 88 | 89 | Cuando estés desarrollando, es conveniente que pases los test a menudo por dos 90 | motivos: 91 | 92 | - Comprobar que avanzas. 93 | - Comprobar que no has roto nada. 94 | 95 | Para ello puedes ejecutar el comando: 96 | 97 | ``` 98 | $ npm run-script watch 99 | ``` 100 | 101 | Esta tarea monitoriza los cambios en los archivos de las carpetas `spec` y 102 | `src` y cuando detecte un cambio, lanzara todos los tests. 103 | 104 | A veces, el error es tan estrepitoso que rompe la monitorización. En tal caso 105 | tendrás que reintroducir el comando manualmente. 106 | 107 | Cada vez que realices una modificación y los tests pasen, haz un commit nuevo. 108 | 109 | ### Depurando tests asíncronos 110 | 111 | Algunos tests son asíncronos y pueden producir _timeouts_. En general un 112 | _timeout_ no es un resultado positivo. El problema de los _timeouts_ es que 113 | pueden ralentizar toda la suite así que la recomendación en estos casos es 114 | afrontarlos uno a uno, desactivando el resto y activándolos poco a poco. 115 | 116 | Reconocerás un test asíncrono porque lleva un parámetro `done` como en el 117 | ejemplo: 118 | 119 | ```js 120 | var EventEmitter = require('events').EventEmitter; 121 | 122 | describe('EventEmitter', function () { 123 | 124 | it('emite eventos arbitrarios', function (done) { 125 | var ee = new EventEmitter(); 126 | ee.on('turn', function(turn) { 127 | expect(turn.number).toBe(1); 128 | done(); 129 | }); 130 | ee.emit('turn', { number: 1 }); 131 | }); 132 | 133 | }); 134 | ``` 135 | 136 | ## Estrategia general para la depuración 137 | 138 | Es muy recomendable que mantengas una rama estable donde todos los tests pasen 139 | y los que no estén desactivados. Cuando te embarques en la tarea de hacer que 140 | un test pase, crea una rama para esa tarea y cuando termines mézclala con la 141 | rama estable. 142 | 143 | Cuando encuentres un error, intenta seguir los siguientes pasos: 144 | 1. Desactiva los tests asíncronos que estén tardando demasiado. **Necesitas un 145 | ciclo de desarrollo rápido.** 146 | 2. **¡¡Lee el error!!**. 147 | 3. Busca en la traza el lugar donde se original el error: 148 | 1. Si es un fallo en una expectativa, localiza el punto de entrada en 149 | tu código. 150 | 2. Deja trazas con `console.log()` inspeccionando el estado de tus objetos. 151 | 4. Salva y relanza los tests a menudo. 152 | -------------------------------------------------------------------------------- /es/03-javascript-en-el-navegador/03-practica/index.md: -------------------------------------------------------------------------------- 1 | # Cliente web para batallas RPG 2 | 3 | A continuación se expone la práctica propuesta para esta unidad. Tras leer este enunciado, se recomienda _encarecidamente_ consultar la **[guía de la práctica](guia.md)** para su realización. 4 | 5 | ## Enunciado 6 | 7 | La práctica consiste en implementar un cliente visual (en este caso, **una página web**) para el juego de batallas de la práctica de la unidad anterior. 8 | 9 | ![Captura de pantalla del cliente](images/screenshot.png) 10 | 11 | Como punto de partida, se ha provisto de un href="start-here.zip" target="_blank">esqueleto de proyecto con los siguientes archivos: 12 | 13 | - `index.html`: código HTML de partida 14 | - `styles.css`: hoja de estilo 15 | - `js/main.js`: el archivo de partida JavaScript 16 | - `js/rpg.js`: un archivo con el código de la librería de batallas de la práctica. También puedes usar tu propio código (consulta la sección de _Adaptación del código de la práctica anterior_). 17 | 18 | En este código inicial se incluye ya implementado lo siguiente: 19 | 20 | - Carga desde el archivo HTML de los recursos JavaScript y CSS. 21 | 22 | - Esqueleto HTML con una interfaz ya hecha. Puedes modificar este HTML para añadir más cosas, o cambiar elementos de la UI que no te convenzan, pero no es obligatorio. 23 | 24 | - Creación de una instancia de `Battle`, así como el setup de las _parties_ y la subscripción a los eventos más relevantes. La información de dichos eventos se imprime por consola (lo cual puedes eliminar/modificar a tu gusto). 25 | 26 | Hay que implementar las siguientes _features_: 27 | 28 | - Mostrar los personajes de ambas _parties_, con sus ID's, puntos de vida y de maná. 29 | 30 | - Marcar qué personaje está seleccionado, cambiando su estilo o añadiendo un carácter especial (p.ej: `*`). 31 | 32 | - Marcar qué personajes están muertos, cambiando su estilo o añadiendo un carácter especial (p.ej: `✝`). 33 | 34 | - Implementar el menú de batalla con sus siguientes estados: selección de acción, selección de objetivo y selección de hechizo. 35 | 36 | - Mostrar información de qué ha pasado cada turno (p.ej `Bat attacked Wizz, but missed`). 37 | 38 | - Mostrar un mensaje al final de la batalla indicando cuál es el bando ganador. 39 | 40 | Para implementar estas _features_ básicas, es recomendable seguir el procedimiento marcado por **la [guía](GUIDE.md) de la práctica**. 41 | 42 | Otras características opcionales que se podrían implementar, serían: 43 | 44 | - En el menú de selección de objetivo, mostrar en un color diferente los personajes de cada _party_. 45 | 46 | - Al terminar la batalla, mostrar un botón o enlace para empezar una nueva (esto se puede hacer simplemente recargando la página). 47 | 48 | - Crear la composición de una o ambas _parties_ de manera aleatoria. 49 | 50 | ## Adaptación del código de la práctica anterior 51 | 52 | En la versión actual de JavaScript no hay ningún mecanismo para gestionar módulos (por ejemplo, usando la función `require` como en Node). Es por esto que no podemos utilizar ni `require` ni `module.exports`. 53 | 54 | Además, ciertas partes que forman parte de la librería estándar de Node, como el módulo `events` para implementar eventos, no forman parte del estándar JavaScript. 55 | 56 | Hay una herramienta, [**Browserify**](http://browserify.org/) que nos permite transformar módulos de Node –con sus dependencias– en código que funciona en el browser. También incluye **polyfills**. 57 | 58 | ### Instrucciones 59 | 60 | #### Opción A: usar el código propio 61 | 62 | Si has acabado la práctica anterior, puedes utilizar ese código en esta. Sigue los pasos que hay a continuación para adaptar ese código de Node a código que puedas ejecutar en el navegador. 63 | 64 | 1. Como vamos a necesitar dos módulos, `Battle` y `entities` (junto a sus dependencias), tenemos que crear un archivo "raíz" con esos dos. Crea en la raíz del directorio de la práctica anterior un archivo `export.js` con el siguiente contenido: 65 | 66 | ```javascript 67 | module.exports = { 68 | "Battle": require('./src/Battle.js'), 69 | "entities": require('./src/entities.js') 70 | }; 71 | ``` 72 | 73 | 2. De nuevo en la raíz del directorio de la práctica anterior, instala Browserify con npm: 74 | 75 | ``` 76 | npm install --save-dev browserify 77 | ``` 78 | 79 | 3. Comprueba que el archivo `package.json` se ha modificado y que ahora aparece Browserify listado dentro de `devDependencies`. Por ejemplo: 80 | 81 | ```json 82 | "devDependencies": { 83 | "browserify": "^13.1.0" 84 | } 85 | ``` 86 | 87 | 4. Edita `package.json` para añadir un comando de script más, que ejecutará Browserify: 88 | 89 | ```json 90 | "scripts": { 91 | "bundle": "browserify export.js --standalone RPG > rpg.js" 92 | } 93 | ``` 94 | 95 | 5. Ejecuta dicho comando, que generará un archivo `rpg.js` en el raíz de ese directorio. 96 | 97 | ```bash 98 | npm run bundle 99 | ``` 100 | 101 | 6. Ahora puedes copiar `rpg.js` al directorio `js` de la práctica 2. Cuando se cargue el archivo con una etiqueta `