/* termlib-invaders a termlib.js application (c) mass:werk (N. Landsteiner) 2008 based on JS/UIX invaders (c) mass:werk (N. Landsteiner) 2005 all rights reserved v1.11 termlib-invaders is a simple text-mode invaders game. requires termlib.js 1.4 or better . requires a terminal set to at least 68 cols x 20 rows. uses namespace 'invaders' in object 'env' of the termlib.js Terminal instance. the entire code is contained inside the single global object 'TermlibInvaders'. (the massive 'apply'-syntax is a bit awkward, but this is, which does it.) example call from inside a termlib.js Terminal instance's handler: if ( TermlibInvaders.start(this) ) { return; } else { // oops, terminal doesn't meet the requirements this.write('Sorry, invaders failed.'); } call with a max game screen of 80 cols x 25 rows: TermlibInvaders.start(this, 80, 25); */ TermlibInvaders = { version: '1.11 (original)', start: function( termref, maxcols, maxrows ) { if (!Terminal || !termref || parseFloat(termref.version)<1.4) { // color support required return false; } if (termref.conf.cols<68 || termref.conf.rows<20) { // required min. dimensions: 68 x 20 return false; } if (parseFloat(termref.version)>=1.5) { // backup the screen termref.backupScreen(); } var gc=TermlibInvaders.getStyleColorFromHexString; termref.env.invaders= { termref: termref, maxCols: maxcols || 0, maxRows: maxrows || 0, charMode: termref.charMode, paused: false, moveAll: true, // setup values rows: 3, cols: 5, maxBombs: 3, bombRate: 0.005, timer: null, delay: 50, newWaveDelay: 1500, textColor: gc(TermlibInvaders.textColor), invaderColor: gc(TermlibInvaders.invaderColor), invaderHitColor: gc(TermlibInvaders.invaderHitColor), bombColor: gc(TermlibInvaders.bombColor), blockColor: gc(TermlibInvaders.blockColor), statusColor: gc(TermlibInvaders.statusColor), shotColor: gc(TermlibInvaders.shotColor), shipColor: gc(TermlibInvaders.shipColor), shipHitColor: gc(TermlibInvaders.shipHitColor), alertColor: gc(TermlibInvaders.alertColor), frameColor: gc(TermlibInvaders.frameColor) }; TermlibInvaders.init.apply(termref); return true; }, // color definitions (colors will match nearest webcolor) textColor: '#00cc00', invaderColor: '#00cc00', invaderHitColor: '#66aa66', bombColor: '#cccc00', blockColor: '#bbbb00', statusColor: '#00bb00', shotColor: '#aacc00', shipColor: '#aacc00', shipHitColor: '#aaaaaa', alertColor: '#ff9900', // frame definitions // the frame is only drawn, if the terminal is bigger // than the game's max dimensions. if you do not want // to draw any frames leave 'frameChar' empty (''). frameChar: '*', frameColor: '#777777', // global assets sprites: [ ' ',' (^o^) ',' (^-^) ',' ( ) ',' ( )', ' (=^=) ',' ((.)) ',' ( . ) ','( (.) )', ' ( . ) ','( . )',' . ',' ' ], splashScreen: [ '%c(#0c0)%+i** T E R M L I B - I N V A D E R S **%-i', '', '', '%c(#0c0)Instructions:', '', '%c(#0c0) use cursor LEFT and RIGHT to move', '%c(#0c0) (or use vi movements alternatively)', '%c(#0c0) press space to fire', '%c(#0c0)', '%c(#0c0) press "q" or "esc" to quit,', '%c(#0c0) "p" to pause the game.', '', '', '%c(#0c0)%+r press any key to start the game %-r', '', '', '%c(#0c0)(c) mass:werk N.Landsteiner 2005-2008', '%c(#0c0)based on JS/UIX-Invaders by mass:werk' ], splashScreenWidth: 40, // width of splash-screen in chars gameOverScreen: [ ' ', '%c(#f90) G A M E O V E R ! ', ' ', '%c(#0c0) press any key to restart,', '%c(#0c0) "q" or "esc" for quit. ', ' ' ], gameOverScreenWidth: 26, invObject: function(y,x) { this.x=x; this.y=y; this.status=1; }, // handlers: // 'this' refers to ther termlib.js Terminal instcance // 'inv' refers to the TermlibInvaders instance init: function() { var inv=this.env.invaders; // back up the terminal state inv.termHandler=this.handler; if (this.maxLines != this.conf.rows) { inv.charBuf=new Array(); inv.styleBuf=new Array(); for (var r=this.conf.rows-1; r>=this.maxLines; r--) { var cb=new Array(); var sb=new Array(); var tcb=this.charBuf[r]; var tsb=this.styleBuf[r]; for (var c=0; c=this.conf.cols; c++) { cb[c]=tcb[c]; sb[c]=tsb[c]; } inv.charBuf.push(cb); inv.styleBuf.push(sb); } this.maxLines = this.conf.rows; } if (this.maxCols!=this.conf.cols) { inv.termMaxCols=this.maxCols; this.maxCols=this.conf.cols; } else { inv.termMaxCols=-1; } inv.keyRepeatDelay1=this.keyRepeatDelay1; inv.keyRepeatDelay2=this.keyRepeatDelay2; this.keyRepeatDelay1=this.keyRepeatDelay2=inv.delay-1; // output init-screen this.clear(); TermlibInvaders.writeToCenter.apply(this, [TermlibInvaders.splashScreen, TermlibInvaders.splashScreenWidth]); this.charMode=true; this.lock=false; this.handler=TermlibInvaders.splashScreenHandler; }, splashScreenHandler: function() { var key = this.inputChar; if (key==this.termKey.ESC || key==113) { TermlibInvaders.exit.apply(this); return; } // setup the game var inv=this.env.invaders; TermlibInvaders.buildScreen.apply(this); inv.maxRight=inv.width-7; inv.wave=0; inv.score=0; var d=Math.floor(inv.width/5); var d1=Math.floor((inv.width-3*d)/2); inv.blockpos=new Array(); for (var i=0; i<4; i++) { var x=d1+i*d; inv.blockpos.push(x-1); inv.blockpos.push(x); inv.blockpos.push(x+1); } TermlibInvaders.newWave.apply(this); }, newWave: function() { this.clear(); var inv=this.env.invaders; inv.wave++; var s='W A V E # '+inv.wave; var c=Math.floor((this.conf.cols-s.length)/2); var r=Math.floor((this.conf.rows-3)/2)-4; this.typeAt(r, c, s, 4 | inv.textColor); this.typeAt(r+2, c, 'Get ready ...', inv.textColor); inv.timer=setTimeout(function() { TermlibInvaders.waveStart.apply(inv.termref); }, inv.newWaveDelay); this.lock=true; }, waveStart: function() { var inv=this.env.invaders; clearTimeout(inv.timer); this.clear(); TermlibInvaders.drawFrame.apply(this); inv.smove=0; inv.phase=1; inv.dir=1; inv.population=0; inv.shot= inv.shotX= 0 inv.over=false; inv.bombs=0; inv.invrows=(inv.wave==2)? inv.rows+1:inv.rows; inv.invcols=(inv.wave<=2)? inv.cols:inv.cols+1; var changed=inv.changed=new Array(); inv.inv=new Array(); for (var r=0; r0), shotx=inv.shotX, shoty=inv.shipY-inv.shot; var bomb= inv.bomb,block=inv.block, blocky=inv.blockY, isblockrow=false; var sprites=TermlibInvaders.sprites, invclr=inv.invaderColor; var moveAll=inv.moveAll; if (shot && inv.shot>1) TermlibInvaders.drawSprite(term, shoty+1,shotx,' ',0); for (var r=0; ri.x && shotx<(i.x+6)) { i.status=2; inv.population--; inv.score+=50; inv.shot=shot=0; TermlibInvaders.drawSprite(term, i.y,i.x, sprites[3], inv.invaderHitColor); } else if (moveAll) { TermlibInvaders.drawSprite(term, i.y,i.x, sprites[inv.phase], invclr ); if (i.yinv.shipX && b.x<(inv.shipX+6)) { inv.over=true; } else { b=bomb[n]=null; inv.bombs--; } } else if (shot) { if ((b.y==shoty || b.y==shoty+1) && Math.abs(b.x-shotx)<2) { b=bomb[n]=null; inv.bombs--; inv.score+=5; inv.shot=shot=0 } } if (b) { TermlibInvaders.drawSprite(term, b.y,b.x, 'V', inv.bombColor); b.y++; } } } if (shot) { if (shoty>0) { if (shoty==blocky && inv.block[shotx]) { inv.block[shotx]=false; TermlibInvaders.drawSprite(term, blocky,shotx, ' ', 0); inv.shot=0; } else { TermlibInvaders.drawSprite(term, shoty,shotx, '|', inv.shotColor); inv.shot++; } } else { inv.shot=0; } } if (moveAll) { inv.invbottom=bb; } else { inv.invleft=bl; inv.invright=br; if (dir==-1 && bl==0) { inv.dir=1; } else if (dir==1 && br==inv.maxRight) { inv.dir=-1; } inv.phase=(inv.phase==1)? 2:1; } // restore any overwritten blocks if (isblockrow) { var blockpos=inv.blockpos; for (var i=0; i=TermlibInvaders.sprites.length) { TermlibInvaders.writeToCenter.apply(this, [TermlibInvaders.gameOverScreen, TermlibInvaders.gameOverScreenWidth]); this.lock=false; this.handler=TermlibInvaders.splashScreenHandler; } else { TermlibInvaders.drawSprite(this, inv.shipY,inv.shipX, TermlibInvaders.sprites[inv.phase++], inv.shipHitColor); this.redraw(inv.top+inv.shipY); inv.timer=setTimeout(function() { TermlibInvaders.gameOver.apply(inv.termref); }, inv.delay*3); } }, keyHandler: function() { var inv=this.env.invaders; var key=this.inputChar; if (key==this.termKey.ESC || key==113) { // esc or q TermlibInvaders.exit.apply(this); } else if (key==112 || inv.paused) { // p or paused TermlibInvaders.pause.apply(this); } // cursor movements else if (key==this.termKey.LEFT || key==104) { // left if (inv.shipX>0) inv.smove=-1; return; } else if (key==this.termKey.RIGHT || key==108) { // right if (inv.shipX0 && this.maxCols>inv.maxCols) { inv.width = inv.maxCols; inv.left= Math.floor((this.maxCols-inv.maxCols)/2); inv.right= inv.left+inv.width; } else { inv.width= inv.right= this.maxCols; inv.left=0; } if (inv.maxRows>0 && this.maxLines>inv.maxRows) { inv.height = inv.maxRows; inv.top= Math.floor((this.maxLines-inv.maxRows)/2); inv.bottom=inv.top+inv.height; } else { inv.height= inv.bottom= this.maxLines; inv.top=0; } inv.shipCenter=Math.floor((inv.width-3)/2); inv.statusRow=inv.bottom-1; inv.maxRight=inv.width-7; inv.shipY=inv.height-3; inv.bombMaxY=inv.height-7; inv.blockY=inv.height-5; }, drawFrame: function() { var inv=this.env.invaders; if (TermlibInvaders.frameChar) { var r0, r1, i; var c = TermlibInvaders.frameChar.charCodeAt(0); var cc= inv.frameColor; if (inv.height+1=1.5) { // backup the screen this.restoreScreen(); } else { // reset the terminal "manually" this.clear(); this.handler=inv.termHandler; if (inv.charBuf) { for (var r=0; r=0) this.maxCols=inv.termMaxCols; this.charMode=inv.charMode; } this.keyRepeatDelay1=inv.keyRepeatDelay1; this.keyRepeatDelay2=inv.keyRepeatDelay2; delete inv.termref; this.lock=false; // delete instance and leave with a prompt delete this.env.invaders; this.prompt(); }, getStyleColorFromHexString: function(clr) { // returns a stylevector for the given color-string var cc=Terminal.prototype.globals.webifyColor(clr.replace(/^#/,'')); if (cc) { return Terminal.prototype.globals.webColors[cc]*0x10000; } return 0; } }; // eof