├── .gitignore ├── Characters ├── Animation.js ├── Building.js ├── Bullets.js ├── Burst.js ├── Button.js ├── Gobj.js ├── Hero.js ├── Magic.js ├── Map.js ├── Neutral.js ├── Protoss.js ├── Terran.js ├── Units.js ├── Upgrade.js └── Zerg.js ├── Controller ├── keyController.js └── mouseController.js ├── GameRule ├── Cheat.js ├── Game.js ├── Levels.js ├── Multiplayer.js ├── Referee.js ├── Resource.js └── SC_server.js ├── LICENSE.md ├── README.md ├── SVN.log ├── Utils ├── gFrame.js ├── jquery.min.js └── sourceLoader.js ├── bgm └── Deleted ├── cache.manifest ├── css ├── buttonStyle.css ├── mapStyle.css ├── portraitStyle.css └── style.css ├── favicon.ico ├── img ├── Bg │ └── Deleted ├── Charas │ └── Deleted ├── Demo │ ├── Demo.jpg │ └── Demo.png ├── Maps │ └── Deleted └── Menu │ └── Deleted └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | Thumbs.db 2 | .svn/ 3 | .idea/ 4 | Original/ 5 | -------------------------------------------------------------------------------- /Characters/Gobj.js: -------------------------------------------------------------------------------- 1 | //Gobj is original object used in StarCraft 2 | var Gobj=function(props){ 3 | this.x=props.x; 4 | this.y=props.y; 5 | if (props.target instanceof Gobj){ 6 | this.x=(props.target.posX()-this.width/2)>>0; 7 | this.y=(props.target.posY()-this.height/2)>>0; 8 | } 9 | this.action=0;//Only for moving 10 | this.status=""; 11 | this.buffer={};//Buffer names 12 | this.override={};//Buffer effects 13 | this.bufferObjs=[]; 14 | this.allFrames={}; 15 | this.team=props.team?props.team:0;//team 0 by default 16 | }; 17 | 18 | //Default value used if not initialize properly 19 | Gobj.prototype.name="Gobj"; 20 | Gobj.prototype.width=0; 21 | Gobj.prototype.height=0; 22 | Gobj.prototype.isEnemy=function(){ 23 | return this.team!=Game.team; 24 | }; 25 | Gobj.prototype.posX=function(){ 26 | return this.x+this.width/2; 27 | }; 28 | Gobj.prototype.posY=function(){ 29 | return this.y+this.height/2; 30 | }; 31 | Gobj.prototype.imgPos={ 32 | moving:{ 33 | left:[0,0,0,0,0,0,0,0], 34 | top:[0,0,0,0,0,0,0,0] 35 | } 36 | }; 37 | //Only for moving 38 | Gobj.prototype.frame={ 39 | moving:1 40 | }; 41 | //Better only for moving 42 | Gobj.prototype.speed={x:0,y:0}; 43 | 44 | //Basic behaviors 45 | Gobj.prototype.detectOutOfBound=function(){ 46 | //Do nothing here 47 | }; 48 | //Only for moving 49 | Gobj.prototype.updateLocation=function(){ 50 | //Override here 51 | }; 52 | Gobj.prototype.animeFrame=function(){ 53 | //Animation play 54 | this.action++; 55 | if (this.action>=this.frame[this.status]) { 56 | this.action=0; 57 | } 58 | }; 59 | Gobj.prototype.moving=function(){ 60 | //Clear old timer 61 | this.stop(); 62 | //Launch new moving timer 63 | this.status="moving"; 64 | var myself=this; 65 | var movingFrame=function(){ 66 | myself.animeFrame(); 67 | //Relocate character 68 | myself.updateLocation(); 69 | //Detect OutOfBound 70 | myself.detectOutOfBound(); 71 | }; 72 | this.allFrames['moving']=movingFrame; 73 | var animateFrame=function(){ 74 | //Only play animation, will not move 75 | myself.animeFrame(); 76 | }; 77 | this.allFrames['animate']=animateFrame; 78 | }; 79 | Gobj.prototype.stop=function(){ 80 | //Clear both kinds of timer 81 | delete this.allFrames['moving']; 82 | delete this.allFrames['dock']; 83 | delete this.allFrames['animate']; 84 | }; 85 | Gobj.prototype.playFrames=function(){ 86 | var frames=this.allFrames; 87 | for (var type in frames){ 88 | frames[type](); 89 | } 90 | }; 91 | Gobj.prototype.die=function(){ 92 | //Clear old timer 93 | this.stop(); 94 | this.status="dead"; 95 | this.action=0; 96 | //If has die animation 97 | if (this.dieEffect) { 98 | new this.dieEffect({x:this.posX(),y:this.posY()}); 99 | } 100 | }; 101 | Gobj.prototype.include=function(obj){ 102 | return (obj.posY()>this.y)&&(obj.posY()this.x)&&(obj.posX()this.y)&&(ythis.x)&&(xrect.start.x) && (this.posX()rect.start.y) && (this.posY()Map.offsetX) && (this.x<(Map.offsetX+Game.HBOUND)) 141 | && ((this.y+this.height)>Map.offsetY) && (this.y<(Map.offsetY+Game.VBOUND)); 142 | }; 143 | Gobj.prototype.sightInsideScreen=function(){ 144 | return ((this.x+this.width)>(Map.offsetX-this.get('sight'))) && (this.x<(Map.offsetX+Game.HBOUND+this.get('sight'))) 145 | && ((this.y+this.height)>(Map.offsetY-this.get('sight'))) && (this.y<(Map.offsetY+Game.VBOUND+this.get('sight'))); 146 | }; 147 | Gobj.prototype.softCollideWith=function(chara,N){ 148 | if (N==null) N=1; 149 | //Twice radius of hard collision 150 | return chara.insideSquare({centerX:this.posX(),centerY:this.posY(),radius:[this.width*N,this.height*N]}); 151 | }; 152 | Gobj.prototype.collideWith=function(chara){ 153 | //Bounding box: right-left-down-up 154 | return !((this.x>(chara.x+chara.width)) || (chara.x>(this.x+this.width)) 155 | || (this.y>(chara.y+chara.height)) || (chara.y>(this.y+this.height))); 156 | }; 157 | Gobj.prototype.isIdle=function(){ 158 | return this.status=="dock"; 159 | }; 160 | Gobj.prototype.canSee=function(enemy){ 161 | return enemy.inside({centerX:this.posX(),centerY:this.posY(),radius:this.get('sight')}); 162 | }; 163 | Gobj.prototype.get=function(prop){ 164 | //Currently only support upgrade for unit properties, no buildings 165 | var result=eval('this.'+prop);//Can get A.B.C 166 | //ShareFlag is symbol for team sharing array, not speed matrix array 167 | if (result instanceof Array) return result[this.team]; 168 | else return result; 169 | }; 170 | Gobj.prototype.addBuffer=function(bufferObj,onAll){ 171 | for (var prop in bufferObj){ 172 | //Register in override if not exist 173 | if (!this.override[prop]) this.override[prop]=[]; 174 | var buffer=bufferObj[prop]; 175 | //Add buffer into override list 176 | this.override[prop].unshift(buffer); 177 | //Override unit property by time sequence if has 178 | if (this[prop]!=null || prop.indexOf('isInvisible')!=-1 || onAll) this[prop]=buffer; 179 | } 180 | this.bufferObjs.push(bufferObj); 181 | //Refresh 182 | if (this==Game.selectedUnit) Game.refreshInfo(); 183 | }; 184 | Gobj.prototype.removeBuffer=function(bufferObj){ 185 | var bufferObjIndex=this.bufferObjs.indexOf(bufferObj); 186 | //Buffer obj still exist, prevent remove twice 187 | if (bufferObjIndex!=-1){ 188 | for (var prop in bufferObj){ 189 | var buffer=bufferObj[prop]; 190 | var overrideList=this.override[prop]; 191 | //Remove buffer from override list 192 | var index=overrideList.indexOf(buffer); 193 | if (index!=-1) overrideList.splice(index,1); 194 | //Have other buffer, apply it by time sequence 195 | if (overrideList.length>0) this[prop]=overrideList[0]; 196 | else delete this[prop]; 197 | } 198 | //Remove from bufferObjs 199 | this.bufferObjs.splice(bufferObjIndex,1); 200 | //Refresh 201 | if (this==Game.selectedUnit) Game.refreshInfo(); 202 | //Remove successfully 203 | return true; 204 | } 205 | //Remove failure 206 | else return false; 207 | }; 208 | Gobj.prototype.cannotMove=function(){ 209 | return (this instanceof Building) || Boolean(this.burrowBuffer); 210 | }; 211 | Gobj.prototype.evolveTo=function(props){ 212 | //Init 213 | var charaType=props.type,burstArr=props.burstArr,mixin=props.mixin,rallyPoint=props.rallyPoint; 214 | var newTypeChara=null,selectedStatus=[this.selected,(this==Game.selectedUnit)],team=this.team; 215 | //Hide die burst and sound for old unit, then die 216 | this.dieEffect=this.sound.death=null; 217 | this.die(); 218 | if (this.processing && !props.chain) delete this.processing; 219 | //Birth function 220 | var bornAt=function(chara){ 221 | var prop={target:chara,team:team}; 222 | if (mixin) _$.mixin(prop,mixin); 223 | newTypeChara=new charaType(prop); 224 | if (rallyPoint) newTypeChara.destination=rallyPoint; 225 | //Fix cannot select egg issue 226 | Game.commandTimeout(function(){ 227 | if (selectedStatus[0]) Game.addIntoAllSelected(newTypeChara); 228 | if (selectedStatus[1]) Game.changeSelectedTo(newTypeChara); 229 | },0); 230 | }; 231 | //Burst chain 232 | if (burstArr){ 233 | var pos={x:this.posX(),y:this.posY()}; 234 | var birth=new Burst[burstArr[0]](pos); 235 | var evolveChain=function(N){ 236 | return function(){ 237 | birth=new Burst[burstArr[N]](pos); 238 | if ((N+1)1) birth.callback=evolveChain(1); 251 | //Finish evolve chain 252 | else birth.callback=function(){ 253 | var times=charaType.prototype.birthCount; 254 | if (times==null) times=1; 255 | for (var N=0;Nthis.get('MP')) this.magic=this.get('MP'); 43 | } 44 | }, 45 | items:{ 46 | '6':{name:'Yamato'}, 47 | '7':{name:'DefensiveMatrix'}, 48 | '8':{name:'EMPShockwave'}, 49 | '9':{name:'Irradiate'} 50 | }, 51 | cost:{ 52 | man:8 53 | }, 54 | //Override 55 | dock:function(){ 56 | //Use the same behavior 57 | Zerg.Devourer.prototype.dock.call(this); 58 | } 59 | } 60 | }); 61 | Hero.Tassadar=Unit.extends({ 62 | constructorPlus:function(props){ 63 | //Same action mapping 64 | this.imgPos.dock=this.imgPos.moving; 65 | this.frame.dock=this.frame.moving; 66 | //Override 67 | this.magic=this.get('MP'); 68 | }, 69 | prototypePlus: { 70 | //Add basic unit info 71 | name: "Tassadar", 72 | imgPos: { 73 | moving: { 74 | left: [0,95,190,285,380,475,570,665,0,95,190,285,380,475,570,665], 75 | top: [90,90,90,90,90,90,90,90,0,0,0,0,0,0,0,0] 76 | } 77 | }, 78 | width: 95,//95N 79 | height: 90, 80 | frame: { 81 | moving: 1, 82 | stop: 1 83 | }, 84 | //Only for moving status, override 85 | speed:15, 86 | HP: 999, 87 | SP: 999, 88 | armor:3, 89 | MP: 999, 90 | sight:385, 91 | detector: Gobj.detectorBuffer, 92 | dieEffect:Burst.BigBlueExplode, 93 | isFlying:true, 94 | unitType:Unit.BIG, 95 | attackType:AttackableUnit.NORMAL_ATTACK, 96 | recover:function(){ 97 | if (this.lifethis.get('HP')) this.life=this.get('HP'); 100 | } 101 | if (this.shieldthis.get('SP')) this.shield=this.get('SP'); 104 | } 105 | if (this.magicthis.get('MP')) this.magic=this.get('MP'); 108 | } 109 | }, 110 | items:{ 111 | '2':{name:'Ensnare'}, 112 | '3':{name:'Plague'}, 113 | '4':{name:'Lockdown'}, 114 | '5':{name:'EMPShockwave'}, 115 | '6':{name:'Irradiate'}, 116 | '7':{name:'PsionicStorm'}, 117 | '8':{name:'MaelStorm'}, 118 | '9':{name:'StasisField'} 119 | }, 120 | cost:{ 121 | man:8 122 | }, 123 | //Override 124 | dock:function(){ 125 | //Use the same behavior 126 | Zerg.Devourer.prototype.dock.call(this); 127 | } 128 | } 129 | }); 130 | Hero.Kerrigan=AttackableUnit.extends({ 131 | constructorPlus:function(props){ 132 | //Override 133 | this.magic=this.get('MP'); 134 | }, 135 | prototypePlus: { 136 | //Add basic unit info 137 | name: "Kerrigan", 138 | imgPos: { 139 | moving: { 140 | left: [ 141 | [0,0,0,0,0,0,0,0,0],[36,36,36,36,36,36,36,36,36], 142 | [73,73,73,73,73,73,73,73,73],[109,109,109,109,109,109,109,109,109], 143 | [146,146,146,146,146,146,146,146,146],[182,182,182,182,182,182,182,182,182], 144 | [219,219,219,219,219,219,219,219,219],[255,255,255,255,255,255,255,255,255], 145 | [292,292,292,292,292,292,292,292,292],[365,365,365,365,365,365,365,365,365], 146 | [401,401,401,401,401,401,401,401,401],[438,438,438,438,438,438,438,438,438], 147 | [474,474,474,474,474,474,474,474,474],[511,511,511,511,511,511,511,511,511], 148 | [547,547,547,547,547,547,547,547,547],[584,584,584,584,584,584,584,584,584] 149 | ], 150 | top: [ 151 | [0,43,86,129,172,215,258,301,344],[0,43,86,129,172,215,258,301,344], 152 | [0,43,86,129,172,215,258,301,344],[0,43,86,129,172,215,258,301,344], 153 | [0,43,86,129,172,215,258,301,344],[0,43,86,129,172,215,258,301,344], 154 | [0,43,86,129,172,215,258,301,344],[0,43,86,129,172,215,258,301,344], 155 | [0,43,86,129,172,215,258,301,344],[0,43,86,129,172,215,258,301,344], 156 | [0,43,86,129,172,215,258,301,344],[0,43,86,129,172,215,258,301,344], 157 | [0,43,86,129,172,215,258,301,344],[0,43,86,129,172,215,258,301,344], 158 | [0,43,86,129,172,215,258,301,344],[0,43,86,129,172,215,258,301,344] 159 | ] 160 | }, 161 | attack: { 162 | left: [ 163 | [0,0,0,0],[36,36,36,36], 164 | [73,73,73,73],[109,109,109,109], 165 | [146,146,146,146],[182,182,182,182], 166 | [219,219,219,219],[255,255,255,255], 167 | [292,292,292,292],[365,365,365,365], 168 | [401,401,401,401],[438,438,438,438], 169 | [474,474,474,474],[511,511,511,511], 170 | [547,547,547,547],[584,584,584,584] 171 | ], 172 | top: [ 173 | [387,430,473,516],[387,430,473,516], 174 | [387,430,473,516],[387,430,473,516], 175 | [387,430,473,516],[387,430,473,516], 176 | [387,430,473,516],[387,430,473,516], 177 | [387,430,473,516],[387,430,473,516], 178 | [387,430,473,516],[387,430,473,516], 179 | [387,430,473,516],[387,430,473,516], 180 | [387,430,473,516],[387,430,473,516] 181 | ] 182 | }, 183 | dock: { 184 | left: [0,36,73,109,146,182,219,255,292,365,401,438,474,511,547,584], 185 | top: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] 186 | } 187 | }, 188 | width: 36,//36.5N>>0 189 | height: 43,//43N 190 | frame: { 191 | moving: 9, 192 | dock: 1, 193 | attack:4 194 | }, 195 | //Only for moving status, override 196 | speed:10, 197 | HP: 300, 198 | damage: 20, 199 | armor:1, 200 | MP: 300, 201 | sight:315, 202 | attackRange: 210, 203 | attackInterval: 2200, 204 | dieEffect:Burst.HumanDeath, 205 | attackEffect:Burst.FireSpark, 206 | isFlying:false, 207 | unitType:Unit.SMALL, 208 | attackType:AttackableUnit.WAVE_ATTACK, 209 | recover:function(){ 210 | if (this.magicthis.get('MP')) this.magic=this.get('MP'); 213 | } 214 | }, 215 | cost:{ 216 | man:1 217 | }, 218 | items:{ 219 | '6':{name:'StimPacks'}, 220 | '7':{name:'Cloak'}, 221 | '8':{name:'Lockdown'} 222 | }, 223 | //Override 224 | dock:function(){ 225 | //Use the same behavior 226 | AttackableUnit.turnAround.call(this); 227 | } 228 | } 229 | }); 230 | Hero.Sarah=AttackableUnit.extends({ 231 | constructorPlus:function(props){ 232 | //Override 233 | this.magic=this.get('MP'); 234 | }, 235 | prototypePlus: { 236 | //Add basic unit info 237 | name: "Sarah", 238 | imgPos: { 239 | moving: { 240 | left: [ 241 | [0,0,0,0,0,0,0,0],[62,62,62,62,62,62,62,62], 242 | [124,124,124,124,124,124,124,124],[186,186,186,186,186,186,186,186], 243 | [248,248,248,248,248,248,248,248],[310,310,310,310,310,310,310,310], 244 | [372,372,372,372,372,372,372,372],[434,434,434,434,434,434,434,434], 245 | [496,496,496,496,496,496,496,496],[620,620,620,620,620,620,620,620], 246 | [682,682,682,682,682,682,682,682],[744,744,744,744,744,744,744,744], 247 | [806,806,806,806,806,806,806,806],[868,868,868,868,868,868,868,868], 248 | [930,930,930,930,930,930,930,930],[992,992,992,992,992,992,992,992] 249 | ], 250 | top: [ 251 | [0,58,116,174,232,290,348,406],[0,58,116,174,232,290,348,406], 252 | [0,58,116,174,232,290,348,406],[0,58,116,174,232,290,348,406], 253 | [0,58,116,174,232,290,348,406],[0,58,116,174,232,290,348,406], 254 | [0,58,116,174,232,290,348,406],[0,58,116,174,232,290,348,406], 255 | [0,58,116,174,232,290,348,406],[0,58,116,174,232,290,348,406], 256 | [0,58,116,174,232,290,348,406],[0,58,116,174,232,290,348,406], 257 | [0,58,116,174,232,290,348,406],[0,58,116,174,232,290,348,406], 258 | [0,58,116,174,232,290,348,406],[0,58,116,174,232,290,348,406] 259 | ] 260 | }, 261 | attack: { 262 | left: [ 263 | [0,0,0,0,0,0,0,0],[62,62,62,62,62,62,62,62], 264 | [124,124,124,124,124,124,124,124],[186,186,186,186,186,186,186,186], 265 | [248,248,248,248,248,248,248,248],[310,310,310,310,310,310,310,310], 266 | [372,372,372,372,372,372,372,372],[434,434,434,434,434,434,434,434], 267 | [496,496,496,496,496,496,496,496],[620,620,620,620,620,620,620,620], 268 | [682,682,682,682,682,682,682,682],[744,744,744,744,744,744,744,744], 269 | [806,806,806,806,806,806,806,806],[868,868,868,868,868,868,868,868], 270 | [930,930,930,930,930,930,930,930],[992,992,992,992,992,992,992,992] 271 | ], 272 | top: [ 273 | [464,522,580,638,696,754,812,870],[464,522,580,638,696,754,812,870], 274 | [464,522,580,638,696,754,812,870],[464,522,580,638,696,754,812,870], 275 | [464,522,580,638,696,754,812,870],[464,522,580,638,696,754,812,870], 276 | [464,522,580,638,696,754,812,870],[464,522,580,638,696,754,812,870], 277 | [464,522,580,638,696,754,812,870],[464,522,580,638,696,754,812,870], 278 | [464,522,580,638,696,754,812,870],[464,522,580,638,696,754,812,870], 279 | [464,522,580,638,696,754,812,870],[464,522,580,638,696,754,812,870], 280 | [464,522,580,638,696,754,812,870],[464,522,580,638,696,754,812,870] 281 | ] 282 | }, 283 | dock: { 284 | left: [0,62,124,186,248,310,372,434,496,620,682,744,806,868,930,992], 285 | top: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] 286 | } 287 | }, 288 | width: 62,//(N-1) 289 | height: 58,//(N-1) 290 | frame: { 291 | moving: 8, 292 | dock: 1, 293 | attack: 8 294 | }, 295 | //Only for moving status, override 296 | speed:10, 297 | HP: 500, 298 | SP: 500, 299 | damage: 40, 300 | armor:2, 301 | plasma:0, 302 | MP: 500, 303 | sight:315, 304 | attackRange: 70, 305 | attackInterval: 2000, 306 | dieEffect:Burst.HumanDeath, 307 | attackEffect:Burst.FireSpark, 308 | isFlying:false, 309 | unitType:Unit.SMALL, 310 | attackType:AttackableUnit.NORMAL_ATTACK, 311 | recover:function(){ 312 | if (this.lifethis.get('HP')) this.life=this.get('HP'); 315 | } 316 | if (this.shieldthis.get('SP')) this.shield=this.get('SP'); 319 | } 320 | if (this.magicthis.get('MP')) this.magic=this.get('MP'); 323 | } 324 | }, 325 | cost:{ 326 | man:2 327 | }, 328 | items:{ 329 | '6':{name:'Cloak'}, 330 | '7':{name:'PsionicStorm'}, 331 | '8':{name:'Plague'}, 332 | '9':{name:'Ensnare'} 333 | }, 334 | //Override 335 | dock:function(){ 336 | //Use the same behavior 337 | AttackableUnit.turnAround.call(this); 338 | } 339 | } 340 | }); 341 | Hero.DevilHunter=AttackableUnit.extends({ 342 | constructorPlus:function(props){ 343 | //Override 344 | this.magic=this.get('MP'); 345 | }, 346 | prototypePlus: { 347 | //Add basic unit info 348 | name: "DevilHunter", 349 | imgPos: { 350 | moving: { 351 | left: [ 352 | [0,0,0,0,0,0,0,0],[62,62,62,62,62,62,62,62], 353 | [124,124,124,124,124,124,124,124],[186,186,186,186,186,186,186,186], 354 | [248,248,248,248,248,248,248,248],[310,310,310,310,310,310,310,310], 355 | [372,372,372,372,372,372,372,372],[434,434,434,434,434,434,434,434], 356 | [496,496,496,496,496,496,496,496],[620,620,620,620,620,620,620,620], 357 | [682,682,682,682,682,682,682,682],[744,744,744,744,744,744,744,744], 358 | [806,806,806,806,806,806,806,806],[868,868,868,868,868,868,868,868], 359 | [930,930,930,930,930,930,930,930],[992,992,992,992,992,992,992,992] 360 | ], 361 | top: [ 362 | [0,58,116,174,232,290,348,406],[0,58,116,174,232,290,348,406], 363 | [0,58,116,174,232,290,348,406],[0,58,116,174,232,290,348,406], 364 | [0,58,116,174,232,290,348,406],[0,58,116,174,232,290,348,406], 365 | [0,58,116,174,232,290,348,406],[0,58,116,174,232,290,348,406], 366 | [0,58,116,174,232,290,348,406],[0,58,116,174,232,290,348,406], 367 | [0,58,116,174,232,290,348,406],[0,58,116,174,232,290,348,406], 368 | [0,58,116,174,232,290,348,406],[0,58,116,174,232,290,348,406], 369 | [0,58,116,174,232,290,348,406],[0,58,116,174,232,290,348,406] 370 | ] 371 | }, 372 | attack: { 373 | left: [ 374 | [0,0,0,0,0,0,0,0],[62,62,62,62,62,62,62,62], 375 | [124,124,124,124,124,124,124,124],[186,186,186,186,186,186,186,186], 376 | [248,248,248,248,248,248,248,248],[310,310,310,310,310,310,310,310], 377 | [372,372,372,372,372,372,372,372],[434,434,434,434,434,434,434,434], 378 | [496,496,496,496,496,496,496,496],[620,620,620,620,620,620,620,620], 379 | [682,682,682,682,682,682,682,682],[744,744,744,744,744,744,744,744], 380 | [806,806,806,806,806,806,806,806],[868,868,868,868,868,868,868,868], 381 | [930,930,930,930,930,930,930,930],[992,992,992,992,992,992,992,992] 382 | ], 383 | top: [ 384 | [464,522,580,638,696,754,812,870],[464,522,580,638,696,754,812,870], 385 | [464,522,580,638,696,754,812,870],[464,522,580,638,696,754,812,870], 386 | [464,522,580,638,696,754,812,870],[464,522,580,638,696,754,812,870], 387 | [464,522,580,638,696,754,812,870],[464,522,580,638,696,754,812,870], 388 | [464,522,580,638,696,754,812,870],[464,522,580,638,696,754,812,870], 389 | [464,522,580,638,696,754,812,870],[464,522,580,638,696,754,812,870], 390 | [464,522,580,638,696,754,812,870],[464,522,580,638,696,754,812,870], 391 | [464,522,580,638,696,754,812,870],[464,522,580,638,696,754,812,870] 392 | ] 393 | }, 394 | dock: { 395 | left: [0,62,124,186,248,310,372,434,496,620,682,744,806,868,930,992], 396 | top: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] 397 | } 398 | }, 399 | width: 62,//(N-1) 400 | height: 58,//(N-1) 401 | frame: { 402 | moving: 8, 403 | dock: 1, 404 | attack: 8 405 | }, 406 | //Only for moving status, override 407 | speed:10, 408 | HP: 2000, 409 | SP: 2000, 410 | MP: 200, 411 | damage: 30, 412 | armor:0, 413 | plasma:0, 414 | sight:315, 415 | detector:Gobj.detectorBuffer, 416 | attackRange: 210, 417 | attackInterval: 1500, 418 | fireDelay:600, 419 | dieEffect:Burst.SmallZergFlyingDeath, 420 | isFlying:false, 421 | unitType:Unit.SMALL, 422 | attackType:AttackableUnit.NORMAL_ATTACK, 423 | recover:function(){ 424 | if (this.lifethis.get('HP')) this.life=this.get('HP'); 427 | } 428 | if (this.shieldthis.get('SP')) this.shield=this.get('SP'); 431 | } 432 | if (this.magicthis.get('MP')) this.magic=this.get('MP'); 435 | } 436 | }, 437 | cost:{ 438 | man:8 439 | }, 440 | upgrade:['UpgradeGroundWeapons','UpgradeGroundArmor','UpgradePlasmaShields'], 441 | //Override 442 | dock:function(){ 443 | //Use the same behavior 444 | AttackableUnit.turnAround.call(this); 445 | } 446 | } 447 | }); 448 | -------------------------------------------------------------------------------- /Characters/Map.js: -------------------------------------------------------------------------------- 1 | var Map={ 2 | currentMap:'Switchback',//By default 3 | ready:false, 4 | offsetX:0, 5 | offsetY:0, 6 | speed:40, 7 | triggerMargin:20, 8 | //To synchronize drawing map and units, will not refresh immediately 9 | needRefresh:false, 10 | fogFlag:true, 11 | fogUnits:[],//Units need to draw fog on screen 12 | allUnits:[],//Units need to draw fog on minimap 13 | batchSize:0,//Draw fog by each batch 14 | miniCxt:$('canvas[name="mini_map"]')[0].getContext('2d'), 15 | fogCanvas:document.createElement('canvas'), 16 | shadowCanvas:document.createElement('canvas'),//Pre-render for fog shadow 17 | insideStroke:{ 18 | width:0, 19 | height:0 20 | }, 21 | //Init map 22 | setCurrentMap:function(name){ 23 | Map.currentMap=name; 24 | $('canvas[name="mini_map"]').attr('class',name); 25 | //Init inside stroke size 26 | Map.insideStroke.width=(130*Game.HBOUND/Map.getCurrentMap().width)>>0; 27 | Map.insideStroke.height=(130*Game.VBOUND/Map.getCurrentMap().height)>>0; 28 | //Init fog relative 29 | Map.fogCxt=Map.fogCanvas.getContext('2d'); 30 | Map.fogCanvas.width=130; 31 | Map.fogCanvas.height=Math.round(130*Map.getCurrentMap().height/Map.getCurrentMap().width); 32 | Map.fogCanvas.ratio=130/Map.getCurrentMap().width; 33 | Map.shadowCanvas.width=Map.shadowCanvas.height=100; 34 | Map.shadowCxt=Map.shadowCanvas.getContext('2d'); 35 | //Prepared fog shadow for quick render 36 | var radial=Map.shadowCxt.createRadialGradient(50,50,25,50,50,50); 37 | radial.addColorStop(0,'rgba(0,0,0,1)'); 38 | radial.addColorStop(1,'rgba(0,0,0,0)'); 39 | Map.shadowCxt.fillStyle=radial; 40 | Map.shadowCxt.beginPath(); 41 | Map.shadowCxt.arc(50,50,50,0,Math.PI*2); 42 | Map.shadowCxt.fill(); 43 | //Map is ready after current map set 44 | Map.ready=true; 45 | }, 46 | getCurrentMap:function(){ 47 | return sourceLoader.sources['Map_'+Map.currentMap]; 48 | }, 49 | //Draw interface call 50 | drawFogAndMinimap:function(){ 51 | if (Map.fogFlag){ 52 | Map.refreshFog(); 53 | //Draw fog on main map 54 | var ratio=Map.fogCanvas.ratio; 55 | Game.fogCxt.clearRect(0,0,Game.HBOUND,Game.VBOUND); 56 | Game.fogCxt.drawImage(Map.fogCanvas,Math.round(Map.offsetX*ratio),Math.round(Map.offsetY*ratio), 57 | Math.round(Game.HBOUND*ratio),Math.round(Game.VBOUND*ratio),0,0,Game.HBOUND,Game.VBOUND); 58 | } 59 | //Draw mini-map 60 | Map.drawMiniMap(); 61 | }, 62 | //Used by drawFogAndMinimap 63 | refreshFog:function(){ 64 | //Reset composite operation 65 | Map.fogCxt.globalCompositeOperation='source-over'; 66 | //Brush black fog to clean old fog 67 | Map.fogCxt.fillStyle='rgba(0,0,0,1)'; 68 | Map.fogCxt.fillRect(0,0,Map.fogCanvas.width,Map.fogCanvas.height); 69 | //Other things have sight 70 | var parasitedEnemies=Unit.allEnemyUnits().filter(function(chara){ 71 | return chara.buffer.Parasite==Game.team; 72 | }); 73 | var scannerSweeps=Burst.allEffects.filter(function(anime){ 74 | return anime.constructor.name=="ScannerSweep" && anime.team==Game.team; 75 | }); 76 | var addInObjs=parasitedEnemies.concat(scannerSweeps); 77 | //Clear fog 78 | Map.fogCxt.globalCompositeOperation='destination-out'; 79 | //Initial 80 | Map.allUnits=Unit.allOurUnits().concat(Building.ourBuildings()).concat(addInObjs); 81 | //Draw fog 82 | Map.fogCxt.fillStyle='rgba(0,0,0,1)'; 83 | var ratio=Map.fogCanvas.ratio; 84 | Map.allUnits.forEach(function(chara){ 85 | //Clear fog on screen for our units inside screen 86 | var centerX=Math.round(chara.posX()*ratio); 87 | var centerY=Math.round(chara.posY()*ratio); 88 | var radius=Math.round(chara.get('sight')*ratio<<1); 89 | Map.fogCxt.drawImage(Map.shadowCanvas,0,0,100,100,centerX-radius,centerY-radius,radius<<1,radius<<1); 90 | }); 91 | }, 92 | //Used by drawFogAndMinimap: draw red&green block and white stroke 93 | drawMiniMap:function(){ 94 | //Selected map size 95 | var mapWidth=Map.getCurrentMap().width; 96 | var mapHeight=Map.getCurrentMap().height; 97 | //Clear mini-map 98 | Map.miniCxt.clearRect(0,0,130,130); 99 | //Re-draw mini-map points 100 | var miniX,miniY,rectSize; 101 | Building.allBuildings.concat(Unit.allUnits).forEach(function(chara){ 102 | //Filter out invisible enemy 103 | if (chara['isInvisible'+Game.team] && chara.isEnemy()) return; 104 | miniX=(130*chara.x/mapWidth)>>0; 105 | miniY=(130*chara.y/mapHeight)>>0; 106 | Map.miniCxt.fillStyle=(chara.isEnemy())?'red':'lime'; 107 | rectSize=(chara instanceof Building)?4:3; 108 | Map.miniCxt.fillRect(miniX,miniY,rectSize,rectSize); 109 | }); 110 | //Draw fog on mini-map 111 | if (Map.fogFlag) Map.miniCxt.drawImage(Map.fogCanvas,0,0,Map.fogCanvas.width,Map.fogCanvas.height,0,0,130,130); 112 | //Re-draw inside stroke 113 | Map.miniCxt.strokeStyle='white'; 114 | Map.miniCxt.lineWidth=2; 115 | Map.miniCxt.strokeRect((130*Map.offsetX/mapWidth)>>0,(130*Map.offsetY/mapHeight)>>0,Map.insideStroke.width,Map.insideStroke.height); 116 | }, 117 | drawMud:function(){ 118 | var _increments=[[0,1],[-1,0],[0,-1],[1,0]]; 119 | var mudRadius=120; 120 | var mudIncrements=_$.mapTraverse(_increments,function(x){ 121 | return x*mudRadius/2; 122 | }); 123 | Game.backCxt.save(); 124 | Game.backCxt.beginPath(); 125 | //Create fill style for mud 126 | var mudPattern=Game.backCxt.createPattern(sourceLoader.sources['Mud'],"repeat"); 127 | Game.backCxt.fillStyle=mudPattern; 128 | Building.allBuildings.filter(function(chara){ 129 | return (chara instanceof Building.ZergBuilding) && !chara.noMud && chara.insideScreen(); 130 | }).forEach(function(chara){ 131 | var centerX=chara.posX()-Map.offsetX; 132 | var centerY=chara.posY()-Map.offsetY; 133 | var pos=[centerX+mudRadius,centerY-mudRadius]; 134 | Game.backCxt.moveTo(pos[0],pos[1]); 135 | for(var M=0,angle=-Math.PI/4;M<4;M++,angle+=Math.PI/2){ 136 | for(var N=0;N<5;N++){ 137 | Game.backCxt.arc(pos[0],pos[1],mudRadius/4,angle,angle+Math.PI/2); 138 | if (N<4) { 139 | pos[0]+=mudIncrements[M][0]; 140 | pos[1]+=mudIncrements[M][1]; 141 | } 142 | } 143 | } 144 | }); 145 | //Stroke edge clearly 146 | Game.backCxt.strokeStyle="#212"; 147 | Game.backCxt.lineWidth=3; 148 | Game.backCxt.stroke(); 149 | //Fill mud 150 | Game.backCxt.fill(); 151 | Game.backCxt.restore(); 152 | }, 153 | drawBg:function(){ 154 | //Clear background 155 | Game.backCxt.clearRect(0,0,Game.HBOUND,Game.VBOUND); 156 | //Draw map as background 157 | Game.backCxt.drawImage(Map.getCurrentMap(),Map.offsetX,Map.offsetY,Game.HBOUND,Game.VBOUND-Game.infoBox.height+5, 158 | 0,0,Game.HBOUND,Game.VBOUND-Game.infoBox.height+5); 159 | //Draw mud for ZergBuildings 160 | Map.drawMud(); 161 | }, 162 | refresh:function(direction){ 163 | var edgeX=Map.getCurrentMap().width-Game.HBOUND; 164 | var edgeY=Map.getCurrentMap().height-Game.VBOUND+Game.infoBox.height-5; 165 | var onlyMap; 166 | switch (direction){ 167 | case "LEFT": 168 | Map.offsetX-=Map.speed; 169 | if (Map.offsetX<0) Map.offsetX=0; 170 | break; 171 | case "RIGHT": 172 | Map.offsetX+=Map.speed; 173 | if (Map.offsetX>edgeX) Map.offsetX=edgeX; 174 | break; 175 | case "TOP": 176 | Map.offsetY-=Map.speed; 177 | if (Map.offsetY<0) Map.offsetY=0; 178 | break; 179 | case "BOTTOM": 180 | Map.offsetY+=Map.speed; 181 | if (Map.offsetY>edgeY) Map.offsetY=edgeY; 182 | break; 183 | case "MAP": 184 | onlyMap=true; 185 | break; 186 | } 187 | Map.drawBg(); 188 | //Need re-calculate fog when screen moves 189 | if (!onlyMap) Map.drawFogAndMinimap(); 190 | }, 191 | clickHandler:function(event){ 192 | //Mouse at (clickX,clickY) 193 | var clickX=event.pageX-$('canvas[name="mini_map"]').offset().left; 194 | var clickY=event.pageY-$('canvas[name="mini_map"]').offset().top; 195 | //Relocate map center 196 | Map.relocateAt(Map.getCurrentMap().width*clickX/130,Map.getCurrentMap().height*clickY/130); 197 | }, 198 | dblClickHandler:function(event){ 199 | //Mouse at (clickX,clickY) 200 | var clickX=event.pageX-$('canvas[name="mini_map"]').offset().left; 201 | var clickY=event.pageY-$('canvas[name="mini_map"]').offset().top; 202 | //Map (clickX,clickY) to position (mapX,mapY) on map 203 | var mapX=Map.getCurrentMap().width*clickX/130; 204 | var mapY=Map.getCurrentMap().height*clickY/130; 205 | //Move selected units to (mapX,mapY) 206 | Unit.allUnits.filter(function(chara){ 207 | return (chara.team==Game.team) && chara.selected; 208 | }).forEach(function(chara){ 209 | if (chara.attack) chara.stopAttack(); 210 | chara.targetLock=true; 211 | chara.moveTo(mapX,mapY); 212 | }); 213 | }, 214 | relocateAt:function(centerX,centerY){ 215 | //Get map edge 216 | var edgeX=Map.getCurrentMap().width-Game.HBOUND; 217 | var edgeY=Map.getCurrentMap().height-Game.VBOUND+Game.infoBox.height-5; 218 | //Map (centerX,centerY) to position (offsetX,offsetY) on top-left in map 219 | var offsetX=(centerX-Game.HBOUND/2)>>0; 220 | if (offsetX<0) offsetX=0; 221 | if (offsetX>edgeX) offsetX=edgeX; 222 | var offsetY=(centerY-(Game.VBOUND-Game.infoBox.height+5)/2)>>0; 223 | if (offsetY<0) offsetY=0; 224 | if (offsetY>edgeY) offsetY=edgeY; 225 | //Relocate map 226 | Map.offsetX=offsetX; 227 | Map.offsetY=offsetY; 228 | Map.needRefresh=true;//For synchronize 229 | } 230 | }; -------------------------------------------------------------------------------- /Characters/Neutral.js: -------------------------------------------------------------------------------- 1 | /******* Define Neutral units *******/ 2 | var Neutral={}; 3 | Neutral.Ragnasaur=Unit.extends({ 4 | constructorPlus:function(props){ 5 | //Same action mapping 6 | this.imgPos.dock=this.imgPos.moving; 7 | }, 8 | prototypePlus: { 9 | //Add basic unit info 10 | name: "Ragnasaur", 11 | imgPos: { 12 | moving: { 13 | left: [ 14 | [26,26,26,26,26,26,26,26,26],[130,130,130,130,130,130,130,130,130], 15 | [234,234,234,234,234,234,234,234,234],[338,338,338,338,338,338,338,338,338], 16 | [442,442,442,442,442,442,442,442,442],[546,546,546,546,546,546,546,546,546], 17 | [650,650,650,650,650,650,650,650,650],[754,754,754,754,754,754,754,754,754], 18 | [858,858,858,858,858,858,858,858,858],[1066,1066,1066,1066,1066,1066,1066,1066,1066], 19 | [1170,1170,1170,1170,1170,1170,1170,1170,1170],[1274,1274,1274,1274,1274,1274,1274,1274,1274], 20 | [1378,1378,1378,1378,1378,1378,1378,1378,1378],[1482,1482,1482,1482,1482,1482,1482,1482,1482], 21 | [1586,1586,1586,1586,1586,1586,1586,1586,1586],[1690,1690,1690,1690,1690,1690,1690,1690,1690] 22 | ], 23 | top: [ 24 | [26,130,234,338,442,546,650,754,858],[26,130,234,338,442,546,650,754,858], 25 | [26,130,234,338,442,546,650,754,858],[26,130,234,338,442,546,650,754,858], 26 | [26,130,234,338,442,546,650,754,858],[26,130,234,338,442,546,650,754,858], 27 | [26,130,234,338,442,546,650,754,858],[26,130,234,338,442,546,650,754,858], 28 | [26,130,234,338,442,546,650,754,858],[26,130,234,338,442,546,650,754,858], 29 | [26,130,234,338,442,546,650,754,858],[26,130,234,338,442,546,650,754,858], 30 | [26,130,234,338,442,546,650,754,858],[26,130,234,338,442,546,650,754,858], 31 | [26,130,234,338,442,546,650,754,858],[26,130,234,338,442,546,650,754,858] 32 | ] 33 | } 34 | }, 35 | width: 52,//104N+26 36 | height: 52, 37 | frame: { 38 | moving: 9, 39 | dock: 1 40 | }, 41 | //Only for moving status, override 42 | speed:6, 43 | HP: 60, 44 | armor:0, 45 | sight:175, 46 | dieEffect:Burst.RagnasaurDeath, 47 | isFlying:false, 48 | unitType:Unit.SMALL, 49 | recover:Building.ZergBuilding.prototype.recover, 50 | //Override 51 | dock:function(){ 52 | //Use the same behavior 53 | Unit.walkAround.call(this); 54 | } 55 | } 56 | }); 57 | Neutral.Rhynsdon=Unit.extends({ 58 | constructorPlus:function(props){ 59 | //Same action mapping 60 | this.imgPos.dock=this.imgPos.moving; 61 | }, 62 | prototypePlus: { 63 | //Add basic unit info 64 | name: "Rhynsdon", 65 | imgPos: { 66 | moving: { 67 | left: [ 68 | [26,26,26,26,26,26,26,26,26,26,26],[130,130,130,130,130,130,130,130,130,130,130], 69 | [234,234,234,234,234,234,234,234,234,234,234],[338,338,338,338,338,338,338,338,338,338,338], 70 | [442,442,442,442,442,442,442,442,442,442,442],[546,546,546,546,546,546,546,546,546,546,546], 71 | [650,650,650,650,650,650,650,650,650,650,650],[754,754,754,754,754,754,754,754,754,754,754], 72 | [858,858,858,858,858,858,858,858,858,858,858],[1066,1066,1066,1066,1066,1066,1066,1066,1066,1066,1066], 73 | [1170,1170,1170,1170,1170,1170,1170,1170,1170,1170,1170],[1274,1274,1274,1274,1274,1274,1274,1274,1274,1274,1274], 74 | [1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378],[1482,1482,1482,1482,1482,1482,1482,1482,1482,1482,1482], 75 | [1586,1586,1586,1586,1586,1586,1586,1586,1586,1586,1586],[1690,1690,1690,1690,1690,1690,1690,1690,1690,1690,1690] 76 | ], 77 | top: [ 78 | [26,130,234,338,442,546,650,754,858,962,1066],[26,130,234,338,442,546,650,754,858,962,1066], 79 | [26,130,234,338,442,546,650,754,858,962,1066],[26,130,234,338,442,546,650,754,858,962,1066], 80 | [26,130,234,338,442,546,650,754,858,962,1066],[26,130,234,338,442,546,650,754,858,962,1066], 81 | [26,130,234,338,442,546,650,754,858,962,1066],[26,130,234,338,442,546,650,754,858,962,1066], 82 | [26,130,234,338,442,546,650,754,858,962,1066],[26,130,234,338,442,546,650,754,858,962,1066], 83 | [26,130,234,338,442,546,650,754,858,962,1066],[26,130,234,338,442,546,650,754,858,962,1066], 84 | [26,130,234,338,442,546,650,754,858,962,1066],[26,130,234,338,442,546,650,754,858,962,1066], 85 | [26,130,234,338,442,546,650,754,858,962,1066],[26,130,234,338,442,546,650,754,858,962,1066] 86 | ] 87 | } 88 | }, 89 | width: 52,//104N+26 90 | height: 52, 91 | frame: { 92 | moving: 11, 93 | dock: 1 94 | }, 95 | //Only for moving status, override 96 | speed:6, 97 | HP: 60, 98 | armor:0, 99 | sight:175, 100 | dieEffect:Burst.RhynsdonDeath, 101 | isFlying:false, 102 | unitType:Unit.SMALL, 103 | recover:Building.ZergBuilding.prototype.recover, 104 | //Override 105 | dock:function(){ 106 | //Use the same behavior 107 | Unit.walkAround.call(this); 108 | } 109 | } 110 | }); 111 | Neutral.Ursadon=Unit.extends({ 112 | constructorPlus:function(props){ 113 | //Same action mapping 114 | this.imgPos.dock=this.imgPos.moving; 115 | }, 116 | prototypePlus: { 117 | //Add basic unit info 118 | name: "Ursadon", 119 | imgPos: { 120 | moving: { 121 | left: [ 122 | [15,15,15,15,15,15,15,15],[107,107,107,107,107,107,107,107], 123 | [199,199,199,199,199,199,199,199],[291,291,291,291,291,291,291,291], 124 | [383,383,383,383,383,383,383,383],[475,475,475,475,475,475,475,475], 125 | [567,567,567,567,567,567,567,567],[659,659,659,659,659,659,659,659], 126 | [751,751,751,751,751,751,751,751],[935,935,935,935,935,935,935,935], 127 | [1027,1027,1027,1027,1027,1027,1027,1027],[1119,1119,1119,1119,1119,1119,1119,1119], 128 | [1211,1211,1211,1211,1211,1211,1211,1211],[1303,1303,1303,1303,1303,1303,1303,1303], 129 | [1395,1395,1395,1395,1395,1395,1395,1395],[1487,1487,1487,1487,1487,1487,1487,1487] 130 | ], 131 | top: [ 132 | [15,107,199,291,383,475,567,659],[15,107,199,291,383,475,567,659], 133 | [15,107,199,291,383,475,567,659],[15,107,199,291,383,475,567,659], 134 | [15,107,199,291,383,475,567,659],[15,107,199,291,383,475,567,659], 135 | [15,107,199,291,383,475,567,659],[15,107,199,291,383,475,567,659], 136 | [15,107,199,291,383,475,567,659],[15,107,199,291,383,475,567,659], 137 | [15,107,199,291,383,475,567,659],[15,107,199,291,383,475,567,659], 138 | [15,107,199,291,383,475,567,659],[15,107,199,291,383,475,567,659], 139 | [15,107,199,291,383,475,567,659],[15,107,199,291,383,475,567,659] 140 | ] 141 | } 142 | }, 143 | width: 62,//92N+15 144 | height: 62, 145 | frame: { 146 | moving: 8, 147 | dock: 1 148 | }, 149 | //Only for moving status, override 150 | speed:6, 151 | HP: 60, 152 | armor:0, 153 | sight:175, 154 | dieEffect:Burst.UrsadonDeath, 155 | isFlying:false, 156 | unitType:Unit.SMALL, 157 | recover:Building.ZergBuilding.prototype.recover, 158 | //Override 159 | dock:function(){ 160 | //Use the same behavior 161 | Unit.walkAround.call(this); 162 | } 163 | } 164 | }); 165 | Neutral.Bengalaas=Unit.extends({ 166 | constructorPlus:function(props){ 167 | //Same action mapping 168 | this.imgPos.dock=this.imgPos.moving; 169 | }, 170 | prototypePlus: { 171 | //Add basic unit info 172 | name: "Bengalaas", 173 | imgPos: { 174 | moving: { 175 | left: [ 176 | [38,38,38,38,38,38,38,38,38,38,38,38],[166,166,166,166,166,166,166,166,166,166,166,166], 177 | [294,294,294,294,294,294,294,294,294,294,294,294],[422,422,422,422,422,422,422,422,422,422,422,422], 178 | [550,550,550,550,550,550,550,550,550,550,550,550],[678,678,678,678,678,678,678,678,678,678,678,678], 179 | [806,806,806,806,806,806,806,806,806,806,806,806],[934,934,934,934,934,934,934,934,934,934,934,934], 180 | [1062,1062,1062,1062,1062,1062,1062,1062,1062,1062,1062,1062],[1318,1318,1318,1318,1318,1318,1318,1318,1318,1318,1318,1318], 181 | [1446,1446,1446,1446,1446,1446,1446,1446,1446,1446,1446,1446],[1574,1574,1574,1574,1574,1574,1574,1574,1574,1574,1574,1574], 182 | [1702,1702,1702,1702,1702,1702,1702,1702,1702,1702,1702,1702],[1830,1830,1830,1830,1830,1830,1830,1830,1830,1830,1830,1830], 183 | [1958,1958,1958,1958,1958,1958,1958,1958,1958,1958,1958,1958],[2086,2086,2086,2086,2086,2086,2086,2086,2086,2086,2086,2086] 184 | ], 185 | top: [ 186 | [38,166,294,422,550,678,806,934,1062,1190,1318,1446],[38,166,294,422,550,678,806,934,1062,1190,1318,1446], 187 | [38,166,294,422,550,678,806,934,1062,1190,1318,1446],[38,166,294,422,550,678,806,934,1062,1190,1318,1446], 188 | [38,166,294,422,550,678,806,934,1062,1190,1318,1446],[38,166,294,422,550,678,806,934,1062,1190,1318,1446], 189 | [38,166,294,422,550,678,806,934,1062,1190,1318,1446],[38,166,294,422,550,678,806,934,1062,1190,1318,1446], 190 | [38,166,294,422,550,678,806,934,1062,1190,1318,1446],[38,166,294,422,550,678,806,934,1062,1190,1318,1446], 191 | [38,166,294,422,550,678,806,934,1062,1190,1318,1446],[38,166,294,422,550,678,806,934,1062,1190,1318,1446], 192 | [38,166,294,422,550,678,806,934,1062,1190,1318,1446],[38,166,294,422,550,678,806,934,1062,1190,1318,1446], 193 | [38,166,294,422,550,678,806,934,1062,1190,1318,1446],[38,166,294,422,550,678,806,934,1062,1190,1318,1446] 194 | ] 195 | } 196 | }, 197 | width: 52, 198 | height: 52,//128N+38 199 | frame: { 200 | moving: 12, 201 | dock: 1 202 | }, 203 | //Only for moving status, override 204 | speed:6, 205 | HP: 60, 206 | armor:0, 207 | sight:175, 208 | dieEffect:Burst.BengalaasDeath, 209 | isFlying:false, 210 | unitType:Unit.SMALL, 211 | recover:Building.ZergBuilding.prototype.recover, 212 | //Override 213 | dock:function(){ 214 | //Use the same behavior 215 | Unit.walkAround.call(this); 216 | } 217 | } 218 | }); 219 | Neutral.Scantid=Unit.extends({ 220 | constructorPlus:function(props){ 221 | //Same action mapping 222 | this.imgPos.dock=this.imgPos.moving; 223 | }, 224 | prototypePlus: { 225 | //Add basic unit info 226 | name: "Scantid", 227 | imgPos: { 228 | moving: { 229 | left: [ 230 | [12,12,12,12,12,12,12,12,12,12,12,12],[104,104,104,104,104,104,104,104,104,104,104,104], 231 | [196,196,196,196,196,196,196,196,196,196,196,196],[288,288,288,288,288,288,288,288,288,288,288,288], 232 | [380,380,380,380,380,380,380,380,380,380,380,380],[472,472,472,472,472,472,472,472,472,472,472,472], 233 | [564,564,564,564,564,564,564,564,564,564,564,564],[656,656,656,656,656,656,656,656,656,656,656,656], 234 | [748,748,748,748,748,748,748,748,748,748,748,748],[932,932,932,932,932,932,932,932,932,932,932,932], 235 | [1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024],[1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116], 236 | [1208,1208,1208,1208,1208,1208,1208,1208,1208,1208,1208,1208],[1300,1300,1300,1300,1300,1300,1300,1300,1300,1300,1300,1300], 237 | [1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,1392],[1484,1484,1484,1484,1484,1484,1484,1484,1484,1484,1484,1484] 238 | ], 239 | top: [ 240 | [12,104,196,288,380,472,564,656,748,840,932,1024],[12,104,196,288,380,472,564,656,748,840,932,1024], 241 | [12,104,196,288,380,472,564,656,748,840,932,1024],[12,104,196,288,380,472,564,656,748,840,932,1024], 242 | [12,104,196,288,380,472,564,656,748,840,932,1024],[12,104,196,288,380,472,564,656,748,840,932,1024], 243 | [12,104,196,288,380,472,564,656,748,840,932,1024],[12,104,196,288,380,472,564,656,748,840,932,1024], 244 | [12,104,196,288,380,472,564,656,748,840,932,1024],[12,104,196,288,380,472,564,656,748,840,932,1024], 245 | [12,104,196,288,380,472,564,656,748,840,932,1024],[12,104,196,288,380,472,564,656,748,840,932,1024], 246 | [12,104,196,288,380,472,564,656,748,840,932,1024],[12,104,196,288,380,472,564,656,748,840,932,1024], 247 | [12,104,196,288,380,472,564,656,748,840,932,1024],[12,104,196,288,380,472,564,656,748,840,932,1024] 248 | ] 249 | } 250 | }, 251 | width: 68,//92N+12 252 | height: 68, 253 | frame: { 254 | moving: 12, 255 | dock: 1 256 | }, 257 | //Only for moving status, override 258 | speed:6, 259 | HP: 60, 260 | armor:0, 261 | sight:175, 262 | dieEffect:Burst.ScantidDeath, 263 | isFlying:false, 264 | unitType:Unit.SMALL, 265 | recover:Building.ZergBuilding.prototype.recover, 266 | //Override 267 | dock:function(){ 268 | //Use the same behavior 269 | Unit.walkAround.call(this); 270 | } 271 | } 272 | }); 273 | Neutral.Kakaru=Unit.extends({ 274 | constructorPlus:function(props){ 275 | //Same action mapping 276 | this.imgPos.dock=this.imgPos.moving; 277 | this.frame.dock=this.frame.moving; 278 | }, 279 | prototypePlus: { 280 | //Add basic unit info 281 | name: "Kakaru", 282 | imgPos: { 283 | moving: { 284 | left: [ 285 | [12,12,12,12,12,12,12,12,12,12,12,12],[104,104,104,104,104,104,104,104,104,104,104,104], 286 | [196,196,196,196,196,196,196,196,196,196,196,196],[288,288,288,288,288,288,288,288,288,288,288,288], 287 | [380,380,380,380,380,380,380,380,380,380,380,380],[472,472,472,472,472,472,472,472,472,472,472,472], 288 | [564,564,564,564,564,564,564,564,564,564,564,564],[656,656,656,656,656,656,656,656,656,656,656,656], 289 | [748,748,748,748,748,748,748,748,748,748,748,748],[932,932,932,932,932,932,932,932,932,932,932,932], 290 | [1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024],[1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116], 291 | [1208,1208,1208,1208,1208,1208,1208,1208,1208,1208,1208,1208],[1300,1300,1300,1300,1300,1300,1300,1300,1300,1300,1300,1300], 292 | [1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,1392],[1484,1484,1484,1484,1484,1484,1484,1484,1484,1484,1484,1484] 293 | ], 294 | top: [ 295 | [12,104,196,288,380,472,564,656,748,840,932,1024],[12,104,196,288,380,472,564,656,748,840,932,1024], 296 | [12,104,196,288,380,472,564,656,748,840,932,1024],[12,104,196,288,380,472,564,656,748,840,932,1024], 297 | [12,104,196,288,380,472,564,656,748,840,932,1024],[12,104,196,288,380,472,564,656,748,840,932,1024], 298 | [12,104,196,288,380,472,564,656,748,840,932,1024],[12,104,196,288,380,472,564,656,748,840,932,1024], 299 | [12,104,196,288,380,472,564,656,748,840,932,1024],[12,104,196,288,380,472,564,656,748,840,932,1024], 300 | [12,104,196,288,380,472,564,656,748,840,932,1024],[12,104,196,288,380,472,564,656,748,840,932,1024], 301 | [12,104,196,288,380,472,564,656,748,840,932,1024],[12,104,196,288,380,472,564,656,748,840,932,1024], 302 | [12,104,196,288,380,472,564,656,748,840,932,1024],[12,104,196,288,380,472,564,656,748,840,932,1024] 303 | ] 304 | } 305 | }, 306 | width: 68,//92N+12 307 | height: 68, 308 | frame: { 309 | moving: 12 310 | }, 311 | //Only for moving status, override 312 | speed:6, 313 | HP: 60, 314 | armor:0, 315 | sight:210, 316 | dieEffect:Burst.KakaruDeath, 317 | isFlying:true, 318 | unitType:Unit.SMALL, 319 | recover:Building.ZergBuilding.prototype.recover, 320 | //Override 321 | dock:function(){ 322 | //Use the same behavior 323 | Unit.walkAround.call(this); 324 | } 325 | } 326 | }); -------------------------------------------------------------------------------- /Controller/keyController.js: -------------------------------------------------------------------------------- 1 | var keyController={ 2 | shift:false, 3 | ctrl:false, 4 | disable:false, 5 | start:function(){ 6 | //Keyboard settings 7 | window.onkeydown=function(event){ 8 | //Will not switch page by Ctrl+N,cannot debug 9 | //event.preventDefault(); 10 | //Sometimes need to disable shortcut key 11 | if (keyController.disable && event.keyCode!=13) return; 12 | switch (event.keyCode){ 13 | //Press SHIFT down 14 | case 16: 15 | keyController.shift=true; 16 | break; 17 | //Press CTRL down 18 | case 17: 19 | keyController.ctrl=true; 20 | break; 21 | //Press number 22 | case 48:case 49:case 50:case 51:case 52: 23 | case 53:case 54:case 55:case 56:case 57: 24 | var teamNum=String.fromCharCode(event.keyCode); 25 | //Building team 26 | if (keyController.ctrl) { 27 | Game.addSelectedIntoTeam(teamNum); 28 | } 29 | //Call team 30 | else { 31 | Game.callTeam(teamNum); 32 | } 33 | break; 34 | //Move map 35 | case 37: 36 | Map.needRefresh="LEFT"; 37 | break; 38 | case 38: 39 | Map.needRefresh="TOP"; 40 | break; 41 | case 39: 42 | Map.needRefresh="RIGHT"; 43 | break; 44 | case 40: 45 | Map.needRefresh="BOTTOM"; 46 | break; 47 | //Replay speed control 48 | case 107: 49 | case 33: 50 | //Speed up + or pageUp 51 | Button.speedUpHandler(); 52 | break; 53 | case 109: 54 | case 34: 55 | //Slow down - or pageDown 56 | Button.slowDownHandler(); 57 | break; 58 | //Shortcut keys: 59 | //Press M 60 | case 77: 61 | if ($.makeArray($('div.panel_Control button')).some(function(btn){ 62 | return btn.className=='move' 63 | })) Button.moveHandler(); 64 | break; 65 | //Press S 66 | case 83: 67 | if ($.makeArray($('div.panel_Control button')).some(function(btn){ 68 | return btn.className=='stop' 69 | })) Button.stopHandler(); 70 | break; 71 | //Press A 72 | case 65: 73 | if ($.makeArray($('div.panel_Control button')).some(function(btn){ 74 | return btn.className=='attack' 75 | })) Button.attackHandler(); 76 | break; 77 | //Press P 78 | case 80: 79 | if ($.makeArray($('div.panel_Control button')).some(function(btn){ 80 | return btn.className=='patrol' 81 | })) Button.patrolHandler(); 82 | break; 83 | //Press H 84 | case 72: 85 | if ($.makeArray($('div.panel_Control button')).some(function(btn){ 86 | return btn.className=='hold' 87 | })) Button.holdHandler(); 88 | break; 89 | //Press ENTER 90 | case 13: 91 | Cheat.handler(); 92 | break; 93 | } 94 | }; 95 | window.onkeyup=function(event){ 96 | switch (event.keyCode){ 97 | //Press SHIFT up 98 | case 16: 99 | keyController.shift=false; 100 | break; 101 | //Press CTRL up 102 | case 17: 103 | keyController.ctrl=false; 104 | break; 105 | } 106 | }; 107 | } 108 | }; -------------------------------------------------------------------------------- /Controller/mouseController.js: -------------------------------------------------------------------------------- 1 | var mouseController={ 2 | down:false, 3 | drag:false, 4 | startPoint:{x:0,y:0}, 5 | endPoint:{x:0,y:0}, 6 | isMultiSelect:function(){ 7 | return keyController.shift; 8 | }, 9 | isJoinTeam:function(){ 10 | return keyController.ctrl; 11 | }, 12 | leftClick:function(event){ 13 | //Mouse at (clickX,clickY) 14 | var offset=$('#fogCanvas').offset(); 15 | var clickX=event.pageX-offset.left; 16 | var clickY=event.pageY-offset.top; 17 | //Intercept event inside infoBox 18 | if (clickY>Game.infoBox.y) return; 19 | //Selection mode 20 | if (Button.callback==null) { 21 | //Find selected one, convert position 22 | var selectedOne=Game.getSelectedOne(clickX+Map.offsetX,clickY+Map.offsetY); 23 | //Cannot select enemy invisible unit 24 | if ((selectedOne instanceof Gobj) && selectedOne['isInvisible'+Game.team] && selectedOne.isEnemy()) return; 25 | //Single select will unselect all units and only choose selected one 26 | //Multi select will keep selected status and do nothing 27 | if (!mouseController.isMultiSelect()) 28 | Game.unselectAll(); 29 | //If has selected one 30 | if (selectedOne instanceof Gobj) { 31 | //Sound effect 32 | if (!(selectedOne.isEnemy())) selectedOne.sound.selected.play(); 33 | //Cannot multiSelect with enemy 34 | if (selectedOne.isEnemy() || (Game.selectedUnit.isEnemy && Game.selectedUnit.isEnemy())) 35 | Game.unselectAll(); 36 | //Only selected one to show portrait 37 | Game.changeSelectedTo(selectedOne); 38 | //Add into allSelected if not included 39 | Game.addIntoAllSelected(selectedOne); 40 | } 41 | else { 42 | //Click null 43 | Game.changeSelectedTo({}); 44 | Game.unselectAll(); 45 | } 46 | } 47 | //Button mode 48 | else { 49 | //Callback 50 | Button.execute(event); 51 | } 52 | //Hide tooltip when click 53 | $('div.tooltip_Box').hide(); 54 | //Login user statistic 55 | if (Multiplayer.statistic!=null) Multiplayer.statistic.left++; 56 | }, 57 | rightClick:function(event,unlock,btn){ 58 | //Mouse at (clickX,clickY) 59 | var offset=$('#fogCanvas').offset(); 60 | var clickX=event.pageX-offset.left; 61 | var clickY=event.pageY-offset.top; 62 | //Intercept event inside infoBox 63 | if (clickY>Game.infoBox.y) return; 64 | //Show right click cursor 65 | var pos={x:(clickX+Map.offsetX),y:(clickY+Map.offsetY)}; 66 | new Burst.RightClickCursor(pos); 67 | var charas=Game.allSelected.filter(function(chara){ 68 | //Can only control our alive unit 69 | return chara.team==Game.team && chara.status!="dead"; 70 | }); 71 | //Handle user right click 72 | Multiplayer.cmds.push(JSON.stringify({ 73 | uids:Multiplayer.getUIDs(charas), 74 | type:'rightClick', 75 | pos:pos, 76 | unlock:Boolean(unlock), 77 | btn:btn 78 | })); 79 | //Login user statistic 80 | if (Multiplayer.statistic!=null) Multiplayer.statistic.right++; 81 | }, 82 | rightClickHandler:function(charas,pos,unlock,btn){ 83 | //Find selected one or nothing 84 | var selectedEnemy=(charas.length>0)?Game.getSelectedOne(pos.x,pos.y,charas[0].team.toString()):null; 85 | charas.forEach(function(chara){ 86 | //Sound effect 87 | if (!chara.isEnemy() && chara.sound.moving) chara.sound.moving.play(); 88 | //Interrupt old destination routing 89 | if (chara.destination) { 90 | //Break possible dead lock 91 | if (chara.destination.next) chara.destination.next=null; 92 | delete chara.destination; 93 | } 94 | //Cancel possible hold 95 | if (chara.hold) { 96 | delete chara.AI; 97 | delete chara.findNearbyTargets; 98 | delete chara.hold; 99 | Button.refreshButtons(); 100 | } 101 | //Unit cannot attack will always choose move mode 102 | var attackOrMove=(chara.attack)?(selectedEnemy instanceof Gobj):false; 103 | //Attack mode 104 | if (attackOrMove) { 105 | if (chara.cannotMove() && !(chara.isInAttackRange(selectedEnemy))) return; 106 | //Intercept invisible enemy 107 | if (selectedEnemy['isInvisible'+Game.team]) { 108 | if (!chara.isEnemy()) Referee.voice('pError').play(); 109 | return; 110 | } 111 | chara.targetLock=true; 112 | chara.attack(selectedEnemy); 113 | } 114 | //Move mode 115 | else { 116 | if (chara.cannotMove()) return; 117 | //Only attackable units can stop attack 118 | if (chara.attack) chara.stopAttack(); 119 | //Lock destination by default 120 | chara.targetLock=!unlock; 121 | chara.moveTo(pos.x,pos.y); 122 | //Record destination 123 | if (btn=='attack') { 124 | chara.destination={x:pos.x,y:pos.y}; 125 | } 126 | if (btn=='patrol') { 127 | //Patrol dead lock 128 | chara.destination={x:pos.x,y:pos.y}; 129 | chara.destination.next={x:chara.posX(),y:chara.posY(),next:chara.destination}; 130 | } 131 | } 132 | }); 133 | }, 134 | dblClick:function(){ 135 | //Multi select same type units 136 | if (!(Game.selectedUnit.isEnemy && Game.selectedUnit.isEnemy())) { 137 | var charas=Unit.allUnits.filter(function(chara){ 138 | return !(chara.isEnemy()) && chara.insideScreen() && (chara.name==Game.selectedUnit.name); 139 | }); 140 | Game.addIntoAllSelected(charas); 141 | } 142 | }, 143 | //Can control all units 144 | toControlAll:function(){ 145 | //For desktop 146 | if (!Game.isApp){ 147 | //Mouse left click 148 | $('#fogCanvas')[0].onclick=function(event){ 149 | event.preventDefault(); 150 | if (mouseController.drag) { 151 | //End drag, onclick triggered after onmouseup, don't do default left click action 152 | mouseController.drag=false; 153 | } 154 | else { 155 | mouseController.leftClick(event); 156 | } 157 | }; 158 | //Mouse right click 159 | $('#fogCanvas')[0].oncontextmenu=function(event){ 160 | //Prevent context menu show 161 | event.preventDefault(); 162 | //Should not control units during replay 163 | if (Game.replayFlag) return; 164 | mouseController.rightClick(event); 165 | //Cancel pointer 166 | $('div.GameLayer').removeAttr('status'); 167 | //Cancel callback 168 | Button.callback=null; 169 | }; 170 | //Double click 171 | $('#fogCanvas')[0].ondblclick=function(event){ 172 | //Prevent screen select 173 | event.preventDefault(); 174 | mouseController.dblClick(); 175 | }; 176 | //Mouse click start 177 | $('#fogCanvas')[0].onmousedown=function(event){ 178 | event.preventDefault(); 179 | //Do not allow rectangular-multi-select with right click, only left clicks 180 | if (event.which === 3){ 181 | return; 182 | } 183 | if (!mouseController.down) { 184 | //Mouse at (clickX,clickY) 185 | var clickX=event.pageX-$('#fogCanvas').offset().left; 186 | var clickY=event.pageY-$('#fogCanvas').offset().top; 187 | mouseController.startPoint={x:clickX,y:clickY}; 188 | mouseController.down=true; 189 | } 190 | }; 191 | //Mouse drag 192 | $('#fogCanvas')[0].onmousemove=function(event){ 193 | event.preventDefault(); 194 | if (mouseController.down) { 195 | //Mouse at (clickX,clickY) 196 | var clickX=event.pageX-$('#fogCanvas').offset().left; 197 | var clickY=event.pageY-$('#fogCanvas').offset().top; 198 | mouseController.endPoint={x:clickX,y:clickY}; 199 | if (Math.abs(clickX-mouseController.startPoint.x)>5 && 200 | Math.abs(clickY-mouseController.startPoint.y)>5) { 201 | mouseController.drag=true; 202 | } 203 | } 204 | }; 205 | //Global client refresh map 206 | window.onmousemove=function(event){ 207 | event.preventDefault(); 208 | //Mouse at (clickX,clickY) 209 | mouseController.mouseX=event.clientX; 210 | mouseController.mouseY=event.clientY; 211 | }; 212 | //Mouse click end 213 | $('#fogCanvas')[0].onmouseup=function(event){ 214 | event.preventDefault(); 215 | mouseController.down=false; 216 | if (mouseController.drag) { 217 | //Multi select inside rect 218 | Game.multiSelectInRect(); 219 | } 220 | }; 221 | } 222 | //For mobile 223 | else { 224 | $('#fogCanvas')[0].ontouchstart=function(event){ 225 | event.preventDefault(); 226 | //Drag rectangle 227 | if (event.touches.length==2){ 228 | var offsetX=$('#fogCanvas').offset().left; 229 | var offsetY=$('#fogCanvas').offset().top; 230 | mouseController.drag=true; 231 | mouseController.startPoint={x:event.touches[0].pageX-offsetX,y:event.touches[0].pageY-offsetY}; 232 | mouseController.endPoint={x:event.touches[1].pageX-offsetX,y:event.touches[1].pageY-offsetY}; 233 | } 234 | }; 235 | $('#fogCanvas')[0].ontouchend=function(event){ 236 | event.preventDefault(); 237 | if (mouseController.drag) { 238 | //Multi select inside rect 239 | Game.multiSelectInRect(); 240 | //End drag 241 | mouseController.drag=false; 242 | } 243 | }; 244 | mouseController.mobileScreen=new Hammer(window); 245 | mouseController.canvasScreen=new Hammer($('#fogCanvas')[0]); 246 | mouseController.canvasScreen.on('tap',function(event){ 247 | event.preventDefault(); 248 | //Callback 249 | mouseController.leftClick(event.pointers[0]); 250 | }); 251 | mouseController.canvasScreen.on('doubletap',function(event){ 252 | event.preventDefault(); 253 | mouseController.dblClick(); 254 | }); 255 | mouseController.canvasScreen.on('press',function(event){ 256 | //Prevent context menu show 257 | event.preventDefault(); 258 | //Should not control units during replay 259 | if (Game.replayFlag) return; 260 | mouseController.rightClick(event.changedPointers[0]); 261 | //Cancel handler 262 | $('div.GameLayer').removeAttr('status'); 263 | Button.callback=null; 264 | }); 265 | mouseController.canvasScreen.on('panleft',function(event){ 266 | Map.needRefresh="RIGHT"; 267 | }); 268 | mouseController.canvasScreen.on('panright',function(event){ 269 | Map.needRefresh="LEFT"; 270 | }); 271 | mouseController.mobileScreen.on('panup',function(event){ 272 | Map.needRefresh="BOTTOM"; 273 | }); 274 | mouseController.mobileScreen.on('pandown',function(event){ 275 | Map.needRefresh="TOP"; 276 | }); 277 | } 278 | //Both sides 279 | $('div#GamePlay div').on('contextmenu',function(event){ 280 | event.preventDefault(); 281 | }); 282 | $('canvas[name="mini_map"]').on('click',function(event){ 283 | event.preventDefault(); 284 | Map.clickHandler(event); 285 | }); 286 | $('canvas[name="mini_map"]').on('contextmenu',function(event){ 287 | event.preventDefault(); 288 | Map.dblClickHandler(event); 289 | }); 290 | } 291 | }; 292 | -------------------------------------------------------------------------------- /GameRule/Cheat.js: -------------------------------------------------------------------------------- 1 | var Cheat={ 2 | isShown:false, 3 | cwal:false, 4 | gathering:false, 5 | manUnlimited:false, 6 | handler:function(){ 7 | if (Cheat.isShown){ 8 | if (Multiplayer.ON){ 9 | Multiplayer.webSocket.send(JSON.stringify({ 10 | type:'chat', 11 | from:Game.team, 12 | msg:$('input#cheatInput').val() 13 | })); 14 | } 15 | else { 16 | //Forbid cheating during replay 17 | if (!Game.replayFlag){ 18 | var cheatFlag=Cheat.execute($('input#cheatInput').val().toLowerCase()); 19 | if (cheatFlag) { 20 | //Refresh control panel 21 | Game.changeSelectedTo(Game.selectedUnit); 22 | Game.showMessage('Cheat enabled'); 23 | } 24 | } 25 | } 26 | $('#cheat_Box').hide(); 27 | $('input#cheatInput').val(''); 28 | Cheat.isShown=false; 29 | keyController.disable=false; 30 | } 31 | else { 32 | $('#cheat_Box').show(); 33 | $('input#cheatInput').focus(); 34 | Cheat.isShown=true; 35 | keyController.disable=true; 36 | } 37 | }, 38 | execute:function(cheatCode){ 39 | //Forbid cheating when multiplayer mode 40 | if (Multiplayer.ON) return; 41 | var cheatFlag=true; 42 | switch (cheatCode){ 43 | case "show me the money": 44 | Resource[Game.team].mine+=10000; 45 | Resource[Game.team].gas+=10000; 46 | break; 47 | case "black sheep wall": 48 | //Switch between show fog or not show 49 | Map.fogFlag=!Map.fogFlag; 50 | if (Map.fogFlag==false){ 51 | //Clear old fog on screen 52 | Game.fogCxt.clearRect(0,0,Game.HBOUND,Game.VBOUND); 53 | //Redraw mini-map 54 | Map.drawFogAndMinimap(); 55 | } 56 | break; 57 | case "something for nothing": 58 | //Upgrade all grades 59 | for (var grade in Upgrade){ 60 | Upgrade[grade].effect(Game.team); 61 | } 62 | break; 63 | case "full recovery": 64 | Unit.allOurUnits().concat(Building.ourBuildings()).forEach(function(chara){ 65 | chara.life=chara.get('HP'); 66 | if (chara.SP) chara.shield=chara.get('SP'); 67 | if (chara.MP) chara.magic=chara.get('MP'); 68 | }); 69 | break; 70 | case "staying alive": 71 | Referee.winCondition=Referee.loseCondition=function(){ 72 | return false; 73 | }; 74 | break; 75 | case "operation cwal": 76 | Cheat.cwal=!(Cheat.cwal); 77 | break; 78 | case "the gathering": 79 | Cheat.gathering=!(Cheat.gathering); 80 | break; 81 | case "food for thought": 82 | Cheat.manUnlimited=!(Cheat.manUnlimited); 83 | break; 84 | case "power overwhelming": 85 | if (Cheat.oldCalculateDamageBy){ 86 | var tempCalculateDamageBy= $.extend([],Cheat.oldCalculateDamageBy); 87 | Cheat.oldCalculateDamageBy=[Unit.prototype.calculateDamageBy,Building.prototype.calculateDamageBy]; 88 | Unit.prototype.calculateDamageBy=tempCalculateDamageBy[0]; 89 | Building.prototype.calculateDamageBy=tempCalculateDamageBy[1]; 90 | } 91 | else { 92 | Cheat.oldCalculateDamageBy=[Unit.prototype.calculateDamageBy,Building.prototype.calculateDamageBy]; 93 | Unit.prototype.calculateDamageBy=function(enemyObj){ 94 | if (enemyObj.isEnemy && enemyObj.isEnemy()) return 0; 95 | else return Cheat.oldCalculateDamageBy[0].call(this,enemyObj); 96 | }; 97 | Building.prototype.calculateDamageBy=function(enemyObj){ 98 | if (enemyObj.isEnemy && enemyObj.isEnemy()) return 0; 99 | else return Cheat.oldCalculateDamageBy[1].call(this,enemyObj); 100 | }; 101 | } 102 | break; 103 | case "big daddy": 104 | var daddy=new Hero.HeroCruiser({x:Map.offsetX+Game.HBOUND/2,y:Map.offsetY+Game.VBOUND/2}); 105 | Game.changeSelectedTo(daddy); 106 | break; 107 | case "big mommy": 108 | var mommy=new Hero.Sarah({x:Map.offsetX+Game.HBOUND/2,y:Map.offsetY+Game.VBOUND/2}); 109 | Game.changeSelectedTo(mommy); 110 | break; 111 | case "game over man": 112 | case "gg": 113 | Game.lose(); 114 | break; 115 | case "there is no cow level": 116 | case "your gg": 117 | Game.win(); 118 | break; 119 | case "fuck your mother": 120 | Unit.allEnemyUnits().concat(Building.enemyBuildings()).forEach(function(chara){ 121 | chara.die(); 122 | }); 123 | break; 124 | case "fuck my asshole": 125 | Unit.allOurUnits().concat(Building.ourBuildings()).forEach(function(chara){ 126 | chara.die(); 127 | }); 128 | break; 129 | case "liuda is god": 130 | Cheat.execute('black sheep wall'); 131 | Referee.winCondition=Referee.loseCondition=function(){ 132 | return false; 133 | }; 134 | Unit.allUnits.concat(Building.allBuildings).forEach(function(chara){ 135 | chara.die(); 136 | }); 137 | break; 138 | default: 139 | //Not match any of above cheating code 140 | cheatFlag=false; 141 | break; 142 | } 143 | return cheatFlag; 144 | } 145 | }; 146 | -------------------------------------------------------------------------------- /GameRule/Multiplayer.js: -------------------------------------------------------------------------------- 1 | var Multiplayer={ 2 | ON:false,//by default 3 | webSocket:null, 4 | cmds:[], 5 | snapshotFlag:false, 6 | replaySnapshotFlag:true, 7 | getSocket:function(){ 8 | if (window.WebSocket) { 9 | //ServerList: (1)HongKong:nvhae.com (3)Canada:104.128.82.12 10 | var webSocket=Multiplayer.webSocket=new WebSocket('ws://nvhae.com:28082'); 11 | webSocket.onerror=function(){ 12 | //Offline flag for Store&Forward 13 | Game.offline=true; 14 | }; 15 | return webSocket; 16 | } 17 | else return null; 18 | }, 19 | sendUserInfo:function(){ 20 | var webSocket=Multiplayer.getSocket(); 21 | if (webSocket) { 22 | webSocket.onopen=function(){ 23 | webSocket.send(JSON.stringify({type:'login',level:Game.level,team:Game.team,version:navigator.userAgent, 24 | platform:navigator.platform,language:navigator.language,size:{x:innerWidth,y:innerHeight}})); 25 | Multiplayer.statistic={left:0,right:0}; 26 | //Test parse info 27 | var url = 'http://chaxun.1616.net/s.php?type=ip&output=json&callback=?&_='+Math.random(); 28 | $.getJSON(url, function(data){ 29 | webSocket.send(JSON.stringify({ 30 | type:'log',log:"Isp("+data.Isp+"), Browser("+data.Browser+"), OS("+data.OS+")"})); 31 | }); 32 | //Test snapshot 33 | if (Multiplayer.snapshotFlag){ 34 | var N=1; 35 | setInterval(function(){ 36 | webSocket.send(JSON.stringify({ 37 | type:'snapshot', 38 | units:Unit.allUnits.sort(function(u1,u2){ 39 | if (u1.team==u2.team) return u1.name.localeCompare(u2.name); 40 | else return u1.team-u2.team; 41 | }).map(function(chara){ 42 | var result=(chara.name+' HP'+chara.life+' T'+chara.team+' ['+(chara.x>>0)+','+(chara.y>>0)+']'); 43 | if (chara.magic!=null) result+=' M'+chara.magic; 44 | return result; 45 | }), 46 | buildings:Building.allBuildings.sort(function(b1,b2){ 47 | if (b1.team==b2.team) return b1.name.localeCompare(b2.name); 48 | else return b1.team-b2.team; 49 | }).map(function(chara){ 50 | return chara.name+' HP'+chara.life+' T'+chara.team+' ['+(chara.x>>0)+','+(chara.y>>0)+']'; 51 | }), 52 | click:{left:Multiplayer.statistic.left,right:Multiplayer.statistic.right}, 53 | count:{ourUnits:Unit.allOurUnits().length,enemyUnits:Unit.allEnemyUnits().length, 54 | ourBuildings:Building.ourBuildings().length,enemyBuildings:Building.enemyBuildings().length}, 55 | num:N 56 | })); 57 | //Reset click statistic 58 | Multiplayer.statistic={left:0,right:0}; 59 | N++; 60 | },60000); 61 | } 62 | //Test replay record every 10 seconds 63 | if (Multiplayer.replaySnapshotFlag) { 64 | setInterval(function(){ 65 | webSocket.send(JSON.stringify({ 66 | type:'replaySnapshot', 67 | replaySnapshot:{ 68 | team:Game.team, 69 | level:Game.level, 70 | cmds:Game.replay, 71 | end:Game.mainTick 72 | } 73 | })); 74 | },10000); 75 | } 76 | }; 77 | } 78 | }, 79 | enable:function(){ 80 | var webSocket=Multiplayer.getSocket(); 81 | if (webSocket) { 82 | webSocket.onopen=function(){ 83 | Game.showMessage("Already connected to server!"); 84 | }; 85 | webSocket.onclose=function(){ 86 | Game.showMessage("You've disconnected from server!"); 87 | }; 88 | webSocket.onerror=function(){ 89 | Game.showMessage("Cannot connect to server..."); 90 | }; 91 | webSocket.onmessage=function(message){ 92 | var msgObj=JSON.parse(message.data); 93 | switch(msgObj.type){ 94 | case "ping": 95 | Multiplayer.webSocket.send(JSON.stringify({type:'pong'})); 96 | console.log('Receive ping'); 97 | break; 98 | case "notice": 99 | Game.showMessage(msgObj.msg); 100 | break; 101 | case "start": 102 | //Choose team 103 | Game.team=msgObj.team; 104 | //Bind controller 105 | mouseController.toControlAll();//Can control all units 106 | keyController.start();//Start monitor 107 | Game.animation(); 108 | break; 109 | case "replay": 110 | Game.saveReplay(msgObj.replay); 111 | break; 112 | case "tick": 113 | Game.serverTick=msgObj.tick; 114 | Multiplayer.parseTickCmd(msgObj); 115 | break; 116 | } 117 | }; 118 | Multiplayer.ON=true; 119 | } 120 | else { 121 | Game.showMessage("Your browser doesn't support WebSocket..."); 122 | } 123 | }, 124 | parseTickCmd:function(msgObj){ 125 | if (msgObj.cmds){ 126 | if (!Game.commands[msgObj.tick]) Game.commands[msgObj.tick]=[]; 127 | msgObj.cmds.forEach(function(cmdStr){ 128 | var cmd=JSON.parse(cmdStr); 129 | switch (cmd.type){ 130 | case 'rightClick': 131 | Game.commands[msgObj.tick].push(function(){ 132 | //Closures 133 | var uids=cmd.uids; 134 | var pos=cmd.pos; 135 | var unlock=cmd.unlock; 136 | var btn=cmd.btn; 137 | return function(){ 138 | var charas=Multiplayer.getUnitsByUIDs(uids); 139 | mouseController.rightClickHandler(charas,pos,unlock,btn); 140 | }; 141 | }()); 142 | break; 143 | case 'stop': 144 | Game.commands[msgObj.tick].push(function(){ 145 | //Closures 146 | var uids=cmd.uids; 147 | return function(){ 148 | var charas=Multiplayer.getUnitsByUIDs(uids); 149 | Button.stopHandler(charas); 150 | }; 151 | }()); 152 | break; 153 | case 'hold': 154 | Game.commands[msgObj.tick].push(function(){ 155 | //Closures 156 | var uids=cmd.uids; 157 | return function(){ 158 | var charas=Multiplayer.getUnitsByUIDs(uids); 159 | Button.holdHandler(charas); 160 | }; 161 | }()); 162 | break; 163 | case 'magic': 164 | //Scarab and Interceptor 165 | if (cmd.duration){ 166 | Game.commands[msgObj.tick].push(function(){ 167 | //Closures 168 | var uids=cmd.uids; 169 | var name=cmd.name; 170 | var duration=cmd.duration; 171 | return function(){ 172 | var owner=Multiplayer.getUnitsByUIDs(uids)[0]; 173 | if (owner && Resource.paypal.call(owner,Resource.getCost(name))){ 174 | //Cheat: Operation cwal 175 | if (Cheat.cwal) duration=0; 176 | Game.commandTimeout(function(){ 177 | Magic[name].spell.call(owner); 178 | delete owner.processing; 179 | },duration*100); 180 | //Occupy flag 181 | owner.processing={ 182 | name:name, 183 | startTime:Game.mainTick, 184 | time:duration 185 | }; 186 | } 187 | }; 188 | }()); 189 | } 190 | //Normal magic 191 | else { 192 | Game.commands[msgObj.tick].push(function(){ 193 | //Closures 194 | var uids=cmd.uids; 195 | var name=cmd.name; 196 | var pos=cmd.pos; 197 | var creditBill=cmd.creditBill; 198 | return function(){ 199 | var owner=Multiplayer.getUnitsByUIDs(uids)[0]; 200 | if (owner){ 201 | //Need callback with location 202 | if (pos) { 203 | //Spell magic with location in multiplayer mode 204 | if (creditBill) owner.creditBill=creditBill; 205 | Magic[name].spell.call(owner,pos); 206 | } 207 | //Execute magic immediately 208 | else { 209 | if (Resource.paypal.call(owner,Resource.getCost(name))){ 210 | Magic[name].spell.call(owner); 211 | } 212 | } 213 | } 214 | }; 215 | }()); 216 | } 217 | break; 218 | case 'upgrade': 219 | if (cmd.duration){ 220 | Game.commands[msgObj.tick].push(function(){ 221 | //Closures 222 | var uids=cmd.uids; 223 | var name=cmd.name; 224 | var duration=cmd.duration; 225 | var team=cmd.team; 226 | return function(){ 227 | var owner=Multiplayer.getUnitsByUIDs(uids)[0]; 228 | //Still owner alive and can afford payment 229 | if (owner && Resource.paypal.call(owner,Resource.getCost(name))){ 230 | //Cheat: Operation cwal 231 | if (Cheat.cwal) duration=0; 232 | Game.commandTimeout(function(){ 233 | Upgrade[name].effect(team); 234 | delete owner.processing; 235 | if (team==Game.team){ 236 | Referee.voice('upgrade')[Game.race.selected].play(); 237 | Game.refreshInfo(); 238 | Game.showMessage('Upgrade complete'); 239 | } 240 | },duration*100); 241 | //Occupy flag 242 | owner.processing={ 243 | name:name, 244 | startTime:Game.mainTick, 245 | time:duration 246 | }; 247 | } 248 | }; 249 | }()); 250 | } 251 | else { 252 | Game.commands[msgObj.tick].push(function(){ 253 | //Closures 254 | var team=cmd.team; 255 | var name=cmd.name; 256 | return function(){ 257 | //Will effect immediately 258 | Upgrade[name].effect(team); 259 | }; 260 | }()); 261 | } 262 | break; 263 | case 'unit': 264 | if (cmd.evolve){ 265 | Game.commands[msgObj.tick].push(function(){ 266 | //Closures 267 | var uids=cmd.uids; 268 | var unitType=cmd.name; 269 | var duration=cmd.duration; 270 | switch (cmd.evolve){ 271 | case 'archon': 272 | return function(){ 273 | var chara=Multiplayer.getUnitsByUIDs(uids)[0]; 274 | if (chara && Resource.paypal.call(chara,Resource.getCost(unitType))){ 275 | //Evolve as Archon or DarkArchon 276 | var evolve=chara.evolveTo({type:Building.ProtossBuilding[unitType+'Evolve']}); 277 | Game.commandTimeout(function(){ 278 | if (evolve.status!='dead'){ 279 | evolve.evolveTo({type:Protoss[unitType],burstArr:[unitType+'Birth']}); 280 | } 281 | },duration*100); 282 | //Processing flag 283 | evolve.processing={ 284 | name:unitType, 285 | startTime:Game.mainTick, 286 | time:duration 287 | }; 288 | } 289 | }; 290 | case 'zerg': 291 | var exceptions=['Guardian','Devourer'];//Closure 292 | return function(){ 293 | var chara=Multiplayer.getUnitsByUIDs(uids)[0]; 294 | if (chara && Resource.paypal.call(chara,Resource.getCost(unitType))){ 295 | //Evolve as egg 296 | var egg; 297 | //Clossure: which base larvas belong to 298 | var base=chara.owner; 299 | //Evolve as cocoon 300 | if (exceptions.indexOf(unitType)!=-1){ 301 | egg=chara.evolveTo({type:Building.ZergBuilding.Cocoon}); 302 | } 303 | else { 304 | egg=chara.evolveTo({type:Building.ZergBuilding.Egg}); 305 | if (unitType=='Lurker') egg.action=18; 306 | } 307 | //Cheat: Operation cwal 308 | if (Cheat.cwal) duration=0; 309 | Game.commandTimeout(function(){ 310 | if (egg.status!='dead'){ 311 | //Evolve 312 | if (exceptions.indexOf(unitType)!=-1){ 313 | //Cocoon 314 | egg.evolveTo({type:Zerg[unitType],burstArr:[unitType+'Birth']}); 315 | } 316 | else { 317 | //Egg 318 | egg.evolveTo({type:Zerg[unitType],burstArr:['EggBirth',unitType+'Birth'],rallyPoint:base?base.rallyPoint:null}); 319 | } 320 | } 321 | },duration*100); 322 | //Processing flag on egg 323 | egg.processing={ 324 | name:unitType, 325 | startTime:Game.mainTick,//new Date().getTime() 326 | time:duration 327 | }; 328 | } 329 | }; 330 | } 331 | }()); 332 | } 333 | else Game.commands[msgObj.tick].push(function(){ 334 | //Closures 335 | var uids=cmd.uids; 336 | var unitType=cmd.name; 337 | var duration=cmd.duration; 338 | //Find unit name from which race 339 | var Race; 340 | [Zerg,Terran,Protoss,Hero].forEach(function(race){ 341 | if (race[unitType]!=null) Race=race; 342 | }); 343 | return function(){ 344 | var owner=Multiplayer.getUnitsByUIDs(uids)[0]; 345 | if (owner && Resource.paypal.call(owner,Resource.getCost(unitType))){ 346 | //Cheat: Operation cwal 347 | if (Cheat.cwal) duration=0; 348 | Game.commandTimeout(function(){ 349 | var trainedUnit; 350 | if (Race[unitType].prototype.isFlying) 351 | trainedUnit=new Race[unitType]({x:owner.x,y:owner.y,team:owner.team}); 352 | else 353 | trainedUnit=new Race[unitType]({x:owner.x,y:owner.y+owner.height,team:owner.team}); 354 | delete owner.processing; 355 | if (owner.rallyPoint) trainedUnit.destination=owner.rallyPoint; 356 | },duration*100); 357 | //Occupy flag 358 | owner.processing={ 359 | name:unitType, 360 | startTime:Game.mainTick, 361 | time:duration 362 | }; 363 | } 364 | }; 365 | }()); 366 | break; 367 | case 'build': 368 | Game.commands[msgObj.tick].push(function(){ 369 | //Closures 370 | var uids=cmd.uids; 371 | var buildName=cmd.name; 372 | var BuildType=cmd.buildType; 373 | var pos=cmd.pos; 374 | return function(){ 375 | var farmer=Multiplayer.getUnitsByUIDs(uids)[0]; 376 | if (farmer && Resource.paypal.call(farmer,Resource.getCost(buildName))){ 377 | //Destination building name 378 | farmer.buildName=buildName; 379 | //Farmer build with location 380 | if (pos) farmer['build'+BuildType](pos); 381 | //Evolve to another building 382 | else farmer['build'+BuildType](); 383 | } 384 | }; 385 | }()); 386 | break; 387 | } 388 | }); 389 | } 390 | }, 391 | getUIDs:function(charas){ 392 | return charas.map(function(chara){ 393 | return chara.id; 394 | }); 395 | }, 396 | getUnitsByUIDs:function(uids){ 397 | return Unit.allUnits.concat(Building.allBuildings).filter(function(chara){ 398 | //Need filter out dead units to execute commands 399 | return uids.indexOf(chara.id)!=-1 && chara.status!='dead'; 400 | }); 401 | } 402 | }; -------------------------------------------------------------------------------- /GameRule/Referee.js: -------------------------------------------------------------------------------- 1 | var Referee={ 2 | ourDetectedUnits:[],//Detected enemies 3 | enemyDetectedUnits:[],//Detected ours 4 | _pos:[[-1,0],[1,0],[0,-1],[0,1]],//Collision avoid 5 | tasks:['judgeArbiter','judgeDetect','judgeCollision','judgeRecover','judgeDying','judgeMan', 6 | 'addLarva','coverFog','alterSelectionMode','judgeBuildingInjury','judgeWinLose','saveReplaySnapshot'], 7 | voice:(function(){ 8 | var voice; 9 | return function(name){ 10 | //Single instance pattern 11 | if (!voice) voice={ 12 | pError:new Audio(Game.CDN+'bgm/PointError.wav'), 13 | button:new Audio(Game.CDN+'bgm/Button.wav'), 14 | resource:{ 15 | Zerg:{ 16 | mine:new Audio(Game.CDN+'bgm/mine.Zerg.wav'), 17 | gas:new Audio(Game.CDN+'bgm/gas.Zerg.wav'), 18 | man:new Audio(Game.CDN+'bgm/man.Zerg.wav'), 19 | magic:new Audio(Game.CDN+'bgm/magic.Zerg.wav') 20 | }, 21 | Terran:{ 22 | mine:new Audio(Game.CDN+'bgm/mine.Terran.wav'), 23 | gas:new Audio(Game.CDN+'bgm/gas.Terran.wav'), 24 | man:new Audio(Game.CDN+'bgm/man.Terran.wav'), 25 | magic:new Audio(Game.CDN+'bgm/magic.Terran.wav') 26 | }, 27 | Protoss:{ 28 | mine:new Audio(Game.CDN+'bgm/mine.Protoss.wav'), 29 | gas:new Audio(Game.CDN+'bgm/gas.Protoss.wav'), 30 | man:new Audio(Game.CDN+'bgm/man.Protoss.wav'), 31 | magic:new Audio(Game.CDN+'bgm/magic.Protoss.wav') 32 | } 33 | }, 34 | upgrade:{ 35 | Zerg:new Audio(Game.CDN+'bgm/upgrade.Zerg.wav'), 36 | Terran:new Audio(Game.CDN+'bgm/upgrade.Terran.wav'), 37 | Protoss:new Audio(Game.CDN+'bgm/upgrade.Protoss.wav') 38 | } 39 | }; 40 | return voice[name]; 41 | } 42 | })(), 43 | winCondition:function(){ 44 | //By default: All our units and buildings are killed 45 | return (Unit.allEnemyUnits().length==0 && Building.enemyBuildings().length==0); 46 | }, 47 | loseCondition:function(){ 48 | //By default: All enemies and buildings are killed 49 | return (Unit.allOurUnits().length==0 && Building.ourBuildings().length==0); 50 | }, 51 | judgeArbiter:function(){ 52 | //Every 0.4 sec 53 | if (Game.mainTick%4==0){ 54 | //Special skill: make nearby units invisible 55 | var arbiterBuffer=Protoss.Arbiter.prototype.bufferObj; 56 | var allArbiters=Game.getPropArray([]); 57 | Unit.allUnits.forEach(function(chara){ 58 | if (chara.name=='Arbiter') allArbiters[chara.team].push(chara); 59 | }); 60 | //Clear old units' Arbiter buffer 61 | Referee.underArbiterUnits.forEach(function(charas){ 62 | charas.forEach(function(chara){ 63 | chara.removeBuffer(arbiterBuffer); 64 | }); 65 | }); 66 | Referee.underArbiterUnits=Game.getPropArray([]); 67 | allArbiters.forEach(function(arbiters,N){ 68 | //Find new under arbiter units 69 | arbiters.forEach(function(arbiter){ 70 | //Find targets: same team units inside Arbiter sight, exclude Arbiter 71 | var targets=Game.getInRangeOnes(arbiter.posX(),arbiter.posY(),arbiter.get('sight'),N,true,null,function(chara){ 72 | return arbiters.indexOf(chara)==-1; 73 | }); 74 | Referee.underArbiterUnits[N]=Referee.underArbiterUnits[N].concat(targets); 75 | }); 76 | $.unique(Referee.underArbiterUnits[N]); 77 | }); 78 | //Arbiter buffer effect on these units 79 | Referee.underArbiterUnits.forEach(function(charas){ 80 | charas.forEach(function(chara){ 81 | chara.addBuffer(arbiterBuffer); 82 | }); 83 | }); 84 | } 85 | }, 86 | //detectorBuffer are reverse of arbiterBuffer 87 | judgeDetect:function(){ 88 | //Every 0.4 sec 89 | if (Game.mainTick%4==0){ 90 | //Same detector buffer reference 91 | var detectorBuffer=Gobj.detectorBuffer; 92 | var allDetectors=Game.getPropArray([]); 93 | Unit.allUnits.forEach(function(chara){ 94 | if (chara.detector) allDetectors[chara.team].push(chara); 95 | }); 96 | //Clear old units detected buffer 97 | Referee.detectedUnits.forEach(function(charas,team){ 98 | //For each team 99 | charas.forEach(function(chara){ 100 | chara.removeBuffer(detectorBuffer[team]); 101 | }); 102 | }); 103 | Referee.detectedUnits=Game.getPropArray([]); 104 | allDetectors.forEach(function(detectors,N){ 105 | //Find new under detector units 106 | detectors.forEach(function(detector){ 107 | //Find targets: enemy invisible units inside detector sight 108 | var targets=Game.getInRangeOnes(detector.posX(),detector.posY(),detector.get('sight'),N+'',true,null,function(chara){ 109 | return chara['isInvisible'+Game.team]; 110 | }); 111 | Referee.detectedUnits[N]=Referee.detectedUnits[N].concat(targets); 112 | }); 113 | $.unique(Referee.detectedUnits[N]); 114 | }); 115 | //Detector buffer effect on these units 116 | Referee.detectedUnits.forEach(function(charas,team){ 117 | //For each team 118 | charas.forEach(function(chara){ 119 | chara.addBuffer(detectorBuffer[team]); 120 | }); 121 | }); 122 | //PurpleEffect, RedEffect and GreenEffect are also detector, override invisible 123 | Animation.allEffects.filter(function(effect){ 124 | return (effect instanceof Animation.PurpleEffect) || 125 | (effect instanceof Animation.RedEffect) || 126 | (effect instanceof Animation.GreenEffect) 127 | }).forEach(function(effect){ 128 | var target=effect.target; 129 | for (var team=0;teamN 180 | var units=Unit.allGroundUnits().concat(Building.allBuildings); 181 | for(var N=0;N>0]; 199 | if (chara1 instanceof Unit){ 200 | chara1.x+=colPos[0]; 201 | chara1.y+=colPos[1]; 202 | dist=1; 203 | } 204 | else { 205 | if (chara2 instanceof Unit){ 206 | chara2.x+=colPos[0]; 207 | chara2.y+=colPos[1]; 208 | dist=1; 209 | } 210 | } 211 | } 212 | if (dist>0; 219 | var adjustY=K*(chara1.y-chara2.y)>>0; 220 | //Adjust location 221 | var interactRatio1=0; 222 | var interactRatio2=0; 223 | if (chara1 instanceof Building){ 224 | interactRatio1=0; 225 | //Building VS Unit 226 | if (chara2 instanceof Unit) interactRatio2=2; 227 | //Building VS Building 228 | else interactRatio2=0; 229 | } 230 | else { 231 | //Unit VS Unit 232 | if (chara2 instanceof Unit) { 233 | if (chara1.status=="moving"){ 234 | //Move VS Move 235 | if (chara2.status=="moving"){ 236 | interactRatio1=1; 237 | interactRatio2=1; 238 | } 239 | //Move VS Dock 240 | else { 241 | interactRatio1=2; 242 | interactRatio2=0; 243 | } 244 | } 245 | else { 246 | //Dock VS Move 247 | if (chara2.status=="moving"){ 248 | interactRatio1=0; 249 | interactRatio2=2; 250 | } 251 | //Dock VS Dock 252 | else { 253 | interactRatio1=1; 254 | interactRatio2=1; 255 | } 256 | } 257 | } 258 | //Unit VS Building 259 | else { 260 | interactRatio1=2; 261 | interactRatio2=0; 262 | } 263 | } 264 | chara1.x+=interactRatio1*adjustX; 265 | chara1.y+=interactRatio1*adjustY; 266 | chara2.x-=interactRatio2*adjustX; 267 | chara2.y-=interactRatio2*adjustY; 268 | } 269 | } 270 | } 271 | units=Unit.allFlyingUnits(); 272 | for(var N=0;N>0]; 282 | chara1.x+=colPos[0]; 283 | chara1.y+=colPos[1]; 284 | dist=1; 285 | } 286 | if (dist>0; 290 | var adjustY=K*(chara1.y-chara2.y)>>0; 291 | //Adjust location 292 | chara1.x+=adjustX; 293 | chara1.y+=adjustY; 294 | chara2.x-=adjustX; 295 | chara2.y-=adjustY; 296 | } 297 | } 298 | } 299 | }, 300 | coverFog:function(){ 301 | //No need to set interval as 1sec 302 | if (Game.mainTick%10==0) Map.drawFogAndMinimap(); 303 | }, 304 | alterSelectionMode:function(){ 305 | //GC after some user changes 306 | $.extend([],Game.allSelected).forEach(function(chara){ 307 | if (chara.status=='dead' || (chara['isInvisible'+Game.team] && chara.isEnemy())) 308 | Game.allSelected.splice(Game.allSelected.indexOf(chara),1); 309 | }); 310 | //Alter info UI: Multi selection mode 311 | if (Game.allSelected.length>1){ 312 | //Need minor refresh or big move 313 | if (_$.arrayEqual(Game.allSelected,Game._oldAllSelected)){ 314 | //Only refresh 315 | Game.refreshMultiSelectBox(); 316 | } 317 | else { 318 | //Redraw multiSelection div 319 | Game.drawMultiSelectBox(); 320 | //Record this operation 321 | Game._oldAllSelected=_$.mixin([],Game.allSelected); 322 | } 323 | //Show multiSelection box 324 | $('div.override').show(); 325 | $('div.override div.multiSelection').show(); 326 | } 327 | //Alter info UI: Single selection mode 328 | else { 329 | $('div.override').hide(); 330 | $('div.override div.multiSelection').hide(); 331 | } 332 | }, 333 | addLarva:function(){ 334 | //Every 20 sec 335 | if (Game.mainTick%200==0){ 336 | Building.allBuildings.filter(function(build){ 337 | return build.produceLarva; 338 | }).forEach(function(build){ 339 | //Can give birth to 3 larvas 340 | for(var N=0;N<3;N++){ 341 | if (build.larvas[N]==null || build.larvas[N].status=="dead"){ 342 | build.larvas[N]=new Zerg.Larva({x:(build.x+N*48),y:(build.y+build.height+4),team:build.team}); 343 | //Which base larva belongs to 344 | build.larvas[N].owner=build; 345 | break; 346 | } 347 | } 348 | }); 349 | } 350 | }, 351 | judgeBuildingInjury:function(){ 352 | //Every 1 sec 353 | if (Game.mainTick%10==0){ 354 | Building.allBuildings.filter(function(build){ 355 | return build.injuryOffsets; 356 | }).forEach(function(build){ 357 | var injuryLevel=(1-build.life/build.HP)/0.25>>0; 358 | if (injuryLevel>3) injuryLevel=3; 359 | var curLevel=build.injuryAnimations.length; 360 | if (injuryLevel>curLevel){ 361 | var offsets=build.injuryOffsets; 362 | var scale=build.injuryScale?build.injuryScale:1; 363 | for (var N=curLevel;N1) build.sound.selected=build.sound.onfire; 369 | } 370 | } 371 | if (injuryLevelinjuryLevel;N--){ 373 | //Clear injury animations 374 | build.injuryAnimations.pop().die(); 375 | } 376 | if ((build instanceof Building.TerranBuilding) || (build instanceof Building.ProtossBuilding)){ 377 | if (injuryLevel<=1) build.sound.selected=build.sound.normal; 378 | } 379 | } 380 | }); 381 | } 382 | }, 383 | judgeMan:function(){ 384 | //Update current man and total man for all teams 385 | //?We may only need to judge our team's man for client consume use 386 | var curMan=Game.getPropArray(0),totalMan=Game.getPropArray(0); 387 | Unit.allUnits.concat(Building.allBuildings).forEach(function(chara){ 388 | if (chara.cost && chara.cost.man) (curMan[chara.team])+=chara.cost.man; 389 | if (chara.manPlus) (totalMan[chara.team])+=chara.manPlus; 390 | //Transport 391 | if (chara.loadedUnits) { 392 | chara.loadedUnits.forEach(function(passenger){ 393 | if (passenger.cost && passenger.cost.man) (curMan[passenger.team])+=passenger.cost.man; 394 | }); 395 | } 396 | }); 397 | for (var N=0;NResource[team].mine){ 56 | oweFlag=true; 57 | Game.showMessage('Not enough minerals...mine more minerals'); 58 | //Advisor voice 59 | Referee.voice('resource')[Game.race.selected].mine.play(); 60 | } 61 | if(cost['gas'] && cost['gas']>Resource[team].gas){ 62 | oweFlag=true; 63 | Game.showMessage('Not enough Vespene gases...harvest more gas'); 64 | //Advisor voice 65 | Referee.voice('resource')[Game.race.selected].gas.play(); 66 | } 67 | if(cost['man'] && cost['man']>(Resource[team].totalMan-Resource[team].curMan) && !Cheat.manUnlimited){ 68 | oweFlag=true; 69 | switch(Game.race.selected){ 70 | case 'Zerg': 71 | Game.showMessage('Too many underlings...create more Overlords'); 72 | break; 73 | case 'Terran': 74 | Game.showMessage('Not enough supplies...build more Supply Depots'); 75 | break; 76 | case 'Protoss': 77 | Game.showMessage('Not enough psi...build more Pylons'); 78 | break; 79 | } 80 | //Advisor voice 81 | Referee.voice('resource')[Game.race.selected].man.play(); 82 | } 83 | if(cost['magic'] && cost['magic']>this.magic){ 84 | oweFlag=true; 85 | Game.showMessage('Not enough energy'); 86 | //Advisor voice 87 | Referee.voice('resource')[Game.race.selected].magic.play(); 88 | } 89 | if (oweFlag){ 90 | //Payment failed 91 | return false; 92 | } 93 | else { 94 | if (!this.creditBill){ 95 | //Pay immediately 96 | if(cost['mine']){ 97 | Resource[team].mine-=cost['mine']; 98 | } 99 | if(cost['gas']){ 100 | Resource[team].gas-=cost['gas']; 101 | } 102 | if(cost['magic']){ 103 | this.magic-=cost['magic']; 104 | } 105 | } 106 | //Already paid 107 | return true; 108 | } 109 | } 110 | //No bill 111 | else return true; 112 | }, 113 | //Pay credit card bill 114 | payCreditBill:function(){ 115 | var cost=this.creditBill; 116 | //Paid credit bill, no longer owe money this time 117 | delete this.creditBill; 118 | return Resource.paypal.call(this,cost); 119 | } 120 | }; -------------------------------------------------------------------------------- /GameRule/SC_server.js: -------------------------------------------------------------------------------- 1 | var http=require('http'); 2 | var httpServer=http.createServer(function(request,response){ 3 | console.log('Receive request from '+request.url); 4 | response.end(''); 5 | }); 6 | httpServer.listen(28082); 7 | console.log('HTTP server starts listening port 28082...'); 8 | var wsServer=new (require('websocket').server)({ 9 | httpServer:httpServer 10 | });//Closure 11 | wsServer.rooms=[]; 12 | var multiPlayerNum=2;//Closure 13 | var webSockets=[];//Closure 14 | var getServerTime=function(){ 15 | var now=new Date(); 16 | var timestamp=now.getFullYear()+'-'+(now.getMonth()+1)+'-'+now.getDate()+' ' 17 | +now.getHours()+':'+now.getMinutes()+':'+now.getSeconds(); 18 | return timestamp; 19 | }; 20 | wsServer.on('request',function(request){ 21 | var socket=request.accept();//Closure 22 | var latencyMeasures=[{start:new Date()}];//Closure 23 | var IP=request.remoteAddress;//Closure 24 | webSockets.push(socket); 25 | console.log(getServerTime()+' '+IP+' is connected...'); 26 | socket.send(JSON.stringify({type:'ping'})); 27 | socket.on('message',function(message){ 28 | var data=message.utf8Data; 29 | data=JSON.parse(data); 30 | switch(data.type){ 31 | case 'pong': 32 | var times=latencyMeasures.length; 33 | latencyMeasures[times-1].end=new Date(); 34 | //console.log('Receive pong '+times); 35 | if (times==5) { 36 | var latency=0; 37 | latencyMeasures.forEach(function(latencyMeasure){ 38 | latency+=(latencyMeasure.end-latencyMeasure.start); 39 | }); 40 | latency=(latency/times)>>0; 41 | socket.tickLag=Math.ceil(latency*2/100);//Can adjust it here 42 | console.log(IP+' average latency is '+latency); 43 | console.log(IP+' tick lag is '+socket.tickLag); 44 | if (webSockets.length==multiPlayerNum){ 45 | //Game start: Init new room 46 | var room={id:wsServer.rooms.length,webSockets:webSockets};//Closure 47 | webSockets=[]; 48 | console.log('Open room '+room.id);//test 49 | room.tick=0; 50 | room.clientTicks=[]; 51 | for (var N=0;N>0; 68 | socket.name=names[index]; 69 | socket.color=colors[index]; 70 | names.splice(index,1); 71 | colors.splice(index,1); 72 | socket.send(JSON.stringify({type:'start',team:N})); 73 | //Start server ticking, trigger clients 74 | socket.send(JSON.stringify({type:'tick',tick:(room.tick+room.roomLag)})); 75 | }); 76 | wsServer.rooms.push(room); 77 | //Start server ticking 78 | setInterval(function(){ 79 | var minTick=Math.min.apply({},room.clientTicks); 80 | //console.log('minTick:'+minTick); 81 | if (room.tick'+socket.name+': '+data.msg+''})); 117 | }); 118 | } 119 | break; 120 | case 'getReplay': 121 | socket.send(JSON.stringify({type:'replay',replay:socket.room.replay})); 122 | break; 123 | case 'login': 124 | console.log(getServerTime()+' '+IP+' login to level '+data.level+' in team '+data.team); 125 | console.log(IP+' Version: '+data.version); 126 | console.log(IP+' Browser: size={x:'+data.size.x+',y:'+data.size.y+'} lang='+data.language+' OS='+data.platform); 127 | break; 128 | case 'snapshot': 129 | console.log('\n####### '+getServerTime()+' '+IP+' snapshot No.'+data.num+' #######'); 130 | console.log('Click statistics: left='+data.click.left+' right='+data.click.right); 131 | console.log('Unit statistics: ours='+data.count.ourUnits+' enemies='+data.count.enemyUnits); 132 | if (data.count.ourUnits+data.count.enemyUnits<20) console.log(data.units); 133 | console.log('Building statistics: ours='+data.count.ourBuildings+' enemies='+data.count.enemyBuildings); 134 | if (data.count.ourBuildings+data.count.enemyBuildings<20) console.log(data.buildings); 135 | console.log('##########################################\n'); 136 | break; 137 | case 'replaySnapshot': 138 | socket.replaySnapshot=JSON.stringify(data.replaySnapshot); 139 | break; 140 | case 'log': 141 | //Just print out 142 | console.log(data.log); 143 | } 144 | }); 145 | socket.on('close',function(message){ 146 | //Broadcast disconnect info 147 | var msg=(getServerTime()+' '+(socket.name?socket.name:IP)+' has left from game...'); 148 | console.log(msg); 149 | //Print replay 150 | if (socket.replaySnapshot) { 151 | console.log('################ '+IP+' replay: ################'); 152 | console.log(socket.replaySnapshot); 153 | console.log('################################################################'); 154 | } 155 | //Kick off losing connection user 156 | if (socket.room){ 157 | var webSockets=socket.room.webSockets; 158 | webSockets.splice(webSockets.indexOf(socket),1); 159 | //Make game to continue without player control 160 | socket.room.clientTicks[socket.team]=Number.MAX_VALUE; 161 | webSockets.forEach(function(webSocket){ 162 | webSocket.send(JSON.stringify({type:'notice',msg:msg})); 163 | }); 164 | } 165 | }); 166 | }); 167 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Ryuta (@gloomyson) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SC-Js 2 | Classic RTS game at html5 canvas and javascript, only js codes, copyright materials removed 3 | 4 | ## Getting started 5 | * Download the latest version from github: https://github.com/gloomyson/SC_Js/archive/master.zip 6 | * Unzip the folder 7 | * Extract original resources from starcraft and add into bgm & img folder 8 | * Double click `index.html` in the folder (this should open the game with your browser) 9 | * You can play without image/audio materials by input available CDN location instead, for example 'www.nvhae.com/starcraft' 10 | * Press the radio button (circle next to the level name) to select a level and play 11 | 12 | ## Former 2015 version features: 13 | * All units/buildings/bullets/maps/magics and animations completed 14 | * Support war fog, zerg creep 15 | * Control panel, different buttons and icons 16 | * Support cheat code 17 | * Mouse and key control complete 18 | * Seven basic levels to test units and buildings 19 | * Three additional levels for playing: Champain, HUNTERXHUNTER and ProtectAthena 20 | 21 | ## Newly added features in latest version 22 | * One additional level added: Tower Defense 23 | * Support replay your game playing 24 | * Experimental: Basic network play support in level 2, players can chat with each other in multiplayer mode 25 | * Experimental: Android install package for play on mobile devices 26 | * Check svn.log for other detailed changes 27 | 28 | ## Notice 29 | 1. Need extract resource from orginal starcraft game, and add them into bgm/img folder before play 30 | * List serveral useful extract tools: MpqWorkshop, GRPEdit and RetroGRP 31 | 2. Need setup server before play in multiplayer mode, follow below steps: 32 | * Install NodeJs on your machine 33 | * Install websocket module: input 'npm install websocket' in cmd 34 | * Start SC_server: input 'node GameRule\SC_server.js' in cmd 35 | 3. To play it on mobile device, install Android install package on your device: [SC.apk](http://www.nvhae.com/starcraft/starcraft.apk) 36 | * Tap once equals mouse click to select/unselect units 37 | * Tap twice equals mouse double click to select all same typed units 38 | * Hold pressing on screen equals mouse right click to set moving destination 39 | * Two fingers press on screen equals mouse dragging to select multiple units inside rectangle 40 | * Slide finger on screen to pan left/right/up/down 41 | 42 | ## Try it online at: 43 | [SC Html5 Online](http://www.nvhae.com/starcraft/) 44 | -------------------------------------------------------------------------------- /Utils/gFrame.js: -------------------------------------------------------------------------------- 1 | //gFrame namespace: DOM selector function 2 | var _$=function(selector){ 3 | var selectors=selector.trim().split(' '); 4 | var result=document;//Overall 5 | for (var N=0;N0){ 152 | var mixinProto=fathers[0].prototype; 153 | for (N=1;N '+ref; 339 | } 340 | refObjs.push(_$.instModule(ref)); 341 | } 342 | }); 343 | } 344 | //Override module function with instance 345 | _$.modules[name]=module.apply(window,refObjs); 346 | } 347 | _$.instModule.refStack.pop(); 348 | return _$.modules[name]; 349 | }; 350 | _$.instModule.refStack=[]; 351 | //Register module builder function into _$.modules 352 | _$.define=function(refArr,builderFunc){ 353 | refArr.forEach(function(ref){ 354 | if (ref[0]=='=') return; 355 | //Recursion loading if that module not loaded 356 | if (!_$.modules[ref]) _$.sourceLoader.load(ref); 357 | }); 358 | //Builder loaded 359 | builderFunc.refArr=refArr; 360 | builderFunc._$isBuilder=true; 361 | _$.define.loadedBuilders.push(builderFunc); 362 | //_$.modules[pathName]=builderFunc; 363 | }; 364 | _$.define.loadedBuilders=[]; 365 | //Run callback functions with module references 366 | _$.require=function(refArr,callback){ 367 | refArr.forEach(function(ref){ 368 | if (ref[0]=='=') return; 369 | //Recursion loading if that module not loaded 370 | if (!_$.modules[ref]) _$.sourceLoader.load(ref); 371 | }); 372 | _$.sourceLoader.allOnLoad(function(){ 373 | var refObjs=[]; 374 | refArr.forEach(function(ref){ 375 | //Recursion instantiate 376 | refObjs.push(_$.instModule(ref)); 377 | }); 378 | callback.apply(window,refObjs); 379 | }); 380 | }; 381 | //Constructor extension: changed to multiple inherit 382 | _$.declare=function(globalName,fathers,plusObj){ 383 | if (arguments.length==2){ 384 | plusObj=fathers; 385 | fathers=globalName; 386 | globalName=null; 387 | } 388 | if (!fathers) fathers=[]; 389 | var constructPlus=plusObj.constructor; 390 | delete plusObj.constructor; 391 | var protoPlus=plusObj; 392 | var child=_$.extends(fathers,{constructorPlus:constructPlus,prototypePlus:protoPlus}); 393 | if (globalName) window[globalName]=child; 394 | return child; 395 | }; 396 | 397 | //Publish & Subscribe topic 398 | _$.topic={}; 399 | _$.subscribe=function(topic,callback){ 400 | if (!_$.topic[topic]) _$.topic[topic]={callbacks:[]}; 401 | _$.topic[topic].callbacks.push(callback); 402 | }; 403 | //Need add .owner on callback to identify who is subscriber 404 | _$.unSubscribe=function(topic,callback){ 405 | if (_$.topic[topic] && _$.topic[topic].callbacks){ 406 | var index=_$.topic[topic].callbacks.indexOf(callback); 407 | _$.topic[topic].callbacks.splice(index,1); 408 | } 409 | }; 410 | _$.publish=function(topic,msgObj){ 411 | if (_$.topic[topic]){ 412 | _$.topic[topic].callbacks.forEach(function(callback){ 413 | callback.call(window,msgObj); 414 | }) 415 | } 416 | }; 417 | 418 | //lang.delegate:cover with one proto layer 419 | _$.delegate=function(chara,bufferObj){ 420 | var func=function(){}; 421 | func.prototype=chara; 422 | return _$.mixin(new func(),bufferObj); 423 | }; 424 | 425 | //lang.hitch:bind context this with function 426 | _$.hitch=function(func,thisP){ 427 | //Higher-order function: compress this pointer into closure here 428 | return function() { 429 | func.apply(thisP,arguments); 430 | }; 431 | }; 432 | 433 | //Convert array-like to real array 434 | _$.toArray=function(arr){ 435 | var result=[]; 436 | for (var N=0;Ndiv{ 138 | display:none; 139 | } 140 | div.infoLeft span._Health { 141 | color:green; 142 | } 143 | div.infoLeft span._Shield { 144 | color:white; 145 | } 146 | div.infoLeft span._Magic { 147 | color:darkviolet; 148 | } 149 | div#GamePlay div.panel_Info div.infoCenter { 150 | left:45%; 151 | top:0; 152 | width:300px; 153 | height:100%; 154 | color:white; 155 | } 156 | div#GamePlay div.panel_Info div.infoCenter h3 { 157 | margin:0; 158 | font:18px Verdana,Arial; 159 | } 160 | div#GamePlay div.panel_Info div.infoCenter p { 161 | margin:2px 0; 162 | font:12px Verdana,Arial; 163 | } 164 | div#GamePlay div.panel_Info div.infoCenter p.detector { 165 | color:yellow; 166 | } 167 | div#GamePlay div.panel_Info div.infoCenter p.icons span { 168 | display:inline-block; 169 | width:36px; 170 | height:34px; 171 | background-image: url('../img/Menu/CmdIcons.png'); 172 | border:2px solid white; 173 | border-radius:6px; 174 | margin-right:1px; 175 | } 176 | div#GamePlay div.panel_Info div.infoRight { 177 | right:10px; 178 | top:0; 179 | width:200px; 180 | height:100%; 181 | } 182 | div.infoRight>div{ 183 | width:100%; 184 | height:34px; 185 | padding:0 2px; 186 | border:2px solid; 187 | border-radius:6px; 188 | color:white; 189 | font:bold 0.6em Verdana,Arial; 190 | vertical-align:middle; 191 | line-height:34px; 192 | display:none; 193 | } 194 | div.infoRight div.upgraded{ 195 | top:10px; 196 | border-color:lime; 197 | } 198 | div.infoRight div.upgrading{ 199 | bottom:10px; 200 | border-color:blue; 201 | } 202 | div.infoRight div[name='icon']{ 203 | width:32px; 204 | height:34px; 205 | background-image: url('../img/Menu/CmdIcons.png'); 206 | /*For text inside icon*/ 207 | color:red; 208 | font:bolder 1.8em Verdana,Arial; 209 | text-align:right; 210 | line-height:46px; 211 | vertical-align:bottom; 212 | } 213 | div#GamePlay div.infoRight div.upgraded div[name='icon']{ 214 | position:relative; 215 | display:inline-block; 216 | } 217 | div.infoRight div[name='icon'][disabled='true']{ 218 | background-image: url('../img/Menu/CmdIconsDisabled.png'); 219 | } 220 | div.infoRight div.upgrading div[name='processing']{ 221 | right:6px; 222 | width:156px; 223 | height:100%; 224 | text-align:center; 225 | color:white; 226 | font:bold 0.6em Arial; 227 | } 228 | div.infoRight div.upgrading div.processBar{ 229 | width:100%; 230 | height:14px; 231 | background:url('../img/Menu/Thingy.png') 0 -14px; 232 | } 233 | div.infoRight div.upgrading div.processedBar{ 234 | height:100%; 235 | background:url('../img/Menu/Thingy.png') 0 -28px; 236 | } 237 | div#GamePlay div.panel_Control { 238 | right:0; 239 | bottom:0; 240 | width:150px; 241 | height:155px; 242 | background-image: url('../img/Menu/ControlPanel.png'); 243 | } 244 | div#GamePlay div.panel_Info { 245 | border-width:22px 0 0 0; 246 | border-style:solid; 247 | } 248 | div#GamePlay[race="Zerg"] div.panel_Map { 249 | background-position: 0 bottom; 250 | } 251 | div#GamePlay[race="Zerg"] div.panel_Info { 252 | -webkit-border-image: url('../img/Menu/ControlPanel.png') 22 1265 0 163 repeat; 253 | -o-border-image: url('../img/Menu/ControlPanel.png') 22 1265 0 163 repeat; 254 | border-image: url('../img/Menu/ControlPanel.png') 22 1265 0 163 repeat; 255 | } 256 | div#GamePlay[race="Zerg"] div.panel_Control { 257 | background-position: -400px bottom; 258 | } 259 | div#GamePlay[race="Protoss"] div.panel_Map { 260 | background-position: -555px bottom; 261 | } 262 | div#GamePlay[race="Protoss"] div.panel_Info { 263 | -webkit-border-image: url('../img/Menu/ControlPanel.png') 22 743 0 745 repeat; 264 | -o-border-image: url('../img/Menu/ControlPanel.png') 22 743 0 745 repeat; 265 | border-image: url('../img/Menu/ControlPanel.png') 22 743 0 745 repeat; 266 | } 267 | div#GamePlay[race="Protoss"] div.panel_Control { 268 | background-position: -958px bottom; 269 | } 270 | div#GamePlay[race="Terran"] div.panel_Map { 271 | background-position: -1110px bottom; 272 | } 273 | div#GamePlay[race="Terran"] div.panel_Info { 274 | -webkit-border-image: url('../img/Menu/ControlPanel.png') 22 149 0 1284 repeat; 275 | -o-border-image: url('../img/Menu/ControlPanel.png') 22 149 0 1284 repeat; 276 | border-image: url('../img/Menu/ControlPanel.png') 22 149 0 1284 repeat; 277 | } 278 | div#GamePlay[race="Terran"] div.panel_Control { 279 | background-position: -1516px bottom; 280 | } 281 | div#GamePlay div.panel_Info div.override{ 282 | width:100%; 283 | height:100%; 284 | background:black; 285 | position:relative; 286 | } 287 | div#GamePlay div.panel_Info div.override div{ 288 | position:relative; 289 | } 290 | div#GamePlay div.override div.multiSelection{ 291 | margin:0 auto; 292 | width:330px; 293 | height:104px; 294 | overflow:hidden; 295 | display:none; 296 | } 297 | div#GamePlay div.panel_Info div.override div.multiSelection div{ 298 | float:left; 299 | width:45px; 300 | height:42px; 301 | background-image:url('../img/Charas/Portrait.png'); 302 | background-size:720px 210px; 303 | border:3px solid white; 304 | border-radius:6px; 305 | margin:2px; 306 | } 307 | div#GamePlay div.warning_Box { 308 | width:400px; 309 | height:30px; 310 | left:50%; 311 | margin-left:-200px; 312 | background: rgba(255,255,255,0.3); 313 | text-align:center; 314 | line-height:30px; 315 | font:bold 24px Verdana,Arial; 316 | color:red; 317 | display:none; 318 | } 319 | div#GamePlay div#cheat_Box { 320 | width:400px; 321 | height:30px; 322 | text-align:center; 323 | left:50%; 324 | margin-left:-200px; 325 | bottom:130px; 326 | font:bold 15px Verdana,Arial; 327 | color:white; 328 | display:none; 329 | } 330 | div#GamePlay div#cheat_Box input#cheatInput { 331 | width:300px; 332 | background-color:transparent; 333 | border-top:none; 334 | border-left:none; 335 | border-right:none; 336 | border-bottom:2px solid white; 337 | font:bold 1em Arial; 338 | color:white; 339 | } 340 | div#GamePlay div.message_Box { 341 | width:400px; 342 | left:50%; 343 | margin-left:-200px; 344 | bottom:140px; 345 | text-align:center; 346 | font:bolder 0.8em Verdana,Arial; 347 | color:white; 348 | display:none; 349 | } 350 | div#GamePlay div.message_Box p { 351 | margin:0; 352 | } 353 | div#GamePlay div.resource_Box { 354 | right:30px; 355 | top:20px; 356 | font:bolder 1em Verdana,Arial; 357 | color: #00ff00; 358 | } 359 | div#GamePlay div.resource_Box div, div#GamePlay div.tooltip_Box div.cost div{ 360 | position:relative; 361 | width:14px; 362 | height:14px; 363 | background-image:url('../img/Menu/Thingy.png'); 364 | display:inline-block; 365 | } 366 | div#GamePlay div.mine{ 367 | background-position:0 0; 368 | } 369 | div#GamePlay[race="Zerg"] div.gas{ 370 | background-position:-14px 0; 371 | } 372 | div#GamePlay[race="Terran"] div.gas{ 373 | background-position:-28px 0; 374 | } 375 | div#GamePlay[race="Protoss"] div.gas{ 376 | background-position:-42px 0; 377 | } 378 | div#GamePlay[race="Zerg"] div.man{ 379 | background-position:-56px 0; 380 | } 381 | div#GamePlay[race="Terran"] div.man{ 382 | background-position:-70px 0; 383 | } 384 | div#GamePlay[race="Protoss"] div.man{ 385 | background-position:-84px 0; 386 | } 387 | div#GamePlay div.magic{ 388 | background-position:-98px 0; 389 | } 390 | div#GamePlay div.resource_Box>span{ 391 | position:relative; 392 | min-width:50px; 393 | display:inline-block; 394 | } 395 | div#GamePlay div.tooltip_Box { 396 | font:bold 0.8em Arial; 397 | color:white; 398 | background:black none; 399 | border:2px solid blue; 400 | padding-right:10px; 401 | display:none; 402 | } 403 | div#GamePlay div.tooltip_Box *{ 404 | vertical-align:top; 405 | position:relative; 406 | display:none; 407 | } 408 | div#GamePlay div.tooltip_Box div.itemName{ 409 | text-transform:capitalize; 410 | display:block; 411 | } 412 | /* For mobile */ 413 | @media screen and (max-width: 700px){ 414 | .levelSelectionBg{ 415 | transform:scale(0.7); 416 | transform-origin:bottom; 417 | -ms-transform:scale(0.7); 418 | -ms-transform-origin:bottom; 419 | -moz-transform:scale(0.7); 420 | -moz-transform-origin:bottom; 421 | -webkit-transform:scale(0.7); 422 | -webkit-transform-origin:bottom; 423 | -o-transform:scale(0.7); 424 | -o-transform-origin:bottom; 425 | } 426 | div#GamePlay div.panel_Info div.infoLeft { 427 | left:5px; 428 | width:auto; 429 | } 430 | div#GamePlay div.panel_Info div.infoLeft div { 431 | font-size:10px; 432 | } 433 | div#GamePlay div.panel_Info div.infoCenter { 434 | left:30%; 435 | width:auto; 436 | } 437 | div#GamePlay div.panel_Info div.infoRight { 438 | width:160px; 439 | transform:scale(0.8); 440 | transform-origin:right; 441 | -ms-transform:scale(0.8); 442 | -ms-transform-origin:right; 443 | -moz-transform:scale(0.8); 444 | -moz-transform-origin:right; 445 | -webkit-transform:scale(0.8); 446 | -webkit-transform-origin:right; 447 | -o-transform:scale(0.8); 448 | -o-transform-origin:right; 449 | } 450 | div.infoRight div.upgrading div[name='processing']{ 451 | right:0; 452 | transform:scaleX(0.8); 453 | transform-origin:right; 454 | -ms-transform:scaleX(0.8); 455 | -ms-transform-origin:right; 456 | -moz-transform:scaleX(0.8); 457 | -moz-transform-origin:right; 458 | -webkit-transform:scaleX(0.8); 459 | -webkit-transform-origin:right; 460 | -o-transform:scaleX(0.8); 461 | -o-transform-origin:right; 462 | } 463 | } -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gloomyson/SC_Js/4981ce27b29c4970390159b59d414a5e19326fbf/favicon.ico -------------------------------------------------------------------------------- /img/Bg/Deleted: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gloomyson/SC_Js/4981ce27b29c4970390159b59d414a5e19326fbf/img/Bg/Deleted -------------------------------------------------------------------------------- /img/Charas/Deleted: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gloomyson/SC_Js/4981ce27b29c4970390159b59d414a5e19326fbf/img/Charas/Deleted -------------------------------------------------------------------------------- /img/Demo/Demo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gloomyson/SC_Js/4981ce27b29c4970390159b59d414a5e19326fbf/img/Demo/Demo.jpg -------------------------------------------------------------------------------- /img/Demo/Demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gloomyson/SC_Js/4981ce27b29c4970390159b59d414a5e19326fbf/img/Demo/Demo.png -------------------------------------------------------------------------------- /img/Maps/Deleted: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gloomyson/SC_Js/4981ce27b29c4970390159b59d414a5e19326fbf/img/Maps/Deleted -------------------------------------------------------------------------------- /img/Menu/Deleted: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gloomyson/SC_Js/4981ce27b29c4970390159b59d414a5e19326fbf/img/Menu/Deleted -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | StarCraft 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |
Now Loading...
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | 29 | 30 | 31 | 32 |
33 | 34 |
35 |
36 |
37 |
38 |
39 | / / 40 |
41 |
42 | / 43 |
44 |
45 |
46 |

47 |

Detector

48 |

Kill:

49 |

Damage:

50 |

Armor:

51 |

Plasma:

52 |

Passenger:

53 |

54 |
55 |
56 |
57 | Upgraded: 58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | %
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 | Message: 80 |
81 |
82 |
83 |
84 | 85 |
86 | 87 |
88 | / 89 |
90 |
91 |
92 |
93 |
94 | 95 |
96 | 97 |
98 | 99 |
100 | 101 |
102 |
103 |
104 |
105 |
106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 141 | 142 | 143 | --------------------------------------------------------------------------------