/* this is a partually port from as3 so this is a mess */ Game= { Rally: { Ball: { BallCollisions: {}, BallEval: {} }, Player: { PlayerCollisions: {}, PlayerEvalA: {}, PlayerEvalXY: {}, KeyHandler: {} }, Referee: {}, Scale: {}, Time: {}, Timer: {}, BorderLines: {}, FieldLines: {}, ServeLines: {}, MiddleLines: {MiddleLine: {}} }, Referee: {}, Wait: {}, }; NetworkClient={}; document.addEventListener('DOMContentLoaded', function() { window.addEventListener('resize', resize); resize(); keySpaceOccupied=false; chat=(function() { var mesI=0; var listNode=document.querySelector('#chat ._list'); var inputNode=document.querySelector('#chat input'); var mesTemplateNode=document.querySelector('templates > .chat_message'); inputNode.addEventListener('focusin', function() { keySpaceOccupied=true; }); inputNode.addEventListener('focusout', function() { keySpaceOccupied=false; }); inputNode.addEventListener('keydown', function(event) { if(event.keyCode==13) { networkClient.messageSend({tp: 'chat', text: inputNode.value}); inputNode.value=''; } }); return { receive: function(mes) { var mesNode=mesTemplateNode.cloneNode(true); mesNode.querySelector('._name').innerText=mes.name; mesNode.querySelector('._text').innerText=mes.text; listNode.prepend(mesNode); //listNode.scrollTo(0, listNode.scrollHeight); if(mesI==30) { //listNode.childNodes[0].remove(); var m=listNode.childNodes; m[m.length-1].remove(); } else { mesI++; } } } })(); advice=(function() { var node=document.querySelector('#wait_advice'); var advices=['Совет: набегайте на мяч под углом, чтобы придать ему вращение.', 'Совет: если мяч от соперника летит прямиком в аут, не отбивайте его.', 'Совет: набегайте на мяч, чтобы увеличить его скорость.', 'Совет: следите за вращением мяча, чтобы предугадывать его траекторию.']; return { hide: function() { node.style.display='none'; }, refresh: function() { node.style.display='block'; node.innerText=advices[parseInt(Math.min(advices.length*Math.random(), advices.length-1))]; } } })(); waitView=(function() { var waitNode=document.getElementById('wait'); var waitReadyNode=document.getElementById('wait_ready'); return { status: function(status) { if(! status) { waitNode.classList.remove('success'); waitNode.classList.remove('fail'); } else if(status=='success') { waitNode.classList.add('success'); waitNode.classList.remove('fail'); } else { waitNode.classList.remove('success'); waitNode.classList.add('fail'); } }, ready: function(text) { waitReadyNode.innerHTML=text; waitNode.style.display='flex'; }, hide: function() { waitNode.style.display='none'; document.getElementById('teaching_fail_border').style.display='none'; document.getElementById('teaching_fail_out').style.display='none'; }, show: function() { waitNode.style.display='flex'; } } })(); lobby=(function() { return { hide: function() { document.body.classList.add('lobby_closed'); }, show: function() { document.body.classList.remove('lobby_closed'); } }; })(); teachingStage2=true; teaching=(function() { var playerShadow=document.getElementById('player_shadow'); var ballShadow=document.getElementById('ball_shadow'); var animateInterval=false; var animateTimeout=false; var animateStopped=false; var playerShadowAnimateStop=function() { animateStopped=true; if(animateInterval) { clearInterval(animateInterval); } if(animateTimeout) { clearTimeout(animateTimeout); } animateInterval=false; animateTimeout=false; playerShadow.style.display='none'; ballShadow.style.display='none'; } playerShadowAnimate=function(n, x0, pvx, pvy, ba, pa=0, bv=50) { if(n==0 || animateStopped) { animateTimeout=false; return; } animateStopped=false; /*playerShadow.style.marginTop='-50px'; ballShadow.style.marginLeft='-150px'; playerShadow.style.display='block'; ballShadow.style.display='block';*/ /* var px0=x0-pvx; var py0=x0-pvy; var bx0=x0+bv;*/ var baa=Math.sqrt(1-ba*ba); var i=-1; var m=animateInterval=setInterval(function() { playerShadow.style.marginTop=parseInt(i*pvy)+'px'; playerShadow.style.marginLeft=parseInt(x0+i*pvx)+'px'; playerShadow.style.transform='rotate('+pa+'deg)'; if(i<=0) { ballShadow.style.marginLeft=parseInt(x0+i*bv-9)+'px'; ballShadow.style.marginTop='-4px'; } else { ballShadow.style.marginLeft=parseInt(x0-i*bv*baa-9)+'px'; ballShadow.style.marginTop=parseInt(i*bv*ba-4)+'px'; } if(i==-1) { playerShadow.style.display='block'; ballShadow.style.display='block' } i+=0.007; if(i>1) { clearInterval(m); animateInterval=false; playerShadow.style.display='none'; ballShadow.style.display='none'; //if(! animateStopped) animateTimeout=setTimeout(function(){playerShadowAnimate(n-1, x0, pvx, pvy, ba, pa, bv);}, 1500/*2000*/); } }, 0); } animate4=function() { animateStopped=false; setTimeout(function(){playerShadowAnimate(3, 331, -20, 0, 0.406, 78,120);}, 500); } animate5=function() { animateStopped=false; setTimeout(function(){playerShadowAnimate(3, 331, -20, 0, -0.406, 102,120);}, 500); } animate6=function() { animateStopped=false; setTimeout(function(){playerShadowAnimate(3, 331, -20, -40, 0, 90,120);}, 500); } animate7=function() { animateStopped=false; setTimeout(function(){playerShadowAnimate(3, 331, -20, -40, 0.4, 78,120);}, 500); } var nodeBlock2=document.getElementById('teaching_block2'); var nodeBlock2_2=document.getElementById('teaching_block2_2'); return { stage: false, start: function() { if(localStorage.teachingStage) { teaching['step'+localStorage.teachingStage](); return; } teaching.stage=0; Main.game.rally.player0.holded=false; //if(localStorage.teachingEnd) return; //stage=0; /*var m=false; setTimeout(this.step1, 2000); document.body.addEventListener('keydown', m=function(e) { if(e.keyCode==32) { document.body.removeEventListener('keydown', m); teaching.step2(); } });*/ document.getElementById('border_serve').style.display='none'; setTimeout(function(){teaching.step1();}, 1000); }, /*step1: function() { if(stage>1) return; document.getElementById('teaching_block1').style.display='block'; },*/ step1: function() { //if(ym) ym(64682380,'reachGoal','ts1'); var keysMove=[65, 68, 83, 87]; //var keysTurn=[39, 37]; var keysMoveWas=false; var keysTurnWas=false; var m=false; teaching.stage=2; teachingStage2=true; document.getElementById('teaching_block1').style.display='none'; document.getElementById('teaching_block2').style.display='block'; document.body.addEventListener('keydown', m=function(e) { if(keysMove.indexOf(e.keyCode)!==-1) { document.body.removeEventListener('keydown', m); setTimeout(function(){teaching.step2();}, 1000); } }); }, step2: function() { if(ym) ym(64682380,'reachGoal','ts1'); teaching.stage=2; localStorage.teachingStage=teaching.stage; teachingStage2=true; document.getElementById('teaching_block2').style.display='none'; document.getElementById('teaching_block2_2').style.display='block'; var keysTurn=[39, 37]; var m=false; document.body.addEventListener('keydown', m=function(e) { if(keysTurn.indexOf(e.keyCode)!==-1) { document.body.removeEventListener('keydown', m); setTimeout(function(){teaching.step3();}, 1000); } }); }, step3: function() { if(ym) ym(64682380,'reachGoal','ts2'); teaching.stage=3; localStorage.teachingStage=teaching.stage; teachingStage2=false; //document.getElementById('teaching_block2').style.display='none'; document.getElementById('teaching_block2_2').style.display='none'; document.getElementById('wait_').style.display='block'; document.getElementById('teaching_block1').style.display='block'; //document.getElementById('teaching_block4').style.display='block'; var m=false; document.body.addEventListener('keydown', m=function(e) { if(e.keyCode==32) { Main.game.rally.player0.hold(true, Main.game.rally.time.get()) Main.game.rally.player0.holded=false; document.body.removeEventListener('keydown', m); //teaching.step4(); } }); /*teaching.end();*/ }, step4: function() { if(ym) ym(64682380,'reachGoal','ts3'); teaching.stage=4; animate4(); localStorage.teachingStage=teaching.stage; document.getElementById('wait_').style.display='block'; document.getElementById('teaching_block1').style.display='none'; document.getElementById('teaching_block4').style.display='block'; document.getElementById('border_cort_blue_bottom').style.display='block'; }, step5: function() { if(ym) ym(64682380,'reachGoal','ts4'); teaching.stage=5; animate5(); localStorage.teachingStage=teaching.stage; document.getElementById('wait_').style.display='block'; document.getElementById('teaching_block4').style.display='none'; document.getElementById('teaching_block5').style.display='block'; document.getElementById('border_cort_blue_top').style.display='block'; document.getElementById('border_cort_blue_bottom').style.display='none'; }, step6: function() { Main.game.rally.player0.afterKeyPressedChange(); if(ym) ym(64682380,'reachGoal','ts5'); teaching.stage=6; animate6(); localStorage.teachingStage=teaching.stage; document.getElementById('wait_').style.display='block'; document.getElementById('border_cort_blue_top').style.display='none'; document.getElementById('teaching_block5').style.display='none'; document.getElementById('teaching_block2').style.display='block'; document.getElementById('teaching_block2_cont').innerHTML='Продолжаем! Набегайте на мяч снизу вверх,
чтобы закрутить его.'; teachingStage2=true; }, step7: function() { if(ym) ym(64682380,'reachGoal','ts6'); teaching.stage=7; animate7(); localStorage.teachingStage=teaching.stage; document.getElementById('wait_').style.display='block'; document.getElementById('teaching_block4').style.display='block'; document.getElementById('teaching_block2').style.display='none'; document.getElementById('teaching_block4_cont').innerHTML='Супер! Теперь отбейте мяч сюда,
закрутив его посильнее.'; document.getElementById('border_cort_blue_bottom').style.display='block'; }, step11: function() { if(ym) ym(64682380,'reachGoal','ts7'); teaching.stage=11; localStorage.teachingStage=teaching.stage; teaching.stage11successCount=0; document.getElementById('wait_').style.display='block'; document.getElementById('teaching_block4_cont').innerHTML='...или сюда.'; document.getElementById('teaching_block5_cont').innerHTML='Теперь подаёт соперник! Отбейте мяч сюда...'; document.getElementById('border_cort_blue_bottom').style.display='block'; document.getElementById('teaching_block5').style.display='block'; document.getElementById('teaching_block4').style.display='block'; document.getElementById('border_cort_blue_top').style.display='block'; }, step12: function() { if(ym) ym(64682380,'reachGoal','ts11'); teaching.stage=12; localStorage.teachingEnd=1; document.getElementById('wait_').style.display='none'; document.getElementById('teaching_block4').style.display='none'; document.getElementById('teaching_block5').style.display='none'; document.getElementById('border_cort_blue_top').style.display='none'; document.getElementById('border_cort_blue_bottom').style.display='none'; toast('Поздравляем! Вы прошли обучение.
Пора в бой.'); keySpaceOccupied=true; setTimeout(function(){localStorage.teachingStage='';window.location.href='/?teaching_end';}, 1900); }, end: function() { stage=3; teachingStage2=false; document.getElementById('teaching_block3').style.display='none'; localStorage.teachingEnd=1; }, failBorder: function() { return; if(localStorage.failBorderNotShow) return; document.getElementById('teaching_fail_border').style.display='block'; }, failOut: function() { return; if(localStorage.failOutNotShow) return; document.getElementById('teaching_fail_out').style.display='block'; }, failOutClose: function() { localStorage.failOutNotShow=1; document.getElementById('teaching_fail_out').style.display='none'; }, failBorderClose: function() { localStorage.failBorderNotShow=1; document.getElementById('teaching_fail_border').style.display='none'; }, playerSetPos: function(x, y) { nodeBlock2.style.left=Math.round(x*fieldScale+12)+'px'; nodeBlock2.style.bottom=Math.round((40+y)*fieldScale)+'px'; nodeBlock2.style.width='auto'; nodeBlock2_2.style.left=Math.round(x*fieldScale+12)+'px'; nodeBlock2_2.style.top=Math.round((53-y)*fieldScale)+'px'; }, collision: function(type, number) { //console.log(Main.game.rally.ball.va); console.log(type, number); if(teaching.stage==4) { if(type=='field' && number==3) { toastSuccess(); teaching.stage4success=true; } } else if(teaching.stage==5) { if(type=='field' && number==2) { teaching.stage5success=true; toastSuccess(); } } else if(teaching.stage==6 && type=='player') { if(Math.abs(Main.game.rally.ball.va)>=0.015) { toastSuccess(); teaching.stage6success=true } } else if(teaching.stage==7) { if(type=='player') { teaching.stage7success1=false; if(Main.game.rally.ball.va>=0.02) { toastSuccess(); teaching.stage7success2=true } else teaching.stage7success2=false; } else if(type=='field' && number==3) { teaching.stage7success1=true; if(teaching.stage7success2) toastSuccess(); } } else if(teaching.stage==11) { if(type=='player') { teaching.stage11success=false; } else if(type=='field' && (number==2 || number==3)) { teaching.stage11success=true; toastSuccess(); teaching.stage11successCount++; } } }, rallyStart: function() { if(teaching.stage>=4 && teaching.stage<=7) { playerShadowAnimateStop(); } }, rallyEnd(whoWin) { if(teaching.stage==3) teaching.step4(); else if(teaching.stage==4) { if(teaching.stage4success) teaching.step5(); else { animate4(); toast('Мимо! Попробуйте еще раз.'); } } else if(teaching.stage==5) { if(teaching.stage5success) teaching.step6(); else { animate5(); toast('Мимо! Попробуйте еще раз.'); } } else if(teaching.stage==6) { if(teaching.stage6success) teaching.step7(); else { toast('Слабовато! Закрутите посильнее.'); animate6(); } } else if(teaching.stage==7) { if(! teaching.stage7success1) { animate7(); toast('Мимо! Попробуйте еще раз.'); } else if(! teaching.stage7success2) { animate7(); toast('Слабое вращение! Попробуйте еще раз.'); } else { teaching.step11(); } } else if(teaching.stage==11) { if(! teaching.stage11success) { toast('Мимо! Попробуйте еще раз.'); } else { if(teaching.stage11successCount>=2) { toastSuccess(); teaching.step12(); } else { toast('Здорово! И еще разок для закрепления.'); } } } }, skip: function() { localStorage.teachingEnd=1; window.location.href='/?teaching_end'; } }; })(); toastNode=document.getElementById('toast'); toastContNode=document.getElementById('toast_cont'); toastTimer=false; toast=function(mes, t=3500) { if(toastTimer) clearTimeout(toastTimer); toastContNode.innerHTML=mes; toastNode.style.display='block'; toastNode.style.width='auto'; toastTimer=setTimeout(function(){toastNode.style.display='none';}, t); } toastSuccessMess=['Супер!', 'Отлично!', 'Получилось!']; toastSuccess=function() { toast(toastSuccessMess[parseInt((Math.random()-0.0000001)*toastSuccessMess.length)], 1500) } Main.init(); var lobbyClose=document.querySelector('#lobby_close'); //lobbyClose.addEventListener('click', function(){document.body.classList.add('lobby_closed');}); var lobbyOpen=document.querySelector('#lobby_open'); //lobbyOpen.addEventListener('click', function(){document.body.classList.remove('lobby_closed');}); document.getElementById('help_close').addEventListener('click', function(){localStorage.helpClosed=1;document.body.classList.add('help_closed');}); document.getElementById('help_open').addEventListener('click', function(){localStorage.helpClosed='';document.body.classList.remove('help_closed');}); document.querySelector('#chat #chat_close').addEventListener('click', function(){document.body.classList.add('chat_closed');}); document.getElementById('chat_open').addEventListener('click', function(){document.body.classList.remove('chat_closed');}); teaching.start(); }); var networkClient=false; var time=false; var gameStopWait=false; var gameStopped=false; Main=class { static init() { networkClient=Main.networkClient=NetworkClient.NetworkClient('tennis.thelv.ru', 8083); time=Game.Rally.Time.Time(); Main.view=MainView(); if(! localStorage.auth) localStorage.auth=(Math.random())+(Math.random())+(Math.random())+(Math.random()); //networkClient=Main.networkClient=NetworkClient.NetworkClient('tennis2d.org', 8084); Main.gameCreate('local', false, time.get()); if(localStorage.name) Main.nameSet(localStorage.name); } static messageSend(message, sendType=null) { Main.networkClient.messageSend(message, sendType); } static gameCreate(type, whoMain, t, opponent=false, state=false) { if(type=='network') lobby.hide(); else if(type=='local') lobby.show(); gameStopped=false; document.body.classList.remove('game_local'); document.body.classList.remove('game_network'); document.body.classList.remove('game_view'); document.body.classList.add('game_'+type); if(Main.game) { Main.game.destroy(); } Main.game = Game.Game(type, whoMain, t, opponent, state); } static connectionEstablished(reconnect) { Main.messageSend({auth: localStorage.auth, name: localStorage.name, reconnect: reconnectIs}); } static messageReceive(message) { switch(message.tp) { case 'user_id': Main.userId=message.user_id; break; case 'users': Main.view.users(message); break; case 'invites': Main.view.invites(message); break; case 'game_create': Main.gameCreate('network', message.first_serve, message.t, message.opponent); break; case 'name_set': Main.nameSet(message.name); break; case 'game_stop': Main.gameStop(); break; case 'game_view': Main.gameCreate('view', message.first_serve, message.t, false, message); break; case 'chat': chat.receive(message); break; case 'game_view_leave': Main.gameViewLeave(); break; case 'new_window_opened': Main.newWindowOpened(); break; } } static nameSet(name) { localStorage.name=name; Main.view.name(name); } static nameChange() { var name=''; if(name=prompt('Введите имя:')) { Main.nameSet(name); Main.messageSend({tp: 'name_set', name: name}); } } static invite(id) { if(Main.game.type=='network' && ! Main.game.stopped) { alert('Чтобы начать новую игру, необходимо выйти из этой.'); return; } networkClient.messageSend({tp: 'invite_send', id: id}); } static uninvite(id) { networkClient.messageSend({tp: 'invite_cancel', id: id}); } static gameLeave() { if(gameStopped) { Main.gameCreate('local', false, time.getAbs()); gameStopped=false; } else if(Main.game.type!=='view') { networkClient.messageSend({tp: 'game_leave'}); gameStopWait=true; } else { networkClient.messageSend({tp: 'game_view_leave'}); } } static gameView(id) { networkClient.messageSend({tp: 'game_view', id: id}); } static gameStop() { gameStopped=true; if(gameStopWait) { Main.gameCreate('local', false, time.getAbs()); gameStopWait=false; } else { //Main.game.rally.player0.view.hide(); //Main.view.gameLeaveOpponent(); Main.game.opponentLeave(); lobby.show(); } } static gameViewLeave() { Main.gameCreate('local', false, time.getAbs()); lobby.show(); } static newWindowOpened() { networkClient.stop(); Main.view.newWindowOpened(); } } Main.stage= { addChild: function(a) { // } } MainView=function() { var nameNode=document.querySelector('#name'); var usersFreeNode=document.querySelector('#users_free_list'); var usersNotFreeNode=document.querySelector('#users_not_free_list'); var usersInvitesWhoNode=document.querySelector('#users_invites_who_list'); var usersInvitesFromNode=document.querySelector('#users_invites_from_list'); var gameLeaveNode=document.querySelector('#game_leave'); var waitNode=document.querySelector('#wait'); document.querySelector('#game_leave').addEventListener('click', function() { Main.gameLeave(); }); document.querySelector('#name_change').addEventListener('click', function() { Main.nameChange(); }); var userNodeCreate=function(user, title) { var e=document.createElement('div'); e.setAttribute('class', 'user'); if(title) e.setAttribute('title', title); e.innerHTML=''+user.name+''; return e; } var res= { name: function(name) { nameNode.innerText=name; }, users: function(users) { var free=users.free; usersFreeNode.innerHTML=''; for(var i in free) { var user=free[i]; var e=userNodeCreate(user, 'Пригласить в игру'); (function(id) { e.addEventListener('click', function() { Main.invite(id); }); })(user.id); if(user.id!=Main.userId) usersFreeNode.appendChild(e); } if(free.length==0 || free.length==1) { usersFreeNode.innerText='Никого нет.'; } /*var notFree=users.not_free; usersNotFreeNode.innerHTML=''; for(var i in notFree) { var user=notFree[i]; var e=userNodeCreate(user, 'Пригласить в игру'); (function(id) { e.addEventListener('click', function() { Main.invite(id); }); })(user.id); usersNotFreeNode.appendChild(e); } if(notFree.length==0) { usersNotFreeNode.innerText='Никого нет.'; }*/ var games=users.games; usersNotFreeNode.innerHTML=''; for(var i in games) { var game=games[i]; var user0=game.players[1]; var user1=game.players[0]; var gameNode=document.createElement('div'); gameNode.setAttribute('class', '_game'); var e1=userNodeCreate(user0); (function(id) { e1.addEventListener('click', function() { ///Main.invite(id); }); })(user0.id); var e2=userNodeCreate(user1); (function(id) { e2.addEventListener('click', function() { //Main.invite(id); }); })(user1.id); gameNode.appendChild(e1); var vs=document.createElement('span'); vs.innerHTML=' с '; gameNode.appendChild(vs); gameNode.appendChild(e2); usersNotFreeNode.appendChild(gameNode); (function(id) { gameNode.addEventListener('click', function() { Main.gameView(id); }); })(game.id); } if(games.length==0) { usersNotFreeNode.innerText='Нет игр.'; } }, invites: function(invites) { var who=invites.invites_who; usersInvitesWhoNode.innerHTML=''; for(var i in who) { var user=who[i]; var e=userNodeCreate(user, 'Отменить приглашение'); (function(id) { e.addEventListener('click', function() { Main.uninvite(id); }); })(user.id); usersInvitesWhoNode.appendChild(e); } if(who.length==0) { usersInvitesWhoNode.innerText='Пригласите игрока онлайн поиграть.'; } var from=invites.invites_from; usersInvitesFromNode.innerHTML=''; for(var i in from) { var user=from[i]; var e=userNodeCreate(user, 'Начать игру'); (function(id) { e.addEventListener('click', function() { Main.invite(id); }); })(user.id); usersInvitesFromNode.appendChild(e); } if(from.length==0) { usersInvitesFromNode.innerText='Вас пока никто не пригласил.'; } }, gameLeaveOpponent: function() { waitNode.innerHTML='Оппонент покинул игру'; waitNode.style.display='block'; }, newWindowOpened: function() { document.getElementById('new_window_opened').style.display='flex'; } } return res; } class MathLib { static vAngle(v) { var x=v[0]; var y=v[1]; return x==0 ? (y>0 ? Math.PI/2 : -Math.PI/2) : (x>0 ? Math.atan(y/x) : Math.atan(y/x)+Math.PI); } static getAngleByCoords(x, y) { return x==0 ? (y>0 ? Math.PI/2 : -Math.PI/2) : (x>0 ? Math.atan(y/x) : Math.atan(y/x)+Math.PI); } static getLengthByCoords(x, y) { return Math.sqrt(x*x + y*y); } static linesIntersection(x00, y00, x01, y01, x10, y10, x11, y11) { if((x00==x01 && y00==y01)||(x10==x11 && y10==y11)) { return {intersect: false, linesIntersect: false}; } if(x00==x01 && x10==x11) { return {intersect: false, linesIntersect: false}; } if(x00==x01 || (x10==x11 && y00!=y01)) { var reverse=true; var m=x00;x00=y00;y00=m; m=x01;x01=y01;y01=m; m=x10;x10=y10;y10=m; m=x11;x11=y11;y11=m; } else { var reverse=false; } if(x10==x11 && y00==y01) { x=x10; y=y00; var p0=(x-x00)/(x01-x00); var p1=(y-y10)/(y11-y10); } else { var k0=(y01-y00)/(x01-x00); var k1=(y11-y10)/(x11-x10); if(k0==k1) { return {intersect: false, linesIntersect: false}; } var x=(k0*x00-k1*x10-y00+y10)/(k0-k1); var y=y00+k0*(x-x00); var p0=(x-x00)/(x01-x00); var p1=(x-x10)/(x11-x10); } if (p1>=0 && p1<=1) { if(p0>0 && p0<=1) { if(reverse) { m=x;x=y;y=m; } return {intersect: true, linesIntersect: true, x: x, y:y, k: p0} } else { return {intersect: false, linesIntersect: true, k: p0}; } } else { return {intersect: false, linesIntersect: false}; } } static intersectionWithMovingLine(x00, y00, x01, y01, x10, y10, x11, y11, x20, y20, x21, y21) { var intersection1=linesIntersection(x00, y00, x01, y01, x10, y10, x11, y11); var intersection2=linesIntersection(x00, y00, x01, y01, x20, y20, x21, y21); if(intersection1.intersect) { return intersection1; } if(intersection2.intersect) { return intersection2; } if(intersection1.linesIntersect && intersection2.linesIntersect) { var k1=intersection1.k; var k2=intersection2.k; if(k1>k2) { var m=k1;k1=k2;k2=m; } if((k2>0 && k2<=1) || (k1<=1 && k2>1)) { var k=(Math.max(k1, 0)+Math.min(k2, 1))/2; var x=x00+(x01-x00)*k; var y=y00+(y01-y00)*k; return {intersect: true, k: k, x: x, y: y}; } else { return {intersect: false}; } } else { return {intersect: false}; } } static getLineDirection(x0, y0, x1, y1) { var x = x1 - x0; var y = y1 - y0; var l = MathLib.getLengthByCoords(x, y); if (l != 0) { x = x / l; y = y / l; } return [x, y]; } static decomposeVector(vx, vy, x, y) { var vt = vx * x + vy * y; var vn = - vx * y + vy * x; return [vt, vn]; } static composeVector(vt, vn, x, y) { var vx = vt * x - vn * y; var vy = vt * y + vn * x; return [vx, vy]; } static bounceOfRotatingBall(vt, vn, w, r, k, y) { w = -w; var sgn = (vt < w*r) ? 1 : -1; var dv=((w*r-vt)*sgn / (k*(y+1)) > 2*vn) ? 2*vn*k*sgn : (w*r-vt)/(y+1); var dw = - (-dv*y/r); return [dv, dw]; } /* "v" library - vector operations */ static vPlus(a, b) { return [a[0] + b[0], a[1] + b[1]]; } static vMinus(a, b) { return [a[0] - b[0], a[1] - b[1]]; } static vTurn(v, dir) { return [v[0] * dir[0] - v[1] * dir[1], v[1] * dir[0] + v[0] * dir[1]]; } static vReturn(v, dir) { return [v[0] * dir[0] + v[1] * dir[1], v[1] * dir[0] - v[0] * dir[1]]; } static vLength(v) { return Math.sqrt(v[0] * v[0] + v[1] * v[1]); } static vLengthSqr(v) { return v[0] * v[0] + v[1] * v[1]; } static vNormalize(v) { if (v[0] == 0 && v[1] == 0) { return [0, 0]; } else { var l = MathLib.vLength(v); return [v[0] / l, v[1] / l]; } } static vProduct(v, a) { return [a * v[0], a * v[1]]; } static vVectorProduct(a, b) { return -a[0] * b[1] + a[1] * b[0]; } } View=function() { var e=document.createElement('div'); //e.innerHTML='view'; e.setAttribute('class', 'view'); document.querySelector('#center').append(e); return { showPosition: function(x, y, a) { e.style.left=x+'px'; e.style.top=-y+'px'; }, addChild: function() { // } } } NetworkClient.NetworkClient=function(host, port) { var ws=false; reconnectIs=false; var stopped=false; var res= { NetworkClient: function(host, port) { host=host; port=port; this.connect(); }, messageSend: function(message) { if(ws) ws.send(JSON.stringify(message), function(){}); }, reconnect: function() { if(stopped) return; reconnectIs=true; if(ws) { ws=false; setTimeout(res.connect, 1000); } }, connect: function() { ws=new WebSocket('wss://'+host+':'+port); ws.onclose=function() { res.reconnect(); }; ws.onerror=function() { res.reconnect(); }; ws.onmessage=function(message) { message=JSON.parse(message.data); if (message.g) { Main.game.messageReceive(message); } else { Main.messageReceive(message); } }; ws.onopen=function() { Main.connectionEstablished(reconnectIs); }; }, stop: function() { stopped=true; } } res.NetworkClient(host, port); return res; } Game.Game=function(type, whoMain, t, opponent=false, state=false) { var view=0, rally=0, wait=0, referee=0; //view var gameHeadNode=document.querySelector(false && (type=='local') ? '#game_local_head' : '#game_network_head'); var gameCaptionNode=gameHeadNode.querySelector('._caption'); var res= { get type(){return type;}, set type(a){type=a;}, get whoMain(){return whoMain;}, set whoMain(a){whoMain=a;}, get view(){return view;}, set view(a){view=a;}, get rally(){return rally;}, set rally(a){rally=a;}, get wait(){return wait;}, set wait(a){wait=a;}, get referee(){return referee;}, set referee(a){referee=a;}, stopped: false, Game: function(type, whoMain, t, state) { //view this.type = type; this.whoMain = whoMain; view = Game.GameView(); Main.stage.addChild(view); rally = Game.Rally.Rally(this); switch(type) { case 'local': wait = Game.Wait.Wait(this); break; case 'network': wait=Game.Wait.NetworkWait(this); break; case 'view': wait=Game.Wait.ViewWait(this); break; } referee=Game.Referee.Referee(this, (this.type=='view') ? ! state.state.who_serve: whoMain); time.reset(t); if(type!='local') { time.sync(); } referee.start(); //referee.start() executes from string time.synchronize() rally.viewShowServeLines(true); gameHeadNode.style.display='block'; if(type=='network') { gameCaptionNode.innerText='Матч с '+opponent.name; } else if(type=='local') { gameCaptionNode.innerText='Матч против компьютера'; } else if(type=='view') { waitView.ready('Ожидание готовности игроков'); this.setState(state.state); gameCaptionNode.innerText='Матч '+state.players[1].name+' с '+state.players[0].name; } }, messageReceive: function(message) { switch(message.tp) { case 'pcp': case 'pcpa': rally.player1.messageReceive(message); break; case 'bh': rally.ball.messageReceive(message); break; case 'rw': rally.referee.messageReceive(message); break; case 'wr': wait.messageReceive(message); break; /*case 'ts': rally.time.messageReceive(message); break;*/ } }, messageSend: function(message, sendType=null) { message.g=true; if (type=='network') { Main.networkClient.messageSend(message, sendType); } }, destroy: function() { rally.ball.destroy(); rally.timer.unbind(); rally.player0.unbind(); rally.player1.unbind(); wait.unbind(); gameHeadNode.style.display='none'; }, opponentLeave: function() { wait.opponentLeave(); rally.ball.opponentLeave(); rally.player1.hide(); if(this.type=='view') rally.player0.hide(); this.stopped=true; } } if(type=='view') { res.setState=function(state) { console.log(state); res.referee.scoreSet(state.score); if(state.wait>0) { wait.start(state.wait); } else if(state.wait==-1) { wait.wait(); } else { wait.start(0); } if(state.ball.h) res.messageReceive_(state.ball.h); if(state.players[0].cp) res.messageReceive_(state.players[0].cp); if(state.players[0].cpa) res.messageReceive_(state.players[0].cpa); if(state.players[1].cp) res.messageReceive_(state.players[1].cp); if(state.players[1].cpa) res.messageReceive_(state.players[1].cpa); } res.messageReceive_= function(message) { switch(message.tp) { case 'pcp': if(message.side) { message.x=-message.x; message.y=-message.y; message.vx=-message.vx; message.vy=-message.vy; message.mx=-message.mx; message.my=-message.my; rally.player0.messageReceive(message); } else { rally.player1.messageReceive(message); } break; case 'pcpa': if(message.side) { //message.a=message.a-Math.PI; rally.player0.messageReceive(message); } else { rally.player1.messageReceive(message); } break; case 'bh': if(message.side) { message.x=-message.x; message.y=-message.y; message.vx=-message.vx; message.vy=-message.vy; } rally.ball.messageReceive(message); break; case 'rw': if(message.side) { message.w=! message.w; } rally.referee.messageReceive(message); break; case 'wr': if(message.t>0) { wait.messageReceive(message); break; } /*case 'ts': rally.time.messageReceive(message); break;*/ } }; res.messageReceive=function(message) { res.messageReceive_(message); } } res.Game(type, whoMain, t, state); return res; } Game.GameView=View; /*{ GameView() { //super(1, 1); //x = 600; //y = 330; } }*/ Game.Rally.Rally=function(game) { var Referee=Game.Rally.Referee.Referee; var FieldLines=Game.Rally.FieldLines.FieldLines; var BorderLines=Game.Rally.BorderLines.BorderLines; var MiddleLines=Game.Rally.MiddleLines.MiddleLines; var ServeLines=Game.Rally.ServeLines.ServeLines; var Time=Game.Rally.Time.Time; var Timer=Game.Rally.Timer.Timer; var Player=Game.Rally.Player.Player; var LocalPlayer=Game.Rally.Player.LocalPlayer; var BotPlayer=Game.Rally.Player.BotPlayer; var RemotePlayer=Game.Rally.Player.RemotePlayer; var Ball=Game.Rally.Ball.Ball; var referee=0, fieldLines=0, borderLines=0, middleLines=0, serveLines=0, time=0, timer=0, ball=0, player0=0, player1=0; var serveLinesNode=document.querySelector('#border_serve'); var res= { get game(){return game;}, set game(a){game=a;}, get referee(){return referee;}, set referee(a){referee=a;}, get fieldLines(){return fieldLines;}, set fieldLines(a){fieldLines=a;}, get borderLines(){return borderLines;}, set borderLines(a){borderLines=a;}, get middleLines(){return middleLines;}, set middleLines(a){middleLines=a;}, get serveLines(){return serveLines;}, set serveLines(a){serveLines=a;}, get time(){return time;}, set time(a){time=a;}, get timer(){return timer;}, set timer(a){timer=a;}, get ball(){return ball;}, set ball(a){ball=a;}, get player0(){return player0;}, set player0(a){player0=a;}, get player1(){return player1;}, set player1(a){player1=a;}, Rally: function(game) { this.game = game; time = window.time; fieldLines = FieldLines(); borderLines = BorderLines(); middleLines = MiddleLines(); serveLines = ServeLines(); referee = Referee(game); ball = Ball(game); player0 = (game.type!=='view') ? LocalPlayer(game, true) : RemotePlayer(game, true); player1 = (game.type == 'local') ? BotPlayer(game, false) : RemotePlayer(game, false); //view /*game.view.addChild(middleLines.view); game.view.addChild(serveLines.view); game.view.addChild(fieldLines.view); game.view.addChild(borderLines.view);*/ game.view.addChild(player0.view); game.view.addChild(player1.view); game.view.addChild(ball.view); //start timer = Timer(game); }, shiftTime: function(t) { middleLines.lines[0].shiftTime(t); middleLines.lines[1].shiftTime(t); player0.shiftTime(t); player1.shiftTime(t); ball.shiftTime(t); }, viewShowServeLines: function(show) { if (show) { serveLines.view.visible = true; serveLinesNode.style.display='block'; } else { serveLines.view.visible = false; serveLinesNode.style.display='none'; } } } res.Rally(game); return res; } Game.Rally.Ball.Ball=function(game) { var BallEval=Game.Rally.Ball.BallEval.BallEval; var BallCollisions=Game.Rally.Ball.BallCollisions.BallCollisions; var eval=0, collisions=0, x=0, y=0, vx=0, vy=0, va=0, a=0, r=4, view=0; var res= { get game(){return game;}, set game(a){game=a;}, get eval(){return eval;}, set eval(a){eval=a;}, get collisions(){return collisions;}, set collisions(a){collisions=a;}, get x(){return x;}, set x(a){x=a;}, get y(){return y;}, set y(a){y=a;}, get vx(){return vx;}, set vx(a){vx=a;}, get vy(){return vy;}, set vy(a){vy=a;}, get va(){return va;}, set va(a){va=a;}, get a(){return a;}, set a(b){a=b;}, get r(){return r;}, set r(a){r=a;}, get view(){return view;}, set view(a){view=a;}, //constructor Ball: function(game) { this.game = game; this.eval = BallEval(this); this.a = 0; this.setControlPoint(0, 0, 0, 0, 0, 0); this.collisions = BallCollisions(game, this); //view view = Game.Rally.Ball.BallView(this); this.viewShowPos(); }, //view methods viewShowPos: function() { view.showPosition(this.x, this.y, this.a); }, //logic methods setControlPoint(x, y, vx, vy, va, t) { this.x = x; this.y = y; this.vx = vx; this.vy = vy; this.va = va; eval.init(t); if(game.type=='local' && game.rally.player1) { game.rally.player1.ballHit(x, y, vx, vy, va, t); } }, shiftTime: function(t) { eval.eval(t); collisions.collise(t); this.viewShowPos(); //view //game.rally.middleLines.ballPos(x); }, serve: function(start, who, t) { if (start) { this.setControlPoint(0, 0, 0.25 * (who ? 1: -1), 0, 0, t); collisions.init(t); } else { this.setControlPoint(0, 0, 0, 0, 0, game.rally.time.get()); collisions.init(t); } }, messageSendHit: function(t) { game.messageSend( {tp: 'bh', t: t, x: this.x, y: this.y, vx: this.vx, vy: this.vy, va: this.va} ,{firstConnection: 9, connectionsRange: 3, connectionsCount: 2, seriesName: 'bh'} ); //"bh" == "ball hit" }, messageReceive: function(message) { switch(message.tp) { case 'bh': this.setControlPoint( -message.x, -message.y, -message.vx, -message.vy, message.va, message.t); game.rally.middleLines.hit(1, -message.x, message.t); game.rally.referee.collision('player', 1); collisions.hitWas(1); this.shiftTime(game.rally.time.get()); break; } }, destroy: function() { view.remove(); }, opponentLeave: function() { this.setControlPoint(x, y, 0, 0, va, game.rally.time.get()); } } res.Ball(game); return res; } Game.Rally.Ball.BallView=function() { var e=document.createElement('div'); e.innerHTML='
'; e.setAttribute('id', 'ball'); document.querySelector('#field').append(e); return { showPosition: function(x, y, a) { e.style.left=x+'px'; e.style.top=-y+'px'; e.style.transform='rotate('+(-a)+'rad)'; }, addChild: function() { }, remove: function() { e.remove(); } } } Game.Rally.Ball.BallCollisions.BallCollisions=function(game, ball) { var Ball=Game.Rally.Ball.Ball; var prevX=0, prevY=0, prevT=0, excludeLine=0, excludePlayer=0, m=0.1; var res= { get game(){return game;}, set game(a){game=a;}, get ball(){return ball;}, set ball(a){ball=a;}, get prevX(){return prevX;}, set prevX(a){prevX=a;}, get prevY(){return prevY;}, set prevY(a){prevY=a;}, get prevT(){return prevT;}, set prevT(a){prevT=a;}, get excludeLine(){return excludeLine;}, set excludeLine(a){excludeLine=a;}, get m(){return m;}, set m(a){m=a;}, BallCollisions: function(game, ball) { this.game = game; this.ball = ball; this.init(0); }, init: function(t) { prevX = ball.x; prevY = ball.y; prevT = t; excludeLine = -1; }, collise: function(t) { //столкновение с линиями поля for (var i = 0; i < game.rally.fieldLines.lines.length; i++) { if (i != excludeLine) { var line=game.rally.fieldLines.lines[i].concat(); if (i == 0 || i == 2) { line[1] -= ball.r/2; line[3] -= ball.r/2; } else { line[1] += ball.r/2; line[3] += ball.r/2; } var collisionPoint = this.collisionWithLine(line, t); if (collisionPoint.exists) { this.bounceOfLine(line); ball.setControlPoint(collisionPoint.x, collisionPoint.y, ball.vx, ball.vy, ball.va, collisionPoint.t); ball.viewShowPos(); this.init(collisionPoint.t); excludeLine = i; ball.shiftTime(t); game.rally.referee.collision('field', i); break; } } else { excludeLine = -1; } } //выход за пределы поля for (var i = 0; i < game.rally.borderLines.lines.length; i++) { var line = game.rally.borderLines.lines[i]; var collisionPoint = this.collisionWithLine(line, t); if (collisionPoint.exists) { game.rally.referee.collision('border', 0); } } //столкновение с нашим игроком if (excludePlayer != 0 && game.type!='view') { var player = game.rally.player0.collisions.getParams(); var collisionPoint = this.collisionWithPlayer(player, t); if(collisionPoint.exists) { this.bounceOfPlayer(player); ball.setControlPoint(collisionPoint.x, collisionPoint.y, ball.vx, ball.vy, ball.va, collisionPoint.t); game.rally.middleLines.hit(0, collisionPoint.x, collisionPoint.t); ball.viewShowPos(); this.init(collisionPoint.t); excludePlayer = 0; excludeLine = -1; ball.messageSendHit(collisionPoint.t); ball.shiftTime(t); game.rally.referee.collision('player', 0); } } //столкновение с его игроком if ((game.type=='local') && (excludePlayer != 1) && teaching.stage>10) { var player = game.rally.player1.collisions.getParams(); var collisionPoint = this.collisionWithPlayer(player, t); if (collisionPoint.exists) { this.bounceOfPlayer(player); ball.setControlPoint(collisionPoint.x, collisionPoint.y, ball.vx, ball.vy, ball.va, collisionPoint.t); game.rally.middleLines.hit(1, collisionPoint.x, collisionPoint.t); ball.viewShowPos(); this.init(collisionPoint.t); excludePlayer = 1; excludeLine = -1; ball.shiftTime(t); game.rally.referee.collision('player', 1); } } this.init(t); game.rally.player0.collisions.init(t); game.rally.player1.collisions.init(t); }, collisionWithLine: function(line, t) { var i = MathLib.linesIntersection(prevX, prevY, ball.x, ball.y, line[0], line[1], line[2], line[3]); if (i.intersect) { return { exists: true, x: i.x, y:i.y, t: prevT+(t-prevT)*i.k}; } else { return { exists: false }; } }, collisionWithPlayer: function(player, t) { //положения мяча var ballR0 = [prevX, prevY]; var ballR1 = [ball.x, ball.y]; //положения игрока var plR0 = player.prevXy; var plR1 = player.xy; //углы игрока var plDir0 = player.prevDir; var plDir1 = player.dir; //переходим в новую систему отсчета var ballR0_ = MathLib.vReturn(MathLib.vMinus(ballR0, plR0), plDir0); var ballR1_ = MathLib.vReturn(MathLib.vMinus(ballR1, plR1), plDir0); //сдвиг из-за поворота игрока var turnShiftCoeff = MathLib.vReturn(plDir1, plDir0)[1]; ballR1_[1] -= turnShiftCoeff * ballR1_[0]; //сдвиг на радиус мяча if (ballR1_[1] - ballR0_[1] > 0) { ballR0_[1] += ball.r; ballR1_[1] += ball.r; } else { ballR0_[1] -= ball.r; ballR1_[1] -= ball.r; } //находим точку пересечения var k = ballR0_[1] / (ballR0_[1] - ballR1_[1]); if (k > 0 && k <= 1) { var x = ballR0_[0] + (ballR1_[0] - ballR0_[0]) * k; if (x >= -player.length && x <= player.length) { return { exists: true, x: prevX+(ball.x-prevX)*k, y: prevY+(ball.y-prevY)*k, t: prevT + (t - prevT) * k }; } else return { exists: false }; } else return { exists: false }; }, bounceOfLine: function(line) { var dir = MathLib.getLineDirection(line[0], line[1], line[2], line[3]); var vtn = MathLib.decomposeVector(ball.vx, ball.vy, dir[0], dir[1]); if (vtn[1] > 0) { vtn[0] *= -1; vtn[1] *= -1; dir[0] *= -1; dir[1] *= -1; } var d = MathLib.bounceOfRotatingBall(vtn[0], -vtn[1], ball.va, ball.r, 0.125, 2.5); vtn[0] += d[0]; ball.va += d[1]; vtn[1] *= -(1-0.1*(Math.abs(vtn[1]/0.1))); var v = MathLib.composeVector(vtn[0], vtn[1], dir[0], dir[1]); ball.vx = v[0]; ball.vy = v[1]; }, bounceOfPlayer: function(p) { //направление ракетки игрока var dir = p.dir; //разлагаем скорсть мяча по направлению ракетки (vtn) var vtn = MathLib.decomposeVector(ball.vx, ball.vy, dir[0], dir[1]); //меняем "полярность", чтобы мяч (якобы) налетал на игрока "Vnorm<0" if (vtn[1] > 0) { vtn[0] *= -1; vtn[1] *= -1; dir[0] *= -1; dir[1] *= -1; } //разлагаем скорость игрока по направлению ракетки (pvtn): Vnorm должно быть>0 var pvtn = MathLib.decomposeVector(p.v[0], p.v[1], dir[0], dir[1]); //хитрая формула для расчета "силы удара" (пересчет скорости игрока) pvtn[1] = (pvtn[1] / 0.14) * 0.012 + 0.25 * m; //расчет изменения Vnorm мяча по ЗСИ if (pvtn[1] < 0) { //упрощенные рассмотрение, если игрок набегает на мяч var newVn = vtn[1] + 2 * pvtn[1] / (m + 1); } else { //полноценный ЗСИ var newVn = -vtn[1] + 2 * (vtn[1] * m + pvtn[1]) / (m + 1); } //расчет изменений [Vtang, вращения мяча] исходя из [dVn мяча, Vtang мяча и игрока] var d = MathLib.bounceOfRotatingBall(vtn[0]-pvtn[0], (newVn-vtn[1])/2, ball.va, ball.r, 0.2, 2.5); vtn[0] += d[0]; ball.va += d[1]; vtn[1] = newVn; //пересчитываем скорость мяча из Norm-Tang разложения в X-Y var v = MathLib.composeVector(vtn[0], vtn[1], dir[0], dir[1]); ball.vx = v[0]; ball.vy = v[1]; }, hitWas: function(playerNumber) { excludeLine = -1; excludePlayer = playerNumber; }, reset: function() { excludePlayer = -1; excludeLine = -1; } } res.BallCollisions(game, ball); return res; } Game.Rally.Ball.BallEval.BallEval=function(ball) { var Ball=Game.Rally.Ball.Ball; var evalT=0, evalX=0, evalY=0, evalVx=0, evalVy=0, evalVa=0, evalA=0, rVaCoeff=0.00133333333333, evalDt=3, stopAcceleration=0.00005; var res= { get ball(){return ball;}, set ball(a){ball=a;}, get evalT(){return evalT;}, set evalT(a){evalT=a;}, get evalX(){return evalX;}, set evalX(a){evalX=a;}, get evalY(){return evalY;}, set evalY(a){evalY=a;}, get evalVx(){return evalVx;}, set evalVx(a){evalVx=a;}, get evalVy(){return evalVy;}, set evalVy(a){evalVy=a;}, get evalVa(){return evalVa;}, set evalVa(a){evalVa=a;}, get evalA(){return evalA;}, set evalA(a){evalA=a;}, get rVaCoeff(){return rVaCoeff;}, set rVaCoeff(a){rVaCoeff=a;}, get evalDt(){return evalDt;}, set evalDt(a){evalDt=a;}, get stopAcceleration(){return stopAcceleration;}, set stopAcceleration(a){stopAcceleration=a;}, BallEval: function(ball) { this.ball = ball; }, init: function(t) { evalT = t; evalX = ball.x; evalY = ball.y; evalVx = ball.vx; evalVy = ball.vy; evalA = ball.a; evalVa = ball.va; }, eval: function(t) { do { var actualT=Math.min(Math.max(t, this.evalT), this.evalT+this.evalDt); if(actualT>this.evalT) { var dt=actualT-this.evalT; var x = this.evalX + evalVx * dt; var y = this.evalY + evalVy * dt; var vL = MathLib.getLengthByCoords(evalVx, evalVy); var vA = MathLib.getAngleByCoords(evalVx, evalVy); if (vL != 0) { vA += evalVa * rVaCoeff * dt / vL; var vx = vL * Math.cos(vA); var vy = vL * Math.sin(vA); } else { var vx = evalVx; var vy = evalVy; } var a = evalA + evalVa * dt*0.2; var va = evalVa; if(actualT>=t) { ball.vx=vx; ball.vy=vy; ball.x=x; ball.y = y; ball.va = va; ball.a = a; return; } else { evalVx=vx; evalVy=vy; evalX=x; evalY = y; evalA = a; evalVa = va; evalT += evalDt; } } else { return; } } while(true); } } res.BallEval(ball); return res; } Game.Rally.Player.Player=function(game, side) { var PlayerEvalXY=Game.Rally.Player.PlayerEvalXY.PlayerEvalXY; var PlayerEvalA=Game.Rally.Player.PlayerEvalA.PlayerEvalA; var PlayerCollisions=Game.Rally.Player.PlayerCollisions.PlayerCollisions; var view=0, startX=0, startA=0, holded=0, collisions=0, x=0, y=0, a=0, vx=0, vy=0, va=0, movingX=0, movingY=0, movingA=0, evalXY=0, evalA=0, length=40; var trainingAMax=0.3155192913420435; var trainingAMin=0.13631929134204346; var res= { get view(){return view;}, set view(a){view=a;}, get startX(){return startX;}, set startX(a){startX=a;}, get startA(){return startA;}, set startA(a){startA=a;}, get holded(){return holded;}, set holded(a){holded=a;}, get collisions(){return collisions;}, set collisions(a){collisions=a;}, get x(){return x;}, set x(a){x=a;}, get y(){return y;}, set y(a){y=a;}, get a(){return a;}, set a(b){a=b;}, get vx(){return vx;}, set vx(a){vx=a;}, get vy(){return vy;}, set vy(a){vy=a;}, get va(){return va;}, set va(a){va=a;}, get movingX(){return movingX;}, set movingX(a){movingX=a;}, get movingY(){return movingY;}, set movingY(a){movingY=a;}, get movingA(){return movingA;}, set movingA(a){movingA=a;}, get evalXY(){return evalXY;}, set evalXY(a){evalXY=a;}, get evalA(){return evalA;}, set evalA(a){evalA=a;}, get length(){return length;}, set length(a){length=a;}, Player: function(game, side) { //logic this.game = game; this.side = side; evalXY = PlayerEvalXY(this); evalA = PlayerEvalA(this); holded = true; startX = side ? 360: -360; startA = side ? -Math.PI / 2 : Math.PI / 2; this.setControlPointXY(startX, 0, 0, 0, 0, 0, 0); this.setControlPointA(side ? -Math.PI/2 : Math.PI/2, 0, 0, 0); collisions = PlayerCollisions(this); //view view = Game.Rally.Player.PlayerView(this); this.viewShowPos(); }, //methods //view viewShowPos: function() { view.showPosition(this.x, this.y, this.a); }, //logic hold: function(hold, t) { if (hold) { //if(teaching.stage>2) { holded = true; } this.setControlPointXY(startX, 0, 0, 0, movingX, movingY, t); this.setControlPointA(startA, 0, movingA, t) collisions.init(t); } else { if(this.game.type=='local' && this.side==false && teaching.stage>10) { this.setControlPointA(startA+Math.sign(Math.random()-0.5)*(trainingAMin+Math.random()*(trainingAMax-trainingAMin)), 0, movingA, t); } holded = false; } }, setControlPointXY: function(x, y, vx, vy, movingX, movingY, t) { // console.log(movingX); this.movingX=movingX; this.movingY=movingY; this.x=x; this.y=y; this.vx=vx; this.vy=vy; evalXY.init(t); }, setControlPointA: function(a, va, movingA, t) { //console.log(movingA); this.movingA=movingA; this.a = a; this.va = va; evalA.init(t); }, shiftTime: function(t) { evalXY.eval(t); evalA.eval(t); this.viewShowPos(); if(teachingStage2 && this.local) teaching.playerSetPos(x, y); }, hide: function() { view.hide(); } } res.Player(game, side); return res; } Game.Rally.Player.PlayerView=function() { var e=document.createElement('div'); e.innerHTML='
'; e.setAttribute('class', 'player'); document.querySelector('#field').append(e); return { showPosition: function(x, y, a) { e.style.left=x+'px'; e.style.top=-y+'px'; e.style.transform='rotate('+(-a)+'rad)'; }, addChild: function() { }, remove: function() { e.remove(); }, hide: function() { e.style.visibility='hidden'; } } } Game.Rally.Player.LocalPlayer=function(game, side) { var KeyHandler=Game.Rally.Player.KeyHandler.KeyHandler; var keyHandler=0; var res=Game.Rally.Player.Player(game, side); res.LocalPlayer=function(game, side) { //logic keyHandler = ( side ) ? ( ( game.type == 'local' ) ? KeyHandler(this, 65, 68, 83, 87, 39, 37) : //KeyHandler(this, 65, 68, 83, 87, 39, 37) KeyHandler(this, 65, 68, 83, 87, 39, 37) ) : KeyHandler(this, -1, -1, -1, -1, -1, -1) ; } //logic //вызывается из keyHandler res.afterKeyPressedChange=function() { var movingX=0; var movingY=0; var movingA=0; if(keyHandler.keyLeftPressed){movingX-=1;} if(keyHandler.keyRightPressed){movingX+=1;} if(keyHandler.keyDownPressed){movingY-=1;} if(keyHandler.keyUpPressed){movingY+=1;} if(keyHandler.keyTurnCounterClockWisePressed && teaching.stage!=6){movingA+=1;} if (keyHandler.keyTurnClockWisePressed && teaching.stage!=6) { movingA -= 1; } if(Math.abs(this.movingX*this.movingY)==1) { movingX = movingX / 1.414213; movingY = movingY / 1.414213; } if (movingX != this.movingX || movingY != this.movingY || movingA != this.movingA) { var t = time.get(); this.shiftTime(t); if(movingX!=this.movingX || movingY!=this.movingY) { this.setControlPointXY(this.x, this.y, this.vx, this.vy, movingX, movingY, t); //this.sendMessage({message_type: 'control_point_xy', time: time, x: this.x, y: this.y, a: this.a, moving_x: this.movingX, moving_y: this.movingY, vx: this.controlPointVx, vy: this.controlPointVy, turning: this.turning}, this.sendTypeControlPoint); } if (movingA != this.movingA) { this.setControlPointA(this.a, 0, movingA, t); //this.sendMessage({message_type: 'control_point_a', time: time, x: this.x, y: this.y, a: this.a, moving_x: this.movingX, moving_y: this.movingY, vx: this.controlPointVx, vy: this.controlPointVy, turning: this.turning}, this.sendTypeControlPoint); } } } res.messageSendControlPointXY=function(t) { game.messageSend( {tp: 'pcp', t: t, x: this.x, y: this.y, mx: this.movingX, my: this.movingY, vx: this.vx, vy: this.vy} ,{firstConnection: 1, connectionsRange: 4, connectionsCount: 2, seriesName: 'pcp'} ); //"pcp"=="player_control_point" } res.messageSendControlPointA=function(t) { game.messageSend( {tp: 'pcpa', t: t, a: this.a, ma: this.movingA} ,{firstConnection: 1, connectionsRange: 4, connectionsCount: 2, seriesName: 'pcp'} ); //"pcpa"=="player_control_point" } res.setControlPointXY_=res.setControlPointXY; res.setControlPointXY=function(x, y, vx, vy, movingX, movingY, t) { this.setControlPointXY_(x, y, vx, vy, movingX, movingY, t); this.messageSendControlPointXY(t); } res.setControlPointA_=res.setControlPointA; res.setControlPointA=function(a, va, movingA, t) { this.setControlPointA_(a, va, movingA, t); this.messageSendControlPointA(t); } res.LocalPlayer(game, side); res.unbind=function() { res.view.remove(); keyHandler.unbind(); } res.local=true; return res; } Game.Rally.Player.BotPlayer=function(game, side) { var KeyHandler=Game.Rally.Player.KeyHandler.KeyHandler; var keyHandler=0; var res=Game.Rally.Player.Player(game, side); res.BotPlayer=function(game, side) { //logic } //logic res.setControlPointXY_=res.setControlPointXY; res.setControlPointXY=function(x, y, vx, vy, movingX, movingY, t) { this.setControlPointXY_(x, y, vx, vy, movingX, movingY, t); //this.messageSendControlPointXY(t); } res.setControlPointA_=res.setControlPointA; res.setControlPointA=function(a, va, movingA, t) { this.setControlPointA_(a, va, movingA, t); //this.messageSendControlPointA(t); } res.BotPlayer(game, side); res.unbind=function() { res.view.remove(); // keyHandler.unbind(); } res.shiftTime_=res.shiftTime; res.shiftTime=function(t) { res.shiftTime_(t); if(teaching.stage<20) return; if(this.state=='wait' || this.statePrev=='wait') { movingX=0; movingY=0; } else if(this.state=='side1') { var ball=game.rally.ball; var dir=MathLib.vNormalize([this.goal[0]-this.x, this.goal[1]-this.y]); //console.log(dir); movingX=dir[0]; movingY=dir[1]; if(Math.abs(this.x-ball.x)+Math.abs(this.y-ball.y)<100) this.setControlPointA(res.goalAngle, 0, 0, t); } else if(this.state=='side0') { var dir=MathLib.vNormalize([(game.rally.player0.x-560)-this.x, -this.y]); movingX=dir[0]; movingY=dir[1]; this.setControlPointA(this.startA,0, 0, t); } this.setControlPointXY_(this.x, this.y, this.vx, this.vy, movingX, movingY, t); } res.ballHit=function(x, y, vx, vy, va, t) { res.ballCP={x: x, y: y, vx: vx, vy: vy, va: va, t: t}; } res.ballHitCalc=function(x, y, vx, vy, va, t) { if(va==0) { res.ballCurveCenter=[x, y]; res.ballCurveR=0; res.goal=[x, y]; } else { var v=MathLib.vLength([vx, vy]); res.ballCurveCenter=MathLib.vPlus([x, y], MathLib.vProduct([-vy, vx], v/(va*0.00133333333333))); res.ballCurveR=1/(Math.abs(va)*0.00133333333333)*v*v; var dirToCenter=MathLib.vNormalize(MathLib.vMinus([res.x, res.y], res.ballCurveCenter)); res.goal=MathLib.vPlus(res.ballCurveCenter, MathLib.vProduct(dirToCenter, res.ballCurveR)); var ballHitAngle=[dirToCenter[1], -dirToCenter[0]]; if(ballHitAngle[0]<0) ballHitAngle=[-ballHitAngle[0], -ballHitAngle[1]]; ballHitAngle=MathLib.vAngle(ballHitAngle); var ballCurveRSqr=res.ballCurveR*res.ballCurveR; if ( ! res.borderHitWas && (MathLib.vLengthSqr(MathLib.vMinus(res.ballCurveCenter, [-475, 250]))>ballCurveRSqr)==(MathLib.vLengthSqr(MathLib.vMinus(res.ballCurveCenter, [0, 250]))>ballCurveRSqr) && (MathLib.vLengthSqr(MathLib.vMinus(res.ballCurveCenter, [-475, -250]))>ballCurveRSqr)==(MathLib.vLengthSqr(MathLib.vMinus(res.ballCurveCenter, [0, -250]))>ballCurveRSqr) ) { res.goal=MathLib.vPlus(res.goal, MathLib.vProduct(MathLib.vNormalize(MathLib.vMinus([this.x, this.y], res.goal)), 100)); res.goalAngle=res.a; } else { if(va>0) { if(Math.sign(Math.random()-0.5)>0) { var borderPoint=[475, 250]; } else { var borderPoint=[0, -250]; } } else { if(Math.sign(Math.random()-0.5)>0) { var borderPoint=[475, -250]; } else { var borderPoint=[0, 250]; } } //var borderPoint=[237.5, Math.sign(Math.random()-0.5)*250]; var angleToBorderPoint=MathLib.vAngle(MathLib.vMinus(borderPoint, res.goal)); res.goalAngle=(angleToBorderPoint+ballHitAngle)/2+Math.PI/2; // console.log(res.goal, res.ballCurveCenter, res.ballCurveR); } } } res.ballCP={x: 0, y: 0, vx: 0, vy: 0, va: 0, t: 0}; res.state='wait'; res.statePrev='wait'; res.rallyState=function(state, borderHitWas=false) { res.statePrev=res.state; res.state=state; res.borderHitWas=borderHitWas; if(state!='wait') { res.ballHitCalc(res.ballCP.x, res.ballCP.y, res.ballCP.vx, res.ballCP.vy, res.ballCP.va, res.ballCP.t) } } return res; } Game.Rally.Player.RemotePlayer=function(game, side) { var res=Game.Rally.Player.Player(game, side); res.messageReceive=function(message) { switch(message.tp) { case 'pcp': this.setControlPointXY(-message.x, -message.y, -message.vx, -message.vy, -message.mx, -message.my, message.t); break; case 'pcpa': this.setControlPointA(message.a+Math.PI, message.va, message.ma, message.t); break; } } res.unbind=function(){res.view.remove();}; return res; } Game.Rally.Player.PlayerCollisions.PlayerCollisions=function(player) { var Player=Game.Rally.Player.Player; var prevX=0, prevY=0, prevA=0; var res= { get player(){return player;}, set player(a){player=a;}, get prevX(){return prevX;}, set prevX(a){prevX=a;}, get prevY(){return prevY;}, set prevY(a){prevY=a;}, get prevA(){return prevA;}, set prevA(a){prevA=a;}, PlayerCollisions: function(player) { this.player = player; this.init(0); }, init: function(t) { prevX = player.x; prevY = player.y; prevA = player.a; }, getParams: function() { var r= { xy: [player.x, player.y], prevXy: [prevX, prevY], //line: new Array(getLine(prevX, prevY, prevA), getLine(player.x, player.y, player.a)), dir: [Math.cos(player.a), Math.sin(player.a)], prevDir: [Math.cos(prevA), Math.sin(prevA)], v: new Array(player.vx, player.vy), length: player.length }; return r; }, getLine: function(x, y, a) { return new Array ( x - player.length * Math.cos(a) ,y - player.length * Math.sin(a) ,x + player.length * Math.cos(a) ,y + player.length * Math.sin(a) ); } } res.PlayerCollisions(player); return res; } Game.Rally.Player.PlayerEvalA.PlayerEvalA=function(player) { var Player=Game.Rally.Player.Player; var controlPointTime=0, controlPointA=0, velocity=0.001; var res= { get player(){return player;}, set player(a){player=a;}, get controlPointTime(){return controlPointTime;}, set controlPointTime(a){controlPointTime=a;}, get controlPointA(){return controlPointA;}, set controlPointA(a){controlPointA=a;}, PlayerEvalA: function(player) { this.player = player; }, init: function(t) { controlPointA = player.a; controlPointTime = t; }, eval: function(t) { player.a = controlPointA - player.movingA * velocity * (t - controlPointTime); //console.log(player.a); } } res.PlayerEvalA(player); return res; } Game.Rally.Player.PlayerEvalXY.PlayerEvalXY=function(player) { //var MiddleLine=Game.Rally.MiddleLines.MiddleLine.MiddleLine; var Player=Game.Rally.Player.Player; var evalX=0, evalY=0, evalVx=0, evalVy=0, evalT=0, controlPointTime=0, velocity=0.14, acceleration=0.0005, stopAccelerationCoeff=1.25, autoStopAcceleration=0.00015, length=40, turningVelocity=0.001, evalDt=3; var res= { get player(){return player;}, set player(a){player=a;}, get evalX(){return evalX;}, set evalX(a){evalX=a;}, get evalY(){return evalY;}, set evalY(a){evalY=a;}, get evalVx(){return evalVx;}, set evalVx(a){evalVx=a;}, get evalVy(){return evalVy;}, set evalVy(a){evalVy=a;}, get evalT(){return evalT;}, set evalT(a){evalT=a;}, get controlPointTime(){return controlPointTime;}, set controlPointTime(a){controlPointTime=a;}, get velocity(){return velocity;}, set velocity(a){velocity=a;}, get acceleration(){return acceleration;}, set acceleration(a){acceleration=a;}, get stopAccelerationCoeff(){return stopAccelerationCoeff;}, set stopAccelerationCoeff(a){stopAccelerationCoeff=a;}, get autoStopAcceleration(){return autoStopAcceleration;}, set autoStopAcceleration(a){autoStopAcceleration=a;}, get length(){return length;}, set length(a){length=a;}, get turningVelocity(){return turningVelocity;}, set turningVelocity(a){turningVelocity=a;}, get evalDt(){return evalDt;}, set evalDt(a){evalDt=a;}, PlayerEvalXY: function(player) { this.player = player; }, init: function(t) { evalX = player.x; evalY = player.y; evalVx = player.vx; evalVy = player.vy; evalT = t; controlPointTime = t; }, eval: function(t) { var actualT=0; do { actualT=Math.min(Math.max(t, this.evalT), this.evalT+this.evalDt); if(actualT>this.evalT) { var dt=actualT-this.evalT; var movingX=player.movingX; var movingY=player.movingY; if(movingX==0 && movingY==0) //авто-остановка игрока { var v=Math.sqrt(this.evalVx*this.evalVx+this.evalVy*this.evalVy); if(t-this.controlPointTime<000) { var autoStopAcceleration=this.autoStopAcceleration/2; } else { autoStopAcceleration=this.autoStopAcceleration; } if(v!=0) { var vx=this.evalVx-this.evalVx/v*autoStopAcceleration*dt; var vy=this.evalVy-this.evalVy/v*autoStopAcceleration*dt; if(this.evalVx*vx<0) vx=0; if(this.evalVy*vy<0) vy=0; } else { var vx=0; var vy=0; } } else { var vx=this.evalVx+player.movingX*this.acceleration*dt; var vy=this.evalVy+player.movingY*this.acceleration*dt; var vv=vx*vx+vy*vy; var prevVV=this.evalVx*this.evalVx+this.evalVy*this.evalVy; //лимитируем максимальную скорость if(vv>this.velocity*this.velocity && vv>prevVV) { var newA=(this.evalVx*player.movingY*this.acceleration-this.evalVy*player.movingX*this.acceleration)/this.velocity/this.velocity; //newA=Math.min(newA, this.acceleration/this.velocity); vx=this.evalVx-vy*newA*dt; vy=this.evalVy+vx*newA*dt; } //тормозим быстрее, чем разгоняемся else if(this.evalVx*player.movingX+this.evalVy*player.movingY<0) { var v=Math.sqrt(this.evalVx*this.evalVx+this.evalVy*this.evalVy); var dVt=(this.evalVx*player.movingX+this.evalVy*player.movingY)/v; var dVn=(-this.evalVy*player.movingX+this.evalVx*player.movingY)/v; dVn=dVn;//(1+(0.5*Math.abs(dVt)/this.velocity)); dVt=dVt*this.stopAccelerationCoeff;//0.35/0.25;//2.5; var dVx=dVt*this.evalVx/v-dVn*this.evalVy/v; var dVy=dVt*this.evalVy/v+dVn*this.evalVx/v; dVx=this.acceleration*dVx*dt; dVy=this.acceleration*dVy*dt; if(player.movingX==0) { if(this.evalVx>0) dVx-=10*this.autoStopAcceleration*dt; else if(this.evalVx!=0) dVx+=10*this.autoStopAcceleration*dt; } else if(player.movingY==0) { if(this.evalVy>0) dVy-=10*this.autoStopAcceleration*dt; else if(this.evalVy!=0) dVy+=10*this.autoStopAcceleration*dt; } if(-dVx/this.evalVx>1) { var stopDt=-dt*this.evalVx/dVx; dVx=(dt-stopDt)*this.acceleration*player.movingX; //dVx=-this.evalVx*1.0000001; } if(-dVy/this.evalVy>1) { var stopDt=-dt*this.evalVy/dVy; dVy=(dt-stopDt)*this.acceleration*player.movingY; //dVy=-this.evalVy*1.0000001; } vx=this.evalVx+dVx; vy=this.evalVy+dVy; } //обычный случай else { var v=Math.sqrt(this.evalVx*this.evalVx+this.evalVy*this.evalVy); if(v!=0) { var dVt=(this.evalVx*player.movingX+this.evalVy*player.movingY)/v; var dVn=(-this.evalVy*player.movingX+this.evalVx*player.movingY)/v; dVn=dVn;//*100.5;//0.35/0.25;//2.5; dVx=dVt*this.evalVx/v-dVn*this.evalVy/v; dVy=dVt*this.evalVy/v+dVn*this.evalVx/v; } else { if(player.movingX*player.movingY==0) { var dVt=player.movingX+player.movingY; var dVn=0; dVx=dVt*player.movingX-dVn*player.movingY; dVy=dVt*player.movingY+dVn*player.movingX; } else { var dVt=(player.movingX+player.movingY)/Math.sqrt(2); var dVn=0; dVx=(dVt*player.movingX-dVn*player.movingY)/Math.sqrt(2); dVy=(dVt*player.movingY+dVn*player.movingX)/Math.sqrt(2); } } dVx=this.acceleration*dVx*dt; dVy=this.acceleration*dVy*dt; if(player.movingX==0) { if(this.evalVx>0) dVx-=2*this.autoStopAcceleration*dt; else if(this.evalVx!=0) dVx+=2*this.autoStopAcceleration*dt; } else if(player.movingY==0) { if(this.evalVy>0) dVy-=2*this.autoStopAcceleration*dt; else if(this.evalVy!=0) dVy+=2*this.autoStopAcceleration*dt; } else { if(this.evalVx*(-player.movingY)+this.evalVy*player.movingX>0) { dVx-=(-player.movingY)/Math.sqrt(2)*2*this.autoStopAcceleration*dt; dVy-=(player.movingX)/Math.sqrt(2)*2*this.autoStopAcceleration*dt; } else { dVx+=(-player.movingY)/Math.sqrt(2)*2*this.autoStopAcceleration*dt; dVy+=(player.movingX)/Math.sqrt(2)*2*this.autoStopAcceleration*dt; } } vx=this.evalVx+dVx; vy=this.evalVy+dVy; } } var x=this.evalX+vx*dt; var y = this.evalY + vy * dt; if(actualT>=t) { var playerSideInt = player.side ? 1 : -1; var playerNumber = player.side ? 0 : 1; if (player.holded && Math.abs(x) 0.7) posW+=(pos-0.7)*posXV0/curveXMax; posWV = 1; if (pos > 0.7) posWV+=posXV0/curveXMax; //view view.setPos(-sideSign*posX, -sideSign*posW); }, xAndVByY: function(y) { y = Math.min(Math.abs(y), curveYMax); for (var i = 1; i < curve.length; i++) { if (y <= curve[i][1]) { var curveX= curve[i-1][0] + (curve[i][0]-curve[i-1][0]) * ( (y-curve[i-1][1]) / (curve[i][1]-curve[i-1][1]) ) ; return { x: posX + curveX * posW, v: posV * (posXV + curveX*posWV) }; } } return { x:0, v:0 }; }, init: function() { state = 'dec'; t0 = 0; pos = 0; posX = 0; posV = 0; } } res.MiddleLine(side); return res; } Game.Rally.MiddleLines.MiddleLine.MiddleLineView=function(curve, curveXMax, side) { var prevPosW=0; /*var e=document.createElement('div'); e.innerHTML='
'; e.setAttribute('id', 'ball'); document.querySelector('#center').append(e);*/ var res= { get curve(){return curve;}, set curve(a){curve=a;}, get prevPosW(){return prevPosW;}, set prevPosW(a){prevPosW=a;}, get curveXMax(){return curveXMax;}, set curveXMax(a){curveXMax=a;}, MiddleLineView: function(curve, curveXMax, side) { this.curve = curve; this.curveXMax = curveXMax; }, setPos: function(posX, posW) { /*graphics.lineStyle(1, 0x8000ee, 0); for(var i:Number = 1; i < curve.length; i++) { graphics.moveTo(curve[i-1][0]*prevPosW, curve[i-1][1]); graphics.lineTo(curve[i][0]*prevPosW, curve[i][1]); graphics.moveTo(curve[i-1][0]*prevPosW, -curve[i-1][1]); graphics.lineTo(curve[i][0]*prevPosW, -curve[i][1]); } graphics.lineStyle(1, 0x80bb80, 1);*/ /*if (Math.round(curveXMax * prevPosW) != Math.round(curveXMax * posW)) { graphics.clear(); graphics.lineStyle(1, 0x80bb80, 1); for(var i:Number = 1; i < curve.length; i++) { graphics.moveTo(curve[i-1][0]*posW, curve[i-1][1]); graphics.lineTo(curve[i][0]*posW, curve[i][1]); graphics.moveTo(curve[i-1][0]*posW, -curve[i-1][1]); graphics.lineTo(curve[i][0]*posW, -curve[i][1]); } prevPosW = posW; }*/ x = posX; } } res.MiddleLineView(curve, curveXMax, side); return res; } Game.Referee.Referee=function(game, whoMain) { var Scale=Game.Rally.Scale.Scale; var whoServe=0, score=0, scoreLimits=0, scoreAdv=0, scoreInc=0, winner=-1, setsNumber=-1, view=0; var scoreNode=document.querySelector('#score'); var waitNode=document.querySelector('#wait'); var res= { get game(){return game;}, set game(a){game=a;}, get whoServe(){return whoServe;}, set whoServe(a){whoServe=a;}, get score(){return score;}, set score(a){score=a;}, get scoreLimits(){return scoreLimits;}, set scoreLimits(a){scoreLimits=a;}, get scoreAdv(){return scoreAdv;}, set scoreAdv(a){scoreAdv=a;}, get scoreInc(){return scoreInc;}, set scoreInc(a){scoreInc=a;}, get winner(){return winner;}, set winner(a){winner=a;}, get setsNumber(){return setsNumber;}, set setsNumber(a){setsNumber=a;}, get view(){return view;}, set view(a){view=a;}, Referee: function(game, whoMain) { this.game = game; whoServe = ! whoMain; this.scoreInit(); //view /*var style:StyleSheet = new StyleSheet(); var styleObj:Object = new Object(); styleObj.textAlign = "center"; styleObj.fontWeight = "normal"; styleObj.fontFamily = "_typewriter"; styleObj.fontSize = 16; style.setStyle(".score", styleObj); view = new TextField(); view.styleSheet = style; with (view) { width = 800; height = 30; multiline = true; wordWrap = true; x = -400;//-Scale.convertX(1); y = -Scale.convertY(1) - 33; } game.view.addChild(view);*/ //Main.stage.addChild(view); waitView.status(); advice.hide(); this.viewShowScore(); }, start: function() { game.wait.wait(); }, rallyEnd: function(whoWin) { whoServe = ! whoServe; this.scoreChange(whoWin); this.viewShowScore(); if (score[2][0] == setsNumber) { winner = 0; } else if (score[2][0] == setsNumber) { winner = 1; } game.wait.wait(); //view view.visible = true; }, rallyStart: function(t) { if (winner>=0) { winner = -1; this.scoreInit(); } if(teaching.stage<10) var whoServe_=true; else whoServe=false; teaching.rallyStart(); game.rally.referee.start(whoServe_, t); //view view.visible = false; }, scoreChange: function(whoWin, type=0) { var whoWinInt = whoWin ? 0 : 1; scoreInc = [type, whoWinInt]; var notWhoWinInt = whoWin ? 1 : 0; var newScore = (score[type][whoWinInt] += 1) if (newScore > scoreLimits[type]) { if (type == 0 && score[type][notWhoWinInt] == scoreLimits[0]) { score[type][whoWinInt] = scoreLimits[0]; if (scoreAdv == whoWinInt) { score[type] = [0, 0]; this.scoreChange(whoWin, type + 1); scoreAdv = -1; } else if(scoreAdv==notWhoWinInt) { scoreAdv = -1; } else { scoreAdv = whoWinInt; } } else { score[type] = [0, 0]; this.scoreChange(whoWin, type + 1); } } if(game.type!='view') { if(whoWin) { waitView.status('success'); } else { waitView.status('fail'); } advice.refresh(); } else { waitView.ready(whoWin ? 'Правый игрок выиграл очко' : 'Левый игрок выиграл очко'); } }, scoreInit: function() { score = [[0, 0], [0, 0], [0, 0]]; scoreLimits = [7, 2]; scoreAdv = -1; scoreInc = [0,-1]; }, scoreSet: function(score_) { score=score_; this.viewShowScore(); }, viewShowScore: function() { var str = 'Счёт (сеты/геймы/подачи): '; for (var i = 2; i >= 0; i--) { if (i == 0 && scoreAdv == 1) { str += 'adv '; } if (i == 0 && ! whoServe) { str += '*'; } if (scoreInc[0] == i && scoreInc[1] == 1) { str += ''+score[i][1]+''; } else { str += score[i][1]; } str+=':' if (scoreInc[0] == i && scoreInc[1] == 0) { str += ''+score[i][0]+''; } else { str += score[i][0]; } if (i == 0 && scoreAdv == 0) { str += ' adv'; } if (i == 0 && whoServe) { str += '*'; } if (i !== 0) { str += ' / '; } } str += ''; //view.htmlText = str; scoreNode.innerHTML=str; } } res.Referee(game, whoMain); return res; } Game.Wait.Wait=function(game) { var view=0, ready=0; var e=document.querySelector('#wait_ready'); res= { get game(){return game;}, set game(a){game=a;}, get view(){return view;}, set view(a){view=a;}, Wait: function(game) { this.game = game; waitView.ready('Нажмите пробел, чтобы начать'); //view /*var style:StyleSheet = new StyleSheet(); var styleObj:Object = new Object(); styleObj.textAlign = "center"; styleObj.fontFamily = "_typewriter"; styleObj.fontSize = 16; style.setStyle(".wait", styleObj); view = new TextField(); view.visible = false; view.background = true; view.backgroundColor = 0xAAAAAA; view.border = true; view.borderColor = 0x888888; view.styleSheet = style; with (view) { width = 300; height = 27; multiline = true; wordWrap = true; htmlText = "Press space to continue."; x = -150; y = -100; } game.view.addChild(view);*/ }, wait: function() { // Main.stage.addEventListener(KeyboardEvent.KEY_UP, ready); document.body.addEventListener('keydown', ready); //view view.visible = true; waitView.show(); }, ready: function(event) { if(event.keyCode == 32 && ! keySpaceOccupied) { /// Main.stage.removeEventListener(KeyboardEvent.KEY_UP, ready); document.body.removeEventListener('keydown', ready); game.referee.rallyStart(time.get()); //view view.visible = false; waitView.hide(); /*if (typeof event.stopPropagation != "undefined") { event.stopPropagation(); } else { event.cancelBubble = true; }*/ event.preventDefault(); return true; } }, unbind: function() { document.body.removeEventListener('keydown', ready); }, opponentLeave: function() { this.unbind(); waitView.status(); advice.hide(); waitView.ready('Оппонент покинул игру'); } } res.Wait(game); ready=res.ready; return res; } Game.Wait.NetworkWait=function(game) { var isReadyWe=0, isReadyHe=0, timer=0, startTime=0, readyWe=0; var waitNode=document.querySelector('#wait'); var waitReadyNode=document.querySelector('#wait_ready'); var res= { get game(){return game;}, set game(a){game=a;}, get isReadyWe(){return isReadyWe;}, set isReadyWe(a){isReadyWe=a;}, get isReadyHe(){return isReadyHe;}, set isReadyHe(a){isReadyHe=a;}, get timer(){return timer;}, set timer(a){timer=a;}, get startTime(){return startTime;}, set startTime(a){startTime=a;}, ready: function(event) { if (event.keyCode == 32) { /// Main.stage.removeEventListener(KeyboardEvent.KEY_UP, ready); game.referee.rallyStart(game.rally.time.get()); //view //view.visible = false; event.preventDefault(); return true; } }, NetworkWait: function(game) { //super(game); this.game=game; isReadyWe = false; isReadyHe = false; this.viewShowReady(); waitNode.style.display='flex'; //timer = new Timer(100, 1); //timer.addEventListener(TimerEvent.TIMER, startImmediately); //view /*var style:StyleSheet = new StyleSheet(); var styleObj:Object = new Object(); styleObj.textAlign = "center"; styleObj.fontFamily = "_typewriter"; styleObj.fontSize = 16; style.setStyle(".wait", styleObj); view = new TextField(); view.visible = false; view.background = true; view.backgroundColor = 0xAAAAAA; view.border = true; view.borderColor = 0x888888; view.styleSheet = style; with (view) { width = 300; height = 62; multiline = true; wordWrap = true; x = -150; y = -100; } game.view.addChild(view);*/ }, wait: function() { //Main.stage.addEventListener(KeyboardEvent.KEY_UP, readyWe); document.body.addEventListener('keydown', readyWe); //view this.viewShowReady(); waitNode.style.display='flex'; //view.visible = true; }, readyWe: function(event) { if (event.keyCode == 32 && ! keySpaceOccupied) { isReadyWe = ! isReadyWe; res.start(-1); event.preventDefault(); return true; } }, start: function(time) { this.viewShowReady(); if (time < 0) { if (! isReadyWe || ! isReadyHe) { if (time == -1) { this.messageSendReady(isReadyWe, -2); } return; } else { time = game.rally.time.get(); this.messageSendReady(true, time); } } //Main.stage.removeEventListener(KeyboardEvent.KEY_UP, this.ready); document.body.removeEventListener('keydown', readyWe); isReadyWe = false; isReadyHe = false; waitNode.style.display='none'; //timer.reset(); startTime = time + 1000; var delay = startTime-game.rally.time.get(); if(delay<=0) { this.startImmediately(null); } else { //timer.reset(); //timer.delay = delay; //timer.start(); setTimeout(this.startImmediately, delay); } //view //view.visible = false; game.referee.view.visible = false; }, startImmediately: function(event) { game.referee.rallyStart(startTime); }, messageReceive: function(message) { switch(message.tp) { case 'wr': isReadyHe = message.r; this.start(message.t); break; } }, messageSendReady: function(ready, time) { game.messageSend( {tp: 'wr', r: ready, t: time} ); //"wr" == "wait ready (or not)" }, viewShowReady: function() { //view.htmlText="Press space if you're "+(isReadyWe ? "not " : "")+"ready (you're "+(isReadyWe ? "" : "not ")+"ready).
Your opponent is "+(isReadyHe ? "" : "not ")+"ready.
"; waitReadyNode.innerHTML='Нажмите пробел, если '+(isReadyWe ? 'не' : '')+' готовы.
Оппонент '+(isReadyHe ? '' : 'не')+' готов.'; }, unbind: function() { document.body.removeEventListener('keydown', readyWe); }, opponentLeave: function() { this.unbind(); waitView.status(); advice.hide(); waitView.ready('Оппонент покинул игру'); } } res.NetworkWait(game); readyWe=res.readyWe; return res; } Game.Wait.ViewWait=function(game) { var isReadyWe=0, isReadyHe=0, timer=0, startTime=0, readyWe=0; var waitNode=document.querySelector('#wait'); var waitReadyNode=document.querySelector('#wait_ready'); var res= { get game(){return game;}, set game(a){game=a;}, get isReadyWe(){return isReadyWe;}, set isReadyWe(a){isReadyWe=a;}, get isReadyHe(){return isReadyHe;}, set isReadyHe(a){isReadyHe=a;}, get timer(){return timer;}, set timer(a){timer=a;}, get startTime(){return startTime;}, set startTime(a){startTime=a;}, ready: function(event) { }, NetworkWait: function(game) { //super(game); this.game=game; isReadyWe = false; isReadyHe = false; this.viewShowReady(); waitNode.style.display='flex'; //timer = new Timer(100, 1); //timer.addEventListener(TimerEvent.TIMER, startImmediately); //view /*var style:StyleSheet = new StyleSheet(); var styleObj:Object = new Object(); styleObj.textAlign = "center"; styleObj.fontFamily = "_typewriter"; styleObj.fontSize = 16; style.setStyle(".wait", styleObj); view = new TextField(); view.visible = false; view.background = true; view.backgroundColor = 0xAAAAAA; view.border = true; view.borderColor = 0x888888; view.styleSheet = style; with (view) { width = 300; height = 62; multiline = true; wordWrap = true; x = -150; y = -100; } game.view.addChild(view);*/ }, wait: function() { //view this.viewShowReady(); waitNode.style.display='flex'; //view.visible = true; }, readyWe: function(event) { }, start: function(time) { this.viewShowReady(); if (time < 0) { return; } isReadyWe = false; isReadyHe = false; waitNode.style.display='none'; //timer.reset(); startTime = time + 1000; var delay = startTime-game.rally.time.get(); if(delay<=0) { this.startImmediately(null); } else { //timer.reset(); //timer.delay = delay; //timer.start(); setTimeout(this.startImmediately, delay); } //view //view.visible = false; game.referee.view.visible = false; }, startImmediately: function(event) { game.referee.rallyStart(startTime); }, messageReceive: function(message) { switch(message.tp) { case 'wr': isReadyHe = message.r; this.start(message.t); break; } }, messageSendReady: function(ready, time) { game.messageSend( {tp: 'wr', r: ready, t: time} ); //"wr" == "wait ready (or not)" }, viewShowReady: function() { //view.htmlText="Press space if you're "+(isReadyWe ? "not " : "")+"ready (you're "+(isReadyWe ? "" : "not ")+"ready).
Your opponent is "+(isReadyHe ? "" : "not ")+"ready.
"; //waitReadyNode.innerHTML='Нажмите пробел, если '+(isReadyWe ? 'не' : '')+' готовы.
Оппонент '+(isReadyHe ? '' : 'не')+' готов.'; //waitReadyNode.inner //waitView.ready('Ожидание готовности игроков'); }, unbind: function() { document.body.removeEventListener('keydown', readyWe); }, opponentLeave: function() { this.unbind(); waitView.status(); advice.hide(); waitView.ready('Игра окончена'); } } res.NetworkWait(game); readyWe=res.readyWe; return res; }