let reacts=[],rnd,r=[0,0,1],fr=60,fSamples=20,pn=0,grTex,mtTex,glContext,lxz=300,dt=1,frs=[],players=[],playerPos=[],
testReact,cp=[0,0,0],mouseVect=[1,0,0],sqr3=Math.sqrt(3),scsr=name.endsWith('1624222'),buildHistory=[]
let char={p:[3200,-41,274],v:[0,0,0],dir:0,anchor:-1,building:0},ft=false,
fix:[],edges:[],faces:[],offset:[0,-3,0],
buildCenter:[0,0,0],targetCenter:[0,0,0],selected:[-1,0,0],selectHandler:[-1,0,0],
rotating:0,configure:0,controller:0,
addingControl:0,newControl:{key:'NONE',on:'press',type:'spin',speed:0,angle:0,damping:10,backtrace:-1},
build_rnd,glContext2,simFR=60,buildFR=60
ft=loadFont('https://openprocessing.org/sketch/1341463/files/Ubuntu-Regular.ttf')
createCanvas(Math.min(windowWidth-10,800),Math.min(windowHeight-10,550))
grTex=createGraphics(200,200)
grTex.fill(196,120,57);grTex.noStroke()
grTex.circle(Math.floor(Math.random()*200),Math.floor(Math.random()*200),4)
mtTex=createGraphics(200,200)
mtTex.fill('#BBB');mtTex.noStroke()
mtTex.stroke(80,80,80,15)
mtTex.line(Math.floor(Math.random()*600)-200,Math.floor(Math.random()*600)-200,Math.floor(Math.random()*600)-200,Math.floor(Math.random()*600)-200)
mtTex.stroke('#888');mtTex.noFill();mtTex.strokeWeight(2)
mtTex.triangle(0,0,200,0,100,100*sqr3)
rnd=createGraphics(width,height,WEBGL)
glContext=rnd._renderer.GL
rnd.frustum(-Math.min(width,height)/height/10,Math.min(width,height)/height/10,Math.min(width,height)/width/10,-Math.min(width,height)/width/10,0.1,1e5)
build_rnd=createGraphics(1,1,WEBGL)
build_rnd.frustum(-Math.min(width,height)/height/10,Math.min(width,height)/height/10,Math.min(width,height)/width/10,-Math.min(width,height)/width/10,0.1,1e5)
build_rnd.setAttributes('antialias',false)
glContext2=build_rnd._renderer.GL
char.reactive=new Reactive({
char.reactive.generateFriction='[0,0,0]'
char.reactive.returnCollides=1
testReact.generateFriction='[0,0,0]'
testReact.returnCollides=1
SCH.addServer('sketch162230','SND')
SCH.addServer('https://echoserver.lrCr.repl.co','RPL')
SCH.addServer('https://echoes-vm.herokuapp.com','HKU')
SCH.whenPing((t1,t2,t3)=>{pn=t3-t1})
let a=players.filter(x=>x.id2==e.id2)
a[0].joints=e2.joints||[]
a[0].points=e2.points||[]
a[0].a=e2.a||[[0,0,1],[0,1,0]]
id:e.id,id2:e.id2,m:e.m,t:e.t,active:e.active||1,fix:e2.fix||[],edges:e2.edges||[],joints:e2.joints||[],fc:e2.fc||[],faces:e2.faces||[],points:e2.points||[],
p:e2.p||[0,0,0],a:e2.a||[[0,0,1],[0,1,0]],
craftMap:e.craftMap,jt:e2.jt,builder:e.builder
let a=playerPos.filter(x=>x.id==e.id)
if(!a[0].t||(a[0].t<e.t)){
playerPos.push({id:e.id,p:e.p,t:e.t,dir:e.dir})
SCH.on('deleteCraft',e=>{
players=players.filter(x=>x.id2!=e.id2)
SCH.on('removeOtherCraft',e=>{
if(e.id2.startsWith(SCH.id)){
let r=parseInt(e.id2.replace(SCH.id,'').replace('_fix',''))
SCH.emit('deleteCraft',{id2:e.id2})
reacts=[...reacts.slice(0,r),...reacts.slice(r+1,reacts.length)]
let e=localStorage.getItem('windswept_currentBuild')
buildUI.edges=e.edges||[]
buildUI.faces=e.faces||[]
buildUI.controller=e.controller||0
buildUI.hardpoints=e.hardpoints||[]
cameraMode=cameraMode%cameraModes()
frs=frs.slice(1,frs.length)
for(let i=0;i<frs.length;i++){
if(mouseButton==LEFT&&(keyIsDown(192)||mouseIsPressed)){
r[0]-=(mouseX-pmouseX)/200
r[1]=Math.max(-Math.PI/2,Math.min(Math.PI/2,r[1]+(mouseY-pmouseY)/200))
let xz=r[2]*Math.min(300,lxz+20),rt=50,sp=r[2]*10
let dr_r=eval('['+getCamData()+']')
let cp=vadd(vadd(char.p,char.building?buildUI.offset:[0,0,0]),vmultf([dr_r[0],dr_r[1],dr_r[2]],1/30))
testReact.anchor.a=[[1,0,0],[0,1,0]]
testReact.anchor.v=[0,0,0]
testReact.anchor.r=[[0,0,0],[0,0,0]]
let n=testReact.collidef(p=>{
let s=[Math.max(0,Math.min(ns.dm[0]-1,Math.floor(p[0]/ns.sc))),Math.max(0,Math.min(ns.dm[1]-1,Math.floor(p[2]/ns.sc)))]
if((p[0]/ns.sc+p[2]/ns.sc-Math.floor(p[0]/ns.sc)-Math.floor(p[2]/ns.sc))>1){
if((p[2]/ns.sc+Math.ceil(p[0]/ns.sc)-p[0]/ns.sc-Math.floor(p[2]/ns.sc))>1){
if((p[2]/ns.sc+Math.ceil(p[0]/ns.sc)-p[0]/ns.sc-Math.floor(p[2]/ns.sc))>1){
return {p:[0,20,0],d:[[0,0,1],[0,1,0]]}
let x=Math.min(4,Math.ceil(dt)),x2=Math.min(4,Math.ceil(dt)),dr_r=eval('['+getCamData()+']')
let drs=vmultf(vnorm([dr_r[0]-dr_r[3],0,dr_r[2]-dr_r[5]]),-5)
cp=vadd(vadd(char.p,char.building?buildUI.offset:[0,0,0]),vmultf([dr_r[0],dr_r[1],dr_r[2]],1/30))
let zt=vnorm([dr_r[0],dr_r[1],dr_r[2]])
let cros=vcross(zt,[0,1,0])
let up=vnorm(vcross(zt,cros))
mouseVect=vnorm(vtransform([zt,up],[0,0,0],[-5,(height/2-mouseY)/80,(mouseX-width/2)/80]))
if(!scsr)mouseVect=[0,0,0]
for(let i=0;i<reacts[char.anchor].reactive.fix.length;i++){
testReact.anchor.a=[[1,0,0],[0,1,0]]
testReact.anchor.v=[0,0,0]
testReact.anchor.r=[[0,0,0],[0,0,0]]
testReact.anchor.p=vadd(reacts[char.anchor].reactive.fromGrid(reacts[char.anchor].reactive.fix[i]),[0,0.1,0])
let n=testReact.collidef(p=>{
let s=[Math.max(0,Math.min(ns.dm[0]-1,Math.floor(p[0]/ns.sc))),Math.max(0,Math.min(ns.dm[1]-1,Math.floor(p[2]/ns.sc)))]
if((p[0]/ns.sc+p[2]/ns.sc-Math.floor(p[0]/ns.sc)-Math.floor(p[2]/ns.sc))>1){
if((p[2]/ns.sc+Math.ceil(p[0]/ns.sc)-p[0]/ns.sc-Math.floor(p[2]/ns.sc))>1){
if((p[2]/ns.sc+Math.ceil(p[0]/ns.sc)-p[0]/ns.sc-Math.floor(p[2]/ns.sc))>1){
return {p:[0,20,0],d:[[0,0,1],[0,1,0]]}
build_rnd.clear(255,255,255)
xz*Math.sin(r[0])*Math.cos(r[1]),-Math.sin(r[1])*xz,xz*Math.cos(r[0])*Math.cos(r[1]),
...vadd([xz*Math.sin(r[0])*Math.cos(r[1]),-Math.sin(r[1])*xz,xz*Math.cos(r[0])*Math.cos(r[1])],mouseVect),
build_rnd.translate(...vmultf(vadd(char.p,char.building?buildUI.offset:[0,0,0]),-30))
build_rnd.fill(255);rnd.stroke(0)
build_rnd.strokeWeight(1)
eval('rnd.camera('+getCamData()+')')
rnd.fill(130,176,250);rnd.noStroke()
glContext.clear(glContext.DEPTH_BUFFER_BIT)
rnd.translate(...vmultf(vadd(char.p,char.building?buildUI.offset:[0,0,0]),-30))
rnd.fill(255);rnd.stroke(0)
if(keyIsDown(keyToCode('code','w')))char.v=vadd(char.v,vmultf(drs,1/200*(keyIsDown(32)?3:1)))
if(keyIsDown(keyToCode('code','s')))char.v=vadd(char.v,vmultf(drs,-1/200*(keyIsDown(32)?3:1)))
if(keyIsDown(keyToCode('code','a')))char.v=vadd(char.v,vmultf(vcross(drs,[0,1,0]),-1/200*(keyIsDown(32)?3:1)))
if(keyIsDown(keyToCode('code','d')))char.v=vadd(char.v,vmultf(vcross(drs,[0,1,0]),1/200*(keyIsDown(32)?3:1)))
char.reactive.anchor.a=[[1,0,0],[0,1,0]]
char.reactive.anchor.v=[0,0,0]
char.reactive.anchor.r=[[0,0,0],[0,0,0]]
char.reactive.anchor.p=char.p
if(char.p[0]>ns.dm[0]*ns.sc-ns.sc*1){
char.p[0]=ns.dm[0]*ns.sc-ns.sc*1
if(char.p[2]>ns.dm[1]*ns.sc-ns.sc*1){
char.p[2]=ns.dm[1]*ns.sc-ns.sc*1
let n=char.reactive.collidef(p=>{
let s=[Math.max(0,Math.min(ns.dm[0]-1,Math.floor(p[0]/ns.sc))),Math.max(0,Math.min(ns.dm[1]-1,Math.floor(p[2]/ns.sc)))]
if((p[0]/ns.sc+p[2]/ns.sc-Math.floor(p[0]/ns.sc)-Math.floor(p[2]/ns.sc))>1){
if((p[2]/ns.sc+Math.ceil(p[0]/ns.sc)-p[0]/ns.sc-Math.floor(p[2]/ns.sc))>1){
if((p[2]/ns.sc+Math.ceil(p[0]/ns.sc)-p[0]/ns.sc-Math.floor(p[2]/ns.sc))>1){
return {p:[0,2000,0],d:[[0,0,1],[0,1,0]]}
char.v[1]=Math.max(-n[0][0][1][1]/(char.building?dt:1),-10)
char.v[1]=Math.max(-0.4,char.v[1])
if(keyIsDown(keyToCode('code','SPACE')))char.v[1]=char.v[1]-2/100*(keyIsDown(16)?100:1)
else char.v[1]=Math.min(0.5,char.v[1]+3/100)
if(vmag(char.v)>170)char.v=vmultf(vnorm(char.v),170)
char.v=vadd([0,char.v[1],0],vmultf(vsnap(char.v,[0,1,0]),0.9))
char.p=vadd(char.p,vmultf(char.v,char.building?dt:1))
if(vmag([char.v[0],0,char.v[2]])>0.01)char.dir=((Math.PI-Math.atan2(char.v[2],char.v[0]))*0.2+char.dir*0.8)%(Math.PI*2)
rnd.fill(150);rnd.noStroke()
rnd.directionalLight(255,255,255,-0.3,1,0)
rnd.translate(...vadd(char.p,[0,0.5,0]))
rnd.ambientLight(66,191,245)
let b_p=vmultf(vnorm([-4,i,j]),0.36)
char.p=reacts[char.anchor].reactive.anchor.p
for(let i=0;i<playerPos.length;i++){
rnd.fill(150);rnd.noStroke()
rnd.directionalLight(255,255,255,-0.3,1,0)
rnd.translate(...vadd(playerPos[i].p,[0,0.5,0]))
rnd.rotateY(playerPos[i].dir)
rnd.ambientLight(66,191,245)
let b_p=vmultf(vnorm([-4,i,j]),0.36)
playerPos=playerPos.filter(x=>x.t>0)
for(let i=0;i<reacts.length;i++){
reacts[i].control(i==char.anchor)
reacts[i].applyForce([0,0,0],reacts[i].reactive.absolNormal([0,8,0]))
reacts[i].collide(dt/x3,[0,0,0],[[0,0,1],[-1,0,0]])
reacts[i].collide(dt/x3,[0,0,0],[[1,0,0],[0,0,-1]])
reacts[i].collide(dt/x3,[ns.dm[0]*ns.sc-ns.sc*1,0,ns.dm[0]*ns.sc-ns.sc*1],[[0,0,1],[1,0,0]])
reacts[i].collide(dt/x3,[ns.dm[0]*ns.sc-ns.sc*1,0,ns.dm[0]*ns.sc-ns.sc*1],[[1,0,0],[0,0,1]])
reacts[i].collidef(dt/x3,p=>{
let s=[Math.max(0,Math.min(ns.dm[0]-1,Math.floor(p[0]/ns.sc))),Math.max(0,Math.min(ns.dm[1]-1,Math.floor(p[2]/ns.sc)))]
if((p[0]/ns.sc+p[2]/ns.sc-Math.floor(p[0]/ns.sc)-Math.floor(p[2]/ns.sc))>1){
if((p[2]/ns.sc+Math.ceil(p[0]/ns.sc)-p[0]/ns.sc-Math.floor(p[2]/ns.sc))>1){
if((p[2]/ns.sc+Math.ceil(p[0]/ns.sc)-p[0]/ns.sc-Math.floor(p[2]/ns.sc))>1){
return {p:[0,20,0],d:[[0,0,1],[0,1,0]]}
let n=reacts[i].joints.length
reacts[i].applyJointForce(y,reacts[i].joints[y].axis[0]?[0,1,0]:[1,0,0],vmultf(reacts[i].joints[y].a.r[reacts[i].joints[y].axis[0]?1:0],reacts[i].joints[y].friction))
if(reacts[i].deletable)reacts[i].deleteTime--
reacts=reacts.filter(x=>!((x.deletable&&x.deleteTime<1)||(x.dependent&&!reacts.filter(r=>r.uid==x.dependentID).length)))
glContext.clear(glContext.DEPTH_BUFFER_BIT)
let closest={id:-1,d:10,selfCraft:1}
for(let i=0;i<reacts.length;i++){
let r=vmag(vadd(reacts[i].reactive.anchor.p,vmultf(char.p,-1)))
for(let i=0;i<players.length;i++){
if(!players[i].active)continue
let r=vmag(vadd(players[i].p,vmultf(char.p,-1)))
if(char.anchor<0&&!char.building){
rnd.noStroke();rnd.fill(0)
drawScreenAt(vadd(reacts[closest.id].reactive.anchor.p,[0,-1,0]),cp)
rnd.translate(-1/2,0.05,0)
glContext.clear(glContext.DEPTH_BUFFER_BIT)
rnd.noStroke();rnd.fill(255)
drawScreenAt(vadd(reacts[closest.id].reactive.anchor.p,[0,-1,0]),cp)
rnd.textAlign(CENTER,CENTER);rnd.textSize(1/2)
rnd.noStroke();rnd.fill(0)
drawScreenAt(vadd(players[closest.id].p,[0,-1,0]),cp)
rnd.translate(-1/2,0.05,0)
glContext.clear(glContext.DEPTH_BUFFER_BIT)
rnd.noStroke();rnd.fill(255)
drawScreenAt(vadd(players[closest.id].p,[0,-1,0]),cp)
rnd.textAlign(CENTER,CENTER);rnd.textSize(1/2)
image(rnd,0,0,width,height)
if(keyIsDown(18))image(build_rnd,50,50,20,20)
if(char.building)buildScreenMenu()
text('Controls:',10,height-10)
text('Airspeed: '+(Math.floor(vmag(reacts[char.anchor].reactive.anchor.v)*120,10)/10).toString()+' blocks/second',10,height-30)
for(let s=0;s<reacts[char.anchor].controls.length;s++){
let kz=reacts[char.anchor].controls[s].key
if(reacts[char.anchor].controls[s].type=='default')continue
if(kyt.includes(kz))continue
rect(a-2,height-27,textWidth(kz)+5,18)
if(frameCount%10==0)fr=Math.floor(frs.reduce((a,b)=>a+b)/fSamples)
text('FPS: '+fr.toString(),10,10)
text((SCH.connected?'Server: '+SCH.server:'Connecting to server...'),width-10,10)
text(SCH.sockets[SCH.chosenSocket].u.startsWith('sketch')?'Ping unavailable on server SND':('Ping: '+pn.toString()),width-10,30)
text('Subdivisions: '+x2.toString(),width-10,50)
if(frameCount%90==0)SCH.ping(Date.now())
for(let i=0;i<reacts.length;i++){
id2:SCH.id+'_fix'+i.toString(),m:150,t:frameCount,active:i!=char.anchor,craftMap:reacts[i].craftMap,builder:0,
edges:reacts[i].edges,fix:reacts[i].fix,faces:reacts[i].faces,points:reacts[i].points,jt:reacts[i].p_jt,p:reacts[i].reactive.anchor.p,a:reacts[i].reactive.anchor.a
let fc=[],fix=[],joints=[]
for(let i=0;i<buildUI.fix.length;i++){
fc.push(buildUI.fix[i].c)
fix.push(buildUI.fix[i].p)
for(let i=0;i<buildUI.edges.length;i++){
if(buildUI.edges[i].type=='joint')joints.push(vmultf(vadd(buildUI.fix[buildUI.edges[i].p[0]].p,buildUI.fix[buildUI.edges[i].p[1]].p),1/2))
id2:SCH.id+'_build',m:150,t:frameCount,active:0,builder:1,craftMap:'{"fix":[],"edges":[],"faces":[],"controller",0}',
edges:buildUI.edges,fix:fix,fc:fc,joints:joints,faces:buildUI.faces,points:buildUI.hardpoints,p:vadd(char.p,vadd(buildUI.buildCenter,buildUI.offset)),a:[[1,0,0],[0,1,0]]
p:char.anchor<0?char.p:[0,100,0],t:150,dir:char.dir
fill(0);noStroke();textAlign(CENTER,CENTER);textSize(30)
text('Loading...',width/2,height/2)
if(buildUI.addingControl&&char.building){
if(keyToCode('key',keyCode))buildUI.newControl.key=keyToCode('key',keyCode)
if(document.getElementById('craftInputDiv').style.display=='block')return
if(key=='c')cameraMode=cameraMode+1
let closest={id:-1,d:10,selfCraft:1}
for(let i=0;i<reacts.length;i++){
let r=vmag(vadd(reacts[i].reactive.anchor.p,vmultf(char.p,-1)))
for(let i=0;i<players.length;i++){
if(!players[i].active)continue
let r=vmag(vadd(players[i].p,vmultf(char.p,-1)))
if(closest.id<0)char.anchor=-1
let a=JSON.parse(players[closest.id].craftMap)
let b=craftFromBuild(players[closest.id].p,a.fix,a.edges,a.faces,a.points,a.controller,{reactive:{anchor:{p:players[closest.id].p,a:players[closest.id].a}}})
char.anchor=reacts.length-1
SCH.emit('removeOtherCraft',{id2:players[closest.id].id2})
SCH.emit('deleteCraft',{id2:SCH.id+'_build'})
if(key=='R'&&!char.building){
let closest={id:-1,d:10,selfCraft:1}
for(let i=0;i<reacts.length;i++){
let r=vmag(vadd(reacts[i].reactive.anchor.p,vmultf(char.p,-1)))
for(let i=0;i<players.length;i++){
if(!players[i].active)continue
let r=vmag(vadd(players[i].p,vmultf(char.p,-1)))
buildUI.edges=buildUI.edges.filter(x=>x.p[0]<buildUI.fix.length&&x.p[1]<buildUI.fix.length)
buildUI.faces=buildUI.faces.filter(x=>x.p[0]<buildUI.fix.length&&x.p[1]<buildUI.fix.length&&x.p[2]<buildUI.fix.length)
buildUI.fix=[{p:[0,0,0],c:'#F00'}]
let b=buildFromCraft(reacts[closest.id])
buildUI.controller=b.controller
SCH.emit('deleteCraft',{id2:SCH.id+'_fix'+closest.id.toString()})
reacts=[...reacts.slice(0,closest.id),...reacts.slice(closest.id+1,reacts.length)]
let b=buildFromCraft(players[closest.id])
buildUI.controller=b.controller
SCH.emit('removeOtherCraft',{id2:players[closest.id].id2})
let b=buildFromCraft(reacts[char.anchor])
buildUI.controller=b.controller
reacts=[...reacts.slice(0,char.anchor),...reacts.slice(char.anchor+1,reacts.length)]
SCH.emit('deleteCraft',{id2:SCH.id+'_fix'+char.anchor.toString()})
reacts[char.anchor].toggle(keyCode)
for(let i=0;i<reacts[char.anchor].controls.length;i++){
if(reacts[char.anchor].controls[i].action.type=='detach'){
if(keyCode==keyToCode('code',reacts[char.anchor].controls[i].key)){
detaches.push(reacts[char.anchor].controls[i].link)
let z=splitCraft(detaches[0],reacts[char.anchor],0)
if(z[1])reacts.push(z[1])
for(let i=0;i<reacts[char.anchor].controls.length;i++){
if(reacts[char.anchor].controls[i].action.type=='detach'){
if(keyCode==keyToCode('code',reacts[char.anchor].controls[i].key)){
detaches.push(reacts[char.anchor].controls[i].link)
if(!detaches.length)break
if(buildUI.addingControl&&char.building){
if(keyToCode('key',keyCode))buildUI.newControl.key=keyToCode('key',keyCode)
let e=document.getElementById('craftInputDiv'),i=document.getElementById('craftInput')
i.value=JSON.stringify({fix:buildUI.fix,edges:buildUI.edges,faces:buildUI.faces,points:buildUI.hardpoints,controller:buildUI.controller})
let e=document.getElementById('craftInputDiv'),i=document.getElementById('craftInput')
if(document.getElementById('craftInputDiv').style.display=='block')return
if(document.getElementById('craftInputDiv').style.display=='block')return
r[2]=Math.max(0.1,Math.min(10,r[2]*(1+e.delta/1000)))