📜 ⬆️ ⬇️

Roguelike / RPG in JavaScript (30 lines of code)

After a series of posts about the implementation of unpretentious toys on JavaScript in 30 lines, I decided to try myself in this "competition". After sitting in the evening, it turned out to create a “full-fledged” Roguelike / RPG (I do not understand genres too much, but something happened in this direction). At the same time I learned JavaScript (I had never written on it before, somehow I indulge in all C ++).

image

Features:


About the game

You play as a brave knight who must save the princess from the dragon. On the way to the dragon cave with a knight, various adventures take place, such as fighting monsters and visiting small settlements. After battles with monsters, the hero gets gold and life experience. Gold can be spent on treatment and purchase of healing potions in the shops that are on the way. Life experience increases the level of the hero, from which his attack and defense grows. The goal of the game is to comprehensively prepare for the fight with the dragon, and then defeat him and free the princess.
')
Link to fiddle .

Code

As a basis, I took the code from this post , and then “doped” it to the state I needed.

Source code (look at fiddle ):
Source
(function(elid, wi, he, exp, pot, gld, hp, lvl, cur_e, e_sz){ var hit = function(){ evs[cur_e][8]-=lvl*4+6; if(evs[cur_e][8]<=0) { alert("Monster defeated and you got "+evs[cur_e][10]+"EXP and $"+evs[cur_e][11]); exp+=evs[cur_e][10];gld+=evs[cur_e][11]; while(exp>=lvl*10){exp-=lvl*10;lvl++;alert("Level Up!")} cur_e++; if(cur_e==e_sz) alert("Victory!") } else { hp-=Math.max(0,evs[cur_e][9]-lvl*2-1); if(hp<=0) alert("Game Over!") } } var use = function(){ if (pot>0){pot--;hp+=10;if(hp>100)hp=100} } var nxt = function(){ cur_e++ }, battle = ["Attack","Use Potion +10HP","Skip Battle",hit,use,nxt], canvas=document.querySelector(elid), ctx=canvas.getContext("2d"), evs=[], e_tp=[ ["Shop", "Wizard provides his services", "Buy Potion $20", "Full Heal $100", "Leave", function(){ if(gld>=20){gld-=20;pot++} }, function(){ if(gld>=100){gld-=100;hp=100} }, nxt ], ["Skeleton", "A terrible skeleton on your way"].concat(battle,70,15,25,100), ["Goblin", "Green goblin wants to get your money"].concat(battle,50,10,15,70), ["Slime", "What the strange jelly monster?"].concat(battle,20,6,7,30), ["Dragon", "Omg! It is evil Dragon!","Attack","Use Potion +10HP","-",hit,use,,300,25,100,1000 ] ], q=e_tp.length-1; canvas.width=wi; canvas.height=he; for (var i=0;i<e_sz-1;i++) evs.push( e_tp[Math.floor(Math.random()*q)].slice(0) ); evs.push( e_tp[q].slice(0) ); var game = setInterval(function(){ ctx.clearRect(0,0,wi,he); ctx.fillText("NanoRPG in 30 lines of JavaScript by ripatti",10,15); ctx.fillText("LVL "+lvl+" HP "+hp+"/100 EXP "+exp+"/"+lvl*10+" ATK "+(lvl*4+6)+ " DEF "+(lvl*2+1)+" Gold $"+gld+" Potions "+pot,10,30); for (var i=0;i<e_sz;i++) ctx.fillText((i==e_sz-1||i<=cur_e)?evs[i][0]:"??",i*50+15,70); ctx.fillText("@",cur_e*50+25,60); ctx.fillText(evs[cur_e][1],20,100); if (evs[cur_e].length>8) ctx.fillText("Enemy HP "+evs[cur_e][8],250,100); for (var i=0;i<3;i++) { ctx.strokeRect(i*120+5,120,110,20); ctx.fillText(evs[cur_e][i+2],i*120+10,133); } }, 100); document.addEventListener('click', function(e){ for (var i=0;i<3;i++) if (i*120+5<=e.pageX && e.pageX<i*120+115 && 120<=e.pageY && e.pageY<140) if (hp>0) evs[cur_e][i+5]() }, false); })("#canvas",365,150,0,3,100,100,1,0,7); 


A slightly more readable version in 50 lines (look at fiddle ):
Source
 (function(elid, wi, he, exp, pot, gld, hp, lvl, cur_e, e_sz){ var hit = function(){ evs[cur_e][8]-=lvl*4+6; if (evs[cur_e][8]<=0) { alert("Monster defeated and you got "+evs[cur_e][10]+"EXP and $"+evs[cur_e][11]); exp+=evs[cur_e][10]; gld+=evs[cur_e][11]; while (exp>=lvl*10) { exp-=lvl*10; lvl++; alert("Level Up!") } cur_e++; if (cur_e==e_sz) alert("Victory!") } else { hp-=Math.max(0,evs[cur_e][9]-lvl*2-1); if(hp<=0) alert("Game Over!") } } var use = function(){ if (pot>0){pot--;hp+=10;if(hp>100)hp=100} } var nxt = function(){ cur_e++ } var battle = ["Attack", "Use Potion +10HP", "Skip Battle", hit, use, nxt]; var canvas=document.querySelector(elid), ctx=canvas.getContext("2d"); canvas.width=wi; canvas.height=he; var evs=[], e_tp=[ ["Shop", "Wizard provides his services", "Buy Potion $20", "Full Heal $100", "Leave", function(){ if(gld>=20){gld-=20;pot++} }, function(){ if(gld>=100){gld-=100;hp=100} }, nxt ], ["Skeleton", "A terrible skeleton on your way"].concat(battle,70,15,25,100), ["Goblin", "Green goblin wants to get your money"].concat(battle,50,10,15,70), ["Slime", "What the strange jelly monster?"].concat(battle,20,6,7,30), ["Dragon", "Omg! It is evil Dragon!","Attack","Use Potion +10HP","-",hit,use,,300,25,100,1000 ] ]; var q=e_tp.length-1; for (var i=0;i<e_sz-1;i++) evs.push( e_tp[Math.floor(Math.random()*q)].slice(0) ); evs.push( e_tp[q].slice(0) ); var game = setInterval(function(){ ctx.clearRect(0,0,wi,he); ctx.fillText("NanoRPG in 30 lines of JavaScript by ripatti",10,15); ctx.fillText("LVL "+lvl+" HP "+hp+"/100 EXP "+exp+"/"+lvl*10+" ATK "+(lvl*4+6)+ " DEF "+(lvl*2+1)+" Gold $"+gld+" Potions "+pot,10,30); for (var i=0;i<e_sz;i++) ctx.fillText((i==e_sz-1||i<=cur_e)?evs[i][0]:"??",i*50+15,70); ctx.fillText("@",cur_e*50+25,60); ctx.fillText(evs[cur_e][1],20,100); if (evs[cur_e].length>8) ctx.fillText("Enemy HP "+evs[cur_e][8],250,100); for (var i=0;i<3;i++) { ctx.strokeRect(i*120+5,120,110,20); ctx.fillText(evs[cur_e][i+2],i*120+10,133); } }, 100); document.addEventListener('click', function(e){ if (hp>0) for (var i=0;i<3;i++) if (i*120+5<=e.pageX && e.pageX<i*120+115 && 120<=e.pageY && e.pageY<140) evs[cur_e][i+5]() }, false); })("#canvas",365,150,0,3,100,100,1,0,7); 


I did not consider the code in html and css, but if anyone is interested: html - 1 line of code, css - 4.

Conclusion

Thank you agegorin for the race , DjComandos for the snake and linoleum for the arkanoid . It was interesting to read the source code and based on this to write something of my own.

UPD Roguelike / RPG in 4KB JavaScript Code

Comrade shvedovka offered to make a cyclical game with complication, which I actually did. In 30 lines it already did not climb at all and I took another restriction: the code should be no more than 4 kilobytes (this agrees with the remark of lolmaus , and I personally like it more). Having set up a cyclical game, I added a few more events - I filled the game with content for the very limitations.

Namely:

Small changes in the generation of worlds:

And a lot of small changes in terms of balancing everything. And yes ... now the princess can really be saved.

Link to this version of the game on fiddle . It turned out pretty hardcore.

Well, the code , of course. The formatting is not quite visual - I pauled a few spaces to fit in 4000 characters. In principle, the code can be further tightened to cram another event, for example (there was an idea to make something like a casino). But I'm too lazy to do it.

At this work on the game, I think finished.

Source: https://habr.com/ru/post/202684/


All Articles