Open Hacker1254 opened 3 years ago
this has to be fixed
oh and should i make the biomes bigger to seem more closer to the real game?
@GuineaPigBridge where are you?
i need you as you are the main part of this project, im not super good at coding
Ok. So the hearts just need to be responsive. To do this, we just need to make the x and y pos of the hearts canvas.width or height/n.
ok how do we do that?
oh and how do you think we are gonna put in unlimited jumps when swiming?
Ok, so for the swimming, I was thinking of a if
(k === " " ) {
if (Date.now() < p.lastJump + 0) {
p.velocity.y = p.jumpSpeed;
} else {
p.lastJump = Date.now()
}
}
In the water collision.
hmm lets see if that works
sorry my teatcher is watching me
what line dose it go on?
Line 2425
And as for the hearts 💕 you just change the heart for loop into
for(let i = 0;i < Math.floor(p.health/2);i++) {
ctx.drawImage(heart, width / 2 - i*20, height / 2 + 280, 15, 15);
}
Ok, so for the swimming, I was thinking of a if
(k === " " ) { if (Date.now() < p.lastJump + 0) { p.velocity.y = p.jumpSpeed; } else { p.lastJump = Date.now() } }
In the water collision.
im having trouble putting this in, can you send a screenshot of how it has to look like Sorry
for(let i = 0;i < Math.floor(p.health/2);i++) { ctx.drawImage(heart, width / 2 - i*20, height / 2 + 280, 15, 15); }
This is as close as i can get it
thisi s what happens when adding base64 of a Minecraf heart i think i have done everything correct
<img src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAIAAABv85FHAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABSSURBVBhXZY7BDcAwCANZor88O0jH67KZgdgYEZSckDA+KYp58AUIFqjkwjHHi+lBPW8ktTUUcvek++05BBoKvdu1RP6l6xIstYB0CbAT6MLdFzGReAo/QKRrAAAAAElFTkSuQmCC' style='display:none' id='heartimg'>
That's it. It works on mine.
but we really have to add something We need to add way to swich between hearts and no fly and flying and no hearts, you know what i mean
<img src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAIAAABv85FHAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABSSURBVBhXZY7BDcAwCANZor88O0jH67KZgdgYEZSckDA+KYp58AUIFqjkwjHHi+lBPW8ktTUUcvek++05BBoKvdu1RP6l6xIstYB0CbAT6MLdFzGReAo/QKRrAAAAAElFTkSuQmCC' style='display:none' id='heartimg'>
That's it. It works on mine.
am i doing something wrong?
Whu=ich browser are you using?
we need the hearts to resize like the hotbar does
Which browser are you using?
idk, i am on a chromebook if the changes anything
Ok. For the resizing hearts it's the same as the positioning.. you just make the value, 15, into something like width / 100
when testing using http://www.csgnetwork.com/htmlcodetest.html when i downsized the window the heart changed poz
https://github.com/Hacker1254/MineKhan-Modded/issues/1#issuecomment-832757274 do you think this is possable?
That's what I did.
Look. I will share you the code so you can just ctrl paste it!
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Beta Survival!</title>
<link id="vtfont" href="https://fonts.googleapis.com/css2?family=VT323&display=swap" rel="stylesheet">
</head>
<style>
body {
overflow: hidden; /* Hide scrollbars */
}
.world-select {
width: 99vw;
min-width: 300px;
height: calc(100vh - 220px);
position: absolute;
bottom: 120px;
overflow-y: auto;
background-color: RGBA(0, 0, 0, 0.6);
justify-content: center;
margin: 0 auto;
}
.world {
width: 250px;
height: auto;
border: 1px solid black;
font-size: 18px;
font-family: 'Courier New', Courier, monospace;
color: rgb(180, 180, 180);
margin: 0 auto;
margin-top: 15px;
padding: 5px;
cursor: pointer;
}
strong {
color: white;
}
.selected {
border: 3px solid white;
padding: 3px;
}
input[type=text] {
background-color: black;
caret-color: white;
border: 2px solid gray;
color: white;
font-size: 24px;
padding-left: 12px;
}
input[type=text]:focus {
border: 2px solid lightgray;
}
#boxcentertop {
z-index: 1;
width: 80vw;
max-width: 400px;
height: 50px;
position: relative;
top: 30px;
display: block;
margin: 0 auto;
}
.hidden {
display: none !important;
}
#onhover {
background-color: rgba(0, 0, 0, 0.9);
color: rgb(200, 200, 200);
font-family: 'Courier New', Courier, monospace;
word-wrap: normal;
width: auto;
max-width: 400px;
position: absolute;
z-index: 1;
padding: 10px;
cursor: default;
}
#quota {
display: block;
position: absolute;
width: 99vw;
margin: 0 auto;
bottom: 110px;
z-index: 1;
background-color: RGBA(0, 0, 0, 0.6);
justify-content: center;
text-align: center;
color: white;
}
.center {
margin: auto;
width: 60%;
border: 3px solid #73AD21;
padding: 10px;
}
</style>
<body>
<canvas id="overlay" tabindex="0" width="600" height="600" style="position: absolute; top: 0px; left: 0px"></canvas>
<input type="text" id="savebox" class="hidden" spellcheck="false" style="position: absolute; top: 10px; left: 10px; z-index: 1;">
<input type="text" id="boxcentertop" class="hidden" spellcheck="false">
<div id="quota" class="hidden"></div>
<div id="onhover" class="hidden"></div>
<p id="savedirections" class="hidden" style="position: absolute; top: 40px; left: 10px; z-index: 1; background-color: rgba(255, 255, 255, 0.3);">
To save your world, copy/paste the saveString<br>
from this box into the code on line 189.<br>
var loadString = "Your Code Here";<br>
Then save the program as a Spin-off.
</p>
<p id="message" hidden="true" style="position: absolute; top: 10px; right: 10px; z-index: 1; text-align: right; background-color: rgba(255, 255, 255, 0.3);"></p>
<img src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAIAAABv85FHAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABSSURBVBhXZY7BDcAwCANZor88O0jH67KZgdgYEZSckDA+KYp58AUIFqjkwjHHi+lBPW8ktTUUcvek++05BBoKvdu1RP6l6xIstYB0CbAT6MLdFzGReAo/QKRrAAAAAElFTkSuQmCC' style='display:none' id='heartimg'>
<div class="world-select hidden" id="worlds"></div>
<p id="message" class="hidden" style="position: absolute; top: 10px; right: 10px; z-index: 1; text-align: right; background-color: rgba(255, 255, 255, 0.3);"></p>
<script>
// Code edits will erase the world.
// Place save code here to load your world. Make extra sure you got it copied so you don't paste in the wrong thing and delete your world on accident lol
var loadString = ""
</script>
<script type="x-shader/vertex" id="blockVertexShader">
attribute vec3 aVertex;
attribute vec2 aTexture;
attribute float aShadow;
varying vec2 vTexture;
varying float vShadow;
varying float vFog;
uniform mat4 uView;
uniform float uDist;
uniform vec3 uPos;
void main(void) {
vTexture = aTexture;
vShadow = aShadow > 0.0 ? aShadow : 1.0;
gl_Position = uView * vec4( aVertex, 1.0);
float range = max(uDist / 5.0, 8.0);
vFog = clamp((length(uPos.xz - aVertex.xz) - uDist + range) / range, 0.0, 1.0);
}
</script>
<script type="x-shader/fragment" id="blockFragmentShader">
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
uniform sampler2D uSampler;
varying float vShadow;
varying vec2 vTexture;
varying float vFog;
vec4 fog(vec4 color) {
color.r += (0.33 - color.r) * vFog;
color.g += (0.54 - color.g) * vFog;
color.b += (0.72 - color.b) * vFog;
return color;
}
void main(void){
vec4 color = texture2D(uSampler, vTexture);
gl_FragColor = fog(vec4(color.rgb * vShadow, color.a));
if (gl_FragColor.a == 0.0) discard;
}
</script>
<script type="x-shader/vertex" id="2dVertexShader">
attribute vec2 aVertex;
attribute vec2 aTexture;
attribute float aShadow;
varying vec2 vTexture;
varying float vShadow;
void main(void) {
vTexture = aTexture;
vShadow = aShadow;
gl_Position = vec4(aVertex, 0.5, 1.0);
}
</script>
<script type="x-shader/fragment" id="2dFragmentShader">
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
uniform sampler2D uSampler;
varying vec2 vTexture;
varying float vShadow;
void main(void) {
vec4 color = texture2D(uSampler, vTexture);
gl_FragColor = vec4(color.rgb * vShadow, color.a);
if (gl_FragColor.a == 0.0) discard;
}
</script>
<script type="application/javascript">
window.canvas = document.getElementById("overlay")
window.ctx = canvas.getContext("2d")
window.savebox = document.getElementById("savebox")
window.boxCenterTop = document.getElementById("boxcentertop")
window.saveDirections = document.getElementById("savedirections")
window.message = document.getElementById("message")
window.worlds = document.getElementById("worlds")
window.quota = document.getElementById("quota")
var hoverbox = document.getElementById("onhover")
ctx.canvas.width = window.innerWidth
ctx.canvas.height = window.innerHeight
/* Set this text editor to max width by copying this into the JS console:
document.getElementsByClassName("_1chbfei")[0].setAttribute('style', 'max-width: 100vw !important')
*/
var MathGlob = Math
function MineKhan() {
let Math = MathGlob
let liquid = false
let setPixel, getPixels
let textures = {
grassTop: function(n) {
let r = 0, g = 0, b = 0, d = 0
for (let x = 0; x < 16; x++) {
for (let y = 0; y < 16; y++) {
d = Math.random() * 0.25 + 0.65
r = 0x54 * d
g = 0xa0 * d
b = 0x48 * d
setPixel(n, x, y, r, g, b)
}
}
},
grassSide: function(n) {
let r = 0, g = 0, b = 0, d = 0
let pix = getPixels("0g0g70ordrzz0u30g730wa4vzz0xnyl8f11lrk7315qj7jz1fh47pb6553365533033636350335403653650063306333633300635163503655353653535605335031350330553500033033366333433663663535336655335055335553353530355333033503300333336635353663650660554353355635155305303053556333333366353323553060365553063030663533555365534355335530")
for (let i = 0; i < pix.length; i += 4) {
setPixel(n, i >> 2 & 15, i >> 6, pix[i], pix[i+1], pix[i+2], pix[i+3])
}
for (let x = 0; x < 16; x++) {
let m = Math.random() * 4 + 1
for (let y = 0; y < m; y++) {
d = Math.random() * 0.25 + 0.65
r = 0x54 * d
g = 0xa0 * d
b = 0x48 * d
setPixel(n, x, y, r, g, b)
}
}
},
leaves: function(n) {
let r = 0, g = 0, b = 0, a = 0
for (let x = 0; x < 16; x++) {
for (let y = 0; y < 16; y++) {
r = 0
g = Math.floor(Math.random() * 30 + 100)
b = Math.floor(Math.random() * 30)
if (Math.random() < 0.35) {
a = 0
} else {
a = 255
}
setPixel(n, x, y, r, g, b, a)
}
}
},
oakPlanks: function(n) {
let r = 0
for (let y = 0; y < 16; y++) {
let a = (y & 3) === 3 ? 0.7 : 1
for (let x = 0; x < 16; x++) {
let mid = x === 8 && (y & 7) > 3 && a === 1 ? 0.85 : 1
let rit = x === 15 && (y & 7) < 3 && a === 1 ? 0.85 : 1
r = (Math.random() * 0.1 + 0.9) * a * mid * rit
setPixel(n, x, y, 190 * r, 154 * r, 96 * r)
}
}
},
hitbox: function(n) {
for (let x = 0; x < 16; x++) {
for (let y = 0; y < 16; y++) {
setPixel(n, x, y, 0, 0, 0, 255)
}
}
},
Water: function(n) {
let r = 0, g = 0, b = 0, a = 0;
for (let x = 0; x < 16; x++) {
for (let y = 0; y < 16; y++) {
a = 200;
r = 0;
g = 0;
b = random(200, 255);
setPixel(n, x, y, r, g, b, a);
}
}
},
dirt: "0g0g70ordrzz0u30g730wa4vzz0xnyl8f11lrk7315qj7jz1fh47pb6553365533033636350335403653650063306333633300635163503655353653535605335031350330553500033033366333433663663535336655335055335553353530355333033503300333336635353663650660554353355635155305303053556333333366353323553060365553063030663533555365534355335530",
stone: "0g0g40sywflr0wb8hdr0zdjj0f13tzldr3333211210112222221212222220012121001110111222222233232233222111122111212333312223222222211010131223331331112222110010112211122233323223332222212232223332233332021211001212211122222332222233232111232112200101332112211122111321122222222233332222221123322122",
logSide: "0g0g60fl1ssf0l5j1fj0qftm2n0wa7mdb14cs7wf16az8xr3143304330341432315230523034133230223052313113324032313232301342413230325232314343134131524131432313422343433143230343243342324331053324324242433315332332414303333432303231430331343230533053135133424052303323531343314231333343143340313114334314134131331432",
logTop: "0g0g90l5j1fj0qftm2n0wa7mdb0z2esxr15quebj189da7z1cpma671f7ppfj1hzyayn1012101120110111077776768667777017334454555544811738877776777471164766666666756107576445544674601646636666466471165764655656756116576465464674610657646666566460164764434556756116576666666674610757767777787460175454444444447117776676686677711011101120110211",
bedrock: "0g0g509gy58f0e7f7r30o8fd330rkrev31627mkf3111124324211212133434341443012110110111412224232433202422111112014111121134433112221221102211014432344323443410222122211011213234421122344344442110121213211143334134410144431102221123442334402111321134111112343420211101234433211211234421121011044312301123",
glass: "0g0g50ybfh8f0znkiyo12rzshr1au95hb1lytipr4444444444444443411111111111111341114111111111104114111111111110413111111111111041111111111111124111111111111110411111111111111241111111111111124111111111111112411111111111111241111111111111123111111111111412411111111111411231111111111111133323222222222233",
cobblestone: "0g0g60muaccf0r0pekf0un11q711vr5rz1a8mosf1ef1r0f2144011454313543145330554330132314342143342101321132113232134310021354131154443152453321033543234313433211243215332233213541231321542213533311021543232233121341323231101221044532211235411035532354215434213323154331432332113244323212332143111311232121135432",
mossyCobble: "0g0gb0muaccf0mupnnj0p38xdr0r0pekf0rbmj9b0un11q70w1wkxr0y07svz11vr5rz1a8mosf1ef1r0f4199211276438a9619a8812764813858398951644251118533852182851464110531183233866642a176895308948428981498852118851988111851664158385117641484642305126244558824124185442111155214698112124761318998127651764653885847488164588511858685851588531841183352111338a984",
stoneBricks: "0g0g70p2gyyn0rkrev30tj2nlr0xf9ou70zdjj0f12psrnj17g8flr5666666666665550645455555444445065454444445534406434554345434330632334544334324053244333345324302222222222222220110011000111111166666650566666663555544065455554544544306435354445544320634344454444343053345433332322305443344322222220222222221111110000111111",
mossyStoneBricks: "0g0gc0mupnnj0p2gyyn0p38xdr0rbmj9b0rkrev30tj2nlr0w1wkxr0xf9ou70y07svz0zdjj0f12psrnj17g8flrab3668863b88a680b9a28683a9999332ba926363996a2931b9792a679a979721b737793993697591a72397773632297075225752332277924410441110000444886bbba1abbbb6883aaaa991ba9aaaa6a99a9971b97a7a399aa99351b797992a99997371a689a97777573371a897799755223791722757754000041110004400",
bricks: "0g0g90vz62nz0yhavi712oqn7j13rinsv173m8lb193f4zj1b1w1rz1d7u7sv1j1u51b7742888777458777443513444435144410060033100503112256522225565222887747458777474214444315133333151341110600133105522225565522225677458887474588774435344444153444110511331106133325565222225652224777474287774745144444353444441501111106011111065222225655222566",
coalOre: "0g0g70ehg7wf0hjr9j30j7xaf30sywflr0wb8hdr0zdjj0f13tzldr6556544543445666554536666553335454331453344511556665655105655644455443346545645556553215542143464552111065105555433556663334466661156543215455645565354100056665354514334663354455555666634106655443366412111054665105556005455554456665566544455555554655555566",
ironOre: "0g0g80sywflr0wb8hdr0zdjj0f13tzldr1cpl2bj1gbvabj1o4exa71qwyvb33223211210112333221203333220002121005120011265223332322642322311122110013212312223220762217510131227655432542222100223330001133336523210762122312232021644423332021251001330021122222333301643322110033167666421332542223442122221123332233211122222221322222233",
goldOre: "0g0g80sywflr0wb8hdr0zdjj0f13tzldr1x01czj1y6gem71z13ncv1z141z33223211210112333221203333220002121005120011265223332322642322311122110013212312223220762217610131227655432542222100223330001133336523210762122312232021644423332021251001330021122222333301643322110033167655421332642223442122221123332233211122222221322222233",
diamondOre: "0g0g80h634zj0sagdtr0sywflr0wb8hdr0zdjj0f13tzldr1845xbz1ndl24f5445433432334555443425555442224343221342233461445554544604544533344332235434534445442764437632353447611054104444322445552223355556145432764344534454243600045554243413223552243344444555523605544332255367611043554604445004344443345554455433344444443544444455",
redstoneOre: "0g0g90sywflr0wb8hdr0zdjj0f13oi67z13tzldr15wexa71b68mbj1f24cfz1yr4gsf4224211210112444221204444220002121005120011285224442422832422411122110014212412224220862218610141227655342532222100224440001144448524210862122412242021633324442021251001440021122222444401834422110044168655321442832224332122221124442244211122222221422222244",
lapisOre: "0g0ga04hvenz04hvl6n04ihywv066fd3306r2ozj08z4sfz0sywflr0wb8hdr0zdjj0f13tzldr9889877876778999886669999886668787454386777813889889926329989977788776679867978889866428862576797861242398238888723679978767799993189872643386678998687222236258686627661237725788300799668893588779906612366339998700381039799887783339899877788888899888888899",
emeraldOre: "0g0g7004swsf06mdmv30sywflr0wb8hdr0zdjj0f13tzldr1ohjdhb5445432232334555443445615442334343223310333422445555225555546133344361324555104445441061243255353445551054434444332232552323355555545461442244534444441053615224243433223310361344444556155551044223455103322553261334455444344441045554455433344554443544444455",
coalBlock: "0g0g501e50xr03md24f05ul3b308mtq0v0bf3ri73322122002210012222121000210123321000122000022221001243222202210001233222100210020222221000001220132211001122222022210122343221002110123322210000012123221103200212122210002211232102112210012230002113432123322000123420023221000123210012221001222212212221000",
ironBlock: "0g0gb1dawbnj1fj5rlr1hrdssf1m7r1mn1nlyvwf1pa4wsf1qe8xdr1s2ey9r1t6iyv31tqkz5r1ver01r32233333333222232aaaaa9998777772277777777777777105555555666444402aaaaaa999777771277777777777777105555566664444402aa9999977777771277777777777777105555555566644402aaaaaa999977771277777777777777105555556666444402aaaa9999777777127777777777777712222222112111111",
goldBlock: "0g0g91kr8um71mphb0f1w77ain1xakkqn1ypvwu71yr43jz1yzk7pb1z0cef31z10mwv2222332223333221285577888776688125664877623324812564877462224471374777462264467137777462267762302877444667762330287444664462232037444664466222613744664466662461364667766666742032667762262776203267762332446261334462332666224132322332662264701101100011001100",
diamondBlock: "0g0g90434min061d2in0h634zj0l2fpxb0sagdtr0vckf0f1845xbz1ndl24f1z141z33333223332222331378866777664477138445766432235713845766543335561265666543345546126666543346643203766555446643220376555445543323026555445544333412655445544443541245446644444653023446643343664302346643223554341225543223444335123233223443345601101100011001100",
redstoneBlock: "0g0g50vx660v153407319j36671gh49a71runlz34444444444444444433433222334333443342202222232344332311001120234443211012011244443210000000042344302100000011134421100000000122442231000000111344324110210111234432110004001224443321101001110344322211111122224443124211022323443334433223333344444444444444444",
lapisBlock: "0g0gd05lqqkf06zt0xr07js8hr07tw35r084kzr308e6ein08e99fj08yjpq708ys8ov0an2j270c0w4cf0dp94hr0fdf5kv98999989cb848484878b78736733677197b8867767623765967877377863366597687666378a63759a77776636a7736197a767676236763596763666a6233625c636636376632775c633687363332371963237873676327186672236338763619763722332677630877633332336667143766633633367708111111610220000",
emeraldBlock: "0g0g606lfrb306mdmv307ei5fj07xmdbz0iaro5b10c5ptr0000000000000002055555454551144305000000200002430404555411114243050500000002425305050555445212130405054411421203040405451142120305040411020202130404041122120213042105410112420301212222222242030405510000441213012222222222224301011001110014532333333333333332",
tntTop: "0g0g704qh5a70nyecxr13jyl8f14a4flr1d4tukf1otd8u71szz8jj5665566556655665622462246224622462136210012300234431403344004433566006655660566562246204121402246211120000231023443341000111403356651000000506656211020001140224621360200120112344331031140344335661560500611665621462146124612462236223622362234433443344334433",
tntSides: "0g0ga07ipw5b0fbhzwf14a4flr1d4tukf1gwtvcv1ldqupr1otd8u71pk09vj1szz8jj1z141z38863886388638863663266326632663266326632663266326632663266326632663266326632663295775577755777799711071550711059745055115157177795717717115505479750571550550759444444444444444433223322332233226632663266326632663266326632663266326632663266323332333233323332",
tntBottom: "0g0g413jyl8f14a4flr1d4tukf1otd8u72332233223322332300130013001300130013001300130011111111111111111233223322332233230013001300130013001300130013001111111111111111123322332233223323001300130013001300130013001300111111111111111112332233223322332300130013001300130013001300130011111111111111111",
acaciaLogSide: "0g0g60ma181r0oi99fj0pcavi70t8nim70y9464f139ktmn3143304330341432315230523034133230223052313113324032313232301342413230325232314343134131524131432313422343433143230343243342324331053324324242433315332332414303333432303231430331343230533053135133424052303323531343314231333343143340313114334314134131331432",
acaciaLogTop: "0g0gb0j7rlz30kvxmgv0ma2nen0o889hb0t8nim713rcxdr16jehof18hk3r31c3oo3j1fprugv1hy2osf432332211231433449999898a889999329556676777766a2395aa999989996910869888888889782197986677668968318688588886886932879868778789784487986876868968448798688887886833869866567789782287988888888968219799899999a96811976766666666691099988988a8899933412233444322114",
acaciaPlanks: "0g0g711t8qgv13rcxdr16jehof18hk3r31c3oo3j1fprugv1hy2osf5456666656666652554423455544553345554543333445522112210011010010563666636665443545543432344555543345444255544333001121000011121056654466656666535455554333434332654433334444335300122100012110006556655366646566445533433555544443345432554333330110012221010000",
birchLogSide: "0g0g80f1fcov0qqliwv1gxap6n1o60u7z1ptrf271uar6db1uum5mn1z141z36643366634663366346666777634443677744367666636777732100136777737366321101377631211336344363331001266344677766532343777777334556376336775577775777764455577766336336777766634477636777767777577634436633667553210026677763443100000133377761132116331677764336336",
birchLogTop: "0g0ga0mk6h3316m5am719xxgqn1cg9ce71f8hx4v1jowirj1nv4jcv1nvimm71tgjy7z1z141z39818811001809889966665657556666816223343444433718627766665666360153655555555645106465334433563580535525555355368154653544545645995465354353563599546535555455358853653323445645115465555555563510646656666676350064343333333336016665565575566688901188999811009",
birchPlanks: "0g0g717znmrj19xxgqn1cg9ce71f8hx4v1jowirj1nv4jcv1nvimm75456666656666652554423455544553345554543333445522112210011010010563666636665443545543432344555543345444255544333001121000011121056654466666666535455554333434332654433334444335300122100012110006356655366636566445533433554544443345432554333330110012221010000",
darkOakLogSide: "0g0g60besef30dcwlbz0e6y70f0hj7ev30klcs8v0oho0e73143304330341432315230523034133230223052313113324032313232301342413230325232314343134131524131432313422343433143230343243342324331053324324242433315332332414303333432303231430331343230533053135133424052303323531343314231333343143340313114334314134131331432",
darkOakLogTop: "0g0gb08ml79b0auqebj0dcmqdb0e6y70f0f117gf0g4r4730h8wirj0hj7ev30kb0idb0lz2akf0n393wf741441100140744779999898a889999419225565666655a1492aa999989995903859888888889681096985566558958408588288885885941869858668689687786985865858958778698588886885844859855256689681186988888888958109699899999a95800965655555555590399988988a8899944701144777411007",
darkOakPlanks: "0g0g70bejy0v0dcmqdb0g4r4730h8wirj0kb0idb0lz2akf0n393wf5456666656666652554423455544553345554543333445522112210011010010563666636665443545543432344555543345444255544333001121000011121056654466666666535455554333434332654433334444335300122100012110006356655366636566445533433554544443345432554333330110012221010000",
jungleLogSide: "0g0g90h96cxr0htdywv0m9k4xr0ne277j0orqc5b0rkbldr0sxvim70wunksf0ys83cv4688422222666444444114414441001111137510011441866644668842422224146644411143573444422235311010006646442222444886101000466412222444688411068844441122575311222100755444666664355741100112241201661144866643341111623533422111444644111004661664444664122244442222",
jungleLogTop: "0g0g90h96cxr0htdywv0rkbldr0ys83cv160fym7188mku71batekf1f77h1b1h5ei2n3212210011213223377776768667777217444454555544812748877776777470164766666666756107576445544674620646646666466472165764655656756336576465464674633657646666566462264764444556756106576666666674600757767777787460175454444444447117776676686677722310122333211013",
junglePlanks: "0g0g70sxkd1b0xdxkov160fym7188mku71batekf1f77h1b1h5ei2n5456666656666652554423455544553345554543333445522112210011010010563666636665443545543432344555543345444255544333001121000011121056654466666666535455554333434332654433334444335300122100012110006356655366636566445533433554544443345432554333330110012221010000",
spruceLogSide: "0g0g60csc9vj0cskpof0dmmb5r0geuxof0lf4i670nnb4sf3243304330342431325130513034233130113051323223314031323131302341423130315131324343234232514232431323411343433243130343143341314332053314314141433325331331424303333431303132430332343130533053235233414051303313532343324132333343243340323224334324234232332431",
spruceLogTop: "0g0g80ix87pb0nnb4sf0p1n6db0qzu7zz0v5xypr0xy569r106bshr11ueyv31012101120110111066665657556666016223343444433711627766665666361153655555555645106465334433563501535525555355361154653533545645115465353353563510546535555455350153653323445645115465555555563510646656666676350164343333333336116665565575566611011101120110211",
sprucePlanks: "0g0g80nnb4sf0p1n6db0qzu7zz0v5xypr0xe36rj0xy569r106bshr11ueyv36567777767777762665523566655663356665643333556622112210011010010673777737776553656653532355666653356555266655333001121000011121067765577777777636566665333535332765533335555336300122100012110007367766377737677556633533665655553356532665333330110012221010000",
sand: "0g0g61m6x62n1nb9nnj1opn5dr1r80f7j1scbi0v1u0izgf4223213232132313122121130142502432011422222121122331213133132122125213232322122321223332123122121421151211022121212212212111242112322310131232124212221120212231202321232232012311223212331112121213132145321123323230232323221223235332323203223232332321223232",
gravel: "0g0g80rufq4f0vqwlbz0zxiprz125i9rz15rvcan1627mkf1d0twqn1dackxr0341152512122521522122312102333522103133522351352232321522512322132741122210253202140133526552213104226515530122553253522311225353521223310256122311652152322132123553102521325022533562113225212132222537415525331025232422215235323311243310351274122232321212",
blackConcrete: "0g0g40149on3028826702882yn028dp8f1330112022012232303130022112212111032203010022012122012222321221011223213030101033110011212233120230013131003200032022012002002112233122202312230200102211312102222122132011021201223320211021220121122122321331201102120210001220112022023302312210123220102110",
blackWool: "0g0gf0149on3028826702882yn028dp8f03cc2rj03cc3jz03chptr04gg3cv04glqf305kk3y705kpr0f06oo4jj07ss54v08ww5q70a106bj46348b45ab13993299eebb7742570367bc77dc97bcbbdeb996bc768c47cc96ec9749bb115953bb31314c414641672547eebb8879bea7eeb966eebacc45bca9ccbb35ca4657528733577b117949ee7beeccbbecbacb799839349966bb445911796611cd318b9bee92badecb9717bc77cedc97c99beb559b6424c946cd76ac44bc",
blueConcrete: "0g0g30c98pof0c98qgv0c9ecqn1000001101000201011001000101120101211111010200000001000110100100220110011001121111001100100100010200102101101000000011110001101010101011020111110000000101121111101100100100010110111010210111001101000100000111110100101111000120000010100000110001200110010002",
blueWool: "0g0gj0c98pof0c98qgv0c9ecqn0c9edj30ddcr270ddidbz0ddie4f0ehgrnj0ehgsfz0ehmepr0ehmfi70flkt1b0flqfb30flqg3j0flw2db0flw35r0gpugov0gq02yn0gqbd3335237c44ac12992289iidc6632460256de67ge96cebbhic985ce657e46ee85ie9648cc114942cc21214e413541562436iidc7769dia6iic955iicaee34cea9eecb24ea4546427622466d116949ii7biieecciecaed699728249955cb344911695511eg217c9bii92bahiec9617be67eigf86e99cid449b5324e935eh65ae34de",
brownConcrete: "0g0g30pl5gqn0qp3u9r0qp9hbz0211211202222221222122211211122121011102222222212211200222112101121111211111222121222122122221201212121222112122222221220222222122221201121212022122122110221211122221211011221212121010121011111212122221121221222212122112112112211010222111121211220222221122",
brownWool: "0g0gi0qp3u9r0qp9hbz0rt7uv30rtdh4v0sxbunz0sxbvgf0u1a8zj0u1fv9b0v5e8sf0v5e9kv0v5jvun0w9i9dr0xdgmwv0xdm9z30yhkni70yhq9rz0zloo3j0zluadb35228d45bd12aa219bhhed7721570257ef77gfa7dfccghdb95df758f47ff95hfb749dd115a52dd21214f413541571526hhed887aehb7hhda55hhdbff25dfbbffdc25fb4557518722576e117b4bhh7chhffddhfdbfe7ab82924bb55dc245b116b5511fg218dachhb1cbghfda717cf77fhgg97fabdhe55ac5214fa25fg75bf24ef",
cyanConcrete: "0g0g305objsv05obklb05oh6v31112010122111202100121222111122012212211101111202102221210222122211111211221111011021102021212212220211021022022201210110010212210211110202101010201110112020221222112102111202212110222110211221001010211001221212202222122111221102211100222112011211120220211",
cyanWool: "0g0ge05obklb05oh6v305omt4v05omtxb05osg7305oy2gv05p3pj305p9bsv05p9clb05peyv305pkl4v05pklxb05pq87305pq8zj242269347912662167dda95521450145ab55cb659b88cd97649b546b35bb64db7536991146419921113b312431451424dda96656ad75dd9644dd97bb249b77bb9814b73445416521454a115737dd58ddbb99db97ba56762623774498234711474411bc116968dd7187cdb965158b55bdcc65b679da44684213b624bc547b23ab",
grayConcrete: "0g0g20ehlvr30flk9a70000000000000000000000000000000000000000000000000000000000000000000000000000100100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000100000000000000000001000000010000",
grayWool: "0g0gd0ehlvr30flk9a70flpvjz0flpwcf0flvim70gptwxr0gpzjzz0htxxj30hu3jsv0hu3klb0iy1y4f0iy7ke70iy7l6n333359337803663157cca85531350135ab55cb658b88cc87539b545b35bb53ca7535990036329930103b303330350335cc995556ac75cc8633cc87ba339b77ba8813a73435305532355a005737cc58ccab88cb87ba56753533773498333700574300ac205968cc7087cca865058b55bccc55b679ca33684313a634bc537a33ab",
greenConcrete: "0g0g20k2mku70k2s73z0001000100111100101010100101101101101100111010010111001001011000100001100011000100101010110101111010000001011001001000100001010110101110010011010000101011110000010110101000000001010000110101011101000111110011101010011000111100001100011000000111101001101100",
greenWool: "0g0gh0k2mku70k2s6bj0k2s73z0l6qjun0l6w64f0l71rlr0mb054v0mb5qm70mb5ren0nf9qf30nffcov0ojdq7z0ojjbpb0ojoxz30pnnbi70pnsxrz0rw0y6746447c45ab1499419aggcb8841580168ce88ee98bebbfgba96ce867e48ee96gda849cc125953cc41114e424642681546ggcc7789cga8ggb966ggbaed45ceaaedbb15da4658517843586c21894agg8bggdebbgebaec89a749449966cb445a116a6621de327c9bgga1bafgdb9828be88egee98e9acgc559b6414d946ef86ad44ce",
lightBlueConcrete: "0g0g3090ti4f090z4e70a4xipr2120111111121222222121122112211121121211112221221111102221122212112021111111122111112111112121211222221111122121212121112021122112211121211211111121220111212121111121222001122211222221022122111011221111111021211221211211121112212111111122121111211112222111",
lightBlueWool: "0g0gq090z4e70a4xipr0a534zj0a58r9b0b974sf0b9crun0b9ie4f0cdgrnj0cdmepr0cds0zj0dhqein0dhw0sf0dhw1kv0em01dr0em5nnj0fq9o8v0fqfain0gudou70gujb3z0hyhon30hynawv0hynbpb0j2lp8f0j2rbi70k6pptr0latpmn7b57fm89ik26ff63fippnkdd739d04benodepogdkojjppkifblodcfo8doofbpoie8fml21af95mm62428o817b81bd397cppmlffefnpidppkgbbppkioo79loiiookj4aoi8cad93fd55adcn22dh8ippejppookkpokiondfif6f68hhbcmj78ai22cicb22op51fmfjppi3jippokfd2ejoeeoppofdofilpn9agjc738og7copebio78no",
lightGrayConcrete: "0g0g20yjgg730yjggzj1000110001001010100010000000011101100010001001011010001100101010001000000000000010100000000100010100000000001010100010001000000000100000110000100001100000011101110001001011001001001001011000000000011001001001001010000000010000000010100110000000001000001001",
lightGrayWool: "0g0gi0yjgg730yjggzj0znkgsf0znkhkv10rohdr10roi6711vshz311vsirj11vy5tr12zwjcv12zwk5b1440jy7144670f1584kjj1584lbz16c8l4v16c8lxb17gclq745339e55bd1399329bhhfd7732570257ff78hfa7dfcchhdb95df769f57ff95hfb759ed115953ee31215f514551572537hhed9979fhb7hhda55hhdbff35dfbbgfdc25fb5657529733577f117b5bhh8chhfgddhfdbgf79b93935bb56ec355b117b6511fh319e9chhb2cbhhfd9718cf78fhhg97f9bdhf55ac6325fa36fh75bf35ff",
limeConcrete: "0g0g30ppd5a70ppirjz0qth5331121111111100111111111110111110111010211100110111211111211101111112111111111111101110111011110211011011110111110102111110100111000110112112110211112112112111212111111111111111011111011100101111111111212111111111112010011011221111111110111120011110111101111",
limeWool: "0g0gg0ppirjz0qth5330qtmrcv0rxl4vz0rxqr5r0t1p4ov0t1uqyn0u5t4hr0u5yqrj0v9x4an0va2qkf0we143j0xi53wf0ym3hfj0ym94hr10uh5of35237c44bb1299228affcb6632460257cd67ed96bdbbefba85bd657d46dd85fca748cb104942cc21214d403540562435ffcb7779cfb6ffb955ffbbdc34bdbaecbb24cb4546427622465c01694aff7bffcebbfdbbec69a728249955cb344a105a5501ce207c9bffa2bbefcb9607bd77dfee86d9abfc449b5324c935de75bc34cd",
magentaConcrete: "0g0g419jw2rj1anugan1ao02kf1ao03cv2233313332221332333333233213322333223332331233333313323103121223222131233211303233130230322231223302323103002220333103232232223333313231221331233333231232212303302330122232333333332123320111033031332321233232303322232223332223231233212323332233222231230323",
magentaWool: "0g0gs1ao02kf1ao03cv1brygvz1bs435r1bs43y71cw2hhb1cw83r31cw84jj1e06i2n1e0c4cf1e0c54v1f4ainz1f4g4xr1f4g5q71g8ej9b1g8k5j31g8k6bj1hcijun1hco64f1hco6wv1igmkfz1igs6pr1igs7i71jkw7b31jkw83j1kp08ov1lt49a71mxdwxr6945dl77ij14ff43ehrrmjba537a039cmoacqogajojjqrjhe9koa9do7booe9rnhc7elk118f74ll41317o7169719a275arrmkddcfmribrrjg99rrjion57koihonjj38ni798a72db448bam11bh7hrrcjrrnojjrojiomafhd4e47hh99lj578h11ah9911nq41dlfjrrh2jiqrnjfa1cjoccorqpebofhkrm78gj9537ng59oqc9in57mo",
orangeConcrete: "0g0g21p59iwv1q97wfz1011110111101111111011111110000111101110100111111110011111011001001101110110111111111110111111011111101110111111110111111111111111011110111101111011101101101111010010111111110110101110010011101111111010101001101111111011111110111111111001111111101111111111",
orangeWool: "0g0gm1q97wfz1rdbw8v1rdbx1b1rdhjb31shfwu71shfxmn1tljxfj1tljy7z1tlpkhr1upnytb1uptl331uptlvj1vtrzen1vtxlof1vtxmgv1wy1m9r1wy1n271wy79bz1wycwe71wyijgf1wyo5q71wytssf46239f55de02aa219dllhe8731570268hi79kib7eieelled96fi779i58ii96lhd859ff005a52ff20205i504650671537llgf998ahld8lleb66lledih35fiddihee25hd5757519822587h008c5dll9ellhieeliedih7ad92925cc67fe355d007d7600hk209faelld1edllhea709ei89ilkj98iadflh55be7315hb37il86dh35hi",
pinkConcrete: "0g0g31ltks8v1mxj5rz1mxj6kf1122212121112211111211211211122112111221111111121211212111122211111112112121112111111111111112111212221121111111212211112101110212211211122111211112212111211110211211211211111111121212221211121111112212211121211111211212211111111111121112111211212122122111",
pinkWool: "0g0gv1mxj6kf1o1hk3j1o1n6db1p5r6yn1q9v6rj1q9v7jz1rdz7cv1re4uf31si37y71si8u7z1tm78jj1tmcutb1tmcvlr1uqgven1uqmigv1uqs4qn1vuqj271vuw5bz1vuw64f1vv1se71vv7fgf1vvd1q71vvd2in1vviosf1vvipkv1vvobun1vvocn31vvtywv1vvzlz31vw588v1vw591b6a46em78jk25gg53fjuuokdc638c03adoqcesqhckqkktukjfalqcbeq7dqqfaupjd7fml219g83mm52327q716a71ac386buunleedgoujduukhaauukjqp68lqjjrpkk39pj7b9c83ed439dbo12di7juuekuuprkkuqkjrocgje5f57iiabmk679j22bjba12ps31emgkuuj3kjtupkgc1ekqdequssfdqgjluo89hkb637ph6bqtdajp67oq",
purpleConcrete: "0g0g40qo16v30rrzke70rrzl6n0rs57gf3222322033323333333312320220233231302222232332333202332233033330333322320012333323333330233022223332223332223232323303323233332332021220323110033223333322032332312323332322322202232223033232221223233023333332022233332332233232023333233223032323332323323332",
purpleWool: "0g0gm0rrzl6n0rs57gf0sw3kzj0sw3lrz0u01zb30u07lkv0u07mdb0v45zwf0w84dfj0w84e7z0xc8e0v0xc8etb0yg6scf0ygcem70zkasxr10o96gv10o979b10oetj311sd72711sd7un12wh8fz154uxhb47349e45ce13aa32acllfe8742570279fh79jhb7ehddkleca7eh779h48hha7lgc94aee116a52ee31214h414741771547llfe999aflc8lleb77llechg45ehcchged26gc4767519832687f118c4cll9dllgheelhechf7ac93a34cc77ed446c117c7711gj219eadllc1dcklgea719dh99hljia8hacelf56bd7424gb47hk97cg44fh",
redConcrete: "0g0g112voa9r0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
redWool: "0g0gd12voa9r13zmnsv153l1bz167jev3167p14v17bnenz18fls7319jprzz1ano5j31ano6bj1brmjun1brs64f1f3yku7241258227712662157cc9754212401459b45bb647b77bc77548b445b25bb54ca7525881036218821112b202420441224cc8855569c75cc7644cc77ba228b77ba7713a724342155113549015627cc57ccab77cb77b946752522664487223711474401ab105867cc7177bca764057b55bcbb55b678c923674212a624bb547a229b",
whiteConcrete: "0g0g31ktui9r1ku04jj1lxyi2n1121212211221121111221111121122111211111121112111212112211111211212222111111221112211112101110211111122211211211111111212221111112212121212222111121122211111111222111111112121222121111111111211121221112221111121222121112212122111221211212111121221211221212",
whiteWool: "0g0gn1lxyi2n1lxyiv31ly454v1n22inz1n22jgf1n285q71o66j9b1o66k1r1o6c6bj1paajun1qeekfz1ricyrj1riil1b1smgykf1smmlmn1tqkz5r1tqqm7z1uuozr31uuumtb1vyt0cf1vyynen1x2x0xr1y711j37a56dj89hi26ee63egmmliba639a04acllacmlfailiimmigeajlaadl8blleamlgc8ejj219e94jj62429l817a81aa396ammkjddcelmhbmmifaammihll69jlhgllii49lh8a9a93db549bal12bg8gmmcimmlliimlihllaegd6e68ggaaji689g22agaa12lm41djeimmg3ihmmliea1cilcclmmmeblegjml99fia638lf6almcahl68ll",
yellowConcrete: "0g0g41to1w5b1us09of1us5vy71us5wqn2111111122222221232021021213122222211020112110121211022201012212122222312211221122213110202011133121132211112221221211211111112120231210211111201132001211212111211102220222021102211223011100222121202222222132111211111112111112021121120222221222011012122121",
yellowWool: "0g0gj1us5vy71usbj0f1vw9wjj1vwfitb1vwfjlr1vwl5vj1vwl6nz1vwqsxr1x0p6gv1x0utj31x10fsv1x10glb1x163nj1y54h6n1y5a48v1y5a51b1y5frb31y5fs3j1y5lf5r45239e44bc0299219biiec7731470158eg78hga7cgcchicb95dg759g47gg95ifb849ed005942ee20104g404540571436iied9989eib7iica55iicbgf34dgbbgfcc15fb4557419722576e007b4bii8ciifgccigcbge79b92924bb55ec345b006b5500fh209e9ciib1cbhifc9708cg88gihh97g9bdie45ac5314fa35gh85bf34eg",
bookshelf: "0g0gt03fxnnj04laqdb0a0ot1b0b6j6db0c8r6db0deww730df88ov0egz6rj0gpo9330ht5kov0k20av30nf40zj0pnc1dr0qoh8fz0sy416n0w8kcn30wc5n9b0yicu0v11vb08v11vskcf13z03jz16atkvz17fehvj1as1ce71czhmv31e50qv31g6nvgf1gbtpfj1ks44qnommllhlllmmmmlmoo44477444772534oo799kk999ni9637olkffqk99gpjk631lsqffqk64gnik631soqfdqf6gcngf651ookd9kf3c7igf350mlorrsssroorrroolollmmhmllmmmllhoo77227cb7427724ooqk763gck99cb97olpj063gckq4gc10hsqk063a8fqkgcpjsoqf065a87kqgc10omkfe35ccefkcb03oorrsssroorrrrooo",
netherBricks: "0g0g706o77cv08w9lhb0b4bzlr0dcedq70fkb4sf0gof6670iwn6kf0000000000000000556515666555165532340433333405333432133223321333000111000001110015666544155545540543333306433333143333221433243211000001110000014416641644164414330433063304330423143314321333140111011101110111464414666644166433330433344303333322033333330333",
redNetherBricks: "0g0g70c7i51b0efew3j0fjiwov0ive1a70nbiubj0svgd1b0v3ir5r0000000000000000556515666555165532340433333405333432133223321333000111000001110015666544155545540543333306433333143333221433243211000001110000014416641644164414330433063304330423143314321333140111011101110111464414666644166433330433344303333322033333330333",
netherQuartzOre: "0g0gc0hs27zz0m7vq4f0m81d6n0ncb0u70rsft330u2845b0v4m7sv10p0npb1bug2671e4e1vj1n1fv9b1smb9xb1312426644044643344320246610442164643068a921424676466baa866314246766ba8674662643166895678a8544641466565ba95146866310289856642b9742469b814466ab862467ba624646b952364a950268665602426852108b61623214642649a9164941364667689238976124667423804646442246624630246644",
netherrack: "0g0g70hs27zz0m7vq4f0m81d6n0ncb0u70rsft330v4m7sv10p0npb1312425544044543344320245210442154543012432142456545423545531424565424566455254315431566554244541452054554214565531024541154246642454221445541452456540245455112354554025655200242254210254102321454254122154541354556512234565124556423204545442245524530245544",
netherWartBlock: "0g0g60of09vj0suze9r0xana4f1539r7j1brbain1g7rcvz0122001222532010222235202110022112111222202202320122201003202242042220021211122103224300222201201022220122252002210210211223102220000221222200222022221132210202012201104100222102201012211021042230122022012022212012022002220222202202220234022205220322022302",
quartzBlockBottom: "0g0g31sm5mv31tq9ngf1tqfbb32222221111000222121111000002222111100000012222110000001122221110001111000110000011100000110000011111222200001111222222211111111222211000112222220110012222222211222222222210000022222211000000222222110110112222111100112222221100101122222111101222222222210000",
quartzBlockSide: "0g0g61p9z6kf1qe37y71sm5mv31tq9ngf1tqfbb31uujcov5555555555555555543333222224444153322222234444304222223344433330423333222332222153322222332222215333444422223330544444433333333054433222334444405332234444444430544444444432222154444433222222405444332332334440533322334444443042323344444333311100011111110000",
quartzBlockTop: "0g0g61p9z6kf1qe37y71sm5mv31tq9ngf1tqfbb31uujcov5555555555555555543333222224444153322222234444304222223344433330423333222332222153322222332222215333444422223330544444433333333054433222334444405332234444444430544444444432222154444433222222405444332332334440533322334444443042323344444333311100011111110000",
quartzPillar: "0g0g41qe37y71sm5mv31tqfbb31uujcov0202031312130212020302130313031202131313120313020313130213031313131303120302121313020312031203131302131313020303120313031303130312130303131303120213131302131212131312130202120313021213120302031302120313031303130202130303131303120313031213120212131302121202",
quartzPillarTop: "0g0g51p9z6kf1qe37y71sm5mv31tqfbb31uujcov1021223242413020222122211221122002223434434322223123443344443211113412222221431242442344443244222243242222424423423324244142441222442424324234143244242211423322224423334432442421341222222143111123444444443213222234444443222002211222122112221203142423220200",
chiseledQuartzBlock: "0g0g61p9z6kf1qe37y71sm5mv31tq9ngf1tqfbb31uujcov0444404333022440043331222204444003322111000444300222223004443331023011101110022103315544432202200110441100220000554444144033344544433204314444440111230011441101054044444432154104400110111004401444454005544440133321100004443002323155440333300444414444132220",
chiseledQuartzBlockTop: "0g0g51p9z6kf1qe37y71tq9ngf1tqfbb31uujcov0334314232133420021100022000112002044203204421200204120420412021020113032020003102233304212233200000000421110000323433442223344422222222222222220000001321000001044343142143222104011204214110201302130420313020120234032023403002111003201100400222314222133420",
chiseledStoneBricks: "0g0g70oigd8f0qqoef30sz21vj0xf6vpb0yjgikf11vskcf17g711b6666655666566562644424444224444054110000111123205206665566561530640654434442154064064322334315405205430005421630540632000643062052054300053206306215435655420630641644545432053064163232322215406421110000011530556655666655543053343234433232202000000000000000",
smoothStone: "0g0g70wb8hdr0yjgikf12zwkxr17gcnb318kgnwf1asop331d0wq9r1011002200220121054556666553445204334454444555501666455566655442255444545666645006555555554343412556664665545550243343443434466116656545555455620555554566656661054544334454454215555666665566622444566454433452165445556555455104456665566544401022112221000121",
soulSand: "0g0g60egz94v0ht5n270k181z30oho3jz0sxyiv30xeekfz3130033202100434433135531100234004241451321012411423432542212230032442154212211332234104322433212002314322315332220023322345513032322211233445304302332013540423202342330242132302341043213032020354044321232020431445421112202350345131123302354202432103212415",
glowstone: "0g0g80u1fugv0v5phbz10pn01r11uj6db1kqv1tr1x1ncov1y6jzlr1y711j35420045440276423654011240346654024032201131054033034542017510033014766405664136502566552454236751004554020132450531024010352320465230213276501576442654346640335420454220452013000320356304012311354016754036520357660354215764034654201003242013154216521100013",
andesite: "0g0g60sywflr0wb8hdr0yjgikf11vsl4v17gcnb31asolxb2114332104423221245323445330122411334410122443224410133445433231235442223333211433323243210013432324542104543333423310154332333433114143343224533143342221154333332321101544332201331254422332333443443233212331543333334533112233301244323223541321322323234432",
diorite: "0g0g60xfchz311vskcf19of1fj1gd8su71ktouf31smgykf1222352233524355201322135345543151355101325543235255312553213225351243153532255245355541332335235332455214554102243123234555521232203255443244250155213551234541244310134335513055510144221512352542543554223223212343125454353242115521455321245235554124421255",
granite: "0g0ga0l56fi70plgu0v0yhw0sf1424tmn17eb8cf1aqna4f1e3al8f1e3x7un1jnje2n1qczoxr7434244344046445431548244224422341444335249241464143446145532344234232113754334244255342143347242445524442555322424474302155243346344255522344245535242552464245557314243844212555415442024425030323244424324439435524312355344448553215435547245544733324244425",
polishedAndesite: "0g0g90oilzi70sz23gf0v7a3270yjgjcv10ru60v11vyakf11w3ugv19okmwv1e4v30f7878888888788785843334442443134084444443376644307416763343446440844442456433334083344333424433318444634666554320833433332123444184562444466776608434433443344540844667633366444081444444664466208466654333134760844433334465334083333664444334406100000000110000",
polishedDiorite: "0g0g80qqu0ov0xfi5tr11vy6m71asubcv1f94t1b1jpkttr1o60w731ri7bpb7776677777666774764556657643564166764466577434506565333565454441675334576435457176544567733456607566767655346450744765756564534063556655467534516566544565544761664334655465666075673555775644517557654375466340745654576435744072445566545654512101110001100110",
polishedGranite: "0g0g90l56fi70plgu0v0yhw0sf1424tmn17eb8cf1aqna4f1e3al8f1e3x7un1jnje2n8888786866866565843344345444334374744554442446418444243334553342844444453344443262343344443734428345543644444431844444423445544263422344554433636444433443344442734444444464334284473344244442318455444445543341633443264434455264443444334447412111001110111100",
cactusstop: "0g0gp000006w000b8qg000mha0000xptk0018yd40018zy0001k8hk001vh140026pko02a3gmw02a3i7s02aep6g02aeqrc02apzaw02b17ug02bcge002bnoxk02bnqig02byz2002ca7lk04k6yns04ki77c04ktfqw04kthbs04l4pvchh32bd40fhh628hhhlefjjgdnllihkkh3ekmnlg5imlccklh2fnllnjajmleellhbkleelmjmmnllmh7djlddjmmjfjnnle34gmmmonmj5fmmmle0djikonmnjlnkij4flj77jlilmmja5e0hmmhilf3ilmmihjd3flllljfkjlkkmlh7hlnllmnj9fddlj6hmmlgennj9fddlj6hkmld9jnoillkljhhdglkfdjldilhcehh23dhc2f60ch717h",
cactusSide: "0g0gk000006w001k8hk02a3gmw02a3i7s02aeqrc02apzaw02b17ug02bcge002bchyw02bnoxk02bnqig02byz2002ca7lk02clg5404k6yns04ki77c04ki8s804ktfqw04kthbs04l4pvc4chijhh74cgiihfa4chdff402bfijhfa06fdhi734bfiif5017cdfhc704cijica4bffihf717fijica4affggf74bfhjhca08cffff74bhhhe404gffffi704efff734gfffff717ffffca4ggfif402bhhhhca4fffhf734cjhhf404fffiic74cjggi7306bfiib705ggffca19ffhhc719fffjia4cjhff502cjgfhca4cifffa34cfeffda",
}
let blockData = [
{
name: "air",
id: 0,
textures: [],
transparent: true,
shadow: false,
},
{
name: "grass",
textures: [ "dirt", "grassTop", "grassSide" ],
},
{ name: "dirt" },
{ name: "stone" },
{ name: "bedrock" },
{ name: "sand" },
{ name: "gravel" },
{
name: "leaves",
transparent: true,
},
{
name: "glass",
transparent: true,
shadow: false,
},
{ name: "cobblestone" },
{ name: "mossyCobble" },
{ name: "stoneBricks" },
{ name: "mossyStoneBricks" },
{ name: "bricks" },
{ name: "coalOre" },
{ name: "ironOre" },
{ name: "goldOre" },
{ name: "diamondOre" },
{ name: "redstoneOre" },
{ name: "lapisOre" },
{ name: "emeraldOre" },
{ name: "coalBlock" },
{ name: "ironBlock" },
{ name: "goldBlock" },
{ name: "diamondBlock" },
{ name: "redstoneBlock" },
{ name: "lapisBlock" },
{ name: "emeraldBlock" },
{ name: "oakPlanks" },
{
name: "oakLog",
textures: [ "logTop", "logSide" ],
},
{ name: "acaciaPlanks" },
{
name: "acaciaLog",
textures: [ "acaciaLogTop", "acaciaLogSide" ],
},
{ name: "birchPlanks" },
{
name: "birchLog",
textures: [ "birchLogTop", "birchLogSide" ],
},
{ name: "darkOakPlanks" },
{
name: "darkOakLog",
textures: [ "darkOakLogTop", "darkOakLogSide" ],
},
{ name: "junglePlanks" },
{
name: "jungleLog",
textures: [ "jungleLogTop", "jungleLogSide" ],
},
{ name: "sprucePlanks" },
{
name: "spruceLog",
textures: [ "spruceLogTop", "spruceLogSide" ],
},
{
name: "cactus",
textures: ["cactusstop", "cactusSide"]
},
{ name: "whiteWool" },
{ name: "orangeWool" },
{ name: "magentaWool" },
{ name: "lightBlueWool" },
{ name: "yellowWool" },
{ name: "limeWool" },
{ name: "pinkWool" },
{ name: "grayWool" },
{ name: "lightGrayWool" },
{ name: "cyanWool" },
{ name: "purpleWool" },
{ name: "blueWool" },
{ name: "brownWool" },
{ name: "greenWool" },
{ name: "redWool" },
{ name: "blackWool" },
{ name: "whiteConcrete" },
{ name: "orangeConcrete" },
{ name: "magentaConcrete" },
{ name: "lightBlueConcrete" },
{ name: "yellowConcrete" },
{ name: "limeConcrete" },
{ name: "pinkConcrete" },
{ name: "grayConcrete" },
{ name: "lightGrayConcrete" },
{ name: "cyanConcrete" },
{ name: "purpleConcrete" },
{ name: "blueConcrete" },
{ name: "brownConcrete" },
{ name: "greenConcrete" },
{ name: "redConcrete" },
{ name: "blackConcrete" },
{
name: "bookshelf",
textures: [ "oakPlanks", "bookshelf" ]
},
{ name: "netherrack" },
{ name: "soulSand" },
{ name: "glowstone" },
{ name: "netherWartBlock" },
{ name: "netherBricks" },
{ name: "redNetherBricks" },
{ name: "netherQuartzOre" },
{
name: "quartzBlock",
textures: ["quartzBlockBottom", "quartzBlockTop", "quartzBlockSide"]
},
{
name: "quartzPillar",
textures: ["quartzPillarTop", "quartzPillar"]
},
{
name: "chiseledQuartzBlock",
textures: ["chiseledQuartzBlock", "chiseledQuartzBlockTop"]
},
{ name: "chiseledStoneBricks" },
{ name: "smoothStone" },
{ name: "andesite" },
{ name: "polishedAndesite" },
{ name: "diorite" },
{ name: "polishedDiorite" },
{ name: "granite" },
{ name: "polishedGranite" },
{ name: "Water",transparent: true,shadow: false,
},
// { // I swear, if y'all don't stop asking about TNT every 5 minutes!
// name: "tnt",
// textures: ["tntBottom", "tntTop", "tntSides"]
// },
]
const BLOCK_COUNT = blockData.length;
//Set defaults on blockData
(function() {
for (let i = 1; i < BLOCK_COUNT; i++) {
let data = blockData[i]
data.id = i
if (!data.textures) {
data.textures = [ data.name, data.name, data.name, data.name, data.name, data.name ]
} else if (typeof data.textures === "string") {
let texture = data.textures
data.textures = [ texture, texture, texture, texture, texture, texture ]
} else if (data.textures.length === 3) {
data.textures[3] = data.textures[2]
data.textures[4] = data.textures[2]
data.textures[5] = data.textures[2]
} else if (data.textures.length === 2) {
// Top and bottom are the first texture, sides are the second.
data.textures[2] = data.textures[1]
data.textures[3] = data.textures[2]
data.textures[4] = data.textures[2]
data.textures[5] = data.textures[2]
data.textures[1] = data.textures[0]
}
data.transparent = data.transparent || false
data.shadow = data.shadow !== undefined ? data.shadow : true
}
})()
let win = window.parent
let doc = document
let console = win.console
let world
let seedHash
let hash = (function() {
let seed = Math.random() * 2100000000 | 0
let PRIME32_2 = 1883677709
let PRIME32_3 = 2034071983
let PRIME32_4 = 668265263
let PRIME32_5 = 374761393
seedHash = function(s) {
seed = s | 0
}
return function(x, y) {
let h32 = 0
h32 = seed + PRIME32_5 | 0
h32 += 8
h32 += Math.imul(x, PRIME32_3)
h32 = Math.imul(h32 << 17 | h32 >> 32 - 17, PRIME32_4)
h32 += Math.imul(y, PRIME32_3)
h32 = Math.imul(h32 << 17 | h32 >> 32 - 17, PRIME32_4)
h32 ^= h32 >> 15
h32 *= PRIME32_2
h32 ^= h32 >> 13
h32 *= PRIME32_3
h32 ^= h32 >> 16
return h32 / 2147483647
}
})()
let worldSeed
//The noise and random functions are copied from the processing.js source code; these others are polyfills made by me to avoid needing to remove all the pjs draw calls
let currentRandom = null
function Marsaglia(i1, i2) {
// from http://www.math.uni-bielefeld.de/~sillke/ALGORITHMS/random/marsaglia-c
let z = (i1 | 0) || 362436069, w = i2 || hash(521288629, z) * 2147483647 | 0
let nextInt = function() {
z=36969*(z&65535)+(z>>>16) & 0xFFFFFFFF
w=18000*(w&65535)+(w>>>16) & 0xFFFFFFFF
return ((z&0xFFFF)<<16 | w&0xFFFF) & 0xFFFFFFFF
}
this.nextDouble = function() {
let i = nextInt() / 4294967296
return i < 0 ? 1 + i : i
}
this.nextInt = nextInt
}
let randomSeed = function(seed) {
currentRandom = (new Marsaglia(seed)).nextDouble
}
let random = function(min, max) {
if (!max) {
if (min) {
max = min
min = 0
} else {
min = 0
max = 1
}
}
return currentRandom() * (max - min) + min
}
let noiseProfile = { generator: undefined, octaves: 4, fallout: 0.5, seed: undefined }
function PerlinNoise(seed) {
let rnd = seed !== undefined ? new Marsaglia(seed) : Marsaglia.createRandomized()
let i, j
// http://www.noisemachine.com/talk1/17b.html
// http://mrl.nyu.edu/~perlin/noise/
// generate permutation
let perm = new Uint8Array(512)
for(i=0;i<256;++i) {
perm[i] = i
}
for(i=0;i<256;++i) {
let t = perm[j = rnd.nextInt() & 0xFF]; perm[j] = perm[i]; perm[i] = t
}
// copy to avoid taking mod in perm[0]
for(i=0;i<256;++i) {
perm[i + 256] = perm[i]
}
function grad3d(i,x,y,z) {
let h = i & 15; // convert into 12 gradient directions
let u = h<8 ? x : y,
v = h<4 ? y : h===12||h===14 ? x : z
return ((h&1) === 0 ? u : -u) + ((h&2) === 0 ? v : -v)
}
function grad2d(i,x,y) {
let v = (i & 1) === 0 ? x : y
return (i&2) === 0 ? -v : v
}
function grad1d(i,x) {
return (i&1) === 0 ? -x : x
}
function lerp(t,a,b) {
return a + t * (b - a)
}
this.noise3d = function(x, y, z) {
let X = Math.floor(x) & 255, Y = Math.floor(y) & 255, Z = Math.floor(z) & 255
x -= Math.floor(x); y -= Math.floor(y); z -= Math.floor(z)
let fx = (3-2*x)*x*x, fy = (3-2*y)*y*y, fz = (3-2*z)*z*z
let p0 = perm[X]+Y, p00 = perm[p0] + Z, p01 = perm[p0 + 1] + Z,
p1 = perm[X + 1] + Y, p10 = perm[p1] + Z, p11 = perm[p1 + 1] + Z
return lerp(fz,
lerp(fy, lerp(fx, grad3d(perm[p00], x, y, z), grad3d(perm[p10], x-1, y, z)),
lerp(fx, grad3d(perm[p01], x, y-1, z), grad3d(perm[p11], x-1, y-1,z))),
lerp(fy, lerp(fx, grad3d(perm[p00 + 1], x, y, z-1), grad3d(perm[p10 + 1], x-1, y, z-1)),
lerp(fx, grad3d(perm[p01 + 1], x, y-1, z-1), grad3d(perm[p11 + 1], x-1, y-1,z-1))))
}
this.noise2d = function(x, y) {
let X = Math.floor(x)&255, Y = Math.floor(y)&255
x -= Math.floor(x); y -= Math.floor(y)
let fx = (3-2*x)*x*x, fy = (3-2*y)*y*y
let p0 = perm[X]+Y, p1 = perm[X + 1] + Y
return lerp(fy,
lerp(fx, grad2d(perm[p0], x, y), grad2d(perm[p1], x-1, y)),
lerp(fx, grad2d(perm[p0 + 1], x, y-1), grad2d(perm[p1 + 1], x-1, y-1)))
}
this.noise1d = function(x) {
let X = Math.floor(x)&255
x -= Math.floor(x)
let fx = (3-2*x)*x*x
return lerp(fx, grad1d(perm[X], x), grad1d(perm[X+1], x-1))
}
}
let noiseSeed = function(seed) {
noiseProfile.seed = seed
noiseProfile.generator = new PerlinNoise(noiseProfile.seed)
}
let noise = function(x, y, z) {
let generator = noiseProfile.generator
let effect = 1, k = 1, sum = 0
for(let i = 0; i < noiseProfile.octaves; ++i) {
effect *= noiseProfile.fallout
switch (arguments.length) {
case 1:
sum += effect * (1 + generator.noise1d(k*x))/2; break
case 2:
sum += effect * (1 + generator.noise2d(k*x, k*y))/2; break
case 3:
sum += effect * (1 + generator.noise3d(k*x, k*y, k*z))/2; break
}
k *= 2
}
return sum
}
let caveNoise
// Copied and modified from https://github.com/blindman67/SimplexNoiseJS
function openSimplexNoise(clientSeed) {
const SQ4 = 2
const toNums = function(s) { return s.split(",").map(function(s) { return new Uint8Array(s.split("").map(function(v) { return Number(v) })) }) }
const decode = function(m, r, s) { return new Int8Array(s.split("").map(function(v) { return parseInt(v, r) + m })) }
const toNumsB32 = function(s) { return s.split(",").map(function(s) { return parseInt(s, 32) }) }
const NORM_3D = 1.0 / 206.0
const SQUISH_3D = 1 / 3
const STRETCH_3D = -1 / 6
var base3D = toNums("0000110010101001,2110210120113111,110010101001211021012011")
const gradients3D = decode(-11, 23, "0ff7mf7fmmfffmfffm07f70f77mm7ff0ff7m0f77m77f0mf7fm7ff0077707770m77f07f70")
var lookupPairs3D = function() { return new Uint16Array(toNumsB32("0,2,1,1,2,2,5,1,6,0,7,0,10,2,12,2,41,1,45,1,50,5,51,5,g6,0,g7,0,h2,4,h6,4,k5,3,k7,3,l0,5,l1,5,l2,4,l5,3,l6,4,l7,3,l8,d,l9,d,la,c,ld,e,le,c,lf,e,m8,k,ma,i,p9,l,pd,n,q8,k,q9,l,15e,j,15f,m,16a,i,16e,j,19d,n,19f,m,1a8,f,1a9,h,1aa,f,1ad,h,1ae,g,1af,g,1ag,b,1ah,a,1ai,b,1al,a,1am,9,1an,9,1bg,b,1bi,b,1eh,a,1el,a,1fg,8,1fh,8,1qm,9,1qn,9,1ri,7,1rm,7,1ul,6,1un,6,1vg,8,1vh,8,1vi,7,1vl,6,1vm,7,1vn,6")) }
var p3D = decode(-1, 5, "112011210110211120110121102132212220132122202131222022243214231243124213241324123222113311221213131221123113311112202311112022311112220342223113342223311342223131322023113322023311320223113320223131322203311322203131")
const setOf = function(count) { var a = [],i = 0; while (i < count) { a.push(i++) } return a }
const doFor = function(count, cb) { var i = 0; while (i < count && cb(i++) !== true) {} }
function shuffleSeed(seed,count){
seed = seed * 1664525 + 1013904223 | 0
count -= 1
return count > 0 ? shuffleSeed(seed, count) : seed
}
const types = {
_3D : {
base : base3D,
squish : SQUISH_3D,
dimensions : 3,
pD : p3D,
lookup : lookupPairs3D,
}
}
function createContribution(type, baseSet, index) {
var i = 0
const multiplier = baseSet[index ++]
const c = { next : undefined }
while(i < type.dimensions) {
const axis = ("xyzw")[i]
c[axis + "sb"] = baseSet[index + i]
c["d" + axis] = - baseSet[index + i++] - multiplier * type.squish
}
return c
}
function createLookupPairs(lookupArray, contributions){
var i
const a = lookupArray()
const res = new Map()
for (i = 0; i < a.length; i += 2) { res.set(a[i], contributions[a[i + 1]]); }
return res
}
function createContributionArray(type) {
const conts = []
const d = type.dimensions
const baseStep = d * d
var k, i = 0
while (i < type.pD.length) {
const baseSet = type.base[type.pD[i]]
let previous, current
k = 0
do {
current = createContribution(type, baseSet, k)
if (!previous) { conts[i / baseStep] = current; }
else { previous.next = current; }
previous = current
k += d + 1
} while(k < baseSet.length)
current.next = createContribution(type, type.pD, i + 1)
if (d >= 3) { current.next.next = createContribution(type, type.pD, i + d + 2) }
if (d === 4) { current.next.next.next = createContribution(type, type.pD, i + 11) }
i += baseStep
}
const result = [conts, createLookupPairs(type.lookup, conts)]
type.base = undefined
type.lookup = undefined
return result
}
let temp = createContributionArray(types._3D)
const contributions3D = temp[0], lookup3D = temp[1]
const perm = new Uint8Array(256)
const perm3D = new Uint8Array(256)
const source = new Uint8Array(setOf(256))
var seed = shuffleSeed(clientSeed, 3)
doFor(256, function(i) {
i = 255 - i
seed = shuffleSeed(seed, 1)
var r = (seed + 31) % (i + 1)
r += r < 0 ? i + 1 : 0
perm[i] = source[r]
perm3D[i] = (perm[i] % 24) * 3
source[r] = source[i]
})
base3D = undefined
lookupPairs3D = undefined
p3D = undefined
return function(x, y, z) {
const pD = perm3D
const p = perm
const g = gradients3D
const stretchOffset = (x + y + z) * STRETCH_3D
const xs = x + stretchOffset, ys = y + stretchOffset, zs = z + stretchOffset
const xsb = Math.floor(xs), ysb = Math.floor(ys), zsb = Math.floor(zs)
const squishOffset = (xsb + ysb + zsb) * SQUISH_3D
const dx0 = x - (xsb + squishOffset), dy0 = y - (ysb + squishOffset), dz0 = z - (zsb + squishOffset)
const xins = xs - xsb, yins = ys - ysb, zins = zs - zsb
const inSum = xins + yins + zins
var c = lookup3D.get(
(yins - zins + 1) |
((xins - yins + 1) << 1) |
((xins - zins + 1) << 2) |
(inSum << 3) |
((inSum + zins) << 5) |
((inSum + yins) << 7) |
((inSum + xins) << 9)
)
var i, value = 0
while (c !== undefined) {
const dx = dx0 + c.dx, dy = dy0 + c.dy, dz = dz0 + c.dz
let attn = 2 - dx * dx - dy * dy - dz * dz
if (attn > 0) {
i = pD[(((p[(xsb + c.xsb) & 0xFF] + (ysb + c.ysb)) & 0xFF) + (zsb + c.zsb)) & 0xFF]
attn *= attn
value += attn * attn * (g[i++] * dx + g[i++] * dy + g[i] * dz)
}
c = c.next
}
return value * NORM_3D + 0.5
}
}
let PVector = function(x, y, z) {
this.x = x
this.y = y
this.z = z
this.set = function(x, y, z) {
if (y === undefined) {
this.x = x.x
this.y = x.y
this.z = x.z
} else {
this.x = x
this.y = y
this.z = z
}
}
this.normalize = function() {
let mag = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z)
this.x /= mag
this.y /= mag
this.z /= mag
}
this.add = function(v) {
this.x += v.x
this.y += v.y
this.z += v.z
}
this.mult = function(m) {
this.x *= m
this.y *= m
this.z *= m
}
}
let fill = function(r, g, b) {
if (g === undefined) {
g = r
b = r
}
ctx.fillStyle = "rgb(" + r + ", " + g + ", " + b + ")"
}
let stroke = function(r, g, b) {
if (g === undefined) {
g = r
b = r
}
ctx.strokeStyle = "rgb(" + r + ", " + g + ", " + b + ")"
}
let line = function(x1, y1, x2, y2) {
ctx.moveTo(x1, y1)
ctx.lineTo(x2, y2)
}
function text(txt, x, y, h) {
h = h || 0
let lines = txt.split("\n")
for (let i = 0; i < lines.length; i++) {
ctx.fillText(lines[i], x, y + h * i)
}
}
function textSize(size) {
ctx.font = size + 'px Monospace' // VT323
}
let strokeWeight = function(num) {
ctx.lineWidth = num
}
const ARROW = "arrow"
const HAND = "pointer"
const CROSS = "crosshair"
let cursor = function(type) {
canvas.style.cursor = type
}
randomSeed(Math.random() * 10000000 | 0)
async function createDatabase() {
return await new Promise(async (resolve, reject) => {
let request = window.indexedDB.open("MineKhan", 1)
request.onupgradeneeded = function(event) {
let DB = event.target.result
// Worlds will contain and ID containing the timestamp at which the world was created, a "saved" timestamp,
// and a "data" string that's identical to the copy/paste save string
let store = DB.createObjectStore("worlds", { keyPath: "id" })
store.createIndex("id", "id", { unique: true })
store.createIndex("data", "data", { unique: false })
}
request.onsuccess = function(e) {
resolve(request.result)
}
request.onerror = function(e) {
console.error(e)
reject(e)
}
})
}
async function loadFromDB(id) {
return await new Promise(async (resolve, reject) => {
let db = await createDatabase()
let trans = db.transaction("worlds", "readwrite")
let store = trans.objectStore("worlds")
let req = id ? store.get(id) : store.getAll()
req.onsuccess = function(e) {
resolve(req.result)
db.close()
}
req.onerror = function(e) {
resolve(null)
db.close()
}
})
}
async function saveToDB(id, data) {
return new Promise(async (resolve, reject) => {
let db = await createDatabase()
let trans = db.transaction("worlds", "readwrite")
let store = trans.objectStore("worlds")
let req = store.put({ id: id, data: data })
req.onsuccess = function() {
resolve(req.result)
}
req.onerror = function(e) {
reject(e)
}
})
}
async function deleteFromDB(id) {
return new Promise(async (resolve, reject) => {
let db = await createDatabase()
let trans = db.transaction("worlds", "readwrite")
let store = trans.objectStore("worlds")
let req = store.delete(id)
req.onsuccess = function() {
resolve(req.result)
}
req.onerror = function(e) {
reject(e)
}
})
}
function save() {
saveToDB(world.id, {
id: world.id,
edited: Date.now(),
name: world.name,
version: version,
code: world.getSaveString()
}).then(() => world.edited = Date.now()).catch(e => console.error(e))
}
// Expose these functions to the global scope for debugging
win.saveToDB = saveToDB
win.loadFromDB = loadFromDB
win.createDatabase = createDatabase
win.deleteFromDB = deleteFromDB
//globals
//{
let version = "Alpha 0.7.1 - Modded"
let reach = 5 // Max distance player can place or break blocks
let sky = [0.33, 0.54, 0.72] // 0 to 1 RGB color scale
let superflat = false
let trees = true
let caves = true
let blockIds = {}
blockData.forEach(block => blockIds[block.name] = block.id)
win.blockData = blockData
win.blockIds = blockIds
let currentFov
// Configurable and savable settings
let settings = {
renderDistance: 4,
fov: 70, // Field of view in degrees
mouseSense: 100 // Mouse sensitivity as a percentage of the default
}
let locked = true
let generatedChunks
let mouseX, mouseY, mouseDown
let width = window.innerWidth
let height = window.innerHeight
if (height === 400) alert("Canvas is too small. Click the \"Settings\" button to the left of the \"Vote Up\" button under the editor and change the height to 600.")
let generator = {
height: 80, // Height of the hills
smooth: 0.01, // Smoothness of the terrain
extra: 30, // Extra height added to the world.
caveSize: 0.00, // Redefined right above where it's used
biomeSmooth: 0.007 // smooth of biomes
};
let maxHeight = 255
let blockOutlines = false
let blockFill = true
let updateHUD = true
const CUBE = 0
const SLAB = 0x100 // 9th bit
const STAIR = 0x200 // 10th bit
const FLIP = 0x400 // 11th bit
const NORTH = 0 // 12th and 13th bits for the 4 directions
const SOUTH = 0x800
const EAST = 0x1000
const WEST = 0x1800
const ROTATION = 0x1800 // Mask for the direction bits
let blockMode = CUBE
let tex
let textureMap
let dirtBuffer
let dirtTexture
let textureCoords
let texCoordsBuffers
let mainbg, dirtbg // Background images
let bigArray = win.bigArray || new Float32Array(600000)
win.bigArray = bigArray
// Callback functions for all the screens; will define them further down the page
let drawScreens = {
"main menu": () => {},
"options": () => {},
"play": () => {},
"pause": () => {},
"creation menu": () => {},
"inventory": () => {},
"multiplayer menu": () => {},
"comingsoon menu": () => {},
"loadsave menu": () => {},
"dead": () => {}
}
let html = {
pause: {
enter: [window.message],
exit: [window.savebox, window.saveDirections, window.message]
},
"loadsave menu": {
enter: [window.worlds, window.boxCenterTop, window.quota],
exit: [window.worlds, window.boxCenterTop, window.quota],
onenter: () => {
window.boxCenterTop.placeholder = "Enter Save String (Optional)"
if (navigator && navigator.storage && navigator.storage.estimate) {
navigator.storage.estimate().then(data => {
window.quota.innerText = `${data.usage.toLocaleString()} / ${data.quota.toLocaleString()} bytes (${(100 * data.usage / data.quota).toLocaleString(undefined, { maximumSignificantDigits: 2 })}%) of your quota used`
}).catch(console.error)
}
window.boxCenterTop.onmousedown = e => {
let elem = document.getElementsByClassName("selected")
if (elem && elem[0]) {
elem[0].classList.remove("selected")
}
selectedWorld = 0
Button.draw()
}
},
onexit: () => {
window.boxCenterTop.onmousedown = null
}
},
"creation menu": {
enter: [window.boxCenterTop],
exit: [window.boxCenterTop],
onenter: () => {
window.boxCenterTop.placeholder = "Enter World Name"
window.boxCenterTop.value = ""
}
},
loading: {
onenter: startLoad
},
editworld: {
enter: [window.boxCenterTop],
exit: [window.boxCenterTop],
onenter: () => {
window.boxCenterTop.placeholder = "Enter World Name"
window.boxCenterTop.value = ""
}
}
}
let screen = "main menu"
let previousScreen = screen
function changeScene(newScene) {
if (screen === "options") {
saveToDB("settings", settings).catch(e => console.error(e))
}
if (html[screen] && html[screen].exit) {
for (let element of html[screen].exit) {
element.classList.add("hidden")
}
}
if (html[newScene] && html[newScene].enter) {
for (let element of html[newScene].enter) {
element.classList.remove("hidden")
}
}
if (html[newScene] && html[newScene].onenter) {
html[newScene].onenter()
}
if (html[screen] && html[screen].onexit) {
html[screen].onexit()
}
previousScreen = screen
screen = newScene
mouseDown = false
drawScreens[screen]()
Button.draw()
Slider.draw()
}
let hitBox = {}
let holding = 0
let Key = {}
let modelView = win.modelView || new Float32Array(16)
win.modelView = modelView
let glCache
let worlds, selectedWorld = 0
let freezeFrame = 0
let p
let vec1 = new PVector(), vec2 = new PVector(), vec3 = new PVector()
let move = {
x: 0,
y: 0,
z: 0,
ang: Math.sqrt(0.5),
}
let p2 = {
x: 0,
y: 0,
z: 0,
}
let place
let inventory = {
hotbar: [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ],
main: [],
hotbarSlot: 0,
size: 40 * Math.min(width, height) / 600,
holding: 0,
}
//}
function play() {
canvas.onblur()
p.lastBreak = Date.now()
updateHUD = true
use3d()
gl.clearColor(sky[0], sky[1], sky[2], 1.0)
getPointer()
fill(255, 255, 255)
textSize(10)
changeScene("play")
}
let gl
function getPointer() {
if (canvas.requestPointerLock) {
canvas.requestPointerLock()
}
}
function releasePointer() {
if (doc.exitPointerLock) {
doc.exitPointerLock()
}
}
let Block = {
top: 0x4,
bottom: 0x8,
north: 0x20,
south: 0x10,
east: 0x2,
west: 0x1,
}
let Sides = {
top: 0,
bottom: 1,
north: 2,
south: 3,
east: 4,
west: 5,
}
// GLSL Shader code (written in script tags at the top of the file)
let vertexShaderSrc3D = document.getElementById("blockVertexShader").text
let fragmentShaderSrc3D = document.getElementById("blockFragmentShader").text
let vertexShaderSrc2D = document.getElementById("2dVertexShader").text
let fragmentShaderSrc2D = document.getElementById("2dFragmentShader").text
function createProgramObject(curContext, vetexShaderSource, fragmentShaderSource) {
let vertexShaderObject = curContext.createShader(curContext.VERTEX_SHADER)
curContext.shaderSource(vertexShaderObject, vetexShaderSource)
curContext.compileShader(vertexShaderObject)
if (!curContext.getShaderParameter(vertexShaderObject, curContext.COMPILE_STATUS)) {
throw curContext.getShaderInfoLog(vertexShaderObject)
}
let fragmentShaderObject = curContext.createShader(curContext.FRAGMENT_SHADER)
curContext.shaderSource(fragmentShaderObject, fragmentShaderSource)
curContext.compileShader(fragmentShaderObject)
if (!curContext.getShaderParameter(fragmentShaderObject, curContext.COMPILE_STATUS)) {
throw curContext.getShaderInfoLog(fragmentShaderObject)
}
let programObject = curContext.createProgram()
curContext.attachShader(programObject, vertexShaderObject)
curContext.attachShader(programObject, fragmentShaderObject)
curContext.linkProgram(programObject)
if (!curContext.getProgramParameter(programObject, curContext.LINK_STATUS)) {
throw "Error linking shaders."
}
return programObject
}
let program3D, program2D
function objectify(x, y, z, width, height, textureX, textureY) {
return {
x: x,
y: y,
z: z,
w: width,
h: height,
tx: textureX,
ty: textureY
}
}
let shapes = {
/*
[
[(-x, -z), (+x, -z), (+x, +z), (-x, +z)], // minX = 0, minZ = 2, maxX = 6, maxZ = 8
[(-x, +z), (+x, +z), (+x, -z), (-x, -z)], // minX = 9, minZ = 10, maxX = 3, maxZ = 4
[(+x, +y), (-x, +y), (-x, -y), (+x, -y)], // minX = 6, minY = 7, maxX = 0, maxY = 1
[(-x, +y), (+x, +y), (+x, -y), (-x, -y)], // minX = 9, minY = 10, maxX = 3, maxY = 4
[(+y, -z), (+y, +z), (-y, +z), (-y, -z)], // minY = 10, minZ = 11, maxY = 4, maxZ = 5
[(+y, +z), (+y, -z), (-y, -z), (-y, +z)] // minY = 7, minZ = 8, maxY = 1, maxZ = 2
]
*/
cube: {
verts: [
// x, y, z, width, height, textureX, textureY
// 0, 0, 0 is the corner on the top left of the texture
[objectify( 0, 0, 0, 16, 16, 0, 0)], //bottom
[objectify( 0, 16, 16, 16, 16, 0, 0)], //top
[objectify(16, 16, 16, 16, 16, 0, 0)], //north
[objectify( 0, 16, 0, 16, 16, 0, 0)], //south
[objectify(16, 16, 0, 16, 16, 0, 0)], //east
[objectify( 0, 16, 16, 16, 16, 0, 0)] //west
],
cull: {
top: 3,
bottom: 3,
north: 3,
south: 3,
east: 3,
west: 3
},
texVerts: [],
varients: [],
buffer: null,
size: 6
},
slab: {
verts: [
[objectify( 0, 0, 0, 16, 16, 0, 0)], //bottom
[objectify( 0, 8, 16, 16, 16, 0, 0)], //top
[objectify(16, 8, 16, 16, 8, 0, 0)], //north
[objectify( 0, 8, 0, 16, 8, 0, 0)], //south
[objectify(16, 8, 0, 16, 8, 0, 0)], //east
[objectify( 0, 8, 16, 16, 8, 0, 0)] //west
],
cull: {
top: 0,
bottom: 3,
north: 1,
south: 1,
east: 1,
west: 1
},
texVerts: [],
buffer: null,
size: 6,
varients: [],
flip: true,
rotate: false
},
stair: {
verts: [
[objectify( 0, 0, 0, 16, 16, 0, 0)], //bottom
[objectify( 0, 8, 8, 16, 8, 0, 8), objectify( 0, 16, 16, 16, 8, 0, 0)], //top
[objectify(16, 16, 16, 16, 16, 0, 0)], //north
[objectify( 0, 8, 0, 16, 8, 0, 0), objectify( 0, 16, 8, 16, 8, 0, 0)], //south
[objectify(16, 8, 0, 8, 8, 8, 0), objectify(16, 16, 8, 8, 16, 0, 0)], //east
[objectify( 0, 8, 8, 8, 8, 0, 0), objectify( 0, 16, 16, 8, 16, 8, 0)] //west
],
cull: {
top: 0,
bottom: 3,
north: 3,
south: 0,
east: 0,
west: 0
},
texVerts: [],
buffer: null,
size: 10,
varients: [],
flip: true,
rotate: true
},
}
win.shapes = shapes
function compareArr(arr, out) {
let minX = 1000
let maxX = -1000
let minY = 1000
let maxY = -1000
let minZ = 1000
let maxZ = -1000
let min = Math.min
let max = Math.max
let num = 0
for (let i = 0; i < arr.length; i += 3) {
num = arr[i]
minX = minX > num ? num : minX
maxX = maxX < num ? num : maxX
num = arr[i + 1]
minY = minY > num ? num : minY
maxY = maxY < num ? num : maxY
num = arr[i + 2]
minZ = minZ > num ? num : minZ
maxZ = maxZ < num ? num : maxZ
}
out[0] = minX
out[1] = minY
out[2] = minZ
out[3] = maxX
out[4] = maxY
out[5] = maxZ
return out
}
function initShapes() {
function mapCoords(rect, face) {
let x = rect.x
let y = rect.y
let z = rect.z
let w = rect.w
let h = rect.h
let tx = rect.tx
let ty = rect.ty
let tex = [tx+w,ty, tx,ty, tx,ty+h, tx+w,ty+h]
let pos = null
switch(face) {
case 0: // Bottom
pos = [x,y,z, x+w,y,z, x+w,y,z+h, x,y,z+h]
break
case 1: // Top
pos = [x,y,z, x+w,y,z, x+w,y,z-h, x,y,z-h]
break
case 2: // North
pos = [x,y,z, x-w,y,z, x-w,y-h,z, x,y-h,z]
break
case 3: // South
pos = [x,y,z, x+w,y,z, x+w,y-h,z, x,y-h,z]
break
case 4: // East
pos = [x,y,z, x,y,z+w, x,y-h,z+w, x,y-h,z]
break
case 5: // West
pos = [x,y,z, x,y,z-w, x,y-h,z-w, x,y-h,z]
break
}
pos = pos.map(c => c / 16 - 0.5)
let minmax = compareArr(pos, [])
pos.max = minmax.splice(3, 3)
pos.min = minmax
tex = tex.map(c => c / 16 / 16)
return {
pos: pos,
tex: tex
}
}
// 90 degree clockwise rotation; returns a new shape object
function rotate(shape) {
let verts = shape.verts
let texVerts = shape.texVerts
let cull = shape.cull
let pos = []
tex = []
for (let i = 0; i < verts.length; i++) {
let side = verts[i]
pos[i] = []
tex[i] = []
for (let j = 0; j < side.length; j++) {
let face = side[j]
let c = []
pos[i][j] = c
for (let k = 0; k < face.length; k += 3) {
c[k] = face[k + 2]
c[k + 1] = face[k + 1]
c[k + 2] = -face[k]
}
tex[i][j] = texVerts[i][j].slice() // Copy texture verts exactly
if (i === 0) {
// Bottom
c.push(...c.splice(0, 3))
tex[i][j].push(...tex[i][j].splice(0, 2))
}
if (i === 1) {
// Top
c.unshift(...c.splice(-3, 3))
tex[i][j].unshift(...tex[i][j].splice(-2, 2))
}
let minmax = compareArr(c, [])
c.max = minmax.splice(3, 3)
c.min = minmax
}
}
let temp = tex[2] // North
tex[2] = tex[5] // North = West
tex[5] = tex[3] // West = South
tex[3] = tex[4] // South = East
tex[4] = temp // East = North
temp = pos[2] // North
pos[2] = pos[5] // North = West
pos[5] = pos[3] // West = South
pos[3] = pos[4] // South = East
pos[4] = temp // East = North
let cull2 = {
top: cull.top,
bottom: cull.bottom,
north: cull.west,
west: cull.south,
south: cull.east,
east: cull.north
}
let buffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(pos.flat(2)), gl.STATIC_DRAW)
return {
verts: pos,
texVerts: tex,
cull: cull2,
rotate: true,
flip: shape.flip,
buffer: buffer,
size: shape.size,
varients: shape.varients
}
}
// Reflect over the y plane; returns a new shape object
function flip(shape) {
let verts = shape.verts
let texVerts = shape.texVerts
let cull = shape.cull
let pos = []
tex = []
for (let i = 0; i < verts.length; i++) {
let side = verts[i]
pos[i] = []
tex[i] = []
for (let j = 0; j < side.length; j++) {
let face = side[j].slice().reverse()
let c = []
pos[i][j] = c
for (let k = 0; k < face.length; k += 3) {
c[k] = face[k + 2]
c[k + 1] = -face[k + 1]
c[k + 2] = face[k]
}
let minmax = compareArr(c, [])
c.max = minmax.splice(3, 3)
c.min = minmax
tex[i][j] = texVerts[i][j].slice() // Copy texture verts exactly
}
}
let temp = pos[0] // Bottom
pos[0] = pos[1] // Bottom = Top
pos[1] = temp // Top = Bottom
temp = tex[0] // Bottom
tex[0] = tex[1] // Bottom = Top
tex[1] = temp // Top = Bottom
let cull2 = {
top: cull.bottom,
bottom: cull.top,
north: (cull.north & 1) << 1 | (cull.north & 2) >> 1,
west: (cull.west & 1) << 1 | (cull.west & 2) >> 1,
south: (cull.south & 1) << 1 | (cull.south & 2) >> 1,
east: (cull.east & 1) << 1 | (cull.east & 2) >> 1
}
let buffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(pos.flat(2)), gl.STATIC_DRAW)
return {
verts: pos,
texVerts: tex,
cull: cull2,
rotate: shape.rotate,
flip: shape.flip,
buffer: buffer,
size: shape.size,
varients: shape.varients
}
}
for (let shape in shapes) {
let obj = shapes[shape]
let verts = obj.verts
// Populate the vertex coordinates
for (let i = 0; i < verts.length; i++) {
let side = verts[i]
let texArr = []
obj.texVerts.push(texArr)
for (let j = 0; j < side.length; j++) {
let face = side[j]
let mapped = mapCoords(face, i)
side[j] = mapped.pos
texArr.push(mapped.tex)
}
}
if (obj.rotate) {
let v = obj.varients
let east = rotate(obj)
let south = rotate(east)
let west = rotate(south)
v[0] = obj
v[2] = south
v[4] = east
v[6] = west
}
if (obj.flip) {
let v = obj.varients
v[1] = flip(obj)
if (obj.rotate) {
v[3] = flip(v[2])
v[5] = flip(v[4])
v[7] = flip(v[6])
}
}
obj.buffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, obj.buffer)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verts.flat(2)), gl.STATIC_DRAW)
}
for (let i = 0; i < BLOCK_COUNT; i++) {
let baseBlock = blockData[i]
let slabBlock = Object.create(baseBlock)
let stairBlock = Object.create(baseBlock)
slabBlock.shape = shapes.slab
baseBlock.shape = shapes.cube
stairBlock.shape = shapes.stair
blockData[i | SLAB] = slabBlock
blockData[i | STAIR] = stairBlock
let v = slabBlock.shape.varients
for (let j = 0; j < v.length; j++) {
if (v[j]) {
let block = Object.create(baseBlock)
block.shape = v[j]
blockData[i | SLAB | j << 10] = block
}
}
v = stairBlock.shape.varients
for (let j = 0; j < v.length; j++) {
if (v[j]) {
let block = Object.create(baseBlock)
block.shape = v[j]
blockData[i | STAIR | j << 10] = block
}
}
}
}
let indexOrder;
(function() {
let arr = []
for (let i = 0; i < 100000; i++) {
arr.push(0 + i * 4, 1 + i * 4, 2 + i * 4, 0 + i * 4, 2 + i * 4, 3 + i * 4)
}
indexOrder = new Uint32Array(arr)
})()
let hexagonVerts
let slabIconVerts
let stairIconVerts
let blockIcons
{
let side = Math.sqrt(3) / 2
let s = side
let q = s / 2
hexagonVerts = new Float32Array([
0, 1, 1, side, 0.5, 1, 0, 0, 1, -side, 0.5, 1,
0, 0, 1, side, 0.5, 1, side, -0.5, 1, 0, -1, 1,
-side, 0.5, 1, 0, 0, 1, 0, -1, 1, -side, -0.5, 1,
])
slabIconVerts = new Float32Array([
0, 0.5, 1, side, 0, 1, 0, -0.5, 1, -side, 0, 1,
0, -0.5, 1, side, 0, 1, side, -0.5, 1, 0, -1, 1,
-side, 0, 1, 0, -0.5, 1, 0, -1, 1, -side, -0.5, 1,
])
stairIconVerts = [
-s,0.5,0,0,1, 0,1,1,0,1, q,0.75,1,0.5,1, -q,0.25,0,0.5,1, // top of the top step
-q,-0.25,0,0,1, q,0.25,1,0,1, s,0,1,0.5,1, 0,-0.5,0,0.5,1, // top of the bottom step
-q,0.25,0,0,0.6, q,0.75,1,0,0.6, q,0.25,1,0.5,0.6, -q,-0.25,0,0.5,0.6, // front of the top step
0,-0.5,0,0,0.6, s,0,1,0,0.6, s,-0.5,1,0.5,0.6, 0,-1,0,0.5,0.6, // front of the bottom step
-s,0.5,0,0,0.8, -q,0.25,0.5,0,0.8, -q,-0.75,0.5,1,0.8, -s,-0.5,0,1,0.8, // side of the top step
-q,-0.25,0.5,0.5,0.8, 0,-0.5,1,0.5,0.8, 0,-1,1,1,0.8, -q,-0.75,0.5,1,0.8, // side of the bottom step
]
}
function genIcons() {
blockIcons = [null]
blockIcons.lengths = []
let texOrder = [ 1, 2, 3 ]
let shadows = [ 1, 0.4, 0.7 ]
let scale = 0.16 / height * inventory.size
for (let i = 1; i < BLOCK_COUNT; i++) {
let data = []
let block = blockData[i]
for (let j = 11; j >= 0; j--) {
data.push(-hexagonVerts[j * 3 + 0] * scale)
data.push(hexagonVerts[j * 3 + 1] * scale)
data.push(0.1666666)
data.push(textureCoords[textureMap[block.textures[texOrder[Math.floor(j / 4)]]]][(j * 2 + 0) % 8])
data.push(textureCoords[textureMap[block.textures[texOrder[Math.floor(j / 4)]]]][(j * 2 + 1) % 8])
data.push(shadows[Math.floor(j / 4)])
}
let buffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW)
blockIcons[i] = buffer
blockIcons.lengths[i] = 6 * 3
data = []
for (let j = 11; j >= 0; j--) {
let tex = textureCoords[textureMap[block.textures[texOrder[Math.floor(j / 4)]]]]
data.push(-slabIconVerts[j * 3 + 0] * scale)
data.push(slabIconVerts[j * 3 + 1] * scale)
data.push(0.1666666)
data.push(tex[(j * 2 + 0) % 8])
data.push(tex[(j * 2 + 1) % 8])
data.push(shadows[Math.floor(j / 4)])
}
buffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW)
blockIcons[i | SLAB] = buffer
blockIcons.lengths[i | SLAB] = 6 * 3
data = []
let v = stairIconVerts
for (let j = 23; j >= 0; j--) {
let num = Math.floor(j / 8)
let tex = textureCoords[textureMap[block.textures[texOrder[num]]]]
let tx = tex[0]
let ty = tex[1]
data.push(-v[j * 5 + 0] * scale)
data.push(v[j * 5 + 1] * scale)
data.push(0.1666666)
data.push(tx + v[j * 5 + 2] / 16)
data.push(ty + v[j * 5 + 3] / 16)
data.push(shadows[num])
}
buffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW)
blockIcons[i | STAIR] = buffer
blockIcons.lengths[i | STAIR] = 6 * 6
}
}
function uniformMatrix(cacheId, programObj, vrName, transpose, matrix) {
let vrLocation = glCache[cacheId]
if(vrLocation === undefined) {
vrLocation = gl.getUniformLocation(programObj, vrName)
glCache[cacheId] = vrLocation
}
gl.uniformMatrix4fv(vrLocation, transpose, matrix)
}
function vertexAttribPointer(cacheId, programObj, vrName, size, VBO) {
let vrLocation = glCache[cacheId]
if(vrLocation === undefined) {
vrLocation = gl.getAttribLocation(programObj, vrName)
glCache[cacheId] = vrLocation
}
if (vrLocation !== -1) {
gl.enableVertexAttribArray(vrLocation)
gl.bindBuffer(gl.ARRAY_BUFFER, VBO)
gl.vertexAttribPointer(vrLocation, size, gl.FLOAT, false, 0, 0)
}
}
//Generate buffers for every block face and store them
let sideEdgeBuffers
let indexBuffer
function cross(v1, v2, result) {
let x = v1.x,
y = v1.y,
z = v1.z,
x2 = v2.x,
y2 = v2.y,
z2 = v2.z
result.x = y * z2 - y2 * z
result.y = z * x2 - z2 * x
result.z = x * y2 - x2 * y
}
let matrix = new Float32Array(16); // A temperary matrix that may store random data.
let projection = new Float32Array(16)
let defaultModelView = new Float32Array([ -10,0,0,0,0,10,0,0,0,0,-10,0,0,0,0,1 ])
class Matrix {
constructor(arr) {
this.elements = new Float32Array(arr || 16)
}
translate(x, y, z) {
let a = this.elements
a[3] += a[0] * x + a[1] * y + a[2] * z
a[7] += a[4] * x + a[5] * y + a[6] * z
a[11] += a[8] * x + a[9] * y + a[10] * z
a[15] += a[12] * x + a[13] * y + a[14] * z
}
rotX(angle) {
let elems = this.elements
let c = Math.cos(angle)
let s = Math.sin(angle)
let t = elems[1]
elems[1] = t * c + elems[2] * s
elems[2] = t * -s + elems[2] * c
t = elems[5]
elems[5] = t * c + elems[6] * s
elems[6] = t * -s + elems[6] * c
t = elems[9]
elems[9] = t * c + elems[10] * s
elems[10] = t * -s + elems[10] * c
t = elems[13]
elems[13] = t * c + elems[14] * s
elems[14] = t * -s + elems[14] * c
}
rotY(angle) {
let c = Math.cos(angle)
let s = Math.sin(angle)
let elems = this.elements
let t = elems[0]
elems[0] = t * c + elems[2] * -s
elems[2] = t * s + elems[2] * c
t = elems[4]
elems[4] = t * c + elems[6] * -s
elems[6] = t * s + elems[6] * c
t = elems[8]
elems[8] = t * c + elems[10] * -s
elems[10] = t * s + elems[10] * c
t = elems[12]
elems[12] = t * c + elems[14] * -s
elems[14] = t * s + elems[14] * c
}
transpose() {
let matrix = this.elements
let temp = matrix[4]
matrix[4] = matrix[1]
matrix[1] = temp
temp = matrix[8]
matrix[8] = matrix[2]
matrix[2] = temp
temp = matrix[6]
matrix[6] = matrix[9]
matrix[9] = temp
temp = matrix[3]
matrix[3] = matrix[12]
matrix[12] = temp
temp = matrix[7]
matrix[7] = matrix[13]
matrix[13] = temp
temp = matrix[11]
matrix[11] = matrix[14]
matrix[14] = temp
}
copyArray(from) {
let to = this.elements
for (let i = 0; i < from.length; i++) {
to[i] = from[i]
}
}
copyMatrix(from) {
let to = this.elements
from = from.elements
for (let i = 0; i < from.length; i++) {
to[i] = from[i]
}
}
}
class Plane {
constructor(nx, ny, nz) {
this.set(nx, ny, nz)
}
set(nx, ny, nz) {
// Pre-computed chunk offsets to reduce branching during culling
this.dx = nx > 0 ? 16 : 0
this.dy = ny > 0
this.dz = nz > 0 ? 16 : 0
// Normal vector for the plane
this.nx = nx
this.ny = ny
this.nz = nz
}
}
let defaultTransformation = new Matrix([ -10,0,0,0,0,10,0,0,0,0,-10,0,0,0,0,1 ])
class Camera {
constructor() {
this.x = 0
this.y = 0
this.z = 0
this.rx = 0; // Pitch
this.ry = 0; // Yaw
this.currentFov = 0
this.defaultFov = settings.fov
this.targetFov = settings.fov
this.step = 0
this.lastStep = 0
this.projection = new Float32Array(5)
this.transformation = new Matrix()
this.direction = { x: 1, y: 0, z: 0 }; // Normalized direction vector
this.frustum = [] // The 5 planes of the viewing frustum (there's no far plane)
for (let i = 0; i < 5; i++) {
this.frustum.push(new Plane(1, 0, 0))
}
}
FOV(fov, time) {
if (fov === this.currentFov) return
if (!fov) {
let now = Date.now()
fov = this.currentFov + this.step * (now - this.lastStep)
this.lastStep = now
if (Math.sign(this.targetFov - this.currentFov) !== Math.sign(this.targetFov - fov)) {
fov = this.targetFov
}
}
else if (time) {
this.targetFov = fov
this.step = (fov - this.currentFov) / time
this.lastStep = Date.now()
return
} else {
this.targetFov = fov
}
const tang = Math.tan(fov * Math.PI / 360)
const scale = 1 / tang
const near = 1
const far = 1000000
this.currentFov = fov; // Store the state of the projection matrix
this.nearH = near * tang; // This is needed for frustum culling
this.projection[0] = scale / width * height
this.projection[1] = scale
this.projection[2] = -far / (far - near)
this.projection[3] = -1
this.projection[4] = -far * near / (far - near)
}
transform() {
this.transformation.copyMatrix(defaultTransformation)
this.transformation.rotX(this.rx)
this.transformation.rotY(this.ry)
this.transformation.translate(-this.x, -this.y, -this.z)
}
getMatrix() {
let proj = this.projection
let view = this.transformation.elements
matrix[0] = proj[0] * view[0]
matrix[1] = proj[1] * view[4]
matrix[2] = proj[2] * view[8] + proj[3] * view[12]
matrix[3] = proj[4] * view[8]
matrix[4] = proj[0] * view[1]
matrix[5] = proj[1] * view[5]
matrix[6] = proj[2] * view[9] + proj[3] * view[13]
matrix[7] = proj[4] * view[9]
matrix[8] = proj[0] * view[2]
matrix[9] = proj[1] * view[6]
matrix[10] = proj[2] * view[10] + proj[3] * view[14]
matrix[11] = proj[4] * view[10]
matrix[12] = proj[0] * view[3]
matrix[13] = proj[1] * view[7]
matrix[14] = proj[2] * view[11] + proj[3] * view[15]
matrix[15] = proj[4] * view[11]
return matrix
}
setDirection() {
if (this.targetFov !== this.currentFov) {
this.FOV()
}
this.direction.x = -Math.sin(this.ry) * Math.cos(this.rx)
this.direction.y = Math.sin(this.rx)
this.direction.z = Math.cos(this.ry) * Math.cos(this.rx)
this.computeFrustum()
}
computeFrustum() {
let X = vec1
let dir = this.direction
X.x = dir.z
X.y = 0
X.z = -dir.x
X.normalize()
let Y = vec2
Y.set(dir)
Y.mult(-1)
cross(Y, X, Y)
//Near plane
this.frustum[0].set(dir.x, dir.y, dir.z)
let aux = vec3
aux.set(Y)
aux.mult(this.nearH)
aux.add(dir)
aux.normalize()
cross(aux, X, aux)
this.frustum[1].set(aux.x, aux.y, aux.z)
aux.set(Y)
aux.mult(-this.nearH)
aux.add(dir)
aux.normalize()
cross(X, aux, aux)
this.frustum[2].set(aux.x, aux.y, aux.z)
aux.set(X)
aux.mult(-this.nearH * width / height)
aux.add(dir)
aux.normalize()
cross(aux, Y, aux)
this.frustum[3].set(aux.x, aux.y, aux.z)
aux.set(X)
aux.mult(this.nearH * width / height)
aux.add(dir)
aux.normalize()
cross(Y, aux, aux)
this.frustum[4].set(aux.x, aux.y, aux.z)
}
canSee(x, y, z, maxY) {
x -= 0.5
y -= 0.5
z -= 0.5
maxY += 0.5
let px = 0, py = 0, pz = 0, plane = null
let cx = p.x, cy = p.y, cz = p.z
for (let i = 0; i < 5; i++) {
plane = this.frustum[i]
px = x + plane.dx
py = plane.dy ? maxY : y
pz = z + plane.dz
if ((px - cx) * plane.nx + (py - cy) * plane.ny + (pz - cz) * plane.nz < 0) {
return false
}
}
return true
}
}
function trans(matrix, x, y, z) {
let a = matrix
a[3] += a[0] * x + a[1] * y + a[2] * z
a[7] += a[4] * x + a[5] * y + a[6] * z
a[11] += a[8] * x + a[9] * y + a[10] * z
a[15] += a[12] * x + a[13] * y + a[14] * z
}
function rotX(matrix, angle) {
// This function is basically multiplying 2 4x4 matrices together,
// but 1 of them has a bunch of 0's and 1's in it,
// so I removed all terms that multiplied by 0, and just left off the 1's.
// mat2 = [1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1]
let elems = matrix
let c = Math.cos(angle)
let s = Math.sin(angle)
let t = elems[1]
elems[1] = t * c + elems[2] * s
elems[2] = t * -s + elems[2] * c
t = elems[5]
elems[5] = t * c + elems[6] * s
elems[6] = t * -s + elems[6] * c
t = elems[9]
elems[9] = t * c + elems[10] * s
elems[10] = t * -s + elems[10] * c
t = elems[13]
elems[13] = t * c + elems[14] * s
elems[14] = t * -s + elems[14] * c
}
function rotY(matrix, angle) {
//source = c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1
let c = Math.cos(angle)
let s = Math.sin(angle)
let elems = matrix
let t = elems[0]
elems[0] = t * c + elems[2] * -s
elems[2] = t * s + elems[2] * c
t = elems[4]
elems[4] = t * c + elems[6] * -s
elems[6] = t * s + elems[6] * c
t = elems[8]
elems[8] = t * c + elems[10] * -s
elems[10] = t * s + elems[10] * c
t = elems[12]
elems[12] = t * c + elems[14] * -s
elems[14] = t * s + elems[14] * c
}
function transpose(matrix) {
let temp = matrix[4]
matrix[4] = matrix[1]
matrix[1] = temp
temp = matrix[8]
matrix[8] = matrix[2]
matrix[2] = temp
temp = matrix[6]
matrix[6] = matrix[9]
matrix[9] = temp
temp = matrix[3]
matrix[3] = matrix[12]
matrix[12] = temp
temp = matrix[7]
matrix[7] = matrix[13]
matrix[13] = temp
temp = matrix[11]
matrix[11] = matrix[14]
matrix[14] = temp
}
function matMult() {
//Multiply the projection matrix by the view matrix; this is optimized specifically for these matrices by removing terms that are always 0.
let proj = projection
let view = modelView
matrix[0] = proj[0] * view[0]
matrix[1] = proj[0] * view[1]
matrix[2] = proj[0] * view[2]
matrix[3] = proj[0] * view[3]
matrix[4] = proj[5] * view[4]
matrix[5] = proj[5] * view[5]
matrix[6] = proj[5] * view[6]
matrix[7] = proj[5] * view[7]
matrix[8] = proj[10] * view[8] + proj[11] * view[12]
matrix[9] = proj[10] * view[9] + proj[11] * view[13]
matrix[10] = proj[10] * view[10] + proj[11] * view[14]
matrix[11] = proj[10] * view[11] + proj[11] * view[15]
matrix[12] = proj[14] * view[8]
matrix[13] = proj[14] * view[9]
matrix[14] = proj[14] * view[10]
matrix[15] = proj[14] * view[11]
}
function copyArr(a, b) {
for (let i = 0; i < a.length; i++) {
b[i] = a[i]
}
}
function FOV(fov) {
let tang = Math.tan(fov * 0.5 * Math.PI / 180)
let scale = 1 / tang
let near = 1
let far = 1000000
currentFov = fov
projection[0] = scale / width * height
projection[5] = scale
projection[10] = -far / (far - near)
projection[11] = -1
projection[14] = -far * near / (far - near)
}
function initModelView(camera, x, y, z, rx, ry) {
if (camera) {
camera.transform()
uniformMatrix("view3d", program3D, "uView", false, camera.getMatrix())
} else {
copyArr(defaultModelView, modelView)
rotX(modelView, rx)
rotY(modelView, ry)
trans(modelView, -x, -y, -z)
matMult()
transpose(matrix)
uniformMatrix("view3d", program3D, "uView", false, matrix)
}
}
function timeString(millis) {
if (millis > 300000000000 || !millis) {
return "never"
}
const SECOND = 1000
const MINUTE = SECOND * 60
const HOUR = MINUTE * 60
const DAY = HOUR * 24
const YEAR = DAY * 365
if (millis < MINUTE) {
return "just now"
}
let years = Math.floor(millis / YEAR)
millis -= years * YEAR
let days = Math.floor(millis / DAY)
millis -= days * DAY
let hours = Math.floor(millis / HOUR)
millis -= hours * HOUR
let minutes = Math.floor(millis / MINUTE)
if (years) {
return `${years} year${years > 1 ? "s" : ""} and ${days} day${day !== 1 ? "s" : ""} ago`
}
if (days) {
return `${days} day${days > 1 ? "s" : ""} and ${hours} hour${hours !== 1 ? "s" : ""} ago`
}
if (hours) {
return `${hours} hour${hours > 1 ? "s" : ""} and ${minutes} minute${minutes !== 1 ? "s" : ""} ago`
}
return `${minutes} minute${minutes > 1 ? "s" : ""} ago`
}
function roundBits(number) {
return Math.round(number * 1000000) / 1000000
}
function rayTrace(x, y, z, shape) {
let cf, cd = 1e9; //Closest face and distance
let m; //Absolute distance to intersection point
let ix, iy, iz; //Intersection coords
let minX, miny, minz, maxX, maxY, maxZ, min, max; //Bounds of face coordinates
let east = p.direction.x < 0
let top = p.direction.y < 0
let north = p.direction.z < 0
let verts = shape.verts
let faces = verts[0]
//Top and bottom faces
if (top) {
faces = verts[1]
}
if (p.direction.y) {
for (let face of faces) {
min = face.min
minX = min[0]
minZ = min[2]
max = face.max
maxX = max[0]
maxZ = max[2]
m = (y + face[1] - p.y) / p.direction.y
ix = m * p.direction.x + p.x
iz = m * p.direction.z + p.z
if (m > 0 && m < cd && ix >= x + minX && ix <= x + maxX && iz >= z + minZ && iz <= z + maxZ) {
cd = m; //Ray crosses bottom face
cf = top ? "top" : "bottom"
}
}
}
//West and East faces
if (east) {
faces = verts[4]
} else {
faces = verts[5]
}
if (p.direction.x) {
for (let face of faces) {
min = face.min
minY = min[1]
minZ = min[2]
max = face.max
maxY = max[1]
maxZ = max[2]
m = (x + face[0] - p.x) / p.direction.x
iy = m * p.direction.y + p.y
iz = m * p.direction.z + p.z
if (m > 0 && m < cd && iy >= y + minY && iy <= y + maxY && iz >= z + minZ && iz <= z + maxZ) {
cd = m
cf = east ? "east" : "west"
}
}
}
//South and North faces
if (north) {
faces = verts[2]
} else {
faces = verts[3]
}
if (p.direction.z) {
for (let face of faces) {
min = face.min
minX = min[0]
minY = min[1]
max = face.max
maxX = max[0]
maxY = max[1]
m = (z + face[2] - p.z) / p.direction.z
ix = m * p.direction.x + p.x
iy = m * p.direction.y + p.y
if (m > 0 && m < cd && ix >= x + minX && ix <= x + maxX && iy >= y + minY && iy <= y + maxY) {
cd = m
cf = north ? "north" : "south"
}
}
}
return [ cd, cf ]
}
function runRayTrace(x, y, z) {
let block = world.getBlock(x, y, z)
if (block) {
let shape = blockData[block].shape
let rt = rayTrace(x, y, z, blockData[block].shape)
if (rt[1] && rt[0] < hitBox.closest) {
hitBox.closest = rt[0]
hitBox.face = rt[1]
hitBox.pos = [ x, y, z ]
hitBox.shape = blockData[block].shape
}
}
}
function lookingAt() {
// Checks blocks in front of the player to see which one they're looking at
hitBox.pos = null
hitBox.closest = 1e9
if (p.spectator) {
return
}
let blockState = world.getBlock(p2.x, p2.y, p2.z)
if (blockState) {
hitBox.pos = [ p2.x, p2.y, p2.z ]
hitBox.closest = 0
hitBox.shape = blockData[blockState].shape
return
}
let pd = p.direction
// Target block
let tx = Math.round(pd.x * reach + p.x)
let ty = Math.round(pd.y * reach + p.y)
let tz = Math.round(pd.z * reach + p.z)
let minX = p2.x
let maxX = 0
let minY = p2.y
let maxY = 0
let minZ = p2.z
let maxZ = 0
for (let i = 0; i < reach + 1; i++) {
if (i > reach) {
i = reach
}
maxX = Math.round(p.x + pd.x * i)
maxY = Math.round(p.y + pd.y * i)
maxZ = Math.round(p.z + pd.z * i)
if (maxX === minX && maxY === minY && maxZ === minZ) {
continue
}
if (minX !== maxX) {
if (minY !== maxY) {
if (minZ !== maxZ) {
runRayTrace(maxX, maxY, maxZ)
}
runRayTrace(maxX, maxY, minZ)
}
if (minZ !== maxZ) {
runRayTrace(maxX, minY, maxZ)
}
runRayTrace(maxX, minY, minZ)
}
if (minY !== maxY) {
if (minZ !== maxZ) {
runRayTrace(minX, maxY, maxZ)
}
runRayTrace(minX, maxY, minZ)
}
if (minZ !== maxZ) {
runRayTrace(minX, minY, maxZ)
}
if (hitBox.pos) {
return; //The ray has collided; it can't possibly find a closer collision now
}
minZ = maxZ
minY = maxY
minX = maxX
}
}
let inBox = function(x, y, z, w, h, d) {
let iy = y - h/2 - p.topH
let ih = h + p.bottomH + p.topH
let ix = x - w/2 - p.w
let iw = w + p.w*2
let iz = z - d/2 - p.w
let id = d + p.w*2
return p.x > ix && p.y > iy && p.z > iz && p.x < ix + iw && p.y < iy + ih && p.z < iz + id
}
let onBox = function(x, y, z, w, h, d) {
let iy = roundBits(y - h/2 - p.topH)
let ih = roundBits(h + p.bottomH + p.topH)
let ix = roundBits(x - w/2 - p.w)
let iw = roundBits(w + p.w*2)
let iz = roundBits(z - d/2 - p.w)
let id = roundBits(d + p.w*2)
return p.x > ix && p.y > iy && p.z > iz && p.x < ix + iw && p.y <= iy + ih && p.z < iz + id
}
function collided(x, y, z, vx, vy, vz, block) {
if(p.spectator) {
return false
}
let verts = blockData[block].shape.verts
let px = roundBits(p.x - p.w - x)
let py = roundBits(p.y - p.bottomH - y)
let pz = roundBits(p.z - p.w - z)
let pxx = roundBits(p.x + p.w - x)
let pyy = roundBits(p.y + p.topH - y)
let pzz = roundBits(p.z + p.w - z)
let minX, minY, minZ, maxX, maxY, maxZ, min, max
if(block == blockIds.Water) {
p.jumpSpeed = 0.3;
p.gravityStength = -0.03;
liquid = true
r = 255
return false;
}
else{
liquid = false
p.jumpSpeed = 0.3;
p.gravityStength = -0.031;
}
if (block == !blockIds.Water){
p.jumpSpeed = 0.3;
p.gravityStength = -0.031;
}
//Top and bottom faces
let faces = verts[0]
if (vy <= 0) {
faces = verts[1]
}
if (!vx && !vz) {
for (let face of faces) {
min = face.min
minX = min[0]
minZ = min[2]
max = face.max
maxX = max[0]
maxZ = max[2]
if (face[1] > py && face[1] < pyy && minX < pxx && maxX > px && minZ < pzz && maxZ > pz) {
if (vy <= 0) {
p.onGround = true
p.y = Math.round((face[1] + y + p.bottomH) * 10000) / 10000
return false
} else {
return true
}
}
}
return false
}
//West and East faces
if (vx < 0) {
faces = verts[4]
} else if (vx > 0) {
faces = verts[5]
}
if (vx) {
let col = false
for (let face of faces) {
min = face.min
minZ = min[2]
minY = min[1]
max = face.max
maxZ = max[2]
maxY = max[1]
if (face[0] > px && face[0] < pxx && minY < pyy && maxY > py && minZ < pzz && maxZ > pz) {
if (maxY - py > 0.5) {
p.canStepX = false
}
col = true
}
}
return col
}
//South and North faces
if (vz < 0) {
faces = verts[2]
} else if (vz > 0) {
faces = verts[3]
}
if (vz) {
let col = false
for (let face of faces) {
min = face.min
minX = min[0]
minY = min[1]
max = face.max
maxX = max[0]
maxY = max[1]
if (face[2] > pz && face[2] < pzz && minY < pyy && maxY > py && minX < pxx && maxX > px) {
if (maxY - py > 0.5) {
p.canStepZ = false
}
col = true
}
}
return col
}
}
let contacts = {
array: [],
size: 0,
add: function(x, y, z, block) {
if (this.size === this.array.length) {
this.array.push([ x, y, z, block ])
} else {
this.array[this.size][0] = x
this.array[this.size][1] = y
this.array[this.size][2] = z
this.array[this.size][3] = block
}
this.size++
},
clear: function() {
this.size = 0
},
}
let resolveContactsAndUpdatePosition = function() {
let pminX = p2.x - 1
let pmaxX = p2.x + 1
let pminY = p2.y - 2
let pmaxY = p2.y + 1
let pminZ = p2.z - 1
let pmaxZ = p2.z + 1
let block = null
let vel = p.velocity
for (let x = pminX; x <= pmaxX; x++) {
for (let y = pminY; y <= pmaxY; y++) {
for (let z = pminZ; z <= pmaxZ; z++) {
let block = world.getBlock(x, y, z)
if (block) {
contacts.add(x, y, z, block)
}
}
}
}
let dt = (win.performance.now() - p.lastUpdate) / 33
dt = dt > 2 ? 2 : dt
p.previousX = p.x
p.previousY = p.y
p.previousZ = p.z
//Check collisions in the Y direction
p.onGround = false
p.canStepX = false
p.canStepZ = false
p.y += vel.y * dt
for (let i = 0; i < contacts.size; i++) {
block = contacts.array[i]
if (collided(block[0], block[1], block[2], 0, vel.y, 0, block[3])) {
p.y = p.previousY
vel.y = 0
break
}
}
if (p.y === p.previousY && !p.flying) {
p.canStepX = true
p.canStepZ = true
}
var sneakLock = false, sneakSafe = false
if (p.sneaking) {
for (let i = 0; i < contacts.size; i++) {
block = contacts.array[i]
if (onBox(block[0], block[1], block[2], 1, 1, 1)) {
sneakLock = true
break
}
}
}
//Check collisions in the X direction
p.x += vel.x * dt
for (let i = 0; i < contacts.size; i++) {
block = contacts.array[i]
if (collided(block[0], block[1], block[2], vel.x, 0, 0, block[3])) {
if (p.canStepX && !world.getBlock(block[0], block[1] + 1, block[2]) && !world.getBlock(block[0], block[1] + 2, block[2])) {
continue
}
p.x = p.previousX
vel.x = 0
break
}
if (sneakLock && onBox(block[0], block[1], block[2], 1, 1, 1)) {
sneakSafe = true
}
}
if (sneakLock && !sneakSafe) {
p.x = p.previousX
vel.x = 0
}
sneakSafe = false
//Check collisions in the Z direction
p.z += vel.z * dt
for (let i = 0; i < contacts.size; i++) {
block = contacts.array[i]
if (collided(block[0], block[1], block[2], 0, 0, vel.z, block[3])) {
if (p.canStepZ && !world.getBlock(block[0], block[1] + 1, block[2]) && !world.getBlock(block[0], block[1] + 2, block[2])) {
continue
}
p.z = p.previousZ
vel.z = 0
break
}
if (sneakLock && onBox(block[0], block[1], block[2], 1, 1, 1)) {
sneakSafe = true
}
}
if (sneakLock && !sneakSafe) {
p.z = p.previousZ
vel.z = 0
}
if (!p.flying) {
let drag = p.onGround ? 0.5 : 0.85
p.velocity.z += (p.velocity.z * drag - p.velocity.z) * dt
p.velocity.x += (p.velocity.x * drag - p.velocity.x) * dt
} else {
let drag = 0.9
p.velocity.z += (p.velocity.z * drag - p.velocity.z) * dt
p.velocity.x += (p.velocity.x * drag - p.velocity.x) * dt
p.velocity.y += (p.velocity.y * 0.8 - p.velocity.y) * dt
if (p.onGround && !p.spectator) {
p.flying = false
}
}
p.lastUpdate = win.performance.now()
contacts.clear()
lookingAt()
}
let runGravity = function() {
if (p.flying) {
return
}
let dt = (win.performance.now() - p.lastUpdate) / 33
dt = dt > 2 ? 2 : dt
if(p.onGround) {
if(p.velocity.y < -0.5) {
p.health -= -Math.floor(p.velocity.y*-(p.velocity.y*15));
}
if(Key[" "]) {
p.velocity.y = p.jumpSpeed;
p.onGround = false;
} else {
p.velocity.y = 0;
}
} else {
p.velocity.y += p.gravityStength * dt;
if(p.velocity.y < -p.maxYVelocity) {
p.velocity.y = -p.maxYVelocity;
}
}
if(p.health <= 0) {
setTimeout(() => { changeScene("dead"); }, 200);
}
};
function box2(sides, tex) {
if (blockFill) {
let i = 0
for (let side in Block) {
if (sides & Block[side]) {
vertexAttribPointer("aVertex", program3D, "aVertex", 3, sideEdgeBuffers[Sides[side]])
vertexAttribPointer("aTexture", program3D, "aTexture", 2, texCoordsBuffers[textureMap[tex[i]]])
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_INT, 0)
}
i++
}
}
if (blockOutlines) {
vertexAttribPointer("aVertex", program3D, "aVertex", 3, hitBox.shape.buffer)
vertexAttribPointer("aTexture", program3D, "aTexture", 2, texCoordsBuffers[textureMap.hitbox])
for (let i = 0; i < hitBox.shape.size; i++) {
gl.drawArrays(gl.LINE_LOOP, i * 4, 4)
}
}
}
function block2(x, y, z, t, camera) {
if (camera) {
camera.transformation.translate(x, y, z)
uniformMatrix("view3d", program3D, "uView", false, camera.getMatrix())
} else {
//copyArr(modelView, matrix)
trans(modelView, x, y, z)
matMult()
trans(modelView, -x, -y, -z)
transpose(matrix)
uniformMatrix("view3d", program3D, "uView", false, matrix)
}
box2(0xff, blockData[t].textures)
}
function changeWorldBlock(t) {
let pos = hitBox.pos
if(pos && pos[1] > 0 && pos[1] < maxHeight) {
let shape = t && blockData[t].shape
if (t && shape.rotate) {
let pi = Math.PI / 4
if (p.ry <= pi) {} // North; default
else if (p.ry < 3 * pi) {
t |= WEST
} else if (p.ry < 5 * pi) {
t |= SOUTH
} else if (p.ry < 7 * pi) {
t |= EAST
}
}
if (t && shape.flip && hitBox.face !== "top" && (hitBox.face === "bottom" || (p.direction.y * hitBox.closest + p.y) % 1 < 0.5)) {
t |= FLIP
}
world.setBlock(hitBox.pos[0], hitBox.pos[1], hitBox.pos[2], t)
if (t) {
p.lastPlace = Date.now()
} else {
p.lastBreak = Date.now()
}
}
}
function newWorldBlock() {
if(!hitBox.pos || !holding) {
return
}
let pos = hitBox.pos, x= pos[0], y = pos[1], z = pos[2]
switch(hitBox.face) {
case "top":
y += 1
break
case "bottom":
y -= 1
break
case "south":
z -= 1
break
case "north":
z += 1
break
case "west":
x -= 1
break
case "east":
x += 1
break
}
if (!inBox(x, y, z, 1, 1, 1) && !world.getBlock(x, y, z)) {
pos[0] = x
pos[1] = y
pos[2] = z
changeWorldBlock(holding < 0xff ? (holding | blockMode) : holding)
}
}
// Save the coords for a small sphere used to carve out caves
let sphere;
{
let blocks = []
let radius = 3.5
let radsq = radius * radius
for (let i = -radius; i <= radius; i++) {
for (let j = -radius; j <= radius; j++) {
for (let k = -radius; k <= radius; k++) {
if (i*i + j*j + k*k < radsq) {
blocks.push(i|0, j|0, k|0)
}
}
}
}
sphere = new Int8Array(blocks)
}
function isCave(x, y, z) {
// Generate a 3D rigid multifractal noise shell.
// Then generate another one with different coordinates.
// Overlay them on top of each other, and the overlapping parts should form a cave-like structure.
// This is extremely slow, and requires generating 2 noise values for every single block in the world.
// TODO: replace with a crawler system of some sort, that will never rely on a head position in un-generated chunks.
let smooth = 0.02
let caveSize = 0.0055
let cave1 = Math.abs(0.5 - caveNoise(x * smooth, y * smooth, z * smooth)) < caveSize
let cave2 = Math.abs(0.5 - caveNoise(y * smooth, z * smooth, x * smooth)) < caveSize
return (cave1 && cave2)
}
function carveSphere(x, y, z) {
if (y > 3) {
for (let i = 0; i < sphere.length; i += 3) {
world.setBlock(x + sphere[i], y + sphere[i + 1], z + sphere[i + 2], blockIds.air, true)
}
}
}
let renderedChunks = 0
function getBlock(x, y, z, blocks) {
return blocks[((x >> 4) + 1) * 9 + ((y >> 4) + 1) * 3 + (z >> 4) + 1][((x & 15) << 8) + ((y & 15) << 4) + (z & 15)]
}
/**
* Returns a 1 if the face is exposed and should be drawn, or a 0 if the face is hidden
*
* @param {number} x - The X coordinate of the block that may be covering a face
* @param {number} y - The Y coordinate of the block that may be covering a face
* @param {number} z - The Z coordinate of the block that may be covering a face
* @param {Collection} blocks - Some collection of blocks that can return the block at (x, y, z)
* @param {number} type - The blockstate of the block that's being considered for face culling
* @param {function} func - The function that can be called to return a block from the blocks collection
*/
function hideFace(x, y, z, blocks, type, func, sourceDir, dir) {
let block = func.call(world, x, y, z, blocks)
if (!block) {
return 1
}
let data = blockData[block]
let sourceData = blockData[type]
let sourceRange = 3
let hiderRange = 3
if (func !== getBlock || screen === "loading") {
// getBlock is only used during the optimize phase of worldGen
sourceRange = sourceData.shape.cull[sourceDir]
hiderRange = data.shape.cull[dir]
}
if ((sourceRange & hiderRange) !== sourceRange || sourceRange === 0 || block !== type && data.transparent || data.transparent && data.shadow) {
return 1
}
return 0
}
let getShadows = {
shade: [ 1, 0.85, 0.7, 0.6, 0.3 ],
ret: [],
blocks: [],
top: function(x, y, z, block) { // Actually the bottom... How did these get flipped?
let blocks = this.blocks
let ret = this.ret
blocks[0] = blockData[getBlock(x-1, y-1, z-1, block)].shadow
blocks[1] = blockData[getBlock(x, y-1, z-1, block)].shadow
blocks[2] = blockData[getBlock(x+1, y-1, z-1, block)].shadow
blocks[3] = blockData[getBlock(x-1, y-1, z, block)].shadow
blocks[4] = blockData[getBlock(x, y-1, z, block)].shadow
blocks[5] = blockData[getBlock(x+1, y-1, z, block)].shadow
blocks[6] = blockData[getBlock(x-1, y-1, z+1, block)].shadow
blocks[7] = blockData[getBlock(x, y-1, z+1, block)].shadow
blocks[8] = blockData[getBlock(x+1, y-1, z+1, block)].shadow
ret[0] = this.shade[blocks[0] + blocks[1] + blocks[3] + blocks[4]]*0.75
ret[1] = this.shade[blocks[1] + blocks[2] + blocks[4] + blocks[5]]*0.75
ret[2] = this.shade[blocks[5] + blocks[4] + blocks[8] + blocks[7]]*0.75
ret[3] = this.shade[blocks[4] + blocks[3] + blocks[7] + blocks[6]]*0.75
return ret
},
bottom: function(x, y, z, block) { // Actually the top
let blocks = this.blocks
let ret = this.ret
blocks[0] = blockData[getBlock(x-1, y+1, z-1, block)].shadow
blocks[1] = blockData[getBlock(x, y+1, z-1, block)].shadow
blocks[2] = blockData[getBlock(x+1, y+1, z-1, block)].shadow
blocks[3] = blockData[getBlock(x-1, y+1, z, block)].shadow
blocks[4] = blockData[getBlock(x, y+1, z, block)].shadow
blocks[5] = blockData[getBlock(x+1, y+1, z, block)].shadow
blocks[6] = blockData[getBlock(x-1, y+1, z+1, block)].shadow
blocks[7] = blockData[getBlock(x, y+1, z+1, block)].shadow
blocks[8] = blockData[getBlock(x+1, y+1, z+1, block)].shadow
ret[0] = this.shade[blocks[4] + blocks[3] + blocks[7] + blocks[6]]
ret[1] = this.shade[blocks[5] + blocks[4] + blocks[8] + blocks[7]]
ret[2] = this.shade[blocks[1] + blocks[2] + blocks[4] + blocks[5]]
ret[3] = this.shade[blocks[0] + blocks[1] + blocks[3] + blocks[4]]
return ret
},
north: function(x, y, z, block) {
let blocks = this.blocks
let ret = this.ret
blocks[0] = blockData[getBlock(x-1, y-1, z+1, block)].shadow
blocks[1] = blockData[getBlock(x, y-1, z+1, block)].shadow
blocks[2] = blockData[getBlock(x+1, y-1, z+1, block)].shadow
blocks[3] = blockData[getBlock(x-1, y, z+1, block)].shadow
blocks[4] = blockData[getBlock(x, y, z+1, block)].shadow
blocks[5] = blockData[getBlock(x+1, y, z+1, block)].shadow
blocks[6] = blockData[getBlock(x-1, y+1, z+1, block)].shadow
blocks[7] = blockData[getBlock(x, y+1, z+1, block)].shadow
blocks[8] = blockData[getBlock(x+1, y+1, z+1, block)].shadow
ret[0] = this.shade[blocks[5] + blocks[4] + blocks[8] + blocks[7]]*0.95
ret[1] = this.shade[blocks[4] + blocks[3] + blocks[7] + blocks[6]]*0.95
ret[2] = this.shade[blocks[0] + blocks[1] + blocks[3] + blocks[4]]*0.95
ret[3] = this.shade[blocks[1] + blocks[2] + blocks[4] + blocks[5]]*0.95
return ret
},
south: function(x, y, z, block) {
let blocks = this.blocks
let ret = this.ret
blocks[0] = blockData[getBlock(x-1, y-1, z-1, block)].shadow
blocks[1] = blockData[getBlock(x-1, y, z-1, block)].shadow
blocks[2] = blockData[getBlock(x-1, y+1, z-1, block)].shadow
blocks[3] = blockData[getBlock(x, y-1, z-1, block)].shadow
blocks[4] = blockData[getBlock(x, y, z-1, block)].shadow
blocks[5] = blockData[getBlock(x, y+1, z-1, block)].shadow
blocks[6] = blockData[getBlock(x+1, y-1, z-1, block)].shadow
blocks[7] = blockData[getBlock(x+1, y, z-1, block)].shadow
blocks[8] = blockData[getBlock(x+1, y+1, z-1, block)].shadow
ret[0] = this.shade[blocks[1] + blocks[2] + blocks[4] + blocks[5]]*0.95
ret[1] = this.shade[blocks[5] + blocks[4] + blocks[8] + blocks[7]]*0.95
ret[2] = this.shade[blocks[4] + blocks[3] + blocks[7] + blocks[6]]*0.95
ret[3] = this.shade[blocks[0] + blocks[1] + blocks[3] + blocks[4]]*0.95
return ret
},
east: function(x, y, z, block) {
let blocks = this.blocks
let ret = this.ret
blocks[0] = blockData[getBlock(x+1, y-1, z-1, block)].shadow
blocks[1] = blockData[getBlock(x+1, y, z-1, block)].shadow
blocks[2] = blockData[getBlock(x+1, y+1, z-1, block)].shadow
blocks[3] = blockData[getBlock(x+1, y-1, z, block)].shadow
blocks[4] = blockData[getBlock(x+1, y, z, block)].shadow
blocks[5] = blockData[getBlock(x+1, y+1, z, block)].shadow
blocks[6] = blockData[getBlock(x+1, y-1, z+1, block)].shadow
blocks[7] = blockData[getBlock(x+1, y, z+1, block)].shadow
blocks[8] = blockData[getBlock(x+1, y+1, z+1, block)].shadow
ret[0] = this.shade[blocks[1] + blocks[2] + blocks[4] + blocks[5]]*0.8
ret[1] = this.shade[blocks[5] + blocks[4] + blocks[8] + blocks[7]]*0.8
ret[2] = this.shade[blocks[4] + blocks[3] + blocks[7] + blocks[6]]*0.8
ret[3] = this.shade[blocks[0] + blocks[1] + blocks[3] + blocks[4]]*0.8
return ret
},
west: function(x, y, z, block) {
let blocks = this.blocks
let ret = this.ret
blocks[0] = blockData[getBlock(x-1, y-1, z-1, block)].shadow
blocks[1] = blockData[getBlock(x-1, y, z-1, block)].shadow
blocks[2] = blockData[getBlock(x-1, y+1, z-1, block)].shadow
blocks[3] = blockData[getBlock(x-1, y-1, z, block)].shadow
blocks[4] = blockData[getBlock(x-1, y, z, block)].shadow
blocks[5] = blockData[getBlock(x-1, y+1, z, block)].shadow
blocks[6] = blockData[getBlock(x-1, y-1, z+1, block)].shadow
blocks[7] = blockData[getBlock(x-1, y, z+1, block)].shadow
blocks[8] = blockData[getBlock(x-1, y+1, z+1, block)].shadow
ret[0] = this.shade[blocks[7] + blocks[8] + blocks[4] + blocks[5]]*0.8
ret[1] = this.shade[blocks[5] + blocks[4] + blocks[2] + blocks[1]]*0.8
ret[2] = this.shade[blocks[4] + blocks[3] + blocks[1] + blocks[0]]*0.8
ret[3] = this.shade[blocks[6] + blocks[7] + blocks[3] + blocks[4]]*0.8
return ret
},
}
function interpolateShadows(shadows, x, y) {
let sx = (shadows[1] - shadows[0]) * x + shadows[0]
let sx2 = (shadows[3] - shadows[2]) * x + shadows[2]
return (sx2 - sx) * y + sx
}
class Section {
constructor(x, y, z, size, chunk) {
this.x = x
this.y = y
this.z = z
this.size = size
this.arraySize = size * size * size
this.blocks = new Int32Array(this.arraySize)
this.compressed = new Uint8Array(this.arraySize)
this.renderData = []
this.renderLength = 0
this.faces = 0
this.hasVisibleBlocks = false
this.chunk = chunk
this.edited = false
this.caves = !caves
this.pallete = [0]
this.palleteMap = {"0": 0}
this.palleteSize = 0
}
getBlock(x, y, z) {
let s = this.size
return this.blocks[x * s * s + y * s + z]
}
setBlock(x, y, z, blockId) {
let s = this.size
this.blocks[x * s * s + y * s + z] = blockId
}
deleteBlock(x, y, z) {
let s = this.size
this.blocks[x * s * s + y * s + z] = 0
}
optimize() {
let visible = false
let pos = 0
let xx = this.x
let yy = this.y
let zz = this.z
let blockState = 0
let palleteIndex = 0
let index = 0
let s = this.size
let blocks = this.blocks
this.hasVisibleBlocks = false
this.renderLength = 0
let localBlocks = world.getAdjacentSubchunks(xx, yy, zz)
//Check all the blocks in the subchunk to see if they're visible.
for (let i = 0; i < s; i++) {
for (let j = 0; j < s; j++) {
for (let k = 0; k < s; k++, index++) {
blockState = blocks[index]
if (this.palleteMap[blockState] === undefined) {
this.palleteMap[blockState] = this.pallete.length
palleteIndex = this.pallete.length
this.pallete.push(blockState)
} else {
palleteIndex = this.palleteMap[blockState]
}
visible = blockState && (hideFace(i-1, j, k, localBlocks, blockState, getBlock, "west", "east")
| hideFace(i+1, j, k, localBlocks, blockState, getBlock, "east", "west") << 1
| hideFace(i, j-1, k, localBlocks, blockState, getBlock, "bottom", "top") << 2
| hideFace(i, j+1, k, localBlocks, blockState, getBlock, "top", "bottom") << 3
| hideFace(i, j, k-1, localBlocks, blockState, getBlock, "south", "north") << 4
| hideFace(i, j, k+1, localBlocks, blockState, getBlock, "north", "south") << 5)
if (visible) {
pos = (i | j << 4 | k << 8) << 19
this.renderData[this.renderLength++] = 1 << 31 | pos | visible << 13 | palleteIndex
this.hasVisibleBlocks = true
}
}
}
}
}
updateBlock(x, y, z, world) {
if (!world.meshQueue.includes(this.chunk)) {
world.meshQueue.push(this.chunk)
}
let i = x
let j = y
let k = z
let s = this.size
x += this.x
y += this.y
z += this.z
let blockState = this.blocks[i * s * s + j * s + k]
let visible = blockState && (hideFace(x-1, y, z, 0, blockState, world.getBlock, "west", "east")
| hideFace(x+1, y, z, 0, blockState, world.getBlock, "east", "west") << 1
| hideFace(x, y-1, z, 0, blockState, world.getBlock, "bottom", "top") << 2
| hideFace(x, y+1, z, 0, blockState, world.getBlock, "top", "bottom") << 3
| hideFace(x, y, z-1, 0, blockState, world.getBlock, "south", "north") << 4
| hideFace(x, y, z+1, 0, blockState, world.getBlock, "north", "south") << 5)
let pos = (i | j << 4 | k << 8) << 19
let index = -1
// Find index of current block in this.renderData
for (let i = 0; i < this.renderLength; i++) {
if ((this.renderData[i] & 0x7ff80000) === pos) {
index = i
break
}
}
// Update pallete
if (this.palleteMap[blockState] === undefined) {
this.palleteMap[blockState] = this.pallete.length
this.pallete.push(blockState)
}
if (index < 0 && !visible) {
// Wasn't visible before, isn't visible after.
return
}
if (!visible) {
// Was visible before, isn't visible after.
this.renderData.splice(index, 1)
this.renderLength--
this.hasVisibleBlocks = !!this.renderLength
return
}
if (visible && index < 0) {
// Wasn't visible before, is visible after.
index = this.renderLength++
this.hasVisibleBlocks = true
}
this.renderData[index] = 1 << 31 | pos | visible << 13 | this.palleteMap[blockState]
}
genMesh(barray, index) {
if (!this.renderLength) {
return index
}
let length = this.renderLength
let rData = this.renderData
let x = 0, y = 0, z = 0, loc = 0, data = 0, sides = 0, tex = null, x2 = 0, y2 = 0, z2 = 0, verts = null, texVerts = null, texShapeVerts = null, tx = 0, ty = 0
let wx = this.x, wy = this.y, wz = this.z
let blocks = world.getAdjacentSubchunks(wx, wy, wz)
let block = null
let shadows = null
let blockSides = Object.keys(Block)
let side = ""
let shapeVerts = null
let shapeTexVerts = null
let pallete = this.pallete
let intShad = interpolateShadows
for (let i = 0; i < length; i++) {
data = rData[i]
block = blockData[pallete[data & 0x1fff]]
tex = block.textures
sides = data >> 13 & 0x3f
loc = data >> 19 & 0xfff
x = loc & 15
y = loc >> 4 & 15
z = loc >> 8 & 15
x2 = x + this.x
y2 = y + this.y
z2 = z + this.z
shapeVerts = block.shape.verts
shapeTexVerts = block.shape.texVerts
let texNum = 0
for (let n = 0; n < 6; n++) {
side = blockSides[n]
if (sides & Block[side]) {
shadows = getShadows[side](x, y, z, blocks)
let directionalFaces = shapeVerts[Sides[side]]
// if (directionalFaces.length > 1) {
// let average = (shadows[0] + shadows[1] + shadows[2] + shadows[3]) / 4
// shadows[0] = average
// shadows[1] = average
// shadows[2] = average
// shadows[3] = average
// }
for (let facei = 0; facei < directionalFaces.length; facei++) {
verts = directionalFaces[facei]
texVerts = textureCoords[textureMap[tex[texNum]]]
tx = texVerts[0]
ty = texVerts[1]
texShapeVerts = shapeTexVerts[n][facei]
barray[index] = verts[0] + x2
barray[index+1] = verts[1] + y2
barray[index+2] = verts[2] + z2
barray[index+3] = tx + texShapeVerts[0]
barray[index+4] = ty + texShapeVerts[1]
barray[index+5] = shadows[0]
barray[index+6] = verts[3] + x2
barray[index+7] = verts[4] + y2
barray[index+8] = verts[5] + z2
barray[index+9] = tx + texShapeVerts[2]
barray[index+10] = ty + texShapeVerts[3]
barray[index+11] = shadows[1]
barray[index+12] = verts[6] + x2
barray[index+13] = verts[7] + y2
barray[index+14] = verts[8] + z2
barray[index+15] = tx + texShapeVerts[4]
barray[index+16] = ty + texShapeVerts[5]
barray[index+17] = shadows[2]
barray[index+18] = verts[9] + x2
barray[index+19] = verts[10] + y2
barray[index+20] = verts[11] + z2
barray[index+21] = tx + texShapeVerts[6]
barray[index+22] = ty + texShapeVerts[7]
barray[index+23] = shadows[3]
index += 24
}
}
texNum++
}
}
return index
}
carveCaves() {
let wx = this.x + 16, wz = this.z + 16, wy = this.y + 16
for (let x = this.x, xx = 0; x < wx; x++, xx++) {
for (let z = this.z, zz = 0; z < wz; z++, zz++) {
wy = this.chunk.tops[zz * 16 + xx]
for (let y = this.y; y < wy; y++) {
if (isCave(x, y, z)) {
carveSphere(x, y, z)
}
}
}
}
this.caves = true
}
tick() {
for (let i = 0; i < 3; i++) {
let rnd = Math.random() * this.blocks.length | 0
if ((this.blocks[rnd]) === blockIds.grass) {
// Spread grass
let x = (rnd >> 8) + this.x
let y = (rnd >> 4 & 15) + this.y
let z = (rnd & 15) + this.z
if (!blockData[world.getBlock(x, y + 1, z)].transparent) {
world.setBlock(x, y, z, blockIds.dirt, false)
return
}
let rnd2 = Math.random() * 27 | 0
let x2 = rnd2 % 3 - 1
rnd2 = (rnd2 - x2 - 1) / 3
let y2 = rnd2 % 3 - 1
rnd2 = (rnd2 - y2 - 1) / 3
z += rnd2 - 1
x += x2
y += y2
if (world.getBlock(x, y, z) === blockIds.dirt && world.getBlock(x, y + 1, z) === blockIds.air) {
world.setBlock(x, y, z, blockIds.grass, false)
}
}
}
}
}
let emptySection = new Section(0, 0, 0, 16)
let fullSection = new Section(0, 0, 0, 16)
fullSection.blocks.fill(blockIds.bedrock)
class Chunk {
constructor(x, z) {
this.x = x
this.z = z
this.maxY = 0
this.minY = 255
this.sections = []
this.cleanSections = []
this.tops = new Uint8Array(16 * 16); // Store the heighest block at every (x,z) coordinate
this.optimized = false
this.generated = false; // Terrain
this.populated = superflat; // Trees and ores
this.lazy = false
this.edited = false
this.loaded = false
this.caves = !caves
}
getBlock(x, y, z) {
let s = y >> 4
return this.sections.length > s ? this.sections[s].getBlock(x, y & 15, z) : 0
}
setBlock(x, y, z, blockID, hidden, user) {
if (!this.sections[y >> 4]) {
do {
this.sections.push(new Section(this.x, this.sections.length * 16, this.z, 16, this))
} while (!this.sections[y >> 4])
}
if (user && !this.sections[y >> 4].edited) {
this.cleanSections[y >> 4] = this.sections[y >> 4].blocks.slice()
this.sections[y >> 4].edited = true
this.edited = true
}
this.sections[y >> 4].setBlock(x, y & 15, z, blockID, hidden)
}
optimize() {
for (let i = 0; i < this.sections.length; i++) {
this.sections[i].optimize()
}
if (!world.meshQueue.includes(this)) {
world.meshQueue.push(this)
}
this.optimized = true
}
render() {
if (!this.buffer) {
return
}
if (p.canSee(this.x, this.minY, this.z, this.maxY)) {
renderedChunks++
gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer)
gl.vertexAttribPointer(glCache.aVertex, 3, gl.FLOAT, false, 24, 0)
gl.vertexAttribPointer(glCache.aTexture, 2, gl.FLOAT, false, 24, 12)
gl.vertexAttribPointer(glCache.aShadow, 1, gl.FLOAT, false, 24, 20)
gl.drawElements(gl.TRIANGLES, 6 * this.faces, gl.UNSIGNED_INT, 0)
}
}
updateBlock(x, y, z, world, lazy) {
if (this.buffer) {
this.lazy = lazy
if (this.sections.length > y >> 4) {
this.sections[y >> 4].updateBlock(x, y & 15, z, world)
}
}
}
deleteBlock(x, y, z, user) {
if (!this.sections[y >> 4]) {
return
}
if (user && !this.sections[y >> 4].edited) {
this.cleanSections[y >> 4] = this.sections[y >> 4].blocks.slice()
this.sections[y >> 4].edited = true
this.edited = true
}
this.sections[y >> 4].deleteBlock(x, y & 15, z)
this.minY = y < this.minY ? y : this.minY
this.maxY = y > this.maxY ? y : this.maxY
}
carveCaves() {
for (let i = 0; i < this.sections.length; i++) {
if (!this.sections[i].caves) {
this.sections[i].carveCaves()
if (i + 1 >= this.sections.length) {
this.caves = true
}
return
}
}
}
populate() {
randomSeed(hash(this.x, this.z) * 210000000)
let wx = 0, wz = 0, ground = 0, top = 0, rand = 0, place = false
let biomeSmooth = generator.biomeSmooth;
let biome = 0;
let gen = 0;
let trueX = this.x;
let trueZ = this.z;
let smoothness = generator.smooth
let hilliness = generator.hilliness
var items = [blockIds.jungleLog, blockIds.darkOakLog, blockIds.acaciaLog,blockIds.birchLog]
var randomtree = items[Math.floor(Math.random() * items.length)];
for (let i = 0; i < 16; i++) {
for (let k = 0; k < 16; k++) {
wx = this.x + i
wz = this.z + k
ground = this.tops[k * 16 + i]
gen = superflat ? 4 : Math.round(noise((trueX + i) * smoothness, (trueZ + k) * smoothness) * hilliness) + generator.extra
biome = superflat ? 0 : Math.round(noise((trueX + i) * biomeSmooth, (trueZ + k) * biomeSmooth));
if (trees && random() < 0.005 && this.getBlock(i, ground, k) === blockIds.grass) {
top = ground + Math.floor(4.5 + random(2.5))
rand = Math.floor(random(4096))
let tree = random() < 0.6 ? blockIds.oakLog : ++top && blockIds.birchLog
//Center
for (let j = ground + 1; j <= top; j++) {
this.setBlock(i, j, k, tree)
}
this.setBlock(i, top + 1, k, blockIds.leaves)
this.setBlock(i, ground, k, blockIds.dirt)
//Bottom leaves
for (let x = -2; x <= 2; x++) {
for (let z = -2; z <= 2; z++) {
if (x || z) {
if ((x * z & 7) === 4) {
place = rand & 1
rand >>>= 1
if (place) {
world.spawnBlock(wx + x, top - 2, wz + z, blockIds.leaves)
}
} else {
world.spawnBlock(wx + x, top - 2, wz + z, blockIds.leaves)
}
}
}
}
//2nd layer leaves
for (let x = -2; x <= 2; x++) {
for (let z = -2; z <= 2; z++) {
if (x || z) {
if ((x * z & 7) === 4) {
place = rand & 1
rand >>>= 1
if (place) {
world.spawnBlock(wx + x, top - 1, wz + z, blockIds.leaves)
}
} else {
world.spawnBlock(wx + x, top - 1, wz + z, blockIds.leaves)
}
}
}
}
//3rd layer leaves
for (let x = -1; x <= 1; x++) {
for (let z = -1; z <= 1; z++) {
if (x || z) {
if (x & z) {
place = rand & 1
rand >>>= 1
if (place) {
world.spawnBlock(wx + x, top, wz + z, blockIds.leaves)
}
} else {
world.spawnBlock(wx + x, top, wz + z, blockIds.leaves)
}
}
}
}
//Top leaves
world.spawnBlock(wx + 1, top + 1, wz, blockIds.leaves)
world.spawnBlock(wx, top + 1, wz - 1, blockIds.leaves)
world.spawnBlock(wx, top + 1, wz + 1, blockIds.leaves)
world.spawnBlock(wx - 1, top + 1, wz, blockIds.leaves)
}
if (random() < 0.01 && this.getBlock(i, ground, k) && biome > 0.5 && ground > 60) {
top = ground + Math.floor(2.5 + random(1.5));
rand = Math.floor(random(4096));
let tree = blockIds.cactus;
//Center
for (let j = ground + 1; j <= top; j++) {
this.setBlock(i, j, k, tree);
}
this.setBlock(i, ground, k, blockIds.sand);
} // cactus
// Blocks of each per chunk in Minecraft
// Coal: 185.5
// Iron: 111.5
// Gold: 10.4
// Redstone: 29.1
// Diamond: 3.7
// Lapis: 4.1
ground -= 4;
if (random() < 3.7 / 256) {
let y = random() * 16 | 0 + 1;
y = y < ground ? y : ground;
if (this.getBlock(i, y, k)) {
this.setBlock(i, y < ground ? y : ground, k, blockIds.diamondOre);
}
}
if (random() < 111.5 / 256) {
let y = random() * 64 | 0 + 1;
y = y < ground ? y : ground;
if (this.getBlock(i, y, k)) {
this.setBlock(i, y < ground ? y : ground, k, blockIds.ironOre);
}
}
if (random() < 185.5 / 256) {
let y = random() * ground | 0 + 1;
y = y < ground ? y : ground;
if (this.getBlock(i, y, k)) {
this.setBlock(i, y < ground ? y : ground, k, blockIds.coalOre);
}
}
if (random() < 10.4 / 256) {
let y = random() * 32 | 0 + 1;
y = y < ground ? y : ground;
if (this.getBlock(i, y, k)) {
this.setBlock(i, y < ground ? y : ground, k, blockIds.goldOre);
}
}
if (random() < 29.1 / 256) {
let y = random() * 16 | 0 + 1;
y = y < ground ? y : ground;
if (this.getBlock(i, y, k)) {
this.setBlock(i, y < ground ? y : ground, k, blockIds.redstoneOre);
}
}
if (random() < 4.1 / 256) {
let y = random() * 32 | 0 + 1;
y = y < ground ? y : ground;
if (this.getBlock(i, y, k)) {
this.setBlock(i, y < ground ? y : ground, k, blockIds.lapisOre);
}
}
if(random() < 0.1/300) {
let y = 40;
let cobvsmoss = random() < 0.01 ? blockIds.mossyCobble : blockIds.mossyCobble;
for(let xxx = 3;xxx<10;xxx++) {
for(let zzz = 3;zzz<10;zzz++) {
this.setBlock(xxx, y+3, zzz, cobvsmoss);
this.setBlock(xxx, y-3, zzz, cobvsmoss);
for(let hhh = y-3;hhh<y+3;hhh++) {
this.setBlock(xxx,hhh, 3, cobvsmoss);
this.setBlock(xxx,hhh, 9, cobvsmoss);
this.setBlock(3, hhh,zzz, cobvsmoss);
this.setBlock(9, hhh,zzz, cobvsmoss);
}
}
}
for(let xxx = 4;xxx<9;xxx++) {
for(let zzz = 4;zzz<9;zzz++){
for(let yyy = y-2;yyy<y+3;yyy++){
this.setBlock(xxx,yyy,zzz, blockIds.air, true)
}
}
}
this.setBlock(6, y-2, 6, blockIds.coalBlock);
}
}
}
this.populated = true;
}
genMesh() {
let start = win.performance.now()
let barray = bigArray
let index = 0
for (let i = 0; i < this.sections.length; i++) {
index = this.sections[i].genMesh(barray, index)
}
let arrayDone = win.performance.now()
if (!this.buffer) {
this.buffer = gl.createBuffer()
}
let data = barray.slice(0, index)
let maxY = 0
let minY = 255
let y = 0
for (let i = 1; i < data.length; i += 6) {
y = data[i]
maxY = Math.max(maxY, y)
minY = Math.min(minY, y)
}
this.maxY = maxY
this.minY = minY
this.faces = data.length / 24
gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer)
gl.bufferData(gl.ARRAY_BUFFER, data, gl.DYNAMIC_DRAW)
this.lazy = false
}
tick() {
if (this.edited) {
for (let i = 0; i < this.sections.length; i++) {
if (this.sections[i].edited) {
this.sections[i].tick()
}
}
}
}
load() {
let chunkX = this.x >> 4
let chunkZ = this.z >> 4
let load = null
for (let i = 0; i < world.loadFrom.length; i++) {
load = world.loadFrom[i]
if (load.x === chunkX && load.z === chunkZ) {
let y = load.y * 16
for (let j in load.blocks) {
world.setBlock((j >> 8 & 15) + this.x, (j >> 4 & 15) + y, (j & 15) + this.z, load.blocks[j])
}
world.loadFrom.splice(i--, 1)
}
}
this.loaded = true
}
}
let analytics = {
totalTickTime: 0,
worstFrameTime: 0,
totalRenderTime: 0,
totalFrameTime: 0,
lastUpdate: 0,
frames: 1,
displayedTickTime: "0",
displayedRenderTime: "0",
displayedFrameTime: "0",
displayedwFrameTime: 0,
fps: 0,
}
function chunkDist(c) {
let dx = p.x - c.x
let dz = p.z - c.z
if (dx > 16) {
dx -= 16
} else if (dx > 0) {
dx = 0
}
if (dz > 16) {
dz -= 16
} else if (dz > 0) {
dz = 0
}
return Math.sqrt(dx * dx + dz * dz)
}
function sortChunks(c1, c2) { //Sort the list of chunks based on distance from the player
let dx1 = p.x - c1.x - 8
let dy1 = p.z - c1.z - 8
let dx2 = p.x - c2.x - 8
let dy2 = p.z - c2.z - 8
return dx1 * dx1 + dy1 * dy1 - (dx2 * dx2 + dy2 * dy2)
}
function fillReqs(x, z) {
// Chunks must all be loaded first.
var done = true
for (let i = x - 2; i <= x + 2; i++) {
for (let j = z - 2; j <= z + 2; j++) {
let chunk = world.loaded[(i + world.offsetX) * world.lwidth + j + world.offsetZ]
if (!chunk.generated) {
world.generateQueue.push(chunk)
done = false
}
if (!chunk.populated && i >= x - 1 && i <= x + 1 && j >= z - 1 && j <= z + 1) {
world.populateQueue.push(chunk)
done = false
}
}
}
return done
}
function maxDist(x, z, x2, z2) {
let ax = Math.abs(x2 - x)
let az = Math.abs(z2 - z)
return Math.max(ax, az)
}
function renderFilter(chunk) {
return maxDist(chunk.x >> 4, chunk.z >> 4, p.cx, p.cz) <= settings.renderDistance
}
function debug(message) {
let ellapsed = performance.now() - debug.start
if (ellapsed > 30) {
console.log(message, ellapsed.toFixed(2), "milliseconds")
}
}
let fogDist = 16
class World {
constructor() {
generatedChunks = 0
fogDist = 16
p.y = superflat ? 6 : (Math.round(noise(8 * generator.smooth, 8 * generator.smooth) * generator.height) + 2 + generator.extra)
//Initialize the world's arrays
this.chunks = []
this.loaded = []
this.sortedChunks = []
this.offsetX = 0
this.offsetZ = 0
this.lwidth = 0
this.chunkGenQueue = []
this.populateQueue = []
this.generateQueue = []
this.meshQueue = []
this.loadFrom = []
this.lastChunk = ","
}
genChunk(chunk) {
let x = chunk.x >> 4
let z = chunk.z >> 4
let trueX = chunk.x
let trueZ = chunk.z
if (chunk.generated) {
return false
}
let hide = !loadString
let smoothness = generator.smooth
let hilliness = generator.height
let biomeSmooth = generator.biomeSmooth;
let gen = 0;
let biome = 0;
for (let i = 0; i < 16; i++) {
for (let k = 0; k < 16; k++) {
if(gen<120 && !superflat) {
gen = 100;
chunk.setBlock(i, gen, k, blockIds.air);
}
biome = superflat ? 0 : Math.round(noise((trueX + i) * biomeSmooth, (trueZ + k) * biomeSmooth));
gen = superflat ? 4 : Math.round(noise((trueX + i) * smoothness, (trueZ + k) * smoothness) * hilliness) + generator.extra
chunk.tops[k * 16 + i] = gen
if(biome > 0.25 && !superflat){
chunk.tops[k * 16 + i] = gen;
chunk.setBlock(i, gen, k, blockIds.sand);
chunk.setBlock(i, gen - 1, k, blockIds.sand);
chunk.setBlock(i, gen - 2, k, blockIds.sand);
chunk.setBlock(i, gen - 3, k, blockIds.sand);
if(gen<60 && !superflat) {
gen = 59;
chunk.setBlock(i, gen, k, blockIds.Water);
chunk.setBlock(i, gen - 1, k, blockIds.Water);
chunk.setBlock(i, gen - 2, k, blockIds.gravel);
chunk.setBlock(i, gen - 3, k, blockIds.gravel);
}
if(gen>120){
chunk.setBlock(i, gen, k, blockIds.stone);
}
if(gen>140){
chunk.setBlock(i, gen, k, blockIds.sand);
}
}
if(biome < 0.5){
chunk.tops[k * 16 + i] = gen;
chunk.setBlock(i, gen, k, blockIds.grass);
chunk.setBlock(i, gen - 1, k, blockIds.dirt);
chunk.setBlock(i, gen - 2, k, blockIds.dirt);
chunk.setBlock(i, gen - 3, k, blockIds.dirt);
if(gen<60 && !superflat) {
gen = 59;
chunk.setBlock(i, gen, k, blockIds.Water);
chunk.setBlock(i, gen - 1, k, blockIds.Water);
chunk.setBlock(i, gen - 2, k, blockIds.gravel);
chunk.setBlock(i, gen - 3, k, blockIds.gravel);
if (world.getBlock(i, gen, k) === blockIds.Water && world.getBlock(i, gen + 1, k) === blockIds.air) {
world.setBlock(i, gen, k, blockIds.Water, false)
}
}
}
for (let j = 1; j < gen - 3; j++) {
chunk.setBlock(i, j, k, blockIds.stone)
}
chunk.setBlock(i, 0, k, blockIds.bedrock)
}
}
chunk.generated = true
}
getAdjacentSubchunks(x, y, z) {
let minChunkX = x - 16 >> 4
let maxChunkX = x + 16 >> 4
let minChunkY = y - 16 >> 4
let maxChunkY = y + 16 >> 4
let minChunkZ = z - 16 >> 4
let maxChunkZ = z + 16 >> 4
let section = null
let ret = []
for (x = minChunkX; x <= maxChunkX; x++) {
for (let y = minChunkY; y <= maxChunkY; y++) {
for (z = minChunkZ; z <= maxChunkZ; z++) {
if (y < 0) {
ret.push(fullSection.blocks)
} else if (this.chunks[x] && this.chunks[x][z]) {
section = this.chunks[x][z].sections[y] || emptySection
ret.push(section.blocks)
} else {
ret.push(emptySection.blocks)
}
}
}
}
return ret
}
updateBlock(x, y, z, lazy) {
let chunk = this.chunks[x >> 4] && this.chunks[x >> 4][z >> 4]
if (chunk && chunk.buffer) {
chunk.updateBlock(x & 15, y, z & 15, this, lazy)
}
}
getWorldBlock(x, y, z) {
if (!this.chunks[x >> 4] || !this.chunks[x >> 4][z >> 4]) {
return blockIds.air
}
return this.chunks[x >> 4][z >> 4].getBlock(x & 15, y, z & 15)
}
getBlock(x, y, z) {
let X = (x >> 4) + this.offsetX
let Z = (z >> 4) + this.offsetZ
if (y > maxHeight) {
return blockIds.air
} else if (y < 0) {
return blockIds.bedrock
} else if (X < 0 || X >= this.lwidth || Z < 0 || Z >= this.lwidth) {
return this.getWorldBlock(x, y, z)
}
return this.loaded[X * this.lwidth + Z].getBlock(x & 15, y, z & 15)
}
setBlock(x, y, z, blockID, lazy) {
if (!this.chunks[x >> 4] || !this.chunks[x >> 4][z >> 4]) {
return
}
let chunk = this.chunks[x >> 4][z >> 4]
let xm = x & 15
let zm = z & 15
if (blockID) {
chunk.setBlock(xm, y, zm, blockID, false, !lazy)
} else {
chunk.deleteBlock(xm, y, zm, !lazy)
}
if (lazy) {
return
}
//Update the 6 adjacent blocks and 1 changed block
if (xm && xm !== 15 && zm && zm !== 15) {
chunk.updateBlock(xm - 1, y, zm, this, lazy)
chunk.updateBlock(xm + 1, y, zm, this, lazy)
chunk.updateBlock(xm, y - 1, zm, this, lazy)
chunk.updateBlock(xm, y + 1, zm, this, lazy)
chunk.updateBlock(xm, y, zm - 1, this, lazy)
chunk.updateBlock(xm, y, zm + 1, this, lazy)
}
else {
this.updateBlock(x - 1, y, z, lazy)
this.updateBlock(x + 1, y, z, lazy)
this.updateBlock(x, y - 1, z, lazy)
this.updateBlock(x, y + 1, z, lazy)
this.updateBlock(x, y, z - 1, lazy)
this.updateBlock(x, y, z + 1, lazy)
}
chunk.updateBlock(xm, y, zm, this, lazy)
// Update the corner chunks so shadows in adjacent chunks update correctly
if (xm | zm === 0) { this.updateBlock(x - 1, y, z - 1, lazy); }
if (xm === 15 && zm === 0) { this.updateBlock(x + 1, y, z - 1, lazy); }
if (xm === 0 && zm === 15) { this.updateBlock(x - 1, y, z + 1, lazy); }
if (xm & zm === 15) { this.updateBlock(x + 1, y, z + 1, lazy); }
}
spawnBlock(x, y, z, blockID) {
//Sets a block anywhere without causing block updates around it. Only to be used in world gen.
let chunkX = x >> 4
let chunkZ = z >> 4
if (!this.chunks[chunkX]) {
this.chunks[chunkX] = []
}
let chunk = this.chunks[chunkX][chunkZ]
if (!chunk) {
chunk = new Chunk(chunkX * 16, chunkZ * 16)
this.chunks[chunkX][chunkZ] = chunk
}
if (chunk.buffer) {
//Only used if spawning a block post-gen
this.setBlock(x, y, z, blockID, true)
} else if (!chunk.getBlock(x & 15, y, z & 15)) {
chunk.setBlock(x & 15, y, z & 15, blockID, false)
}
}
tick() {
let tickStart = win.performance.now()
let maxChunkX = (p.x >> 4) + settings.renderDistance
let maxChunkZ = (p.z >> 4) + settings.renderDistance
let chunk = maxChunkX + "," + maxChunkZ
if (chunk !== this.lastChunk) {
this.lastChunk = chunk
this.loadChunks()
this.chunkGenQueue.sort(sortChunks)
}
if (Key.leftMouse && !Key.control && p.lastBreak < Date.now() - 250 && screen === "play") {
changeWorldBlock(0)
}
if ((Key.rightMouse || Key.leftMouse && Key.control) && p.lastPlace < Date.now() - 250) {
newWorldBlock()
}
if (Key.leftMouse && p.autoBreak && !Key.control) {
changeWorldBlock(0)
}
for (let i = 0; i < this.sortedChunks.length; i++) {
this.sortedChunks[i].tick()
}
do {
let doneWork = false
debug.start = performance.now()
if (this.meshQueue.length) {
// Update all chunk meshes.
let len = this.meshQueue.length - 1
do {
this.meshQueue.pop().genMesh()
} while(this.meshQueue.length)
doneWork = true
debug("Meshes")
}
if (this.generateQueue.length && !doneWork) {
let chunk = this.generateQueue.pop()
this.genChunk(chunk)
doneWork = true
}
if (this.populateQueue.length && !doneWork) {
let chunk = this.populateQueue[this.populateQueue.length - 1]
if (!chunk.caves) {
chunk.carveCaves()
debug("Carve caves")
} else if (!chunk.populated) {
chunk.populate()
this.populateQueue.pop()
}
doneWork = true
}
if (this.chunkGenQueue.length && !doneWork) {
let chunk = this.chunkGenQueue[0]
if (!fillReqs(chunk.x >> 4, chunk.z >> 4)) {}
else if (!chunk.loaded) {
chunk.load()
} else if (!chunk.optimized) {
chunk.optimize(this)
debug("Optimize")
} else if (!chunk.buffer) {
chunk.genMesh()
debug("Initial mesh")
} else {
this.chunkGenQueue.shift()
generatedChunks++
}
doneWork = true
}
if (!doneWork) {
break
}
} while(win.performance.now() - tickStart < 5)
}
render() {
initModelView(p)
gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT)
p2.x = Math.round(p.x)
p2.y = Math.round(p.y)
p2.z = Math.round(p.z)
renderedChunks = 0
let dist = (settings.renderDistance) * 16
if (this.chunkGenQueue.length) {
this.chunkGenQueue.sort(sortChunks)
let chunk = this.chunkGenQueue[0]
dist = Math.min(dist, chunkDist(chunk))
}
if (dist !== fogDist) {
if (fogDist < dist - 0.1) fogDist += (dist - fogDist) / 120
else if (fogDist > dist + 0.1) fogDist += (dist - fogDist) / 30
else fogDist = dist
}
gl.uniform3f(glCache.uPos, p.x, p.y, p.z)
gl.uniform1f(glCache.uDist, fogDist)
let c = this.sortedChunks
for (let chunk of c) {
chunk.render()
}
gl.uniform3f(glCache.uPos, 0, 0, 0)
if(hitBox.pos) {
blockOutlines = true
blockFill = false
block2(hitBox.pos[0], hitBox.pos[1], hitBox.pos[2], 0, p)
blockOutlines = false
blockFill = true
}
}
loadChunks() {
let renderDistance = settings.renderDistance + 2
let cx = p.x >> 4
let cz = p.z >> 4
p.cx = cx
p.cz = cz
let minChunkX = cx - renderDistance
let maxChunkX = cx + renderDistance
let minChunkZ = cz - renderDistance
let maxChunkZ = cz + renderDistance
this.offsetX = -minChunkX
this.offsetZ = -minChunkZ
this.lwidth = renderDistance * 2 + 1
this.chunkGenQueue.length = 0
if (this.loaded.length > this.lwidth * this.lwidth) {
this.loaded.length = this.lwidth * this.lwidth
}
let i = 0
for (let x = minChunkX; x <= maxChunkX; x++) {
for (let z = minChunkZ; z <= maxChunkZ; z++) {
let chunk
if (!this.chunks[x]) {
this.chunks[x] = []
}
if (!this.chunks[x][z]) {
chunk = new Chunk(x * 16, z * 16)
if (maxDist(cx, cz, x, z) <= settings.renderDistance) {
this.chunkGenQueue.push(chunk)
}
this.chunks[x][z] = chunk
}
chunk = this.chunks[x][z]
if (!chunk.buffer && !this.chunkGenQueue.includes(chunk) && maxDist(cx, cz, x, z) <= settings.renderDistance) {
this.chunkGenQueue.push(chunk)
}
this.loaded[i++] = chunk
}
}
this.sortedChunks = this.loaded.filter(renderFilter)
this.sortedChunks.sort(sortChunks)
}
getSaveString() {
let edited = []
for (let x in this.chunks) {
for (let z in this.chunks[x]) {
let chunk = this.chunks[x][z]
if (chunk.edited) {
for (let y = 0; y < chunk.sections.length; y++) {
if (chunk.sections[y].edited) {
edited.push([ chunk.sections[y], chunk.cleanSections[y] ])
}
}
}
}
}
let pallete = {}
for (let chunks of edited) {
let changes = false
chunks[0].blocks.forEach((id, i) => {
if (id !== chunks[1][i]) {
pallete[id] = true
changes = true
}
})
if (!changes) {
chunks[0].edited = false
}
}
let blocks = Object.keys(pallete).map(n => Number(n))
pallete = {}
blocks.forEach((block, index) => pallete[block] = index)
let rnd = Math.round
let options = p.flying | superflat << 1 | p.spectator << 2 | caves << 3 | trees << 4
let str = world.name + ";" + worldSeed.toString(36) + ";"
+ rnd(p.x).toString(36) + "," + rnd(p.y).toString(36) + "," + rnd(p.z).toString(36) + ","
+ (p.rx * 100 | 0).toString(36) + "," + (p.ry * 100 | 0).toString(36) + "," + options.toString(36) + ";"
+ version + ";"
+ blocks.map(b => b.toString(36)).join(",") + ";"
for (let i = 0; i < edited.length; i++) {
if (!edited[i][0].edited) {
continue
}
let real = edited[i][0]
let blocks = real.blocks
let original = edited[i][1]
str += (real.x / 16).toString(36) + "," + (real.y / 16).toString(36) + "," + (real.z / 16).toString(36) + ","
for (let j = 0; j < original.length; j++) {
if (blocks[j] !== original[j]) {
str += (pallete[blocks[j]] << 12 | j).toString(36) + ","
}
}
str = str.substr(0, str.length - 1); //Remove trailing comma
str += ";"
}
if (str.match(/;$/)) str = str.substr(0, str.length - 1)
return str
}
loadSave(str) {
let data = str.split(";")
if (!str.includes("Alpha")) {
return this.loadOldSave(str)
}
this.name = data.shift()
worldSeed = parseInt(data.shift(), 36)
seedHash(worldSeed)
caveNoise = openSimplexNoise(worldSeed)
noiseSeed(worldSeed)
let playerData = data.shift().split(",")
p.x = parseInt(playerData[0], 36)
p.y = parseInt(playerData[1], 36)
p.z = parseInt(playerData[2], 36)
p.rx = parseInt(playerData[3], 36) / 100
p.ry = parseInt(playerData[4], 36) / 100
let options = parseInt(playerData[5], 36)
p.flying = options & 1
p.spectator = options >> 2 & 1
superflat = options >> 1 & 1
caves = options >> 3 & 1
trees = options >> 4 & 1
let version = data.shift()
this.version = version
// if (version.split(" ")[1].split(".").join("") < 70) {
// alert("This save code is for an older version. 0.7.0 or later is needed")
// }
let pallete = data.shift().split(",").map(n => parseInt(n, 36))
this.loadFrom = []
for (let i = 0; data.length; i++) {
let blocks = data.shift().split(",")
this.loadFrom.push({
x: parseInt(blocks.shift(), 36),
y: parseInt(blocks.shift(), 36),
z: parseInt(blocks.shift(), 36),
blocks: [],
})
for (let j = 0; j < blocks.length; j++) {
let block = parseInt(blocks[j], 36)
let index = block & 0xffffff
let pid = block >> 12
this.loadFrom[i].blocks[index] = pallete[pid]
}
}
}
loadOldSave(str) {
let data = str.split(";");
worldSeed = parseInt(data.shift(), 36);
this.id = Date.now()
this.name = "Old World " + (Math.random() * 1000 | 0)
seedHash(worldSeed);
caveNoise = openSimplexNoise(worldSeed);
noiseSeed(worldSeed);
let playerData = data.shift().split(",");
p.x = parseInt(playerData[0], 36);
p.y = parseInt(playerData[1], 36);
p.z = parseInt(playerData[2], 36);
p.rx = parseInt(playerData[3], 36) / 100;
p.ry = parseInt(playerData[4], 36) / 100;
let editCount = parseInt(data.shift(), 36);
this.loadFrom = [];
let coords = data.shift().split(",").map(function(n) {
return parseInt(n, 36);
});
for (let j = 0; j < coords.length; j += 3) {
this.loadFrom.push({
x: coords[j],
y: coords[j + 1],
z: coords[j + 2],
blocks: [],
})
}
for (let i = 0; data.length > 0; i++) {
let blocks = data.shift().split(",");
for (let j = 0; j < blocks.length; j++) {
let block = parseInt(blocks[j], 36);
let index = block >> 8;
let id = block & 0x7f | (block & 0x80) << 1;
this.loadFrom[i].blocks[index] = id;
}
}
}
}
let defineWorld = function() {
let tickStart = win.performance.now()
world.tick()
analytics.totalTickTime += win.performance.now() - tickStart
let renderStart = win.performance.now()
world.render()
analytics.totalRenderTime += win.performance.now() - renderStart
}
let controls = function() {
move.x = 0
move.z = 0
let dt = (win.performance.now() - p.lastUpdate) / 33
dt = dt > 2 ? 2 : dt
if(Key.w) move.z += p.speed
if(Key.s) move.z -= p.speed
if(Key.a) move.x += p.speed
if(Key.d) move.x -= p.speed
if (p.flying) {
if(Key[" "]) p.velocity.y += 0.06 * dt
if(Key.shift) p.velocity.y -= 0.06 * dt
}
if(Key.arrowleft) p.ry -= 0.1 * dt
if(Key.arrowright) p.ry += 0.1 * dt
if(Key.arrowup) p.rx += 0.1 * dt
if(Key.arrowdown) p.rx -= 0.1 * dt
if (!p.sprinting && Key.q && !p.sneaking && Key.w) {
p.FOV(settings.fov + 10, 250)
p.sprinting = true
}
if(p.sprinting) {
move.x *= p.sprintSpeed
move.z *= p.sprintSpeed
}
if(p.flying) {
move.x *= p.flySpeed
move.z *= p.flySpeed
}
if (!move.x && !move.z) {
if (p.sprinting) {
p.FOV(settings.fov, 100)
}
p.sprinting = false
} else if(Math.abs(move.x) > 0 && Math.abs(move.z) > 0) {
move.x *= move.ang
move.z *= move.ang
}
//Update the velocity, rather than the position.
let co = Math.cos(p.ry)
let si = Math.sin(p.ry)
let friction = p.onGround ? 1 : 0.3
p.velocity.x += (co * move.x - si * move.z) * friction * dt
p.velocity.z += (si * move.x + co * move.z) * friction * dt
const TAU = Math.PI * 2
const PI1_2 = Math.PI / 2
while(p.ry > TAU) p.ry -= TAU
while(p.ry < 0) p.ry += TAU
if(p.rx > PI1_2) p.rx = PI1_2
if(p.rx < -PI1_2) p.rx = -PI1_2
p.setDirection()
}
// Mouse sensitivity variable, used for the settings buttons and in the "mmoved" function
let mouseS = 300
class Slider {
constructor(x, y, w, h, scenes, label, min, max, settingName, callback) {
this.x = x
this.y = y
this.h = h
this.w = Math.max(w, 350)
this.name = settingName
this.scenes = Array.isArray(scenes) ? scenes : [scenes]
this.label = label
this.min = min
this.max = max
this.sliding = false
this.callback = callback
}
draw() {
if (!this.scenes.includes(screen)) {
return
}
let current = (settings[this.name] - this.min) / (this.max - this.min)
// Outline
ctx.beginPath()
strokeWeight(2)
stroke(0)
fill(85)
ctx.rect(this.x - this.w / 2, this.y - this.h / 2, this.w, this.h)
ctx.stroke()
ctx.fill()
// Slider bar
let value = Math.round(settings[this.name])
ctx.beginPath()
fill(130)
let x = this.x - (this.w - 10) / 2 + (this.w - 10) * current - 5
ctx.fillRect(x, this.y - this.h / 2, 10, this.h)
//Label
fill(255, 255, 255)
textSize(16)
ctx.textAlign = 'center'
text(`${this.label}: ${value}`, this.x, this.y + this.h / 8)
}
click() {
if (!mouseDown || !this.scenes.includes(screen)) {
return false
}
if (mouseX > this.x - this.w / 2 && mouseX < this.x + this.w / 2 && mouseY > this.y - this.h / 2 && mouseY < this.y + this.h / 2) {
let current = (mouseX - this.x + this.w / 2) / this.w
if (current < 0) current = 0
if (current > 1) current = 1
this.sliding = true
settings[this.name] = current * (this.max - this.min) + this.min
this.callback(current * (this.max - this.min) + this.min)
this.draw()
}
}
drag() {
if (!this.sliding || !this.scenes.includes(screen)) {
return false
}
let current = (mouseX - this.x + this.w / 2) / this.w
if (current < 0) current = 0
if (current > 1) current = 1
settings[this.name] = current * (this.max - this.min) + this.min
this.callback(current * (this.max - this.min) + this.min)
}
release() {
this.sliding = false
}
static draw() {
for (let slider of Slider.all) {
slider.draw()
}
}
static click() {
for (let slider of Slider.all) {
slider.click()
}
}
static release() {
for (let slider of Slider.all) {
slider.release()
}
}
static drag() {
if (mouseDown) {
for (let slider of Slider.all) {
slider.drag()
}
}
}
static add(x, y, w, h, scenes, label, min, max, defaut, callback) {
Slider.all.push(new Slider(x, y, w, h, scenes, label, min, max, defaut, callback))
}
}
Slider.all = []
class Button {
constructor(x, y, w, h, labels, scenes, callback, disabled, hoverText) {
this.x = x
this.y = y
this.h = h
this.w = w
this.index = 0
this.disabled = disabled || (() => false)
this.hoverText = !hoverText || typeof hoverText === "string" ? (() => hoverText) : hoverText
this.scenes = Array.isArray(scenes) ? scenes : [scenes]
this.labels = Array.isArray(labels) ? labels : [labels]
this.callback = callback
}
mouseIsOver() {
return mouseX >= this.x - this.w / 2 && mouseX <= this.x + this.w / 2 && mouseY >= this.y - this.h / 2 && mouseY <= this.y + this.h / 2
}
draw() {
if (!this.scenes.includes(screen)) {
return
}
let hovering = this.mouseIsOver()
let disabled = this.disabled()
let hoverText = this.hoverText()
// Outline
ctx.beginPath()
if (hovering && !disabled) {
strokeWeight(7)
stroke(255)
cursor(HAND)
} else {
strokeWeight(3)
stroke(0)
}
if (disabled) {
fill(60)
} else {
fill(120)
}
ctx.rect(this.x - this.w / 2, this.y - this.h / 2, this.w, this.h)
ctx.stroke()
ctx.fill()
//Label
fill(255)
textSize(16)
ctx.textAlign = 'center'
text(this.labels[this.index], this.x, this.y + this.h / 8)
if (hovering && hoverText) {
hoverbox.innerText = hoverText
hoverbox.classList.remove("hidden")
if (mouseY < height / 2) {
hoverbox.style.bottom = ""
hoverbox.style.top = mouseY + 10 + "px"
} else {
hoverbox.style.top = ""
hoverbox.style.bottom = height - mouseY + 10 + "px"
}
if (mouseX < width / 2) {
hoverbox.style.right = ""
hoverbox.style.left = mouseX + 10 + "px"
} else {
hoverbox.style.left = ""
hoverbox.style.right = width - mouseX + 10 + "px"
}
}
}
click() {
if (this.disabled() || !mouseDown || !this.scenes.includes(screen)) {
return false
}
if (this.mouseIsOver()) {
this.index = (this.index + 1) % this.labels.length
this.callback(this.labels[this.index])
return true
}
}
static draw() {
hoverbox.classList.add("hidden")
for (let button of Button.all) {
button.draw()
}
}
static click() {
for (let button of Button.all) {
if (button.click()) {
Button.draw()
break
}
}
}
static add(x, y, w, h, labels, scenes, callback, disabled, hoverText) {
Button.all.push(new Button(x, y, w, h, labels, scenes, callback, disabled, hoverText))
}
}
Button.all = []
var initEverything
function initButtons() {
Button.all = []
Slider.all = []
const nothing = () => false
const always = () => true
// Main menu buttons
Button.add(width / 2, height / 2 - 20, 400, 40, "Singleplayer", "main menu", r => changeScene("loadsave menu"))
Button.add(width / 2, height / 2 + 35, 400, 40, "Multiplayer", "main menu", nothing, always, "Multiplayer is possible at https://willard.fun/minekhan/")
Button.add(width / 2, height / 2 + 90, 400, 40, "Options", "main menu", r => changeScene("options"))
// Creation menu buttons
Button.add(width / 2, 135, 300, 40, ["World Type: Normal", "World Type: Superflat"], "creation menu", r => superflat = r === "World Type: Superflat")
Button.add(width / 2, 185, 300, 40, ["Trees: On", "Trees: Off"], "creation menu", r => trees = r === "Trees: On", function() {
if (superflat) {
this.index = 1
trees = false
}
return superflat
})
Button.add(width / 2, 235, 300, 40, ["Caves: On", "Caves: Off"], "creation menu", r => caves = r === "Caves: On", function() {
if (superflat) {
this.index = 1
caves = false
}
return superflat
})
Button.add(width / 2, 285, 300, 40, "Game Mode: Survival", "creation menu", nothing, always, "Fully Coming Soon\n\nAfter the og maker took too long to make this ( i dont mind, i had a lot of fun making this) we have made our Own!, there is many thing that needs to, like an inventory system, or item drop, or mobs, ETC, but here is a beta!")
Button.add(width / 2, 335, 300, 40, "Difficulty: Peaceful", "creation menu", nothing, always, "Coming soon\n\nWe are working on it, but its lower on the list due to the possible difficulty.")
Button.add(width / 2, height - 90, 300, 40, "Create New World", "creation menu", r => {
world = new World()
world.id = Date.now()
let name = boxCenterTop.value || "World"
let number = ""
while(true) {
let match = false
for (let id in worlds) {
if (worlds[id].name === name + number) {
match = true
break
}
}
if (match) {
number = number ? number + 1 : 1
} else {
name = name + number
break
}
}
world.name = name.replace(/;/g, "\u037e")
win.world = world
world.loadChunks()
world.chunkGenQueue.sort(sortChunks)
changeScene("loading")
})
Button.add(width / 2, height - 40, 300, 40, "Cancel", "creation menu", r => changeScene(previousScreen))
// Loadsave menu buttons
const selected = () => !selectedWorld || !worlds[selectedWorld]
let w4 = Math.min(width / 4 - 10, 220)
let x4 = w4 / 2 + 5
let w2 = Math.min(width / 2 - 10, 450)
let x2 = w2 / 2 + 5
let mid = width / 2
Button.add(mid - 3 * x4, height - 30, w4, 40, "Edit", "loadsave menu", r => changeScene("editworld"), () => (selected() || !worlds[selectedWorld].edited))
Button.add(mid - x4, height - 30, w4, 40, "Delete", "loadsave menu", r => {
if (worlds[selectedWorld] && confirm(`Are you sure you want to deleat this world? ${worlds[selectedWorld].name} will be lost forever! (A long time!)`)) {
deleteFromDB(selectedWorld)
window.worlds.removeChild(document.getElementById(selectedWorld))
delete worlds[selectedWorld]
selectedWorld = 0
}
}, () => (selected() || !worlds[selectedWorld].edited), "Delete the world forever.")
Button.add(mid + x4, height - 30, w4, 40, "Export", "loadsave menu", r => {
boxCenterTop.value = worlds[selectedWorld].code
}, selected, "Export the save code into the text box above for copy/paste.")
Button.add(mid + 3 * x4, height - 30, w4, 40, "Cancel", "loadsave menu", r => changeScene("main menu"))
Button.add(mid - x2, height - 75, w2, 40, "Play Selected World", "loadsave menu", r => {
world = new World()
win.world = world
let code
if (!selectedWorld) {
code = boxCenterTop.value
} else {
let data = worlds[selectedWorld]
if (data) {
code = data.code
world.id = data.id
world.edited = data.edited
}
}
if (code) {
try {
world.loadSave(code)
world.id = world.id || Date.now()
}
catch(e) {
alert("Unable to load save")
return
}
changeScene("loading")
}
}, () => !(!selectedWorld && boxCenterTop.value) && !worlds[selectedWorld])
Button.add(mid + x2, height - 75, w2, 40, "Create New World", "loadsave menu", r => changeScene("creation menu"))
Button.add(mid, height / 2, w2, 40, "Save", "editworld", r => {
let w = worlds[selectedWorld]
w.name = boxCenterTop.value.replace(/;/g, "\u037e")
let split = w.code.split(";")
split[0] = w.name
w.code = split.join(";")
saveToDB(w.id, w).then(success => {
initWorldsMenu()
changeScene("loadsave menu")
}).catch(e => console.error(e))
})
Button.add(mid, height / 2 + 50, w2, 40, "Back", "editworld", r => changeScene(previousScreen))
// Pause buttons
Button.add(width / 2, 225, 300, 40, "Resume", "pause", play)
Button.add(width / 2, 275, 300, 40, "Options", "pause", r => changeScene("options"))
Button.add(width / 2, 325, 300, 40, "Save", "pause", save, nothing, () => `Save the world to your computer/browser. Doesn't work in incognito.\n\nLast saved ${timeString(Date.now() - world.edited)}.`)
Button.add(width / 2, 375, 300, 40, "Get Save Code", "pause", r => {
savebox.classList.remove("hidden")
saveDirections.classList.remove("hidden")
savebox.value = world.getSaveString()
})
Button.add(width / 2, 425, 300, 40, "Exit Without Saving", "pause", r => {
savebox.value = world.getSaveString()
initWorldsMenu()
changeScene("main menu")
})
// Options buttons
Button.add(width / 2, 455, width / 3, 40, "Back", "options", r => changeScene(previousScreen))
// Comingsoon menu buttons
Button.add(width / 2, 395, width / 3, 40, "Back", "comingsoon menu", r => changeScene(previousScreen))
Button.add(width / 2 - 220, 195, width / 3, 50, "Respawn", "dead", r => {
changeScene("play");
p.health = 17;
p.x = 8;
p.z = 8;
play();
p.y = superflat ? 6 : (Math.round(noise(8 * generator.smooth, 8 * generator.smooth) * generator.height) + 2 + generator.extra);
});
let settingsState = true;
let mouseSense = 100;
// Multiplayer buttons
Button.add(width / 2, 395, width / 3, 40, "¯\\_(ツ)_/¯", "multiplayer menu", r => changeScene("main menu"))
// Settings Sliders
Slider.add(width/2, 245, width / 3, 40, "options", "Render Distance", 1, 62, "renderDistance", val => settings.renderDistance = Math.round(val))
Slider.add(width/2, 305, width / 3, 40, "options", "FOV", 30, 110, "fov", val => {
p.FOV(val)
if (world) {
p.setDirection()
world.render()
}
})
Slider.add(width/2, 365, width / 3, 40, "options", "Mouse Sensitivity", 30, 400, "mouseSense", val => settings.mouseSense = val)
}
function initTextures() {
let textureSize = 256
let scale = 1 / 16
let texturePixels = new Uint8Array(textureSize * textureSize * 4)
textureMap = {}
textureCoords = []
setPixel = function(textureNum, x, y, r, g, b, a) {
let texX = textureNum & 15
let texY = textureNum >> 4
let offset = (texY * 16 + y) * 1024 + texX * 64 + x * 4
texturePixels[offset] = r
texturePixels[offset + 1] = g
texturePixels[offset + 2] = b
texturePixels[offset + 3] = a !== undefined ? a : 255
}
getPixels = function(str) {
// var w = parseInt(str.substr(0, 2), 36)
// var h = parseInt(str.substr(2, 2), 36)
var colors = []
var pixels = []
var dCount = 0
for (;str[4 + dCount] === "0"; dCount++) {}
var ccount = parseInt(str.substr(4+dCount, dCount+1), 36)
for (var i = 0; i < ccount; i++) {
var num = parseInt(str.substr(5 + 2*dCount + i * 7, 7), 36)
colors.push([ num >>> 24 & 255, num >>> 16 & 255, num >>> 8 & 255, num & 255 ])
}
for (let i = 5 + 2*dCount + ccount * 7; i < str.length; i++) {
let num = parseInt(str[i], 36)
pixels.push(colors[num][0], colors[num][1], colors[num][2], colors[num][3])
}
return pixels
};
{
// Specify the texture coords for each index
const s = scale
for (let i = 0; i < 256; i++) {
let texX = i & 15
let texY = i >> 4
let offsetX = texX * s
let offsetY = texY * s
textureCoords.push(new Float32Array([ offsetX, offsetY, offsetX + s, offsetY, offsetX + s, offsetY + s, offsetX, offsetY + s ]))
}
// Set all of the textures into 1 big tiled texture
let n = 0
for (let i in textures) {
if (typeof textures[i] === "function") {
textures[i](n)
} else if (typeof textures[i] === "string") {
let pix = getPixels(textures[i])
for (let j = 0; j < pix.length; j += 4) {
setPixel(n, j >> 2 & 15, j >> 6, pix[j], pix[j+1], pix[j+2], pix[j+3])
}
}
textureMap[i] = n
n++
}
//Set the hitbox texture to 1 pixel
let arr = new Float32Array(192)
for (let i = 0; i < 192; i += 2) {
arr[i] = textureCoords[textureMap.hitbox][0] + 0.01
arr[i + 1] = textureCoords[textureMap.hitbox][1] + 0.01
}
textureCoords[textureMap.hitbox] = arr
}
// Big texture with everything in it
tex = gl.createTexture()
gl.activeTexture(gl.TEXTURE0)
gl.bindTexture(gl.TEXTURE_2D, tex)
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, textureSize, textureSize, 0, gl.RGBA, gl.UNSIGNED_BYTE, texturePixels)
gl.generateMipmap(gl.TEXTURE_2D)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
gl.uniform1i(glCache.uSampler, 0)
// Dirt texture for the background
let dirtPixels = new Uint8Array(getPixels(textures.dirt))
dirtTexture = gl.createTexture()
gl.activeTexture(gl.TEXTURE1)
gl.bindTexture(gl.TEXTURE_2D, dirtTexture)
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 16, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, dirtPixels)
gl.generateMipmap(gl.TEXTURE_2D)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT)
genIcons()
}
function drawIcon(x, y, id) {
id = id < 0xff ? (id | blockMode) : id
x = x / (3 * height) - 0.1666 * width / height
y = y / (3 * height) - 0.1666
initModelView(null, x, y, 0, 0, 0)
let buffer = blockIcons[id]
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
gl.vertexAttribPointer(glCache.aVertex, 3, gl.FLOAT, false, 24, 0)
gl.vertexAttribPointer(glCache.aTexture, 2, gl.FLOAT, false, 24, 12)
gl.vertexAttribPointer(glCache.aShadow, 1, gl.FLOAT, false, 24, 20)
gl.drawElements(gl.TRIANGLES, blockIcons.lengths[id], gl.UNSIGNED_INT, 0)
}
function hotbar() {
FOV(90)
for(let i = 0; i < inventory.hotbar.length; i ++) {
if(inventory.hotbar[i]) {
let x = width / 2 - inventory.hotbar.length / 2 * inventory.size + (i + 0.5) * inventory.size + 25
let y = height - inventory.size
drawIcon(x, y, inventory.hotbar[i])
}
}
}
function hud() {
if (p.spectator) {
return
}
hotbar()
let s = inventory.size
let x = width / 2 + 0.5
let y = height / 2 + 0.5
// Crosshair
if (!p.spectator) {
ctx.lineWidth = 1
ctx.strokeStyle = "white"
ctx.beginPath()
ctx.moveTo(x - 10, y)
ctx.lineTo(x + 10, y)
ctx.moveTo(x, y - 10)
ctx.lineTo(x, y + 10)
ctx.stroke()
}
//Hotbar
x = width / 2 - 9 / 2 * s + 0.5 + 25
y = height - s * 1.5 + 0.5
ctx.strokeStyle = "black"
ctx.lineWidth = 2
ctx.beginPath()
ctx.moveTo(x, y)
ctx.lineTo(x + s * 9, y)
ctx.moveTo(x, y + s)
ctx.lineTo(x + s * 9, y + s)
for(let i = 0; i <= 9; i++) {
ctx.moveTo(x + i * s, y)
ctx.lineTo(x + i * s, y + s)
}
ctx.stroke()
let heart = document.getElementById('heartimg');
for(let i = 0;i < Math.floor(p.health/2);i++) {
ctx.drawImage(heart, width / 2 - i*20, height / 2 + 280, 15, 15);
}
ctx.strokeStyle = "white"
ctx.lineWidth = 2
ctx.beginPath()
ctx.strokeRect(width / 2 - 9 / 2 * s + inventory.hotbarSlot * s + 25, height - s * 1.5, s, s)
let str = "Average Frame Time: " + analytics.displayedFrameTime + "ms\n"
+ "Worst Frame Time: " + analytics.displayedwFrameTime + "ms\n"
+ "Render Time: " + analytics.displayedRenderTime + "ms\n"
+ "Tick Time: " + analytics.displayedTickTime + "ms\n"
+ "Rendered Chunks: " + renderedChunks.toLocaleString() + " / " + world.loaded.length + "\n"
+ "Generated Chunks: " + generatedChunks.toLocaleString() + "\n"
+ "FPS: " + analytics.fps
if (p.autoBreak) {
text("Super breaker enabled", 5, height - 89, 12);
Text("Health: "+Math.floor(p.health), 5, height - 103, 12);
}
ctx.textAlign = 'right'
text(p2.x + ", " + p2.y + ", " + p2.z, width - 10, 15, 0)
ctx.textAlign = 'left'
text(str, 5, height - 77, 12)
}
function drawInv() {
let x = 0
let y = 0
let s = inventory.size
let s2 = s / 2
let perRow = 13
gl.clearColor(0, 0, 0, 0)
gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT)
ctx.fillStyle = "rgb(127, 127, 127)"
ctx.fillRect(0, 0, canvas.width, canvas.height)
FOV(90)
// Draw the grid
ctx.lineWidth = 1
ctx.strokeStyle = "black"
ctx.beginPath()
for (y = 0; y < 10; y++) {
ctx.moveTo(50.5 - s2, 50.5 - s2 + y * s)
ctx.lineTo(50.5 - s2 + s * perRow, 50.5 - s2 + y * s)
}
y--
for (x = 0; x < perRow + 1; x++) {
ctx.moveTo(50.5 - s2 + s * x, 50.5 - s2)
ctx.lineTo(50.5 - s2 + s * x, 50.5 - s2 + y * s)
}
// Hotbar
x = width / 2 - inventory.hotbar.length / 2 * s + 0.5 + 25
y = height - s * 1.5 + 0.5
ctx.moveTo(x, y)
ctx.lineTo(x + s * 9, y)
ctx.moveTo(x, y + s)
ctx.lineTo(x + s * 9, y + s)
for(let i = 0; i <= inventory.hotbar.length; i ++) {
ctx.moveTo(x + i * s, y)
ctx.lineTo(x + i * s, y + s)
}
ctx.stroke()
let overHot = (mouseX - x) / s | 0
if (mouseX < x + 9 * s && mouseX > x && mouseY > y && mouseY < y + s) {
x += s * overHot
ctx.lineWidth = 2
ctx.strokeStyle = "white"
ctx.beginPath()
ctx.strokeRect(x, y, s, s)
}
//Box highlight in inv
let overInv = Math.round((mouseY - 50) / s) * perRow + Math.round((mouseX - 50) / s)
if (overInv >= 0 && overInv < BLOCK_COUNT - 1 && mouseX < 50 - s2 + perRow * s && mouseX > 50 - s2) {
x = overInv % perRow * s + 50 - s2
y = (overInv / perRow | 0) * s + 50 - s2
ctx.lineWidth = 2
ctx.strokeStyle = "white"
ctx.beginPath()
ctx.strokeRect(x, y, s, s)
}
if (inventory.holding) {
drawIcon(mouseX, mouseY, inventory.holding)
}
for (let i = 1; i < BLOCK_COUNT; i++) {
x = (i - 1) % perRow * s + 50
y = ((i - 1) / perRow | 0) * s + 50
drawIcon(x, y, i)
}
hotbar()
//hud()
ctx.drawImage(gl.canvas, 0, 0)
}
function clickInv() {
let s = inventory.size
let s2 = s / 2
let perRow = 13
let over = Math.round((mouseY - 50) / s) * perRow + Math.round((mouseX - 50) / s)
let x = width / 2 - 9 / 2 * s + 25
let y = height - s * 1.5
let overHot = (mouseX - x) / s | 0
if (mouseX < x + 9 * s && mouseX > x && mouseY > y && mouseY < y + s) {
let temp = inventory.hotbar[overHot]
inventory.hotbar[overHot] = inventory.holding
inventory.holding = temp
} else if (over >= 0 && over < BLOCK_COUNT - 1 && mouseX < 50 - s2 + perRow * s && mouseX > 50 - s2) {
inventory.holding = over + 1
} else {
inventory.holding = 0
}
drawScreens.inventory()
}
let unpauseDelay = 0
function mmoved(e) {
let mouseS = settings.mouseSense / 30000
p.rx -= e.movementY * mouseS
p.ry += e.movementX * mouseS
while(p.ry > Math.PI*2) {
p.ry -= Math.PI*2
}
while(p.ry < 0) {
p.ry += Math.PI*2
}
if(p.rx > Math.PI / 2) {
p.rx = Math.PI / 2
}
if(p.rx < -Math.PI / 2) {
p.rx = -Math.PI / 2
}
}
function trackMouse(e) {
cursor("")
mouseX = e.x
mouseY = e.y
drawScreens[screen]()
Button.draw()
Slider.draw()
Slider.drag()
}
document.onmousemove = trackMouse
document.onpointerlockchange = function() {
if (doc.pointerLockElement === canvas) {
doc.onmousemove = mmoved
} else {
doc.onmousemove = trackMouse
if (screen === "play" && !freezeFrame) {
changeScene("pause")
unpauseDelay = Date.now() + 1000
}
}
for (let key in Key) {
Key[key] = false
}
}
canvas.onmousedown = function(e) {
mouseX = e.x
mouseY = e.y
mouseDown = true
let block, index
switch(e.button) {
case 0:
Key.leftMouse = true
break
case 1:
Key.middleMouse = true
if (!hitBox.pos) break
updateHUD = true
block = world.getBlock(hitBox.pos[0], hitBox.pos[1], hitBox.pos[2]) & 0x3ff
index = inventory.hotbar.indexOf(block)
if (index >= 0) {
inventory.hotbarSlot = index
} else {
inventory.hotbar[inventory.hotbarSlot] = block
}
break
case 2:
Key.rightMouse = true
break
}
if(screen === "play") {
if (doc.pointerLockElement !== canvas) {
getPointer()
p.lastBreak = Date.now()
} else {
place = false
if(e.button === 0) {
if(Key.control) {
place = true
} else {
changeWorldBlock(0)
}
}
holding = inventory.hotbar[inventory.hotbarSlot]
if(e.button === 2 && holding) {
place = true
}
if(place) {
newWorldBlock()
}
}
}else if(screen === 'dead'){
releasePointer()
freezeFrame = true
} else if (screen === "inventory") {
clickInv()
}
Button.click()
Slider.click()
}
canvas.onmouseup = function(e) {
switch(e.button) {
case 0:
Key.leftMouse = false
break
case 1:
Key.middleMouse = false
break
case 2:
Key.rightMouse = false
break
}
mouseDown = false
Slider.release()
}
canvas.onkeydown = function(e) {
let k = e.key.toLowerCase()
if (k === " ") {
e.preventDefault()
}
if (e.repeat || Key[k]) {
return
}
Key[k] = true
if (k === "t") {
initTextures()
}
if (k === "enter") {
blockMode = blockMode === CUBE ? SLAB : (blockMode === SLAB ? STAIR : CUBE)
updateHUD = true
}
if (screen === "play") {
if(k === "p") {
releasePointer()
changeScene("pause")
}
if(k === "") {
p.autoBreak = !p.autoBreak
updateHUD = true
}
if (k === " " && liquid == true) {
if (Date.now() < p.lastJump) {
p.velocity.y += p.jumpSpeed + 1;
} else {
p.lastJump = Date.now()
}
}
if (k === "z") {
p.FOV(10, 300)
}
if (k === "shift" && !p.flying) {
p.sneaking = true
if (p.sprinting) {
p.FOV(settings.fov, 100)
}
p.sprinting = false
p.speed = 0.03
p.bottomH = 1.32
}
if (k === "") {
p.spectator = !p.spectator
p.flying = true
p.onGround = false
updateHUD = true
}
if (k === "e") {
changeScene("inventory")
releasePointer()
}
if (k === ";") {
releasePointer()
freezeFrame = true
}
if(Number(k)) {
inventory.hotbarSlot = Number(k) - 1
holding = inventory.hotbar[inventory.hotbarSlot]
updateHUD = true
}
} else if (screen === "pause") {
if(k === "p") {
play()
}
} else if (screen === "inventory") {
if (k === "e") {
play()
}
if (k === "enter") {
drawScreens.inventory()
}
}
}
canvas.onkeyup = function(e) {
let k = e.key.toLowerCase()
Key[k] = false
if(k === "escape" && (screen === "pause" || screen === "inventory" || screen === "options" && previousScreen === "pause") && Date.now() > unpauseDelay) {
play()
}
if (screen === "play") {
if (k === "z") {
p.FOV(settings.fov, 300)
}
if (k === "shift" && p.sneaking) {
p.sneaking = false
p.speed = 0.075
p.bottomH = 1.62
// p.y += 0.3
}
}
}
canvas.onblur = function() {
for (let key in Key) {
Key[key] = false
}
mouseDown = false
Slider.release()
}
canvas.oncontextmenu = function(e) {
e.preventDefault()
}
window.onbeforeunload = e => {
if (screen === "play" && Key.control) {
releasePointer()
e.preventDefault()
e.returnValue = "Q is the sprint button; Ctrl + W closes the page."
return true
}
}
canvas.onwheel = e => {
e.preventDefault()
e.stopPropagation()
if (e.deltaY > 0) {
inventory.hotbarSlot++
} else if (e.deltaY < 0) {
inventory.hotbarSlot--
}
if (inventory.hotbarSlot > 8) {
inventory.hotbarSlot = 0
} else if (inventory.hotbarSlot < 0) {
inventory.hotbarSlot = 8
}
updateHUD = true
holding = inventory.hotbar[inventory.hotbarSlot]
}
document.onwheel = e => {} // Shouldn't do anything, but it helps with a Khan Academy bug somewhat
window.onresize = e => {
width = window.innerWidth
height = window.innerHeight
canvas.height = height
canvas.width = width
gl.canvas.height = height
gl.canvas.width = width
gl.viewport(0, 0, width, height)
initButtons()
initBackgrounds()
inventory.size = 40 * Math.min(width, height) / 600
genIcons()
use3d()
p.FOV(p.currentFov + 0.0001)
if (screen === "play") {
play()
} else {
drawScreens[screen]();
Button.draw();
Slider.draw();
}
}
function use2d() {
gl.disableVertexAttribArray(glCache.aTexture)
gl.disableVertexAttribArray(glCache.aShadow)
gl.disableVertexAttribArray(glCache.aVertex)
gl.useProgram(program2D)
gl.enableVertexAttribArray(glCache.aVertex2)
gl.enableVertexAttribArray(glCache.aTexture2)
gl.enableVertexAttribArray(glCache.aShadow2)
}
function use3d() {
gl.disableVertexAttribArray(glCache.aTexture2)
gl.disableVertexAttribArray(glCache.aShadow2)
gl.disableVertexAttribArray(glCache.aVertex2)
gl.useProgram(program3D)
gl.enableVertexAttribArray(glCache.aVertex)
gl.enableVertexAttribArray(glCache.aTexture)
gl.enableVertexAttribArray(glCache.aShadow)
}
let maxLoad = 1
function startLoad() {
// Runs when the loading screen is opened; cache the player's position
p2.x = p.x
p2.y = p.y
p2.z = p.z
maxLoad = world.loadFrom.length + 9
}
function initWebgl() {
if (!win.gl) {
let canv = document.createElement('canvas')
canv.width = ctx.canvas.width
canv.height = ctx.canvas.height
canv.style.position = "absolute"
canv.style.zIndex = -1
canv.style.top = "0px"
canv.style.left = "0px"
gl = canv.getContext("webgl", { preserveDrawingBuffer: true, antialias: false, premultipliedAlpha: false })
let ext = gl.getExtension('OES_element_index_uint')
if (!ext) {
alert("Please use a supported browser, or update your current browser.")
}
gl.viewport(0, 0, canv.width, canv.height)
gl.enable(gl.DEPTH_TEST)
gl.enable(gl.BLEND)
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
win.gl = gl
} else {
gl = win.gl
}
if (!document.body.contains(gl.canvas)) {
document.body.append(gl.canvas)
}
modelView = new Float32Array(16)
glCache = {}
program3D = createProgramObject(gl, vertexShaderSrc3D, fragmentShaderSrc3D)
program2D = createProgramObject(gl, vertexShaderSrc2D, fragmentShaderSrc2D)
gl.useProgram(program2D)
glCache.uSampler2 = gl.getUniformLocation(program2D, "uSampler")
glCache.aTexture2 = gl.getAttribLocation(program2D, "aTexture")
glCache.aVertex2 = gl.getAttribLocation(program2D, "aVertex")
glCache.aShadow2 = gl.getAttribLocation(program2D, "aShadow")
gl.useProgram(program3D)
glCache.uSampler = gl.getUniformLocation(program3D, "uSampler")
glCache.uPos = gl.getUniformLocation(program3D, "uPos")
glCache.uDist = gl.getUniformLocation(program3D, "uDist")
glCache.aShadow = gl.getAttribLocation(program3D, "aShadow")
glCache.aTexture = gl.getAttribLocation(program3D, "aTexture")
glCache.aVertex = gl.getAttribLocation(program3D, "aVertex")
gl.uniform1f(glCache.uDist, 1000)
//Send the block textures to the GPU
initTextures()
initShapes()
// These buffers are only used for drawing the main menu blocks
sideEdgeBuffers = {}
for (let side in shapes.cube.verts) {
let edgeBuffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, edgeBuffer)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(shapes.cube.verts[side][0]), gl.STATIC_DRAW)
sideEdgeBuffers[side] = edgeBuffer
}
texCoordsBuffers = []
for (let t in textureCoords) {
let buff = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, buff)
gl.bufferData(gl.ARRAY_BUFFER, textureCoords[t], gl.STATIC_DRAW)
texCoordsBuffers.push(buff)
}
//Bind the Vertex Array Object (VAO) that will be used to draw everything
indexBuffer = gl.createBuffer()
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer)
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indexOrder, gl.STATIC_DRAW)
//Tell it not to render the insides of blocks
gl.enable(gl.CULL_FACE)
gl.cullFace(gl.BACK)
gl.lineWidth(2)
blockOutlines = false
gl.enable(gl.POLYGON_OFFSET_FILL)
gl.polygonOffset(1, 1)
gl.clearColor(sky[0], sky[1], sky[2], 1.0)
gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT)
}
function initBackgrounds() {
// Home screen background
use3d()
gl.clearColor(sky[0], sky[1], sky[2], 1.0)
gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT)
FOV(100)
const HALF_PI = Math.PI / 2
initModelView(null, 0, 0.5, 0, -HALF_PI / 25, -HALF_PI / 3)
gl.disableVertexAttribArray(glCache.aShadow)
gl.vertexAttrib1f(glCache.aShadow, 1.0)
let blocks = [
[7,4,1,7], [7,4,2,7], [7,4,3,7], [7,4,4,7], [7,5,1,7], [7,5,2,7],
[7,5,3,7], [6,4,0,7], [6,4,1,7], [6,4,2,7], [6,4,3,7], [6,4,4,7],
[6,5,0,7], [6,5,1,7], [6,5,2,7], [6,5,3,7], [6,5,4,7], [6,6,3,7],
[6,6,4,7], [6,7,3,7], [5,0,-1,1], [5,0,0,1], [5,0,1,1], [5,0,2,1],
[5,1,2,29], [5,2,2,29], [5,3,2,29], [5,4,2,29], [5,5,2,29], [5,6,2,29],
[5,4,0,7], [5,4,1,7], [5,4,3,7], [5,4,4,7], [5,5,0,7], [5,5,1,7],
[5,5,3,7], [5,5,4,7], [5,6,1,7], [5,6,3,7], [5,7,1,7], [5,7,2,7],
[5,7,3,7], [4,-1,-1,1], [4,-1,0,1], [4,-1,1,1], [4,-1,2,1], [4,0,3,1],
[4,0,4,1], [4,0,5,1], [4,0,6,1], [4,0,7,1], [4,0,8,1], [4,0,9,1],
[4,0,10,1], [4,4,0,7], [4,4,1,7], [4,4,2,7], [4,4,3,7], [4,4,4,7],
[4,5,0,7], [4,5,1,7], [4,5,2,7], [4,5,3,7], [4,5,4,7], [4,6,1,7],
[4,6,2,7], [4,6,3,7], [4,7,4,7], [3,-1,-1,1], [3,-1,0,1], [3,-1,1,1],
[3,-1,2,1], [3,-1,3,1], [3,-1,4,1], [3,0,5,1], [3,0,6,1], [3,0,7,1],
[3,0,8,1], [3,0,9,1], [3,0,10,1], [3,4,1,7], [3,4,2,7], [3,4,3,7],
[3,4,4,7], [3,5,1,7], [3,5,2,7], [3,5,3,7], [2,-1,-1,1], [2,-1,0,1],
[2,-1,1,1], [2,-1,2,1], [2,-1,3,1], [2,-1,4,1], [2,-1,5,1], [2,-1,6,1],
[2,-1,7,1], [2,0,8,1], [2,0,9,1], [2,0,10,1], [1,-2,-1,1], [1,-2,0,1],
[1,-2,1,1], [1,-2,2,1], [1,-2,3,1], [1,-1,4,1], [1,-1,5,1], [1,-1,6,1],
[1,-1,7,1], [1,-1,8,1], [1,-1,9,1], [1,-1,10,1], [0,-2,-1,1], [0,-2,0,1],
[0,-2,1,1], [0,-2,2,1], [0,-2,3,1], [0,-2,4,1], [0,-2,5,1], [0,-1,6,1],
[0,-1,7,1], [0,-1,8,1], [0,-1,9,1], [0,-1,10,1], [-1,-2,-1,1],
[-1,-2,0,1], [-1,-2,1,1], [-1,-2,2,1], [-1,-2,3,1], [-1,-2,4,1],
[-1,-2,5,1], [-1,-2,6,1], [-1,-2,7,1], [-1,-1,8,1], [-1,-1,9,1],
[-1,-1,10,1], [-2,-2,-1,1], [-2,-2,0,1], [-2,-2,1,1], [-2,-2,2,1],
[-2,-2,3,1], [-2,-2,4,1], [-2,-2,5,1], [-2,-2,6,1], [-2,-2,7,1],
[-2,-2,8,1], [-2,-2,9,1], [-2,-1,10,1], [-3,-2,-1,1], [-3,-2,0,1],
[-3,-2,1,1], [-3,-2,2,1], [-3,-2,3,1], [-3,-2,4,1], [-3,-2,5,1],
[-3,-2,6,1], [-3,-2,7,1], [-3,-2,8,1], [-3,-2,9,1], [-3,-2,10,1],
[-3,-2,11,1], [-3,-2,12,1], [-4,-2,-1,1], [-4,-2,0,1], [-4,-2,1,1],
[-4,-2,2,1], [-4,-2,3,1], [-4,-2,4,1], [-4,-2,5,1], [-4,-2,6,1],
[-4,-2,7,1], [-4,-2,8,1], [-4,-2,9,1], [-4,-2,10,1], [-4,-2,11,1],
[-4,-2,12,1], [-5,-2,-1,1], [-5,-2,0,1], [-5,-2,1,1], [-5,-2,2,1],
[-5,-2,3,1], [-5,-2,4,1], [-5,-2,5,1], [-5,-2,6,1], [-5,-2,7,1], [-5,-2,8,1],
[-5,-2,9,1], [-5,-2,10,1], [-5,-2,11,1], [-5,-2,12,1], [-6,-2,-1,1],
[-6,-2,0,1], [-6,-2,1,1], [-6,-2,2,1], [-6,-2,3,1], [-6,-2,4,1],
[-6,-2,5,1], [-6,-2,6,1], [-6,-2,7,1], [-6,-2,8,1], [-6,-2,9,1],
[-6,-2,10,1], [-6,-2,11,1], [-7,-2,3,1], [-7,-2,4,1], [-7,-2,5,1],
[-7,-2,6,1], [-7,-2,7,1], [-7,-2,8,1], [-7,-2,9,1], [-8,-2,2,1], [-8,-2,3,1],
[-8,-2,4,1], [-8,-2,5,1], [-8,-2,6,1], [-8,-2,7,1], [-8,-2,8,1]
]
for (let i = 0; i < blocks.length; i += 1) {
block2(blocks[i][0], blocks[i][1], blocks[i][2], blocks[i][3])
}
gl.enableVertexAttribArray(glCache.aShadow)
let pixels = new Uint8Array(width * height * 4)
gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels)
mainbg = ctx.createImageData(width, height)
let w = width * 4
for (let i = 0; i < pixels.length; i += 4) {
let x = i % w
let y = height - Math.floor(i / w) - 1
let j = y * w + x
mainbg.data[j] = pixels[i]
mainbg.data[j + 1] = pixels[i + 1]
mainbg.data[j + 2] = pixels[i + 2]
mainbg.data[j + 3] = pixels[i + 3]
}
// Dirt background
use2d()
let aspect = width / height
let stack = height / 96
let bright = 0.4
if (dirtBuffer) {
gl.deleteBuffer(dirtBuffer)
}
dirtBuffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, dirtBuffer)
let bgCoords = new Float32Array([
-1, -1, 0, stack, bright,
1, -1, stack * aspect, stack, bright,
1, 1, stack * aspect, 0, bright,
-1, 1, 0, 0, bright
])
gl.bufferData(gl.ARRAY_BUFFER, bgCoords, gl.STATIC_DRAW)
gl.uniform1i(glCache.uSampler2, 1)
gl.clearColor(0, 0, 0, 1)
gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT)
gl.vertexAttribPointer(glCache.aVertex2, 2, gl.FLOAT, false, 20, 0)
gl.vertexAttribPointer(glCache.aTexture2, 2, gl.FLOAT, false, 20, 8)
gl.vertexAttribPointer(glCache.aShadow2, 1, gl.FLOAT, false, 20, 16)
gl.drawArrays(gl.TRIANGLE_FAN, 0, 4)
pixels = new Uint8Array(width * height * 4)
gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels)
dirtbg = ctx.createImageData(width, height)
dirtbg.data.set(pixels)
}
function initPlayer() {
p = new Camera()
p.speed = 0.075
p.health = 20;
p.velocity = new PVector(0, 0, 0)
p.pos = new Float32Array(3)
p.sprintSpeed = 1.5
p.flySpeed = 2.5
p.x = 8
p.y = superflat ? 6 : (Math.round(noise(8 * generator.smooth, 8 * generator.smooth) * generator.height) + 2 + generator.extra)
p.z = 8
p.previousX = 8
p.previousY = 70
p.previousZ = 8
p.w = 3 / 8
p.bottomH = 1.62
p.topH = 0.18
p.onGround = false
p.jumpSpeed = 0.3
p.sprinting = false
p.maxYVelocity = 1.5
p.gravityStength = -0.032
p.lastUpdate = win.performance.now()
p.lastBreak = Date.now()
p.lastPlace = Date.now()
p.lastJump = Date.now()
p.autoBreak = false
p.flying = false
p.sneaking = false
p.spectator = false
win.player = p
win.p2 = p2
}
function initWorldsMenu() {
while (window.worlds.firstChild) {
window.worlds.removeChild(window.worlds.firstChild)
}
selectedWorld = 0
window.boxCenterTop.value = ""
const deselect = () => {
let elem = document.getElementsByClassName("selected")
if (elem && elem[0]) {
elem[0].classList.remove("selected")
}
}
function addWorld(name, version, size, id, edited) {
let div = doc.createElement("div")
div.className = "world"
div.onclick = e => {
deselect()
div.classList.add("selected")
selectedWorld = id
}
let br = "<br>"
div.id = id
div.innerHTML = "<strong>" + name + "</strong>" + br
if (edited){
let str = (new Date(edited).toLocaleDateString(undefined, {
year: "numeric",
month: "short",
day: "numeric",
hour: "numeric",
minute: "2-digit"
}))
div.innerHTML += str + br
}
div.innerHTML += version + br
div.innerHTML += `${size.toLocaleString()} bytes used`
window.worlds.appendChild(div)
}
worlds = {}
if (loadString) {
try {
let tempWorld = new World()
tempWorld.loadSave(loadString)
let now = Date.now()
addWorld(`${tempWorld.name} (Pre-loaded)`, tempWorld.version, loadString.length, now)
worlds[now] = {
code: loadString,
id: now
}
}
catch(e) {
console.log("Unable to load hardcoded save.")
console.error(e)
}
}
loadFromDB().then(res => {
if(res && res.length) {
let index = res.findIndex(obj => obj.id === "settings")
if (index >= 0) {
Object.assign(settings, res[index].data) // Stored data overrides any hardcoded settings
p.FOV(settings.fov)
res.splice(index, 1)
}
}
if (res && res.length) {
res = res.map(d => d.data).filter(d => d && d.code).sort((a, b) => b.edited - a.edited)
for (let data of res) {
addWorld(data.name, data.version, (data.code.length + 60), data.id, data.edited)
worlds[data.id] = data
}
}
window.worlds.onclick = Button.draw
window.boxCenterTop.onkeyup = Button.draw
}).catch(e => console.error(e))
superflat = false
trees = true
caves = true
}
function initEverything() {
console.log("Initializing world.")
worldSeed = Math.random() * 2000000000 | 0
seedHash(worldSeed)
caveNoise = openSimplexNoise(worldSeed)
noiseSeed(worldSeed)
generatedChunks = 0
initPlayer()
initWebgl()
if (win.location.origin === "https://www.kasandbox.org" && (loadString || MineKhan.toString().length !== 183240)) {
// Prevent Ctrl F
message.innerHTML = '.oot lanigiro eht tuo kcehc ot>rb<erus eb ,siht ekil uoy fI>rb<.dralliW yb >a/<nahKeniM>"wen_"=tegrat "8676731005517465/cm/sc/gro.ymedacanahk.www//:sptth"=ferh a< fo>rb<ffo-nips a si margorp sihT'.split("").reverse().join("")
}
initBackgrounds()
drawScreens[screen]()
Button.draw()
Slider.draw()
p.FOV(settings.fov)
initWorldsMenu()
initButtons()
}
// Define all the scene draw functions
(function() {
function title() {
let title = "MINEKHAN"
let subtext = "JAVASCRIPT EDITION WARNING: BUGS"
let font = "VT323,monospace"
strokeWeight(1)
ctx.textAlign = 'center'
ctx.font = "bold 120px " + font
fill(30)
text(title, width / 2, 158)
fill(40)
text(title, width / 2, 155)
ctx.font = "bold 121px " + font
fill(50)
text(title, width / 2, 152)
fill(70)
text(title, width / 2, 150)
fill(90)
ctx.font = "bold 122px " + font
text(title, width / 2, 148)
fill(110)
text(title, width / 2, 145)
ctx.font = "bold 32px " + font
fill(50)
text(subtext, width / 2-1, 180)
text(subtext, width / 2+1, 180)
text(subtext, width / 2, 179)
text(subtext, width / 2, 181)
ctx.font = "bold 32px " + font
fill(150)
text(subtext, width / 2, 180)
}
const clear = () => ctx.clearRect(0, 0, canvas.width, canvas.height)
const dirt = () => ctx.putImageData(dirtbg, 0, 0)
drawScreens["main menu"] = () => {
ctx.putImageData(mainbg, 0, 0)
title()
fill(220)
ctx.font = "20px VT323"
ctx.textAlign = 'left'
text("Minecraft " + version, width - (width - 2), height - 2)
}
drawScreens.play = () => {
controls()
runGravity()
resolveContactsAndUpdatePosition()
if(p.health < 20) {
p.health+=0.04;
}
if(p.health > 20) {
p.health = 20;
}
if (updateHUD) {
clear()
gl.clearColor(0, 0, 0, 0)
gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT)
hud()
ctx.drawImage(gl.canvas, 0, 0)
updateHUD = false
freezeFrame = false
gl.clearColor(sky[0], sky[1], sky[2], 1.0)
}
defineWorld()
}
drawScreens.loading = () => {
// This is really stupid, but it basically works by teleporting the player around to each chunk I'd like to load.
// If chunks loaded from a save aren't generated, they're deleted from the save, so this loads them all.
let sub = maxLoad - world.loadFrom.length - 9
let standing = true
if (world.loadFrom.length) {
let load = world.loadFrom[0]
p.x = load.x * 16
p.y = load.y * 16
p.z = load.z * 16
standing = false
} else {
p.x = p2.x
p.y = p2.y
p.z = p2.z
let cx = p.x >> 4
let cz = p.z >> 4
for (let x = cx - 1; x <= cx + 1; x++) {
for (let z = cz - 1; z <= cz + 1; z++) {
if (!world.chunks[x] || !world.chunks[x][z] || !world.chunks[x][z].buffer) {
standing = false
} else {
sub++
}
}
}
}
if (standing) {
play()
return
}
world.tick()
let progress = Math.round(100 * sub / maxLoad)
dirt()
fill(255)
textSize(30)
ctx.textAlign = "center"
text(`Loading... ${progress}% complete (${sub} / ${maxLoad})`, width / 2, height / 2)
}
drawScreens.inventory = drawInv
drawScreens.pause = () => {
strokeWeight(1)
clear()
ctx.drawImage(gl.canvas, 0, 0)
textSize(60)
fill(0, 0, 0)
ctx.textAlign = 'center'
text("Paused", width / 2, 60)
}
drawScreens.options = () => {
clear()
}
drawScreens["creation menu"] = () => {
dirt()
ctx.textAlign = 'center'
textSize(20)
fill(255)
text("Create New World", width / 2, 20)
}
drawScreens["loadsave menu"] = () => {
dirt()
ctx.textAlign = 'center'
textSize(20)
fill(255)
text("Select World", width / 2, 20)
}
drawScreens.editworld = dirt
})()
// Give the font time to load and redraw the homescreen
setTimeout(e => {
drawScreens[screen]()
Button.draw()
Slider.draw()
}, 100)
let debugMenu = false
function gameLoop() {
let frameStart = win.performance.now()
if (!gl) {
initEverything()
releasePointer()
}
if (screen === "play" || screen === "loading") {
drawScreens[screen]()
}
if (Date.now() - analytics.lastUpdate > 50 && analytics.frames) {
analytics.displayedTickTime = (analytics.totalTickTime / analytics.frames).toFixed(1)
analytics.displayedRenderTime = (analytics.totalRenderTime / analytics.frames).toFixed(1)
analytics.displayedFrameTime = (analytics.totalFrameTime / analytics.frames).toFixed(1)
analytics.fps = Math.round(analytics.frames * 1000 / (Date.now() - analytics.lastUpdate))
analytics.displayedwFrameTime = analytics.worstFrameTime.toFixed(1)
analytics.frames = 0
analytics.totalRenderTime = 0
analytics.totalTickTime = 0
analytics.totalFrameTime = 0
analytics.worstFrameTime = 0
analytics.lastUpdate = Date.now()
updateHUD = true
}
analytics.frames++
analytics.totalFrameTime += win.performance.now() - frameStart
analytics.worstFrameTime = Math.max(win.performance.now() - frameStart, analytics.worstFrameTime)
win.raf = requestAnimationFrame(gameLoop)
}
return gameLoop
}
init = MineKhan()
if (window.parent.raf) {
window.cancelAnimationFrame(window.parent.raf)
console.log("Canceled", window.parent.raf)
}
init()
</script>
</body>
</html>
wut Please do❗
I see, no i mean like by using this
instead of two spin offs
Did it work?
no?
It still didn't work. That's 'cause you're using khan.
i see the white backgrounds are easy to fix i think
Ye. But It still worked!
Hooray!
hmm even having the image to base36 it doesnt work Eh
use this code as the heart img code
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOAAAADgCAYAAAAaLWrhAAAUP0lEQVR4Xu2dXWxcRxXHZ9e7/oyTuM1HE6c4UQGl4qGoqmisygRR0RZVKRICqQXxUNGSF0Ag6AOpGrUvQdAHpFJUFZACVSQiVPGAEMWAhFJDG/qhglBRSZWipMH5aJzabrzrXe8HM2vf6Obm7t65987cMzP3v1KU2J45c87vnP+dude7J4zhBQIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIg4BqBgmsBefEcPnz4gaNHj36Vf932/fGH64+9GwfxfTG/26vbz3rNCdry1i7yHxT27t37hwMHDjypIy8vvvjibY8++uiPh4eHL/vi8vgEGcSpjTjxyoTmrS3+LkxOTv7lwQcffHZiYuKizGSbxpRscjaOr2fPnt0xPT19Z5w5JozduXPn27r8uHDhwg0zMzN7dNnXZXdsbGyuXq8P6LJPaVdcdV19xbmCu8rAlbg6O6ErwfjjcFmAtuZL9XHOVg658BsCzEWarQ/S2YsSBGh9bSIAmwm4LEBbr5q2+q1TB84ycVmAOgsCtkFACQEIUAlGGAGBZAQgwGTcMCtbAt6bBbJdNYPVIMAMIMdcwtn7nZgccjEcAsxFmhGkqQQgQFMzA79yQcBlAeIol4sStjtIlwVod2bgvZ+AsxdTCDBfhW5rIeMpaL7qlDRaJ9/1n5IoBJgSIKaDQFoCtu7ePePGETRtWWB+FgSiOhNk4YOWNSBALVhhVDEBZ4/lzgmwUqn4+4koroNMzOksNp22dcIRdepcrQpgzvWE4Q2HvHuFzt82BdhYLWHtIrGUiZP3gDblItYVdqjZ6BMT1sWaRTt4ni+/fqWuLSfrG7VOgLYxKVeWNmwolVadd+yl/WrbixdvHfjl48ePf2ptjHeF63bD7b8Chh0zrzyqHmi2ygOzp2697uL5W2zL16VNW/9R2z7xeq2vuLYhRkbQ68jtMWtxJmzg4rmdA+dn7xlpypqOXDuTAZzJ2+yG8b8vlvsrawt6TZq82K869cRwqrBt27bTvOXhM1QtD0kFuH///mf562sxgEkP/f4nbmGfH1vHNhTL0nOoBy60VtgLF+bZt954U4srDwwW2ffuvYcNXF6wisv0QoUdfOkVdkoDlampqVePHDlyLxfgexrMR5rUdtyJXFnzgKGVBruOXxcHl5euuakSl0vSK09I7MKnvnI/K7X17k5D9Trb2mixYmtJcwbUmG8VS2yQrTCNTUFJS4FagJncWIctksnCMWqwzQsty5dp8YfFnpEySFE4+Wg3y0JOspa/y6yzHWeTgKGZQ/o2N2oBkl59aPIdvmqhpffoGVw1o93FJMRG+pLtuYcIQfBRWfDrrN3yX3WuPL7L+AiadcxJ1svo6pzRMuEEqHfATC7ENtwDJilQzFFCINdHUCUEZYyE7Toy81wdQ3rZdxVqgriod8AELmMKCLhDAAJ0J5eIxEICEKCFSYPL7hCgFiBuRdypJUSSgAC1ABO4jCkgoJwA2UYAASrPJQxaRoBMfIITtQBJg7esUOCugwSoBeggUoRkGYEWpb/UAsQOSJl9rC0I4J0wqAMQICZAthFQ74DE3LE8CHQIkAmQ+tMQmQauq7tr8B3l/k/cZxog1GQdAeodMJNPQ3hZ0SUGf0egtGsU0xqwrgSNcJiMOukOyDt1dVoH6no1+8rsck19N7uR0mqjJ/+HaIMZ9L6m/uyhn21pucVa9aou3FrtauwJMzLUonsQGrkD8daBXzp79uyONbqep2k+3dNZk/ftLM6+/NfP8daBd4gGSqpft+/bx26+9TbVZlkf761Ze/NfrP23GTa4Uo+0H+fYK/rCvLW8zF5ZqrBGQf218WPFPvbx60eZaFGY9SfwI0F1GeAxee1yhdU1fGj54uDgpQ1Tn356ZePYvM8Ff9vDKI102z0L9Xq9/Pjjjx/qFXuUcXb33Xf/eXp6+s6kAHvNE60Dv7B1M9tYU3tVFjvf4P6vsw2fuauzfHtIXSvaQvUyW/jTH9n5Z37EtvMOY6pfouB4T1DVZjv2hPCCO7eWhRQb1cVE1MnrrTb79vQxdkKxzwExd7Uuc5mNFGlc38WiniRGG43OTiIge1dlr0NY0qu0/9ipUnxx45S98vkvoSLmQUUnoji7r8rYVNtSycTv23LnalTS2fIwEoWMABWVQ3dfOgrnhXdF6QkbFF3zNFLhzhdJssuAJCJIMqdTS0mdzOE8U1jJCDCz9Kh4FKXChuqAwwQV5WdSEar2Hfb0EjBKgGmfGPp//6YXm7z1KKGFWUoyR9hJOk8+GnNG+mslbd1QRiVzt29VXq1yljLzWNsIAkbtgCb+7kxllsLeMaPSfjdbNu8Q3WJK83uwLJjLriGzA8raUjrOtp3M/4ujMBD+ezpdb7/3fAiu77+wmfLwIapYgrHY4ndUXMGfGylAG8XXC7y/eKhio1o3bkF2u3ipsGOiDaOOoCYCkvEpqrijfi6zhsyYqHWifi6zRlZjXDliRvEycgeMcho/BwFXCMgI0NXjtys5RBwWE5ARoMXhwXUQMJsABGh2fuCd4wQgQMcTjPDMJgABmp0feOc4AQjQ8QQjPLMJQIBm5wfeOU4AAnQ8wQjPbAIQoNn5gXeOE4AAHU8wwjObgLPvBRXdy0QDJeWv5WqnMxpeIKCCgJMCFH07RetA8WqW1HaU9NoSXhfyzmYXP3enoshgozsBJwUowhV9O2dfmtGSeyE+r8WflgVgNDcEnBSg18Zue27SiEBtJeCkAKmSYdPn7agYYd2rCTj1FLRbS4ask47Pb2VN3Nz1qtVqz3JwSoAmpQEiNCkb5vri/BG015PJpM1vo3q84ChqbsGb5hl2QNMyAn9yRcCpHTBs5+m1GyXdqYLzwnZZ/E4wVzpKHKxTOyDVQ5hugkycFUzMDQGnBEiZtbi7L6WvWNscAk4eQYPHv6yPg34xZr22OaUFT2QIOLUDRrVmlwGCMSCQJQGZHdD6X2lRBpD0QU+WRYC19BEYGhrqWQIyAtTnnWLL3SKFCBSDhjllBJw6giqjAkMgkBEBCDAj0FgGBMII5FKAlPeEKMN4BMIerLmUv1wKUJSAS0mMV9JujHYlfzIPYbTHquMhSa/fv+lYz42yNi8K19/gYMQOqF3h5tUVPAKBDgEjBKgjF97/ww5x66ALm6oIGCHAq/474qLMqbh7+O3AfBw3VZWKW3ZMqYt01Z4yJ5X5CjtdLrKtLb+hRkqrq/P7+/vYuiL2v5QwjZguLqrvV5aV+zJ3YZ5d3DDGKLu8kgrwvUqFnTlTZ+/XqtJwG31lVmquRI7fvWucsUHS8CJ9xAA5AkJ8b/33f6GDZevBm+wfXysPsdP1S3JOaBpFUqHeHlfnQa309TEBJc4r7vg4tjHWXALd8h63HrzxjbUbsLRnrjTESO4BSVSfhhLmOk2Ash5JBOh0NhEcCMQgAAHGgIWhIKCaAIkAKc/cqgHCHgikIUAiQMozdxpYmAsCqgmQCFB1ELAHArYSgABtzRz8doIAiQBxD+hE7SAIBQRIBIh7QAWZgwknCJAI0AlyCAIEFBCQEaApbxxXEC5MgIBZBGQEqNxj3AMqRwqDlhIgESDuAS2tFritnICMAHEEVY4dBkFglYCMAMEKBK4igI85qysIEgHiHlBdAmEpPQHKeiQRIO4B0xcNpQXck6ijLyNAJ04cKoOg+p941aUdlvwEKDcEyrW1VUGwZ4xK8QmnsQNoS13uDJMIUJy5L6+hrvKeMOXSqhuiP4x4lZvNxIkQNsrNEptbvMzGhjd17MgIptBqsGBLw15OiPF5fwlefg5x+AXZ9ebfYKKBktfDxT93Nd/J6kXMrRcKZndFm5ycPMYDFkdVsZGIBoJez1uvruN0D/dOb+2x5cpAZX5h4vjc3M5iQ20xX1/qZyvVJrs+dis7OT9Ey8ORUvmq4sujGIPiS986MJz/iXqTvdGssUa7zOYaopWXutfC+tGFyb1T/9w9OOzve9irr7N3oPIfrML+V/JCpVIZnJmZ6ems6tNZLDLffOjhnz718589LPa/db5dMfhvYVTsmOL7Yf8W3wvO+caOG9medkuqhWEsp/ngHZs3sBu3bMq9AD1uXt/Obq0D4/L1jxcdzF5rN9nzs+fYuzHy752wutWM+P48/3PXHXe8euTIkc9u2bVrLo2fSeeSHEE9Z2t9xStnBw+YJzBvjMz3/XPENVSAFVfK2sBgUi5d54n7y9pVjYSVL2GVQXEF91/+47YIjAq2yY+5jXaRLa1dhMPqIknNXLkFKhZXqsUiWUZJBejBlDv4RaUKP8+KQFB0WawrrtQa62T14QPBS+bXEARuYUnTCfT6799M9z3gn/dsg8Rt6h2Q9B6UhLgDi8o8VbYoTNJf62IHtKhS4KoWAqSbAASoJacwCgJyBCBAOU4Y5TYBsl2QWoBkgbtdT4jOFgLUArSFE/wEAS0EIEAtWGHUIgKkD3WpBYgjqEWVClfVE6AWoPqIYBEELCJALUDS7d+iPMFVRwlQC9BRrAgLBOQIQIBynDDKXQKkpzBqAZIGn7SmBqipJXUc88IIkD4IRCkZUJRZvRvYv05WaxqAV8YFMhFCgDLpCYxR/YFcimNAmjXJqjVBriSnpMEhuUT4MAgwFT51k7OoAK/Ried1mjUdEiHpYQCfB0ypoW6deVKa1To97Ydp0whXa2AWGqcWoLZctnirw8X+fja01upQVW5Ey8PN3BjvZ8MGWEmq5WGctbNod9jifVZUvgQL8RKtA6tlDd0deJe7BZUOG2RLbSZiBrZnz56ZhYWFMT5N5nQU1g5OrBjWJnGg9c7J207OntvpuVRqrfC+kuUrHga/Fj/wfy/s52LM+pVqp3/pCPdmMHbbw2hAouXhuqKeA57XvUxHd5XzXIP/qS+xxfZQZJDdOHdj/k6pOL/vvn0vrQyPfMCNe60xu63TDZ6ok+DPiuPj46LZmraNIAqGnkxHrbr281OnTq12zl19hYkw6F+v05MHuPP3r3/y9Hee/uGTB1Rfj0e48akbtrK9wyNsY8Xfs00y6IhhouXhhzZt7FSKyqrQ2TpQ7HxCfL89f5Gd5n6rZC6aMX3yvn2/f/i7jzwxMTFxMqRW/MKKup8LrXdu96Ka7MW3QroD6gz80KFD86fi84icsZGPWObHW9HRex3vWan6pfoJa5h/qlsHimOn2PmE+HQw5zvf0rZt297l9ULSu1N1jv328BRUJ92EtsVupXL3S+hG7Gkqd77A4uLYSXpaiw1DcgIEKAkKw0gJ2Hg9kgIGAUphwiBiAmEPUIhdUrM8BKiGI6yAQCICLgvQyXuGRFl2Y5KT+XRZgM7eN7ihJ0QhCLgsQGQYBIwnAAEanyI46DIBlwXo5D2Dy8WYx9hcFiDuAd2paGcvpi4L0J3yQyTOEoAAnU0tArOBAARoQ5bgo7MEIEBnU4vAbCAAAdqQJfjoLAEI0NnUIjAbCECANmQJPkZ90t1aQhCgtamD4y4QgABdyCJisJYAaU8YzdS0vBNm3ud0U3F7v75WQzMS3g9trY+Nat+F46KBEl7xCDgrQN7EZ/b+++8/uoajmxhlRXrlHqRcWRopnTv3keOzsx/dzIpqTxCFIrupztj2/gFWm1+Ol8nI0Q12ot5k/27U2CLvYsbaos2Kmtccb8+4sH50kXcvO8YbKHmt4vxd7vycu7WXDDrjMS/s3r37zf7+/poab82y4ux77ETLw3q9PpACd5g4O9/73dFffeWRxw7+YDyF8bCpoqnR7YNF9sWtN7KRWlWpdbHzvdZusudnz7ElxbuVaJrL+3a+wFsHHuQXvjP8S1mRiRijarAgxMc7or2nFIghxpzdAXW2PDx8+PC74rCosgWfSISweXNpuNP4V3XrwBpvpdhoFpnoQiu2KNWHXbHzcebv8D+XDKltK9xQe4SyImQznVQtiGCUQtRzDX6+1fdytnGSPmT4RHxStrL3jknt2zoPXGJmDjtgTGBrw6PuW5JZxazcEYAAc5dybQHjCJoALQSYABqmhBLA8TNBYUCACaDxKTiCJuOGWQECECBKAgQICUCAhPAdWxpH0AQJhQATQLNxSrmJd2qamDcI0MSswKfcEIAAc5NqBGoiAQjQxKzAp9wQgABzk2oEaiIBCNDErMCn3BCAAJOlGo/ck3HDLPwiHjWgiYCzncs08eqYxQ6oky5sg0AEAQgQJaKKAHbABCQhwATQ+BS8GftabmCSoJac7QmTgIUxU6q8fUSZ93BR+RItKcRrXqVRiDA1TbVZTu2OHQZGR0cXp6amXg54638y6v27264QbNPnjSuNLi5sO1mt7ujvLyuFUS8UROvAD+666cMnqsXiCjcu+hL6WweK9eLsYv7OZ4Xx8fHTa/aU+u26sTjAXWcRKz7e9nBzDwF6Pwry9b72BHiNUH/z3C8feuKxg4e2xPImerBoqjm5d+rlp37x3L1cgOLWQ/gS1q9TVojB2Nqutg6Mppt8BHbAhOx0FRtveXhGHBN1HBV3Dw5Xt+za9X7CkDFNAwE8hNEANYnJSqWSxWkEbyBIkhyNcyBAjXDjmB4eHg4eS+NMlx0LAcqSymgcBJgR6BjLQCQxYNk+FAI0L4NZHEXNizqnHkGA+Uo8dlfD8g0BGpYQze5AgJoBxzUPAcYlhvEgoJAABKgQJkyBQFwCEGBcYvrH45ion7ExK0CAxqQCjuSRAASYx6wjZmMIQIDGpCITR3C8zQSz/CIQoDyrrEZCJFmRNmAdCNCAJMCF/BKAAPObe0RuAAEI0IAkwIX8EoAA85t7RG4AAQjQgCRk6AI+aZEhbJmlIEAZSu6MgQANyyUEaFhCuDs6RYJ8G5ZvJMSwhHB3tP1f0rzvzJB54cIjEDCMABcKLoyG5QTugAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIgIBlBP4PGi0Qg8gxpNoAAAAASUVORK5CYII=
Ok.
but for now, lets just stick with the khan heart
Ok.
I got the infinite jumping working.
YES!
Ok this is where we now chat