Home » Demos » MMORPG

MMORPG

 


About this demo


Here APE is used as a game engine for the web, proof of concept that multiplayer web games can be created without Flash!

Keyboard shortcuts:

Up: Up or Z or W
Down: Down or S
Left: Left or A or Q
Right: Right or D

Fireblast: 1 or F1
Lightning Chain: 2 or F2

Select nearest enemy: Tab

Chat with others: Enter

Which features this demo is using?

  • nickname.js
  • ServerSide JavaScript



A simple chat demo, multi-user and private messages.

APE real time chat

A simple chat demo, multi-user and private messages.

Demo of multi-user moving and chating in real time.

Move demo

Demo of multi-user moving and chating in real time.

Live tweets from Twitter trends, in real time!

Live tweets

Live tweets from Twitter trends, in real time!


Gateway to IRC using the TCPSocket feature. Come chat with us ;)

TCPSocket Demo (IRC)

Gateway to IRC using the TCPSocket feature. Come chat with us ;)

A demo of a small massive multiplayer role playing game!

MMORPG

A demo of a small massive multiplayer role playing game!

Draw with everyone in real time!

Pixelbox

Draw with everyone in real time!


Homepage demo

Homepage demo

This is the demo from the home page of APE, but you've probably already seen it ;-)


Study the source code


Check out the Client JavaScript, HTML and ServerSide JavaScript source code of this demo by reading the following :


  1. <!-- if IE>
  2. <div class="note">This demo might be laggy on Internet Explorer, because canvas are emulated.</div>
  3. <![endif]-->
  4. <link rel="stylesheet" type="text/css" href="http://static.weelya.com/weelya_ape/demos/mmo/mmo.css" />
  5. <!--[if IE]><script type="text/javascript" src="/demos/mmo/excanvas.js"></script><![endif]-->
  6. <!-- <script type="text/javascript" src="/demos/mmo/config.js"></script> -->
  7. <script type="text/javaScript" src="http://static.weelya.com/weelya_ape/demos/mmo/ge.js"></script>
  8. <script type="text/javaScript" src="http://static.weelya.com/weelya_ape/demos/mmo/apemmo.js"></script>
  9. <script type="text/javascript">
  10. //<!--
  11.    var client = new APE.MmoClient('plop');
  12. //-->
  13. </script>
  14. <div id="plop">
  15.         <div id="apemmo">
  16.                 <canvas id="canvas" width="800" height="400"></canvas>
  17.                 <div id="prompt">&nbsp;</div>
  18.         </div>
  19. </div>
  20.  
  1. /*
  2.  * APE MMO DEMO
  3.  *
  4.  * TODO: Synchronize timers.
  5.  *
  6.  *
  7.  *
  8.  *
  9.  */
  10. APE.MmoClient = new Class({
  11.         Extends: APE.Client,
  12.         Implements: Options,
  13.         skills: {
  14.                 'fire': {
  15.                         cooldown: 1000,
  16.                         id: 1,
  17.                         incant: 0
  18.                 },
  19.                 'thunder': {
  20.                         cooldown: 2000,
  21.                         id: 2,
  22.                         incant: 2000
  23.                 }
  24.         },
  25.         options: {
  26.                 ape: {
  27.                         identifier: 'apemmo'
  28.                 },
  29.                 map: {
  30.                         width: 1000,
  31.                         height: 1000
  32.                 },
  33.                 width: 800,
  34.                 height: 400,
  35.                 listener: document,
  36.                 pipe: 'apemmo',
  37.                 start: {
  38.                         x: 400-16,
  39.                         y: 200-48
  40.                 },
  41.                 pas: 2
  42.         },
  43.         els: {},
  44.         chat: {
  45.                 visible: false
  46.         },
  47.         bar: {
  48.  
  49.         },
  50.         started: false,
  51.         units: new Hash(),
  52.         spells: new Hash(),
  53.         ctx: null,//Canvas 2D Context
  54.         cnt: 0,
  55.         mapimg: null,
  56.         loaded: false,
  57.         perso: false,
  58.         spellCnt: 0,
  59.         drawing: false,
  60.         messages: 0,
  61.         nick: false,
  62.         noReapeatKeys: {
  63.                 'up': {},
  64.                 'left':{},
  65.                 'right':{},
  66.                 'down':{}
  67.         },
  68.         keys: {
  69.                 'up': {},
  70.                 'left':{},
  71.                 'right':{},
  72.                 'down':{},
  73.                 'f2':{},
  74.                 'f3':{},
  75.                 'enter':{},
  76.                 'tab':{}
  77.         },
  78.         keysAlias: {
  79.                 'z':'up',
  80.                 'w':'up',
  81.                 'a':'left',
  82.                 'q':'left',
  83.                 's':'down',
  84.                 'd':'right',
  85.                 '1':'f2',
  86.                 '2':'f3'
  87.         },
  88.         initialize: function(target){
  89.  
  90.                 this.els.target = target;
  91.  
  92.                 // INIT //
  93.                 this.x = this.options.start.x;
  94.                 this.y = this.options.start.y;
  95.  
  96.                 Ge.preload('/demos/mmo/img/skill1.png');
  97.                 Ge.preload('/demos/mmo/img/skill2.png');
  98.                 Ge.preload('/demos/mmo/img/map.png', this.drawDesign.bind(this));
  99.  
  100.                 //## RAWS ##//
  101.                 this.addEvent('ready', this.ready);
  102.                 this.addEvent('userJoin', this.userJoin);
  103.                 this.addEvent('userLeft', this.userLeft);
  104.                 this.addEvent('multiPipeCreate', this.multiPipeCreate);
  105.                 this.onError('005', this.requestNick);
  106.                 this.onError('006', this.requestNick);
  107.  
  108.                 this.onRaw('mmo_start', this.rawStart);
  109.                 this.onRaw('mmo_stop', this.rawStop);
  110.                 this.onRaw('mmo_firespell', this.rawFireSpell);
  111.                 this.onRaw('mmo_incant', this.rawIncant);
  112.                 this.onRaw('mmo_player_kill', this.rawPlayerKill);
  113.                 this.onRaw('mmo_error', this.rawError);
  114.                 this.onRaw('mmo_creep', this.rawCreep);
  115.                 this.onRaw('mmo_creeps', this.rawCreeps);
  116.                 this.onRaw('mmo_creep_walk', this.rawCreepWalk);
  117.                 this.onRaw('mmo_creep_kill', this.rawCreepKill);
  118.                 this.onRaw('data', this.rawData);
  119.  
  120.                 //this.onRaw('data', this.rawData);
  121.  
  122.                 //## KEYS ##//
  123.                 this.options.listener.addEvent('keydown', this.keyRealDown.bind(this));
  124.                 this.options.listener.addEvent('keypress', this.keyPress.bind(this));
  125.                 this.options.listener.addEvent('keyup', this.keyRealUp.bind(this));
  126.                
  127.                 window.addEvent('domready', function(){
  128.                         this.load({
  129.                                 'identifier': this.options.ape.identifier,
  130.                                 'complete': this.complete.bind(this)
  131.                         });
  132.                 }.bind(this));
  133.         },
  134.         requestNick: function(){
  135.                 var loop = 0;
  136.                
  137.                 if(this.nick !== false) this.els.ptels.span.set('text', 'Please choose another nickname: ');
  138.                
  139.                 this.els.prompt.fade('in');
  140.         },
  141.         nickClick: function(){
  142.                 this.nick = this.els.ptels.input.get('value');
  143.                 if(this.nick){
  144.                         this.core.start({'name':this.nick});
  145.                 }else{
  146.                         this.requestNick();
  147.                 }
  148.         },
  149.         complete: function(){
  150.                 this.drawDesign();
  151.                 this.requestNick();
  152.  
  153.         },
  154.         ready: function(){
  155.  
  156.                         // START THE TIMER //
  157.  
  158.                         this.interval = setInterval(this.tick.bind(this), 20);
  159.  
  160.                         // JOIN THE CHANNEL //
  161.  
  162.                         this.core.join(this.options.pipe);
  163.                
  164.         },
  165.         drawDesign: function(){
  166.                 if(!this.loaded){
  167.                         this.loaded = true;
  168.                 }else{
  169.                         //this.els.container = new Element('div', {'id':'apemmo'});
  170.                        
  171.                         this.els.container = $('apemmo');
  172.  
  173.                         this.els.canvas = $('canvas');
  174.                         this.els.prompt = $('prompt');
  175.                        
  176.                         this.els.ptels = {};
  177.                         this.els.ptels.p = new Element('p', {'class':'ape_name_prompt'});
  178.                         this.els.ptels.span = new Element('span', {'text':'Please choose a nickname:'});
  179.                         this.els.ptels.input = new Element('input', {
  180.                                 type: 'text',
  181.                                 'class':'text'
  182.                         });
  183.                         this.els.ptels.btn = new Element('button', {
  184.                                 'text':'Connect',
  185.                                 'class':'submit'
  186.                         });
  187.                         this.els.prompt.grab(this.els.ptels.p);
  188.                         this.els.ptels.p.adopt(this.els.ptels.span, this.els.ptels.input, this.els.ptels.btn);
  189.                        
  190.                         this.els.ptels.btn.addEvent('click', this.nickClick.bind(this));
  191.                         /*
  192.                         this.els.canvas = new Element('canvas', {
  193.                                 width: this.options.width,
  194.                                 height: this.options.height
  195.                         });
  196.                         */
  197.                         this.els.canvas.addEvent('click', this.canvasClick.bind(this));
  198.                         this.ctx = this.els.canvas.getContext('2d');
  199.  
  200.                         this.els.info = new Element('div', {'class':'info'});
  201.  
  202.                         // CHAT //
  203.  
  204.                         this.chat.main = new Element('div',{
  205.                                 'class': 'chat'
  206.                         });
  207.                         this.chat.msgs = new Element('div',{
  208.                                 'class':'messages'
  209.                         });
  210.                         this.chat.input = new Element('input',{
  211.                                 type:'text'
  212.                         });
  213.                         this.chat.zone = new Element('div',{
  214.                                 'style':'display:none'
  215.                         })
  216.                         this.chat.zone.grab(this.chat.input);
  217.                         this.chat.main.adopt(this.chat.msgs, this.chat.zone);
  218.  
  219.                         // THE BAR //
  220.  
  221.                         this.bar.main = new Element('div', {
  222.                                 'class': 'bar'
  223.                         });
  224.                         this.bar.spells = new Array();
  225.                         for(var i in this.skills){
  226.                                 var el = new Element('div', {
  227.                                         'class':'spell',
  228.                                         style:'background-image:url(\'/demos/mmo/img/icon_'+this.skills[i].id+'.png\')'
  229.                                 });
  230.                                 var overlay = new Element('div', {'class':'overlay'});
  231.                                 el.grab(overlay);
  232.                                 this.bar.spells.push(el);
  233.                                 this.bar.main.grab(el);
  234.  
  235.                                 el.addEvent('click', this.spellClick.bind(this, i));
  236.                         }
  237.  
  238.                         // GRAB ALL IN MAIN //
  239.  
  240.                         this.els.container.adopt(this.chat.main, this.els.canvas, this.els.info, this.bar.main);
  241.  
  242.                         $(this.els.target).grab(this.els.container);
  243.  
  244.                         this.redraw();
  245.                 }
  246.         },
  247.         multiPipeCreate: function(pipe, data){
  248.                 this.pipe = pipe;
  249.                 //this.core.request.cycledStack.setTime(1000);
  250.         },
  251.         userJoin: function(user, pipe){
  252.                
  253.                 if(user.pubid == this.core.user.pubid){
  254.                         this.els.prompt.fade('out');
  255.                         this.started = true;
  256.                         this.perso = this.addUnit(
  257.                                 '/demos/mmo/img/0'+(user.properties.mmo_avatar)+'.png',
  258.                                 user.pubid,
  259.                                 null,
  260.                                 null,
  261.                                 user.properties['mmo_life']
  262.                         );
  263.  
  264.                 }else if(user.properties['mmo_life'] > 0){
  265.                         var x = null
  266.                         var y = null;
  267.                         if(user.properties && user.properties['posx']){
  268.                                 x = Number(user.properties['posx']);
  269.                                 y = Number(user.properties['posy']);
  270.                         }
  271.                         this.addUnit('/demos/mmo/img/0'+(user.properties.mmo_avatar)+'.png', user.pubid, x, y,user.properties['mmo_life']);
  272.                 }
  273.                
  274.         },
  275.         userLeft: function(user, pipe){
  276.                 this.removeUnit(user.pubid);
  277.         },
  278.         removeUnit: function(pubid){
  279.                 if(this.selected == pubid){
  280.                         this.selected = false;
  281.                         this.perso.stopIncant();
  282.                 }
  283.                 this.units.erase(pubid);
  284.                 if(pubid == this.core.user.pubid){
  285.                         this.options.listener.removeEvents('keydown');
  286.                         this.options.listener.removeEvents('keypress');
  287.                         this.options.listener.removeEvents('keyup');
  288.                         this.error('You have been killed, reload this page to re-spawn...', 15000);
  289.                 }
  290.         },
  291.         addUnit: function(src, key, x, y, life){
  292.  
  293.                 x = x == null ? this.options.start.x : x;
  294.                 y = y == null ? this.options.start.y : y;
  295.  
  296.                 var unit = new Ge.Unit(
  297.                         src,
  298.                         this.els.canvas,
  299.                         {startPosX:x,startPosY:y,width:32,height:48,life:life}
  300.                 );
  301.                 unit.key = key;
  302.                 unit.addEvent('click', this.unitClick.bind(this));
  303.  
  304.                 this.units.set(key, unit);
  305.                 return unit;
  306.         },
  307.  
  308.         // LOG //
  309.         info: function(txt, clas,delay){
  310.                 delay = delay || 1000;
  311.                 var info = new Element('div', {
  312.                         text: txt,
  313.                         'class': clas
  314.                 });
  315.                 this.els.info.grab(info);
  316.  
  317.                 var f = function(el){
  318.                         el.fade('out');
  319.                         el.destroy.delay(delay, el);
  320.                 }
  321.                 f.delay(5000, this, info);
  322.         },
  323.         error: function(txt, delay){
  324.                 this.info(txt, 'error', delay);
  325.         },
  326.  
  327.         // NETWORK //
  328.  
  329.         send: function(cmd, params, addpos){
  330.                 if(this.pipe){
  331.                         if(addpos){
  332.                                 params.pos = {
  333.                                         x: this.x,
  334.                                         y: this.y
  335.                                 }
  336.                         }
  337.                         //console.log('Sending', this.pipe, cmd, params);
  338.                         this.pipe.request.send(cmd, params);
  339.                 }else{
  340.                         //console.log('Sending without pipe');
  341.                 }
  342.         },
  343.         sendStart: function(){
  344.                 this.send('mmo_start',
  345.                         { dir: this.perso.dir },
  346.                         true
  347.                 );
  348.         },
  349.         rawStart: function(params, pipe){
  350.                 if(params.data.user.pubid == this.core.user.pubid) return;
  351.  
  352.                 var unit = this.units.get(params.data.user.pubid);
  353.                 unit.x = params.data.pos.x;
  354.                 unit.y = params.data.pos.y;
  355.                 unit.rotate(params.data.dir);
  356.                 unit.play();
  357.         },
  358.         rawStop: function(params, pipe){
  359.                 if(params.data.user.pubid == this.core.user.pubid) return;
  360.  
  361.                 var unit = this.units.get(params.data.user.pubid);
  362.                 unit.x = params.data.pos.x;
  363.                 unit.y = params.data.pos.y;
  364.                 unit.stop();
  365.         },
  366.         rawUpdate: function(params, pipe){
  367.                 if(params.data.user.pubid == this.core.user.pubid) return;
  368.  
  369.                 var unit = this.units.get(params.data.user.pubid);
  370.                 unit.x = params.data.pos.x;
  371.                 unit.y = params.data.pos.y;
  372.                 //unit.rotate(params.data.dir);
  373.                 //unit.play();
  374.         },
  375.         rawFireSpell: function(params, pipe){
  376.                 if(params.data.from == this.core.user.pubid){
  377.                         this.perso.stopIncant();
  378.                         this.info('You inflicted '+params.data.power+' damages !')
  379.                 }else{
  380.                         var from = this.units.get(params.data.from);
  381.                         if(from) from.stopIncant();
  382.                 }
  383.                 this.spellOn(params.data.spell, params.data.target, params.data.power);
  384.         },
  385.         rawIncant: function(params, pipe){
  386.                 var from_pubid = params.data.from.pubid;
  387.  
  388.                 if(from_pubid == this.core.user.pubid) return;
  389.  
  390.                 var from = this.units.get(from_pubid);
  391.                 if(from)
  392.                         from.startIncant();
  393.         },
  394.         rawError: function(params, pipe){
  395.                 if(params.data.stopIncant) this.perso.stopIncant();
  396.                 if(params.data.stop) this.perso.stop();
  397.                 if(!params.data.dontShow){
  398.                         this.error(params.data.msg);
  399.                 }
  400.         },
  401.         rawData: function(params, pipe){
  402.                 this.addMessage(params.data.from.properties.name, unescape(params.data.msg));
  403.         },
  404.         rawCreeps: function(params, pipe){
  405.                 for (var i in params.data.creeps){
  406.                         if(params.data.creeps.hasOwnProperty(i)){
  407.                                 var creep = params.data.creeps[i];
  408.                                 var unit = this.addCreep(creep);
  409.                         }
  410.                 }
  411.         },
  412.         rawCreep: function(params, pipe){
  413.                 this.addCreep(params.data.creep);
  414.         },
  415.         rawCreepWalk: function(params, pipe){
  416.                 var creep = this.units.get('creep_'+params.data.creep);
  417.                
  418.                 if(creep) creep.walkTo(params.data.target.x, params.data.target.y);
  419.         },
  420.         rawCreepKill: function(params, pipe){
  421.                 //console.log('kreep id dead');
  422.                 var creep = this.units.get('creep_'+params.data.creep);
  423.  
  424.                 if(creep){
  425.                         creep.stop();
  426.                         creep.extras = false;
  427.                         creep.options.loops = 1;
  428.                         creep.startAnim();
  429.                         //TODO
  430.                         creep.addEvent('animationEnd', this.removeUnit.bind(this, ['creep_'+params.data.creep]));
  431.                         //console.log('Creep Started death animation', creep);
  432.                 }else{
  433.                         //console.log('Unknow creep to kill creep_'+params.data.creep);
  434.                 }
  435.         },
  436.         rawPlayerKill: function(params, pipe){
  437.                 var unit = this.units.get(params.data.target);
  438.                 if(unit){
  439.                         unit.options.loops = 1;
  440.                         unit.extras = false;
  441.                         unit.addEvent('animationEnd', this.removeUnit.bind(this, [params.data.target]));
  442.                         unit.startAnim(2);
  443.                 }
  444.         },
  445.         addCreep: function(creep){
  446.                 var unit = this.addUnit(
  447.                         '/demos/mmo/img/creep'+creep.type+'.png',
  448.                         'creep_'+creep.id,
  449.                         creep.pos.x,
  450.                         creep.pos.y,
  451.                         creep.totalLife
  452.                 );
  453.                 unit.life = creep.life;
  454.                 if(creep.target) unit.walkTo(creep.target.x, creep.target.y);
  455.                 return unit;
  456.         },
  457.         // KEYS //
  458.  
  459.         canvasClick: function(ev){
  460.                 var pos = this.els.canvas.getCoordinates();
  461.                 var x = ev.page.x - pos.left, y = ev.page.y - pos.top;
  462.  
  463.                 var map = this.mapPos();
  464.  
  465.                 this.units.each(function(unit, key){
  466.  
  467.                         var ux, uy;
  468.  
  469.                         if(key != this.core.user.pubid){
  470.                                 ux = unit.x+map.x, uy = unit.y+map.y;
  471.                                 if(x > ux && x < ux + 32 && y > uy && y < uy + 48){
  472.  
  473.                                 unit.fireEvent('click', {target:unit,ev:ev});
  474.                                 }
  475.                         }
  476.                 }.bind(this));
  477.  
  478.         },
  479.  
  480.         unitClick: function(ev){
  481.                 this.selectUnit(ev.target);
  482.         },
  483.         selectUnit: function(unit){
  484.                 if(this.selected){
  485.                         var target = this.units.get(this.selected);
  486.                         target.unselect();
  487.                         this.perso.stopIncant();
  488.                 }
  489.                 if(unit.key == this.selected) this.selected = false;
  490.                 else{
  491.                         unit.select();
  492.                         this.selected = unit.key;
  493.                 }
  494.         },
  495.         keyPress: function(ev){
  496.                 if(!this.started) return;
  497.                
  498.                 var key = ev.key;
  499.                 if(!this.keys[key] && (this.chat.visible || !this.keysAlias[key])) return;
  500.                
  501.                 if(this.keysAlias[key]) key = this.keysAlias[key];
  502.                 ev.stop();
  503.         },
  504.  
  505.         keyRealDown: function(ev){
  506.                 if(!this.started){
  507.                         if(ev.key == 'enter'){
  508.                                 this.nickClick();
  509.                         }
  510.                         return;
  511.                 }
  512.                
  513.                 var key = ev.key;
  514.                 if(!this.keys[key] && (this.chat.visible || !this.keysAlias[key])) return;
  515.                 if(this.keysAlias[key]) key = this.keysAlias[key];
  516.  
  517.                 ev.stop();
  518.  
  519.                 if(this.noReapeatKeys[key] && this.keys[key].down){
  520.                         this.noReapeatKeys[key].uped = 0;
  521.                 }else{
  522.                         this.keyDown(ev);
  523.                 }
  524.  
  525.         },
  526.         keyRealUp: function(ev){
  527.                 if(!this.started) return;
  528.                
  529.                 var key = ev.key;
  530.                 if(!this.keys[key] && (this.chat.visible || !this.keysAlias[key])) return;
  531.                 if(this.keysAlias[key]) key = this.keysAlias[key];
  532.                
  533.                 ev.stop();
  534.  
  535.                 if(this.noReapeatKeys[key]){
  536.                         this.noReapeatKeys[key].uped = new Date().getTime();
  537.                 }else{
  538.                         this.keyUp(ev);
  539.                 }
  540.  
  541.         },
  542.         keyDown: function(ev){
  543.                 var key = ev.key;
  544.                 if(!this.keys[key] && (this.chat.visible || !this.keysAlias[key])) return;
  545.  
  546.                 if(this.keysAlias[key]) key = this.keysAlias[key];
  547.                
  548.                 if(key == 'tab'){
  549.                         var keys = this.units.getKeys();
  550.                         if(!this.selected && keys.length > 1){
  551.                                 this.selectUnit(this.units.get(keys[1]));
  552.                         }else if(keys.length > 1){
  553.                                 for(var i=0;i<keys.length;i++){
  554.                                         if(keys[i] == this.selected){
  555.                                                 i++;
  556.                                                 break;
  557.                                         }
  558.                                 }
  559.                                 if(i >= keys.length) i = 0;
  560.                                 if(keys[i]==this.core.user.pubid) i++;
  561.                                 if(i >= keys.length) i = 0;
  562.                                 this.selectUnit(this.units.get(keys[i]));
  563.  
  564.                         }
  565.                 }else if(key == 'enter'){
  566.                         if(this.chat.visible){
  567.                                 var val = this.chat.input.get('value')
  568.                                 if(val != ''){
  569.                                         this.pipe.send(val);
  570.                                         this.addMessage(this.nick, val);
  571.                                         this.chat.input.set('value','');
  572.                                 }else{
  573.                                         this.chat.visible = false;
  574.                                         this.chat.zone.setStyle('display', 'none');
  575.                                 }
  576.                         }else{
  577.                                 this.chat.visible = true;
  578.                                 this.chat.zone.setStyle('display', 'block');
  579.                                 this.chat.input.focus();
  580.                         }
  581.                 }else if(key=='f2' || key=='f3'){
  582.                         if(!this.selected) return this.error('Please select a target');
  583.  
  584.                         this.perso.stopIncant();
  585.                         this.perso.stop();
  586.  
  587.                         switch(key){
  588.                             default:
  589.                                 case 'f2':
  590.                                         this.spell('fire');
  591.                                         break;
  592.                                 case 'f3':
  593.                                         this.spell('thunder');
  594.                                         break;
  595.                         }
  596.                 }else if(!this.keys[key].down && this.perso && this.perso.rotate(key, 0)){
  597.                         this.keys[key].down = new Date().getTime();
  598.                         this.perso.play();
  599.  
  600.                         this.sendStart();
  601.                 }
  602.         },
  603.         keyUp: function(ev){
  604.                 var key = ev.key;
  605.                 if(!this.keys[key] && (this.chat.visible || !this.keysAlias[key])) return;
  606.  
  607.                 if(this.keysAlias[key]) key = this.keysAlias[key];
  608.  
  609.                 this.keys[key].down = false;
  610.                 if(key == this.perso.dir){
  611.                         var newdir = '';
  612.                         var max = 0;
  613.                         for(var i in this.keys){
  614.                                 if(this.keys[i].down > max){
  615.                                         max = this.keys[i].down;
  616.                                         newdir = i;
  617.                                 }
  618.                         }
  619.                         if(newdir != ''){
  620.                                 this.perso.rotate(newdir);
  621.                                 this.sendStart();
  622.                         }else{
  623.                                 this.perso.stop();
  624.                                 this.send('mmo_stop', {}, true);
  625.                         }
  626.                 }
  627.         },
  628.         addMessage: function(nick, msg){
  629.  
  630.                 if(++this.messages > 11){
  631.                         this.chat.msgs.getElement('div').destroy();
  632.                 }
  633.  
  634.                 var line = new Element('div',{});
  635.                 var nickel = new Element('span', {
  636.                         'class':'nick',
  637.                         'text': nick
  638.                 });
  639.                 var ptel = new Element('span', {text:':','class':'pt'});
  640.                 var msgel = new Element('span', {
  641.                         'class':'txt',
  642.                         'text':msg
  643.                 });
  644.                 line.adopt(nickel, ptel, msgel);
  645.                 this.chat.msgs.grab(line);
  646.         },
  647.  
  648.         // SPELL //
  649.         spellClick: function(spell){
  650.                 this.spell(spell);
  651.         },
  652.         spell: function(spell){
  653.                 if(!this.selected){
  654.                         this.error('Target lost');
  655.                         return;
  656.                 }
  657.                 var now = new Date().getTime();
  658.  
  659.                 if(this.skills[spell].last && now - this.skills[spell].last < this.skills[spell].cooldown){
  660.                         this.error('This spell is not ready yet !');
  661.                 }else{
  662.                         this.skills[spell].last = now;
  663.                         if(this.skills[spell].incant > 0) this.perso.startIncant();
  664.                         this.send('mmo_spell', {'spell':spell, 'target':this.selected});
  665.                 }
  666.         },
  667.         spellOn: function(spell, target, power){
  668.  
  669.                 if(target == this.core.user.pubid){
  670.                         this.spellAt(spell, this.x, this.y);
  671.                         this.perso.life -= power;
  672.                         this.info('You lost '+power+' life points !');
  673.                 }else{
  674.                         var unit = this.units.get(target);
  675.  
  676.                         if(unit){
  677.                                 var x = unit.x;
  678.                                 var y = unit.y;
  679.                                 this.spellAt(spell, x ,y);
  680.                                 unit.life -= power;
  681.                         }else{
  682.                                 //console.log('Unknow unit',target);
  683.                         }
  684.                 }
  685.         },
  686.         spellAt: function(type, x, y, onme){
  687.                 //console.log('SpellAt', arguments);
  688.  
  689.                 var spell = new Ge.Spell(type, x, y, this.els.canvas, onme);
  690.  
  691.                 this.spells.set(++this.spellCnt, spell);
  692.  
  693.                 spell.anim.play();
  694.                 spell.anim.addEvent('animationEnd', this.removeSpell.bind(this, this.spellCnt));
  695.  
  696.         },
  697.         removeSpell: function(spell){
  698.                 this.spells.erase(spell);
  699.         },
  700.         // TIMER //
  701.         tick: function(){
  702.                 var now = new Date().getTime();
  703.  
  704.  
  705.                 if(!this.last_tick) this.last_tick = now - 20;
  706.  
  707.                 while(now - this.last_tick > 10){
  708.                         this.last_tick += 20;
  709.  
  710.                         $each(this.noReapeatKeys , function(key, code){
  711.                                 if(key.uped > 0){
  712.                                         if(this.last_tick - key.uped > 50){
  713.                                                 this.keyUp({key:code});
  714.                                                 key.uped = 0;
  715.                                         }
  716.                                 }
  717.                         }.bind(this));
  718.  
  719.                         //console.log('Différence', now - this.last_tick);
  720.  
  721.                         if(this.perso.playing && this.perso.anim == 0){
  722.                                 var add = this.perso.parseDir(this.perso.dir);
  723.                                 this.x += add.x*this.options.pas;
  724.                                 this.y += add.y*this.options.pas;
  725.  
  726.                                 this.x = Math.max(this.x, 0);
  727.                                 this.y = Math.max(this.y, -24);
  728.                                 this.x = Math.min(this.x, this.options.map.width-8);
  729.                                 this.y = Math.min(this.y, this.options.map.height-32);
  730.                         }
  731.  
  732.                         //if(this.cnt%50==0){ this.send('mmo_update', {}, true); }
  733.                         this.redraw(this.cnt++%9==0);
  734.  
  735.                         if(this.cnt%5==0){
  736.                                 this.spells.each(function(spell, key){
  737.                                         spell.tick();
  738.                                 });
  739.                         }
  740.                 }
  741.         },
  742.         mapPos: function(){
  743.                 var mx = this.options.start.x-this.x;
  744.                 var my = this.options.start.y-this.y;
  745.  
  746.                 var rx = Math.min(mx, 0);
  747.                 var ry = Math.min(my, 0);
  748.                 rx = Math.max(rx, this.options.width - this.options.map.width -24);
  749.                 ry = Math.max(ry, this.options.height - this.options.map.height -24);
  750.  
  751.                 return {x:rx, y:ry, ox: mx, oy: my};
  752.         },
  753.         redraw: function(tick){
  754.  
  755.  
  756.                 if(!this.drawing){
  757.                         this.drawing = true;
  758.                         //// Calc ////
  759.                         var map = this.mapPos();
  760.  
  761.                         delete this.drawList;
  762.                         this.drawList = new Array();
  763.  
  764.                         // UNITS //
  765.                         this.units.each(function(unit, pubid){
  766.                                 if(tick) unit.tick();
  767.  
  768.                                 if(pubid == this.core.user.pubid){
  769.  
  770.                                         this.addToDrawList(unit, map.x -map.ox, map.y -map.oy, -map.oy+176);
  771.                                 }else{
  772.                                         unit.walk(this.options.pas, this.options.map.width, this.options.map.height);
  773.                                         this.addToDrawList(unit, map.x, map.y, unit.y+24)
  774.                                 }
  775.                         }.bind(this));
  776.  
  777.                         // SPELLS //
  778.                         this.spells.each(function(spell){
  779.                                 this.addToDrawList(spell, map.x, map.y, spell.z);
  780.                         }.bind(this));
  781.  
  782.                         this.ctx.clearRect(0,0,this.options.width, this.options.height);
  783.  
  784.                         this.ctx.drawImage(Ge.getPreloaded('/demos/mmo/img/map.png').img, Math.round(map.x), Math.round(map.y));
  785.  
  786.                         this.drawList.each(function(line, z){
  787.                                 line.each(function(item){
  788.                                         item[0].draw(item[1], item[2]);
  789.                                 });
  790.                         });
  791.                         this.drawing = false;
  792.                 }else{
  793.                         this.units.each(function(unit, pubid){
  794.                                 if(tick) unit.tick();
  795.                                 unit.walk(this.options.pas, this.options.map.width, this.options.map.height);
  796.                         }.bind(this));
  797.                 }
  798.         },
  799.         addToDrawList: function(item, x, y, z){
  800.                 if(!this.drawList[z]) this.drawList[z] = new Array();
  801.  
  802.                 if(item.z != z){
  803.                         item.z = z;
  804.                         //console.log('Drawing ', item, 'at', [x,y], 'on', z);
  805.                 }
  806.  
  807.                 this.drawList[z].push([item,x,y]);
  808.         }
  809. });
  810.  
  1. //
  2. // mmo.ape.js
  3. //
  4.  
  5. include('framework/mootools.js');
  6. include('utils/debug.js');
  7.  
  8. var ApeMMO_Server = new Class({
  9.         //Properties
  10.         channel: 'apemmo',
  11.         pipe: false,
  12.         userTypeCount: 7,
  13.         creepTypeCount: 2,
  14.         spells: {
  15.                 'fire': {
  16.                         cooldown: 1000,
  17.                         id: 1,
  18.                         incant: 0,
  19.                         power: 20
  20.                 },
  21.                 'thunder': {
  22.                         cooldown: 2000,
  23.                         id: 2,
  24.                         incant: 2000,
  25.                         power: 50
  26.                 }
  27.         },
  28.         creeps: new Hash(),
  29.         creep_id: 0,
  30.         pas: 2,
  31.         //Init
  32.         initialize: function(){
  33.                 Ape.log('[Module] Ape mmo started !');
  34.                 this.registerEvents();
  35.                 this.registerCommands();
  36.                 this.addCreep();
  37.                 this.addCreep();
  38.                 this.addCreep();
  39.                 this.addCreep();
  40.         },
  41.         //Functions
  42.         registerEvents: function(){
  43.                 Ape.addEvent('mkchan', this.mkchan.bind(this));
  44.                 Ape.addEvent('beforeJoin', this.beforeJoin.bind(this));
  45.                 Ape.addEvent('afterJoin', this.afterJoin.bind(this));
  46.         },
  47.         registerCommands: function(){
  48.                 Ape.registerCmd('mmo_start', true, this.cmdStart.bind(this));
  49.                 Ape.registerCmd('mmo_stop', true, this.cmdStop.bind(this));
  50.                 Ape.registerCmd('mmo_spell', true, this.cmdMmoSpell.bind(this));
  51.         },
  52.         sendError: function (user, msg, opt){
  53.                 var opt = opt || {};
  54.                 opt.msg = msg;
  55.                 user.pipe.sendRaw('mmo_error', opt);
  56.         },
  57.         addCreep: function (){
  58.                 var type = $random(1,this.creepTypeCount);
  59.                 var creep = {
  60.                         type: type,
  61.                         pos: {
  62.                                 x: $random(100,800),
  63.                                 y: $random(100,800)
  64.                         },
  65.                         target: false,
  66.                         id: ++this.creep_id,
  67.                         totalLife: 100*type,
  68.                         'private': (function(){
  69.                                 var vals = new Hash();
  70.                                 return function(name, value){
  71.                                         if(value) vals.set(name, value);
  72.                                         return vals.get(name);
  73.                                 };
  74.                         })()
  75.                 };
  76.                 creep.life = creep.totalLife;
  77.                 this.creeps.set(this.creep_id, creep);
  78.                 Ape.setTimeout(this.creepWalk.bind(this, [this.creep_id]), $random(1000,5000));
  79.  
  80.                 if(this.pipe){
  81.                         this.pipe.sendRaw('mmo_creep', {creep:creep});
  82.                 }
  83.         },
  84.         updateCreeps: function(){
  85.                 this.creeps.each(function(creep){
  86.                         //if(creep.target) creep.pos = creep.target;
  87.                         if(creep.target && creep.target.x != creep.pos.x &&  creep.target.x != creep.pos.x){
  88.                                 var s = creep.private('started');
  89.                                 var now = new Date().getTime();
  90.  
  91.                                 while(s.at < now){
  92.                                         if(creep.pos.x < creep.target.x){
  93.                                                 creep.pos.x += Math.min(this.pas/2, creep.target.x-creep.pos.x);
  94.                                         }else if(creep.pos.x > creep.target.x){
  95.                                                 creep.pos.x += Math.max(-this.pas/2, creep.target.x-creep.pos.x);
  96.                                         }else if(creep.pos.y < creep.target.y){
  97.                                                 creep.pos.y += Math.min(this.pas/2, creep.target.y-creep.pos.y);
  98.                                         }else if(creep.pos.y > creep.target.y){
  99.                                                 creep.pos.y += Math.max(-this.pas/2, creep.target.y-creep.pos.y);
  100.                                         }else{
  101.                                                 creep.pos = creep.target;
  102.                                         }
  103.                                         s.at += 20;
  104.                                 }
  105.                         }
  106.                 }.bind(this));
  107.         },
  108.         creepWalk: function(id){
  109.                 if(this.creeps.has(id)){
  110.                         var creep = this.creeps.get(id);
  111.                         if(creep.target){
  112.                                 creep.pos = creep.target;
  113.                                 //Ape.log(this.creeps[creep].id+' started at '+this.creeps[creep].private('started')+', '+(new Date().getTime() - this.creeps[creep].private('started'))+'ms ago !');
  114.                         }
  115.                         var target = {
  116.                                 x: $random(100,900),
  117.                                 y: $random(100,900)
  118.                         }
  119.                         var distance =
  120.                                 Math.abs(creep.pos.x - target.x) +
  121.                                 Math.abs(creep.pos.y - target.y);
  122.  
  123.                         creep.target = target;
  124.                         if(this.pipe){
  125.                                 this.pipe.sendRaw('mmo_creep_walk', {creep:id,target:target});
  126.                         }
  127.                         creep.private('started', {
  128.                                 at: new Date().getTime(),
  129.                                 distance: distance
  130.                         });
  131.                         Ape.setTimeout(this.creepWalk.bind(this, [id]), distance*20/(this.pas/2) + $random(1000,4000));
  132.  
  133.                 }
  134.         },
  135.         endIncant: function (user, spell, target){
  136.  
  137.                 var s = this.spells[spell];
  138.  
  139.                 if(!s){
  140.                         Ape.log('Casting unknow spell'+spell);
  141.                 }else{
  142.  
  143.                         var power = $random(Math.floor(s.power * 0.8), Math.ceil(s.power * 1.2));
  144.  
  145.                         if(target.substr(0, 6)=='creep_'){
  146.                                 var id = Number(target.substr(6));
  147.  
  148.                                 if(this.creeps.has(id)){
  149.                                         var c = this.creeps.get(id);
  150.                                         c.life -= power;
  151.                                         //Ape.log('creep_'+id+' has got '+c.life+' life points');
  152.                                         if(c.life <= 0 ){
  153.                                                 //Ape.log('creep_'+id+' is dead');
  154.                                                 this.creeps.erase(id);
  155.                                                 this.pipe.sendRaw('mmo_creep_kill', {creep: id});
  156.                                                 this.addCreep.delay($random(1000,4000), this);
  157.                                         }
  158.                                 }else{
  159.                                         Ape.log('unknow creep '+target);
  160.                                 }
  161.                         }else{
  162.                                 var usr = Ape.getUserByPubid(target);
  163.                                 if(usr){
  164.                                         //Ape.log('Dammaging '+target+' -'+power+'lp');
  165.                                         usr.setProperty('mmo_life', usr.getProperty('mmo_life')-power);
  166.                                         if(usr.getProperty('mmo_life') <= 0){
  167.                                                 this.pipe.sendRaw('mmo_player_kill', {target:target})
  168.                                         }
  169.                                 }else{
  170.                                         Ape.log('Attacking unknow unit');
  171.                                         return;
  172.                                 }
  173.                         }
  174.                         this.pipe.sendRaw('mmo_firespell', {
  175.                                 from: user.getProperty('pubid'),
  176.                                 spell: spell,
  177.                                 target: target,
  178.                                 power: power
  179.                         });
  180.                 }
  181.         },
  182.         //Events/Commands functions
  183.         cmdStart: function(params, infos){
  184.                 if(this.updateUser(infos.user, params)){
  185.                         params.pipe = {pubid:params.pipe}
  186.                         this.pipe.sendRaw('mmo_start', params, {from:infos.user.pipe});
  187.  
  188.                 }
  189.         },
  190.         cmdStop: function(params, infos){
  191.                 if(this.updateUser(infos.user, params)){
  192.                         params.pipe = {pubid:params.pipe}
  193.                         this.pipe.sendRaw('mmo_stop', params, {from:infos.user.pipe});
  194.                 }
  195.         },
  196.         cmdMmoSpell: function(params, infos){
  197.                 if(this.updateUser(infos.user, params, true)){
  198.  
  199.                         var now = new Date().getTime();
  200.                         var target = params.target;
  201.                         var spell = this.spells[params.spell];
  202.  
  203.                         if(spell){
  204.  
  205.                                 if(now - infos.user.cooldown[params.spell] < spell.cooldown){
  206.                                         this.sendError(infos.user, 'This spell is not ready yet !', {stopIncant:true});
  207.                                 }else{
  208.                                         if(infos.user.incant){
  209.                                                 Ape.clearTimeout(infos.user.incant);
  210.                                         }
  211.                                         //Ape.log('Ok '+params.spell+' '+now+'-'+infos.user.cooldown[params.spell]+'='+(now-infos.user.cooldown[params.spell])+'<'+spell.cooldown);
  212.                                         infos.user.cooldown[params.spell] = now;
  213.  
  214.                                         if(spell.incant){
  215.                                                 var pipe = Ape.getPipe(params.pipe);
  216.  
  217.                                                 pipe.sendRaw('mmo_incant', {'a':'b'}, {from:infos.user.pipe});
  218.  
  219.                                                 infos.user.incant = Ape.setTimeout(this.endIncant.bind(this,[infos.user, params.spell, target]), spell.incant);
  220.                                         }else{
  221.                                                 this.endIncant(infos.user, params.spell, target);
  222.                                         }
  223.                                 }
  224.                         }else{
  225.                                 this.sendError(infos.user, 'Unknow spell', {stopIncant:true});
  226.                         }
  227.                 }
  228.  
  229.         },
  230.         updateUser: function (user, params, doNotStopIncant){
  231.  
  232.                 //User is not on the mmo channel
  233.                 if(!user.mmo_ok) return false;
  234.  
  235.                 params.user = {pubid:user.getProperty('pubid')};
  236.  
  237.                 if(params.pos && params.pos.x && params.pos.y){
  238.                         user.setProperty('posx', params.pos.x);
  239.                         user.setProperty('posy', params.pos.y);
  240.                 }
  241.                 if(params.dir){
  242.                         user.dir = params.dir;
  243.                 }
  244.                 if(user.incant && !doNotStopIncant){
  245.                         Ape.clearTimeout(user.incant);
  246.                 }
  247.                 return true;
  248.         },
  249.         mkchan: function(channel){
  250.                 if(channel.getProperty('name') == this.channel)
  251.                         this.pipe = channel.pipe;
  252.         },
  253.         beforeJoin: function(user, channel){
  254.                 if(channel.getProperty('name') == this.channel){
  255.                         user.mmo_ok = true;
  256.                         user.dir = '';
  257.                         user.cooldown = {};
  258.                         for(var i in this.spells){
  259.                                 user.cooldown[i] = 0;
  260.                         }
  261.                         user.setProperty('mmo_avatar', $random(1,this.userTypeCount));
  262.                         user.setProperty('mmo_life', 1000);
  263.                 }
  264.         },
  265.         afterJoin: function(user, channel){
  266.                 if(channel.getProperty('name') == this.channel) {
  267.                         //Send monster list
  268.                         if(this.creeps.getLength() > 0) {
  269.                                 this.updateCreeps();
  270.                                 user.pipe.sendRaw('mmo_creeps', {creeps:this.creeps});
  271.                         }
  272.                 }
  273.         }
  274.  
  275. });
  276. new ApeMMO_Server();
  277.  
  278. //
  279. // nickname.js
  280. //
  281.  
  282. var userlist = new $H;
  283.  
  284. Ape.registerHookCmd("connect", function(params, cmd) {
  285.  
  286.         if (!$defined(params.name)) return 0;
  287.         if (userlist.has(params.name.toLowerCase())) return ["005", "NICK_USED"];
  288.         if (params.name.length > 16 || params.name.test('[^a-zA-Z0-9]', 'i')) return ["006", "BAD_NICK"];
  289.  
  290.        
  291.         cmd.user.setProperty('name', params.name);
  292.        
  293.         return 1;
  294. });
  295.  
  296. Ape.addEvent('adduser', function(user) {
  297.         userlist.set(user.getProperty('name').toLowerCase(), true);    
  298. });
  299.  
  300. Ape.addEvent('deluser', function(user) {
  301.         userlist.erase(user.getProperty('name').toLowerCase());
  302. });
  303.