class App extends React.Component { constructor() { super(); this.state = this.resetState(); this.handleKey = this.handleKey.bind(this); this.checkMove = this.checkMove.bind(this); this.placeVillains = this.placeVillains.bind(this); this.placeHealth = this.placeHealth.bind(this); this.fightVillain = this.fightVillain.bind(this); this.boostHealth = this.boostHealth.bind(this); this.toggleLights = this.toggleLights.bind(this); this.placeWeapons = this.placeWeapons.bind(this); this.boostWeapon = this.boostWeapon.bind(this); this.hideIntro = this.hideIntro.bind(this); this.fightTheDonald = this.fightTheDonald.bind(this); this.debounceHandleKey = this.debounceHandleKey.bind(this); this.getState = this.getState.bind(this); this.reset = this.reset.bind(this); this.resetState = this.resetState.bind(this); } render() { return ( <div> <h1>Hillary vs. The Donald</h1> <Legend character={this.state.character} toggleLights={this.toggleLights}/> <div className='messageBox'> <Message message={this.state.message.text} type={this.state.message.type} /> </div> <Board onChange={this.hideIntro} reset={this.reset} gameStatus = {this.state.gameStatus} lights={this.state.lights} boardSize={this.state.boardSize} grid={this.state.grid} position={this.state.position} /> </div> ); }; getState() { const newState = { boardSize: { width: 30, height: 19 }, grid: [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ], position: [2, 2], character: { level: 1, xp: 0, health: 100, weapon: 0 }, message: { text: 'Use arrow keys to move', type: 'inform' }, villains: [], theDonald: 1000, gameStatus: 3, lights: 'off' }; return newState; } reset() { const newState = this.resetState(); this.setState(newState); } resetState() { const newState = this.getState(); const newState2 = this.placeVillains(newState); const newState3 = this.placeHealth(newState2); const newState4 = this.placeWeapons(newState3); return newState4; } componentDidMount() { window.addEventListener('keydown', this.debounceHandleKey, false); } componentWillUpdate() { const levels = [0, 100, 250, 450, 700, 1000, 1350, 1750, 2200, 2700]; const character = this.state.character; if (character.xp >= levels[character.level]) { character.level += 1; character.health = levels[character.level]; this.setState({ character: character, message: { text: 'You levelled up!', type: 'good' } }); } if (this.state.gameStatus === 0) { window.removeEventListener('keydown', this.handleKey, false); } } hideIntro(e) { this.setState({ gameStatus: 1 }); } toggleLights(e) { let lights = this.state.lights; lights = lights === 'off' ? 'on' : 'off'; this.setState({ lights: lights }); } placeVillains(newState) { const levels = [0, 100, 250, 450, 700, 1000, 1350, 1750, 2200, 2700]; const rooms = [ {xRange: [0, 10], yRange: [0, 10]}, {xRange: [11, 20], yRange: [0, 10]}, {xRange: [21, 30], yRange: [0, 10]}, {xRange: [21, 30], yRange: [11, 19]}, {xRange: [11, 20], yRange: [11, 19]}, {xRange: [0, 10], yRange: [11, 19]} ]; newState.villains = []; let villain = 11; rooms.forEach(room => { if (villain < 16) { for (let i = 0; i < 6; i++) { const coords = getRandomCoords(room, newState.grid, newState.boardSize.width); newState.grid[coords[1] * newState.boardSize.width + coords[0]] = villain; newState.villains.push({ position: coords[1] * newState.boardSize.width + coords[0], level: villain - 10, health: levels[villain - 10] }); }; } villain++; }); return newState; } placeHealth(newState) { const rooms = [ {xRange: [0, 10], yRange: [0, 10]}, {xRange: [11, 20], yRange: [0, 10]}, {xRange: [21, 30], yRange: [0, 10]}, {xRange: [21, 30], yRange: [11, 19]}, {xRange: [11, 20], yRange: [11, 19]}, {xRange: [0, 10], yRange: [11, 19]} ]; rooms.forEach(room => { for (let i = 0; i < 4; i++) { const coords = getRandomCoords(room, newState.grid, newState.boardSize.width); newState.grid[coords[1] * newState.boardSize.width + coords[0]] = 5; }; }); return newState; } placeWeapons(newState) { const rooms = [ {xRange: [11, 20], yRange: [0, 10]}, {xRange: [21, 30], yRange: [0, 10]}, {xRange: [21, 30], yRange: [11, 19]}, {xRange: [11, 20], yRange: [11, 19]} ]; rooms.forEach(room => { const coords = getRandomCoords(room, newState.grid, newState.boardSize.width); newState.grid[coords[1] * newState.boardSize.width + coords[0]] = 6; }); return newState; } debounceHandleKey(e) { const arrows = [37, 38, 39, 40, 76]; if (arrows.indexOf(e.keyCode) > -1) { e.preventDefault(); } const debounced = _.debounce(this.handleKey, 250); debounced(e); } handleKey(e) { e = e || window.event; if (e.keyCode === 38) { // up arrow this.checkMove(0, -1); } else if (e.keyCode === 40) { // down arrow this.checkMove(0, 1); } else if (e.keyCode === 37) { // left arrow this.checkMove(-1, 0); } else if (e.keyCode === 39) { // right arrow this.checkMove(1, 0); } else if (e.keyCode === 76) { this.toggleLights(); } } checkMove(x, y) { let validMove = true; const grid = this.state.grid; const newX = this.state.position[0] + x; const newY = this.state.position[1] + y; const newSqValue = grid[newY * this.state.boardSize.width + newX]; if (newSqValue === 1) { validMove = false; this.setState({ message: { type: 'alert', text: 'You hit a wall' } }); } else if (newSqValue > 10 && newSqValue <= 20) { validMove = this.fightVillain(newSqValue, newX, newY); } else if (newSqValue === 5) { this.boostHealth(); validMove = true; } else if (newSqValue === 6) { this.boostWeapon(); validMove = true; } else if (newSqValue === 3) { validMove = this.fightTheDonald(); } else { validMove = true; this.setState({ message: { type: 'inform', text: 'Use arrow keys to move' } }); } if (validMove) { grid[this.state.position[1] * this.state.boardSize.width + this.state.position[0]] = 0; grid[newY * this.state.boardSize.width + newX] = 2; this.setState({grid: grid, position: [newX, newY], lights: 'off'}); } } boostHealth() { const level = this.state.character.level; const levels = [0, 100, 250, 450, 700, 1000, 1350, 1750, 2200, 2700]; const boost = Math.floor(0.80 * levels[level] * (Math.random() * (1 - 0.08) + 0.8)); const character = this.state.character; character.health = character.health + boost > levels[level] ? levels[level] : character.health + boost; this.setState({character: character, message: {text: `You collected a health booster. Health level is now ${character.health / levels[level] * 100}%`, type: 'good'}}); } boostWeapon() { const weapons = ['Free Speech', 'a Yale Law Degree', 'a Grammy for Best Spoken Word Album', 'a private email server', 'the Democratic Party nomination']; const character = this.state.character; character.weapon += 1; character.xp += 20; this.setState({ character: character, message: `You picked up ${weapons[character.weapon]}. 10% battle bonus.`, type: 'good' }); } fightVillain(type, x, y) { const levels = [0, 100, 250, 450, 700, 1000, 1350, 1750, 2200, 2700]; const opponent = this.state.villains.find(villain => villain.position === y * this.state.boardSize.width + x); const villains = this.state.villains; const index = villains.indexOf(opponent); const you = this.state.character; opponent.health -= damageCalculation(you.level * 15, you.level * 25, you.weapon); villains[index] = opponent; if (opponent.health > 0) { you.health -= damageCalculation(opponent.level * 10, opponent.level * 20, 0); if (you.health > 0) { this.setState({ villains: villains, character: you, message: { text: `Level ${opponent.level} surrogate: you reduced his health to ${Math.floor(opponent.health / levels[opponent.level] * 100)}%; he reduced yours to ${Math.floor(you.health / levels[you.level] * 100)}%.`, type: 'hit flash' } }); return false; } else if (you.health <= 0) { you.health = 0; this.setState({ villains: villains, character: you, message: { text: `Level ${opponent.level} surrogate: he killed you. Game over.`, type: 'alert' }, gameStatus: 0 }); return false; } } else if (opponent.health <= 0) { villains.splice(index, 1); you.xp += 25 * opponent.level; this.setState({ villains: villains, character: you, message: { text: `Level ${opponent.level} surrogate: You killed him.`, type: 'good' } }); return true; } } fightTheDonald() { const levels = [0, 100, 250, 450, 700, 1000, 1350, 1750, 2200, 2700]; let theDonald = this.state.theDonald; const you = this.state.character; theDonald -= damageCalculation(you.level * 15, you.level * 25, you.weapon); if (theDonald > 0) { you.health -= damageCalculation(80, 140, 0); if (you.health > 0) { this.setState({ character: you, theDonald: theDonald, message: { text: `You reduced The Donald's health to ${Math.floor(theDonald / 1000 * 100)}%; he reduced yours to ${Math.floor(you.health / levels[you.level] * 100)}%`, type: 'hit' } }); return false; } else if (you.health <= 0) { you.health = 0; this.setState({ theDonald: theDonald, character: you, message: { text: 'The Donald defeated you. Better luck next time.', type: 'lose' }, gameStatus: 0 }); return false; } } else if (theDonald <= 0) { this.setState({ theDonald: 0, character: you, message: { text: 'You defeated The Donald! Congrats, President-Elect Hillary', type: 'good' }, gameStatus: 2 }); return true; } } } function getRandomCoords(room, grid, width) { const x = Math.floor(Math.random() * (room.xRange[1] - room.xRange[0])) + room.xRange[0]; const y = Math.floor(Math.random() * (room.yRange[1] - room.yRange[0])) + room.yRange[0]; return grid[y * width + x] == 0 ? [x,y] : getRandomCoords(room, grid, width); } function damageCalculation(min, max, weapon) { const hit = Math.random() * (max - min) + min; const weaponBonus = hit * (weapon * 5 / 100); return Math.floor(hit + weaponBonus); } class Board extends React.Component { constructor(props) { super(props); this.handleClick = this.handleClick.bind(this); this.reset = this.reset.bind(this); } handleClick(e) { e.preventDefault(); return this.props.onChange(e); } reset() { this.props.reset(); } render() { const intro = ( <div onClick={this.handleClick} className='intro'> <h2>Welcome</h2> <p>In this game, you play the role of Hillary. Hillary is trying to find and slay her enemy, The Donald, but his merry band of surrogates will do everything they can to stop her. Collect health boosters and new weapons as you progress. You will need to level up a few times If you want to be strong enough to defeat the big guy. Best of luck!</p> <p className='small'>Use the arrow keys to navigate. Press 'L' to toggle the lights. Click anywhere to begin.</p> </div> ); const gameOverWin = ( <div className='win'> <h2>Congratulations!</h2> <p>You single-handedly slayed The Donald and his band of surrogates. Congrats!</p> <button onClick={this.reset}>Play Again</button> </div> ); const gameOver = ( <div className='lose'> <h2>Game over</h2> <p>You died. Better luck next time.</p> <button onClick={this.reset}>Play Again</button> </div> ); let rows = []; for (let y = 0; y < this.props.boardSize.height; y++) { const row = []; for (let x = 0; x < this.props.boardSize.width; x++) { row.push( <Square key={x} thisSquare={[x, y]} fill={this.props.grid[y * this.props.boardSize.width + x]} position = {this.props.position} width = {this.props.boardSize.width} lights = {this.props.lights} /> ); } rows.push(<Row key={y}>{row}</Row>); } const gameOn = (<div className='board'>{rows}</div>); if (this.props.gameStatus === 1) { return gameOn; } else if (this.props.gameStatus === 0) { return gameOver; } else if (this.props.gameStatus === 2) { return gameOverWin; } else { return intro; } } } const Row = ({children}) => <div className="row">{children}</div>; const Square = ({children, position, lights, thisSquare, width, fill = false}) => ( <div className={getBackgroundColor(fill, position, lights, thisSquare, width)} > {children} </div> ); function getBackgroundColor(fill, position, lights, thisSquare, width) { const sqIndex = thisSquare[1] * width + thisSquare[0]; let sqFill; switch (fill) { case 1: sqFill = "square wall"; break; case 2: sqFill = "square character"; break; case 11: sqFill = "square level1"; break; case 12: sqFill = "square level2"; break; case 13: sqFill = "square level3"; break; case 14: sqFill = "square level4"; break; case 15: sqFill = "square level5"; break; case 5: sqFill = "square heart"; break; case 6: sqFill = "square weapon"; break; case 3: sqFill = "square theDonald"; break; default: sqFill = "square floor"; break; } const posIndex = position[1] * width + position[0]; const visibleArea = [posIndex, posIndex - 1, posIndex - 2, posIndex + 1, posIndex + 2, posIndex - width, posIndex - width + 1, posIndex - width - 1, posIndex - width - width, posIndex + width, posIndex + width + 1, posIndex + width - 1, posIndex + width + width]; const shadowyArea = [posIndex - (3 * width), posIndex + (3 * width), posIndex + (2 * width) + 1, posIndex + (2 * width) - 1, posIndex - (2 * width) + 1, posIndex - (2 * width) - 1, posIndex - width - 2, posIndex - width + 2, posIndex + width - 2, posIndex + width + 2, posIndex - 3, posIndex + 3]; if (lights === 'on') { return sqFill; } else { if (visibleArea.indexOf(sqIndex) > -1) { return sqFill } else if (shadowyArea.indexOf(sqIndex) > -1) { sqFill += " lessdark"; return sqFill; } else { return "square dark"; } } } class Legend extends React.Component { constructor(props) { super(props); this.toggleLights = this.toggleLights.bind(this); } toggleLights(e) { return this.props.toggleLights(e); } render() { const levels = [0, 100, 250, 450, 700, 1000, 1350, 1750, 2200, 2700]; const weapons = ['Free Speech', 'a Yale Law Degree', 'a Grammy for Best Spoken Word Album', 'a private email server', 'the Democratic Party nomination']; const health = Math.floor(this.props.character.health / levels[this.props.character.level] * 100); const weaponBonus = weapons[this.props.character.weapon] * 5; return ( <div className="legendBox"> <div className="legend1"> <h3>Hillary's Stats</h3> <ul> <li><strong>Level:</strong> {this.props.character.level}</li> <li><strong>Health:</strong> {health}%</li> <li><strong>XP:</strong> {this.props.character.xp} / {levels[this.props.character.level]}</li> <li><strong>Armed with:</strong> {weapons[this.props.character.weapon]} {weaponBonus ? `(${weaponBonus}% combat bonus)` : ''}</li> </ul> <Lights onChange={this.props.toggleLights}/> </div> <div className="legend2"> <h3>Good Things</h3> <ul> <li><div className="hillary"></div> Hillary</li> <li><div className="heart"></div> Heart</li> <li><div className="weapon"></div> Weapon</li> </ul> </div> <div className="legend3"> <h3>Bad Things</h3> <ul> <li><div className="surrogate"></div> Surrogates</li> <li><div className="kellyanne"></div> Kellyanne Conway</li> <li><div className="theDonald"></div> The Donald</li> </ul> </div> </div> ) }; } class Lights extends React.Component { constructor(props) { super(props); this.handleClick = this.handleClick.bind(this); } handleClick(e) { e.preventDefault(); return this.props.onChange(e); } render() { return (<button className="lights" onClick={this.handleClick}>Toggle Lights</button>) } } const Message = ({message, type}) => ( <div className={type} > {message} </div> ); ReactDOM.render(<App />, document.querySelector('#app'));

!