define(["sugar-web/activity/activity","tween","rAF","activity/directions","sugar-web/graphics/presencepalette", "sugar-web/env", "sugar-web/graphics/icon", "webL10n", "sugar-web/graphics/palette", "rot", "humane"], function (activity, TWEEN, rAF, directions, presencepalette, env, icon, webL10n, palette, ROT, humane) { requirejs(['domReady!'], function (doc) { activity.setup(); var maze = {}; var ended=false; var oponentEnded = 0; var oponentCount = 0; maze.width = undefined; maze.height = undefined; maze.startPoint = {}; maze.goalPoint = {}; maze.walls = []; maze.visited = []; maze.directions = []; maze.forks = []; var firstentry=true; var xoLogo = ']>'; env.getEnvironment(function(err, environment) { // Set current language to Sugarizer var defaultLanguage = (typeof chrome != 'undefined' && chrome.app && chrome.app.runtime) ? chrome.i18n.getUILanguage() : navigator.language; var language = environment.user ? environment.user.language : defaultLanguage; webL10n.language.code = language; // Shared instances if (environment.sharedId) { console.log("Shared instance"); presence = activity.getPresenceObject(function(error, network) { network.onDataReceived(onNetworkDataReceived); network.onSharedActivityUserChanged(onNetworkUserChanged); }); } // Load from datastore if (!environment.objectId) { runLevel(); } else { activity.getDatastoreObject().loadAsText(function(error, metadata, data) { if (error==null && data!=null) { data = JSON.parse(data); maze = data.maze; gameSize = data.gameSize; updateMazeSize(); updateSprites(); onLevelStart(); } }); } }); var generateXOLogoWithColor = function(color) { var coloredLogo = xoLogo; coloredLogo = coloredLogo.replace("#010101", color.stroke) coloredLogo = coloredLogo.replace("#FFFFFF", color.fill) return "data:image/svg+xml;base64," + btoa(coloredLogo); } var onNetworkDataReceived = function(msg) { if (presence.getUserInfo().networkId === msg.user.networkId) { return; } switch (msg.action){ case 'start': ended=false; oponentEnded=0; maze=msg.content; gameSize=msg.SizeOfGame; oponentCount=msg.oponentCount; updateMazeSize(); updateSprites(); onLevelStart(); break; case 'ended': oponentEnded++; var userName = msg.user.name.replace('<', '<').replace('>', '>'); var html = ""; humane.log(html + webL10n.get("PlayerEndLevel",{user: userName})); break; } }; var onNetworkUserChanged = function(msg) { var userName = msg.user.name.replace('<', '<').replace('>', '>'); var html = ""; if (msg.move === 1) { oponentCount++; humane.log(html + webL10n.get("PlayerJoin",{user: userName})); } else if (msg.move === -1) { oponentCount--; humane.log(html + webL10n.get("PlayerLeave",{user: userName})); } if (isHost) { presence.sendMessage(presence.getSharedInfo().id, { user: presence.getUserInfo(), action: 'start', SizeOfGame: gameSize, oponentCount: oponentCount, content: maze }); } console.log("User "+msg.user.name+" "+(msg.move == 1 ? "join": "leave")); }; var soundType = /(iPad|iPhone|iPod)/g.test(navigator.userAgent) ? '.mp3' : '.ogg'; var canvasWidth; var canvasHeight; var wallColor = "#101010"; var corridorColor = "#ffffff"; var startColor = "hsl(0, 0%, 80%)"; var startPlayerColor = "hsl(0, 90%, 50%)"; var goalColor; var cellWidth; var cellHeight; var dirtyCells = []; var controls = { 'arrows': [38, 39, 40, 37], 'wasd': [87, 68, 83, 65], 'ijkl': [73, 76, 75, 74], 'mouse': [-1, -1, -1, -1] }; var controlNames = ['arrows', 'wasd', 'ijkl', 'mouse']; var controlColors = {}; var controlSprites = {}; var players = {}; var winner; var gameSize = 60; var levelStatus; var levelTransitionRadius; var levelStartingValue; var debug = false; //true; var mazeCanvas = document.getElementById("maze"); var spriteCanvas = document.createElement("canvas"); var updateMazeSize = function () { var toolbarElem = document.getElementById("main-toolbar"); canvasWidth = window.innerWidth; canvasHeight = window.innerHeight - toolbarElem.offsetHeight - 3; cellWidth = Math.floor(canvasWidth / maze.width); cellHeight = Math.floor(canvasHeight / maze.height); mazeCanvas.width = canvasWidth; mazeCanvas.height = canvasHeight; spriteCanvas.width = cellWidth * 2; // number of states spriteCanvas.height = cellHeight * controlNames.length; }; var onWindowResize = function () { updateMazeSize(); updateSprites(); drawMaze(); }; window.addEventListener('resize', onWindowResize); var updateSprites = function () { for (control in controls) { if (control in controlColors) { createPlayerSprite(control); } } } var createPlayerSprite = function (control) { var i = controlNames.indexOf(control); ctx = spriteCanvas.getContext("2d"); drawPlayerFace(ctx, 0, i, controlColors[control].normal); drawPlayerFace(ctx, 1, i, controlColors[control].blocked); return { 'normal': {'image': spriteCanvas, 'x': 0, 'y': i}, 'blocked': {'image': spriteCanvas, 'x': 1, 'y': i} }; } var drawCell = function (ctx, x, y, color) { ctx.fillStyle = color; ctx.fillRect(cellWidth * x, cellHeight * y, cellWidth, cellHeight); } var drawGround = function (ctx, x, y, value) { var color; if (value == 1) { color = wallColor; } else { color = corridorColor; } drawCell(ctx, x, y, color); }; var drawPoint = function (ctx, x, y, color, size) { var centerX = cellWidth * (x + 0.5); var centerY = cellHeight * (y + 0.5); var radius = size * Math.min(cellWidth, cellHeight) / 2; ctx.beginPath(); ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI, false); ctx.fillStyle = color; ctx.fill(); }; var drawPlayerFace = function (ctx, x, y, color) { drawPoint(ctx, x, y, color, 0.9); var eye1X = cellWidth * (x + 0.3); var eye1Y = cellHeight * (y + 0.45); var eyeRadius = 0.28 * Math.min(cellWidth, cellHeight) / 2; ctx.beginPath(); ctx.arc(eye1X, eye1Y, eyeRadius, 0, 2 * Math.PI, false); var eye2X = cellWidth * (x + 0.7); var eye2Y = cellHeight * (y + 0.45); ctx.arc(eye2X, eye2Y, eyeRadius, 0, 2 * Math.PI, false); ctx.fillStyle = "#ffffff"; ctx.fill(); ctx.beginPath(); ctx.arc(eye1X, eye1Y, eyeRadius / 2, 0, 2 * Math.PI, false); ctx.arc(eye2X, eye2Y, eyeRadius / 2, 0, 2 * Math.PI, false); ctx.fillStyle = "#000000"; ctx.fill(); ctx.beginPath(); ctx.moveTo(cellWidth * (x + 0.25), cellHeight * (y + 0.65)); ctx.quadraticCurveTo(cellWidth * (x + 0.5), cellHeight * (y + 0.75), cellWidth * (x + 0.75), cellHeight * (y + 0.65)); ctx.quadraticCurveTo(cellWidth * (x + 0.5), cellHeight * (y + 0.75), cellWidth * (x + 0.75), cellHeight * (y + 0.65)); ctx.quadraticCurveTo(cellWidth * (x + 0.5), cellHeight * (y + 1), cellWidth * (x + 0.25), cellHeight * (y + 0.65)); ctx.quadraticCurveTo(cellWidth * (x + 0.5), cellHeight * (y + 1), cellWidth * (x + 0.25), cellHeight * (y + 0.65)); ctx.fillStyle = "#ffffff"; ctx.fill(); } var drawSprite = function (ctx, x, y, spriteData) { ctx.drawImage(spriteData.image, cellWidth * spriteData.x, cellHeight * spriteData.y, cellWidth, cellHeight, cellWidth * x, cellHeight * y, cellWidth, cellHeight) } var drawMazeCell = function (x, y, ctx) { if (ctx === undefined) { ctx = mazeCanvas.getContext("2d"); } drawGround(ctx, x, y, maze.walls[x][y]); if (maze.visited[x][y] !== undefined) { drawPoint(ctx, x, y, maze.visited[x][y], 0.5); } if (debug) { if (maze.forks[x][y] == 1) { drawPoint(ctx, x, y, '#faa', 0.5); } } if (x == maze.startPoint.x && y == maze.startPoint.y) { drawPoint(ctx, maze.startPoint.x, maze.startPoint.y, startColor, 0.9); } if (x == maze.goalPoint.x && y == maze.goalPoint.y) { drawCell(ctx, maze.goalPoint.x, maze.goalPoint.y, goalColor); } for (control in players) { var player = players[control]; if (x == player.x && y == player.y) { drawSprite(ctx, x, y, player.sprite); } }; } var drawMaze = function (ctx) { if (ctx === undefined) { ctx = mazeCanvas.getContext("2d"); } for (var x=0; x 1 ?"PlayersWaitMany":"PlayersWaitOne"), {count: waitCount})); } } tween.start(); } var nextLevel = function () { gameSize *= 1.2; runLevel(); } var Player = function (control) { this.control = control; this.x = maze.startPoint.x; this.y = maze.startPoint.y; if (!(control in controlColors)) { var hue = Math.floor(Math.random()*360); controlColors[control] = { 'normal': 'hsl(' + hue + ', 90%, 50%)', 'blocked': 'hsl(' + hue + ', 90%, 80%)', 'visited': 'hsl(' + hue + ', 30%, 80%)' }; controlSprites[control] = createPlayerSprite(control); } this.color = controlColors[control].normal; this.sprite = controlSprites[control].normal; this.visitedColor = controlColors[control].visited; this.path = undefined; this.animation = undefined; this.blockTween = undefined; dirtyCells.push({'x': this.x, 'y': this.y}); }; var countOptions = function (x, y) { var dirs = maze.directions[x][y]; return dirs.reduce(function (previousValue, currentValue) { return previousValue + currentValue; }); }; var isDeadEnd = function (x, y) { return countOptions(x, y) == 1; }; var isFork = function (x, y) { return countOptions(x, y) > 2; }; var initialize = function (aspectRatio, size) { maze.height = Math.sqrt(size / aspectRatio); maze.width = maze.height * aspectRatio; maze.height = Math.floor(maze.height); maze.width = Math.floor(maze.width); var maxCellX; var maxCellY; if (maze.width % 2) { maxCellX = maze.width-2; } else { maxCellX = maze.width-3; } if (maze.height % 2) { maxCellY = maze.height-2; } else { maxCellY = maze.height-3; } var startX; var goalY; if (Math.random() < 0.5) { startX = 1; goalX = maxCellX; } else { startX = maxCellX; goalX = 1; } var startY; var goalX; if (Math.random() < 0.5) { startY = 1; goalY = maxCellY; } else { startY = maxCellY; goalY = 1; } maze.startPoint = {'x': startX, 'y': startY}; maze.goalPoint = {'x': goalX, 'y': goalY}; }; var createMatrix = function (width, height) { var matrix = []; for (var x=0; x angle && angle > -135) { player.move('north'); } else if (-45 < angle && angle < 45) { player.move('east'); } else { player.move('west'); } }; if (mazeCanvas.addEventListener) { mazeCanvas.addEventListener("mousedown", mazeClick); } else { mazeCanvas.attachEvent('onclick', mazeClick); } var onKeyDown = function (event) { if (levelStatus == 'transition') { return; } var currentControl; var currentDirection; for (control in controls) { if (controls[control].indexOf(event.keyCode) != -1) { currentControl = control; currentDirection = directions.orders[controls[control]. indexOf(event.keyCode)]; } } if (currentControl === undefined) { return; } if (!(currentControl in players)) { players[currentControl] = new Player(currentControl); } var player = players[currentControl]; player.move(currentDirection); }; document.addEventListener("keydown", onKeyDown); var animateGoal = function (timestamp) { var hue = Math.floor(120 * (1 + Math.cos(timestamp / 3000))); var light = Math.floor(50 + (10 * (1 + Math.cos(timestamp / 300)))); goalColor = 'hsl(' + hue + ', 90%, ' + light + '%)'; dirtyCells.push({'x': maze.goalPoint.x, 'y': maze.goalPoint.y}); } var animate = function (timestamp) { TWEEN.update(timestamp); switch(levelStatus) { case 'transition': drawLevelComplete(); break; case 'starting': animateGoal(timestamp); drawLevelStarting(); break; case 'playing': animateGoal(timestamp); dirtyCells.forEach(function (cell) { drawMazeCell(cell.x, cell.y); }); dirtyCells = []; break; } requestAnimationFrame(animate); // HACK: Force redraw on Android if (/Android/i.test(navigator.userAgent) && document.location.protocol.substr(0,4) != "http") { mazeCanvas.style.display='none'; mazeCanvas.offsetHeight; mazeCanvas.style.display='block'; } }; animate(); // Link presence palette var presence = null; var isHost = false; var palette = new presencepalette.PresencePalette(document.getElementById("network-button"), undefined); palette.addEventListener('shared', function() { palette.popDown(); console.log("Want to share"); presence = activity.getPresenceObject(function(error, network) { if (error) { console.log("Sharing error"); return; } network.createSharedActivity('org.sugarlabs.MazeWebActivity', function(groupId) { console.log("Activity shared"); isHost = true; presence.sendMessage(presence.getSharedInfo().id, { user: presence.getUserInfo(), action: 'connect' }); }); network.onDataReceived(onNetworkDataReceived); network.onSharedActivityUserChanged(onNetworkUserChanged); }); }); document.getElementById("stop-button").addEventListener('click', function (event) { maze.visited = createMatrix(maze.width, maze.height); var data = { maze: maze, gameSize: gameSize, } var jsonData = JSON.stringify(data); activity.getDatastoreObject().setDataAsText(jsonData); activity.getDatastoreObject().save(); }); }); });