Created
November 4, 2015 08:34
-
-
Save dermotbalson/7b6a7562d0de7ab6dac6 to your computer and use it in GitHub Desktop.
Tank 6 collision
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--# Shot | |
--shot | |
ShotList={} | |
function AddShot(p,d,s) | |
table.insert(ShotList,{start=p,pos=p,dir=d,speed=s}) | |
end | |
function DrawShots(Tanks) | |
for i,s in pairs(ShotList) do | |
local m=LookAtMatrix(s.pos,s.pos+s.dir) | |
pushMatrix() | |
modelMatrix(m) | |
Tank.Shot:draw() | |
popMatrix() | |
local newPos=s.pos+s.dir*(s.speed*DeltaTime) | |
for _,t in pairs(Tanks) do | |
--if t.pos:dist(newPos)<Tank.shotRadius or s.dir:dot(t.pos-newPos)<0 then | |
if DistancePointToLine(t.pos,s.pos,s.dir)<Tank.shotRadius and s.dir:dot(t.pos-newPos)<0 then | |
ShotList[i]=nil | |
local objPos,m=RotateVector((s.start-t.pos),-t.angle,0) | |
local objDir=m*s.dir | |
local d,h,a,ang,dam,nam=t:PenetrationTest(objPos,objDir) | |
--[[ | |
if not ang then hitText="Shot missed!" | |
elseif ang<Tank.bounceDot then hitText="Shot bounced!" | |
else | |
local f=(1-d*Tank.damageReductionWithDistance)*ang | |
hitText=nam .. " hit!\n" .. | |
"distance: "..math.floor(d) .."\n" .. | |
"angle: "..math.floor(math.deg(math.acos(ang))) .. "\n".. | |
"adjusted power: "..string.format("%4.2f",f) .."\n" .. | |
"armour: ".. a .. "\n" .. | |
"damage to: "..Tank.DamageList[dam] | |
end | |
--]] | |
else | |
s.pos=newPos | |
end | |
end | |
end | |
end | |
--# Effects | |
--Effects | |
function MakeExplosion(s) | |
local sin,cos,rand,pi2=math.sin,math.cos,math.random,math.pi*2 | |
local ss=s*1.5 | |
local img=image(ss,ss) | |
pushMatrix() | |
setContext(img) | |
translate(ss/2,ss/2) | |
for i=1,1000 do | |
local f=i/2000 | |
local m=0.4-0.2*f | |
local a,d,r=rand()*pi2,rand()*s*.5,rand()*s*m | |
local col=150+100*f | |
fill(color(col,col,col,50)) | |
rotate(a) | |
ellipse(d*sin(a),d*cos(a),r) | |
end | |
setContext() | |
popMatrix() | |
local m=mesh() | |
local x1,y1,z,x2,y2=-s/2,-s/2,0,s/2,s/2 | |
m.vertices={vec3(x1,y1,z),vec3(x2,y1,z),vec3(x2,y2,z),vec3(x2,y2,z),vec3(x1,y2,z),vec3(x1,y1,z)} | |
m.texture=img | |
m.texCoords={vec2(0,0),vec2(1,0),vec2(1,1),vec2(1,1),vec2(0,1),vec2(0,0)} | |
m:setColors(color(255)) | |
m.shader=shader(TransparentFadeShader.v,TransparentFadeShader.f) | |
m.shader.a=1 | |
return m,img | |
end | |
function MakeShell() | |
return CreateBlock(0.15,0.15,50,color(255,255,0,100),vec3(0,0,-5)) | |
end | |
--# Shaders | |
--Shaders | |
TankFogShader={ | |
v=[[ | |
uniform mat4 modelViewProjection; | |
uniform mat4 mModel; | |
uniform vec4 directColor; | |
uniform vec4 directDirection; | |
uniform vec4 ambientColor; | |
uniform float offset; | |
attribute vec4 position; | |
attribute vec3 normal; | |
attribute vec2 texCoord; | |
varying lowp vec4 vColor; | |
varying highp vec2 vTexCoord; | |
varying highp vec4 vPosition; | |
void main() | |
{ | |
gl_Position = modelViewProjection * position; | |
vTexCoord = vec2(texCoord.x+offset,texCoord.y); | |
vec4 norm = normalize(mModel * vec4( normal, 0.0 )); | |
float diffuse = max( 0.0, dot( norm, directDirection )); | |
vPosition = mModel*position; | |
vColor = diffuse * directColor + ambientColor; | |
} | |
]], | |
f=[[ | |
precision highp float; | |
uniform lowp sampler2D texture; | |
uniform float offset2; | |
uniform vec4 mistColor; | |
uniform float fog; | |
uniform vec3 camPos; | |
varying lowp vec4 vColor; | |
varying highp vec2 vTexCoord; | |
varying highp vec4 vPosition; | |
void main() | |
{ | |
vec4 col = texture2D(texture, vec2(mod(vTexCoord.x+offset2,1.0),mod(vTexCoord.y,1.0)))*vColor; | |
float f = clamp(distance(vPosition.xyz,camPos)/fog,0.0,1.0); | |
col = mix( col, mistColor, f); | |
col.a=1.0; | |
gl_FragColor=col; | |
} | |
]]} | |
TankShader={ | |
v=[[ | |
uniform mat4 modelViewProjection; | |
uniform mat4 mModel; | |
uniform vec4 directColor; | |
uniform vec4 directDirection; | |
uniform vec4 ambientColor; | |
uniform float offset; | |
attribute vec4 position; | |
attribute vec3 normal; | |
attribute vec2 texCoord; | |
varying lowp vec4 vColor; | |
varying highp vec2 vTexCoord; | |
varying highp vec4 vPosition; | |
void main() | |
{ | |
gl_Position = modelViewProjection * position; | |
vTexCoord = vec2(texCoord.x+offset,texCoord.y); | |
vec4 norm = normalize(mModel * vec4( normal, 0.0 )); | |
float diffuse = max( 0.0, dot( norm, directDirection )); | |
vPosition = mModel*position; | |
vColor = diffuse * directColor + ambientColor; | |
} | |
]], | |
f=[[ | |
precision highp float; | |
uniform lowp sampler2D texture; | |
uniform float offset2; | |
varying lowp vec4 vColor; | |
varying highp vec2 vTexCoord; | |
varying highp vec4 vPosition; | |
void main() | |
{ | |
vec4 col = texture2D(texture, vec2(mod(vTexCoord.x+offset2,1.0),mod(vTexCoord.y,1.0)))*vColor; | |
col.a=1.0; | |
gl_FragColor=col; | |
} | |
]]} | |
TreeShader={ | |
v=[[ | |
uniform mat4 modelViewProjection; | |
attribute vec4 position; | |
attribute vec2 texCoord; | |
varying highp vec2 vTexCoord; | |
void main() | |
{ | |
gl_Position = modelViewProjection * position; | |
vTexCoord = texCoord; | |
} | |
]], | |
f=[[ | |
precision highp float; | |
uniform lowp sampler2D texture; | |
uniform vec4 mistColor; | |
uniform float visibility; | |
varying highp vec2 vTexCoord; | |
void main() | |
{ | |
vec4 col = texture2D(texture,vTexCoord); | |
if (col.a<0.05) discard; | |
else { | |
col=mix(mistColor,col,visibility); | |
gl_FragColor=col; | |
} | |
} | |
]]} | |
LightingShader={ | |
v=[[ | |
uniform mat4 modelViewProjection; | |
uniform mat4 mModel; | |
uniform vec4 directColor; | |
uniform vec4 directDirection; | |
uniform vec4 ambientColor; | |
uniform float fog; | |
uniform vec4 mistColor; | |
uniform vec3 camPos; | |
attribute vec4 position; | |
attribute vec4 color; | |
attribute vec3 normal; | |
varying lowp vec4 vColor; | |
void main() | |
{ | |
gl_Position = modelViewProjection * position; | |
vec4 norm = normalize(mModel * vec4( normal, 0.0 )); | |
float diffuse = max( 0.0, dot( norm, directDirection )); | |
vec4 p=mModel*position; | |
float f = clamp(distance(p.xyz,camPos)/fog,0.0,1.0); | |
vColor = mix(color * ( diffuse * directColor + ambientColor ),mistColor,f); | |
vColor.a=1.0; | |
} | |
]], | |
f=[[ | |
precision highp float; | |
varying lowp vec4 vColor; | |
void main() | |
{ | |
gl_FragColor=vColor; | |
} | |
]]} | |
--Tile | |
TileShader = { | |
v = [[ | |
uniform mat4 modelViewProjection; | |
uniform mat4 mModel; | |
attribute vec4 position; | |
attribute vec2 texCoord; | |
varying highp vec2 vTexCoord; | |
varying highp vec4 vPosition; | |
void main() | |
{ | |
vTexCoord = texCoord; | |
vPosition=mModel*position; | |
gl_Position = modelViewProjection * position; | |
} | |
]], | |
f = [[ | |
precision highp float; | |
uniform lowp sampler2D texture; | |
uniform float fog; | |
uniform vec4 mistColor; | |
uniform vec3 camPos; | |
varying highp vec2 vTexCoord; | |
varying highp vec4 vPosition; | |
void main() | |
{ | |
lowp vec4 col = texture2D(texture, vec2(mod(vTexCoord.x,1.0),mod(vTexCoord.y,1.0))); | |
float f = clamp(distance(vPosition.xyz,camPos)/fog,0.0,1.0); | |
gl_FragColor = mix(col,mistColor,f); | |
} | |
]]} | |
--# Terrain | |
--Terrain2 | |
local atan,rand,min,max,cos=math.atan,math.random,math.min,math.max,math.cos | |
local rad=math.pi/180 | |
local Terrain={} | |
function MakeTerrain(w,d,pic,f) | |
local w,d,pic,f=w or 1000,d or 1000,pic or "Dropbox:3D-grass2x",f or .02 | |
LoadImages() | |
local floor=mesh() | |
local img=readImage(pic) | |
floor.texture=img | |
local wn,dn=w/img.width/f,d/img.height/f | |
floor.vertices={vec3(0,0,0),vec3(w,0,0),vec3(w,0,-d),vec3(w,0,-d),vec3(0,0,-d),vec3(0,0,0)} | |
floor.texCoords={vec2(0,0),vec2(wn,0),vec2(wn,dn),vec2(wn,dn),vec2(0,dn),vec2(0,0)} | |
floor=SetLighting(floor,TileShader) | |
local trees={} | |
for i=1,200 do | |
table.insert(trees,AddBillboard(0,0,w,-d,treeImages,0.15)) | |
end | |
Terrain={floor=floor,trees=trees} | |
end | |
function LoadImages() | |
treeImages={readImage("Dropbox:3D-tree"),readImage("Dropbox:3D-tree3"),readImage("Dropbox:3D-tree7"), | |
readImage("Dropbox:3D-tree8")} | |
end | |
function AddBillboard(x1,z1,x2,z2,imgList,f) | |
local i=rand(1,#imgList) | |
local h=0.5+rand() | |
local w=imgList[i].width*h*f --width | |
local pos=vec3(rand(x1,x2),imgList[i].height*f/4*h,rand(min(z1,z2),max(z1,z2))) | |
local m=mesh() | |
m:addRect(0,0,w,imgList[i].height*h*f) | |
m.texture=imgList[i] | |
m.shader=shader(TreeShader.v,TreeShader.f) | |
m.shader.mistColor=backgroundColor | |
return {m=m,pos=pos,r=w/2} | |
end | |
function DrawTerrain() | |
Terrain.floor.shader.camPos=camPos | |
Terrain.floor:draw() | |
DrawBillboards(Terrain.trees,camPos) | |
end | |
function LookAt(source,target) | |
local z=(source-target):normalize() | |
local x=(vec3(0,1,0):cross(z)):normalize() | |
local y=(z:cross(x)):normalize() | |
return matrix(x.x,x.y,x.z,0, y.x,y.y,y.z,0, z.x,z.y,z.z,0, source.x,source.y,source.z,1) | |
end | |
function GetScreenRect(t1,t2,s) --t1 is camera, t2 is target, s is vec3 size of object | |
--get direction and distance | |
local v=(t2.pos-t1.pos) | |
local d=v:len()-u/2 --position of t2 is assumed to be its centre, so adjust by 1\2 depth | |
v=v/d | |
--get drawing rectangle | |
local w = u*WIDTH*projectionMatrix()[1]/d/2 | |
local h = s.y*HEIGHT*projectionMatrix()[6]/d/2 | |
--draw our tank | |
local m = modelMatrix()*viewMatrix()*projectionMatrix() | |
local X,Y=(m[13]/m[16]+1)*WIDTH/2, (m[14]/m[16]+1)*HEIGHT/2 | |
return X-w/2,Y-h/2,w,h | |
end | |
function DrawBillboards(billboards) | |
local PM1=projectionMatrix()[1] | |
for _,b in pairs(billboards) do | |
b.pov=IsVisible(b.pos,camPos,camLook,b.r) | |
b.visibility=max(0,1-b.pos:dist(camPos)/fogRange) | |
end | |
--calc distance to camera and sort from furthest to nearest, every two seconds | |
if ElapsedTime>(lastSort or -9) +2 then | |
table.sort(billboards,function(a,b) return a.visibility<b.visibility end) | |
lastSort=ElapsedTime | |
end | |
for i=1,#billboards do | |
local b=billboards[i] | |
if b.pov and b.visibility>0 then | |
pushMatrix() | |
--move to the centre of our image | |
local p=b.pos | |
translate(p:unpack()) | |
--calculate difference in x and z | |
local dx,dz=p.x-camPos.x,-p.z+camPos.z | |
--calculate angle we need to turn | |
local ang=atan(dx/-dz)/rad | |
--turn and draw | |
rotate(ang,0,1,0) | |
b.m.shader.visibility=b.visibility | |
b.m:draw() | |
popMatrix() | |
end | |
end | |
end | |
function IsVisible(pos,camPos,camLook,radius) | |
look=camPos+(camLook-camPos)*5000 | |
local xDist=(pos-camPos):dist(math.max(math.min((pos-camPos):dot(look-camPos)/ | |
(look-camPos):lenSqr(),1),0)*(look-camPos)) | |
return xDist-radius<camPos:dist(pos)/projectionMatrix()[1] | |
end | |
function IsVisible(pos,camPos,camLook,radius) | |
local xDist=DistancePointToLine(pos,camPos,camLook) | |
return xDist-radius<camPos:dist(pos)/projectionMatrix()[1] | |
end | |
function DistancePointToLine(p,a,b) | |
b=p+(b-a)*5000 | |
return (p-a):dist(math.max(math.min((p-a):dot(b-a)/ | |
(b-a):lenSqr(),1),0)*(b-camPos)) | |
end | |
--# Utility | |
--Utility | |
function CreateBlock(w,h,d,col,pos,tex,ms) --width,height,depth,colour,position,texture | |
pos=pos or vec3(0,0,0) | |
local x,X,y,Y,z,Z=pos.x-w/2,pos.x+w/2,pos.y-h/2,pos.y+h/2,pos.z-d/2,pos.z+d/2 | |
local v={vec3(x,y,Z),vec3(X,y,Z),vec3(X,Y,Z),vec3(x,Y,Z),vec3(x,y,z),vec3(X,y,z),vec3(X,Y,z),vec3(x,Y,z)} | |
local vert={v[1],v[2],v[3],v[1],v[3],v[4],v[2],v[6],v[7],v[2],v[7],v[3],v[6],v[5],v[8],v[6],v[8],v[7], | |
v[5],v[1],v[4],v[5],v[4],v[8],v[4],v[3],v[7],v[4],v[7],v[8],v[5],v[6],v[2],v[5],v[2],v[1]} | |
local texCoords | |
if tex then | |
local t={vec2(0,0),vec2(1,0),vec2(0,1),vec2(1,1)} | |
texCoords={t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3], | |
t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3]} | |
end | |
local n={vec3(0,0,1),vec3(1,0,0),vec3(0,0,-1),vec3(-1,0,0),vec3(0,1,0),vec3(0,-1,0)} | |
local norm={} | |
for i=1,6 do for j=1,6 do norm[#norm+1]=n[i] end end | |
if not ms then ms=mesh() end | |
if ms.size==0 then | |
ms.vertices=vert | |
ms.normals=norm | |
if tex then ms.texture,ms.texCoords=tex,texCoords end | |
else | |
for i=1,#vert do | |
table.insert(ms.vertices,vert[i]) | |
table.insert(ms.normals,norm[i]) | |
if tex then table.insert(ms.texCoords,texCoords[i]) end | |
end | |
end | |
ms:setColors(col or color(255)) | |
return ms | |
end | |
function AddBlock(w,h,d,col,pos,rot,texture,vertices,texCoords,normals,colors) --width,height,depth,colour,position,texture | |
local x,X,y,Y,z,Z=-w/2,w/2,-h/2,h/2,-d/2,d/2 | |
local v={vec3(x,y,Z),vec3(X,y,Z),vec3(X,Y,Z),vec3(x,Y,Z),vec3(x,y,z),vec3(X,y,z),vec3(X,Y,z),vec3(x,Y,z)} | |
local vert={v[1],v[2],v[3],v[1],v[3],v[4],v[2],v[6],v[7],v[2],v[7],v[3],v[6],v[5],v[8],v[6],v[8],v[7], | |
v[5],v[1],v[4],v[5],v[4],v[8],v[4],v[3],v[7],v[4],v[7],v[8],v[5],v[6],v[2],v[5],v[2],v[1]} | |
if rot then | |
m=modelMatrix() | |
m=m:translate(pos:unpack()) | |
m=m:rotate(rot.x,1,0,0) m=m:rotate(r.y,0,1,0) m=m:rotate(r.z,0,0,1) | |
for i=1,#vert do vertices[#vertices+1]=m*vert[i] end | |
else | |
for i=1,#vert do vertices[#vertices+1]=pos+vert[i] end | |
end | |
local tex | |
if texture then | |
local t={vec2(0,0),vec2(1,0),vec2(0,1),vec2(1,1)} | |
tex={t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3], | |
t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3]} | |
for i=1,#tex do texCoords[#texCoords+1]=tex[i] end | |
end | |
local n={vec3(0,0,1),vec3(1,0,0),vec3(0,0,-1),vec3(-1,0,0),vec3(0,1,0),vec3(0,-1,0)} | |
for i=1,6 do | |
for j=1,6 do | |
normals[#normals+1]=n[i] | |
colors[#colors+1]=col | |
end | |
end | |
return vertices,texCoords,normals,colors | |
end | |
--o is start of line, d is normalised line direction | |
--v0, v1, v2 are triangle vertices | |
function LineIntersectsTriangle(o,d,v1,v2,v3,n) | |
if d:dot(n)>0 then return end | |
local e1,e2=v2-v1,v3-v1 | |
local p=d:cross(e2) | |
local det=e1:dot(p) | |
if math.abs(det)<0.001 then return end | |
local inv_det=1/det | |
local t=o-v1 | |
local u=t:dot(p)*inv_det | |
if u<0 or u>1 then return end | |
local q=t:cross(e1) | |
local v=d:dot(q)*inv_det | |
if v<0 or u+v>1 then return end | |
local a=e2:dot(q)*inv_det | |
if a>0 then return a,o+d*a end | |
end | |
function LookAtMatrix(source,target,up) | |
local Z=(source-target):normalize() | |
up=up or vec3(0,1,0) | |
local X=(up:cross(Z)):normalize() | |
local Y=(Z:cross(X)):normalize() | |
return matrix(X.x,X.y,X.z,0,Y.x,Y.y,Y.z,0,Z.x,Z.y,Z.z,0,source.x,source.y,source.z,1) | |
end | |
function RotateVector(v,y,z) | |
local m=matrix(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1) | |
if y then m=m:rotate(y,0,1,0) end | |
if z then m=m:rotate(z,0,0,1) end | |
return m*v,m | |
end | |
function Limit(a,L) | |
if a>0 then return math.min(a,L) else return -math.min(-a,L) end | |
end | |
function lineDistB(p,a,b) | |
return (p-a):dist(math.max(math.min((p-a):dot(b-a)/(b-a):lenSqr(),1),0)*(b-a)) | |
end | |
--# Model | |
--Model | |
Tank=class() | |
Tank.turretMaxAngle=40 | |
Tank.barrelMaxAngles=vec2(-5,20) | |
Tank.maxSpeed=vec2(5,16) --pixels/sec | |
Tank.maxSpeedChange=0.1 | |
Tank.reloadTime=5 | |
Tank.width=19 | |
Tank.count=0 | |
Tank.shotRadius=15 | |
Tank.bounceDot=0.35 | |
Tank.damageReductionWithDistance=1/10000 | |
Tank.shotForce=50 | |
Tank.turnRate=0.5 | |
Tank.turretTurnRate=1 | |
Tank.collisionRange=50 | |
Tank.avoidStrength=50 | |
Tank.colors={color(158, 171, 48, 255),color(74, 81, 45, 255)} | |
function Tank:init(p) | |
if not Tank.model then | |
Tank.LoadTextures() | |
Tank.CreateModel() | |
end | |
self.trackOffset=0 | |
self.turretAngle=0 | |
self.gunAngle=0 | |
self.startOfBarrel=vec3(-3.8,5.75,0) | |
self.endOfBarrel=vec3(-10,0,0) --position of end of barrel relative to its base | |
self.muzzleSpeed=2000 --px/s | |
self.health=100 | |
self.speed=0.25 | |
self.angle=-90 | |
self.angleTarget=self.angle | |
self.rad=math.rad(self.angle) | |
self.pos=vec3(0,0,0) | |
self.offset2=math.random() | |
self.wobble=math.random() | |
self.lastShot=-99 | |
Tank.count=Tank.count+1 | |
self.player=p | |
self.id=self | |
self.ammoStatus,self.speedStatus,self.aimStatus,self.visStatus,self.rotationStatus=1,1,1,1,1 | |
self.health=1 | |
end | |
function Tank:draw(camPos,camLook) | |
self.trackOffset=self.trackOffset-self.speed/200 | |
self:Drive() | |
if self.player and not IsVisible(self.pos,camPos,camLook,Tank.width/2) then return end | |
local m=Tank.model | |
m.track.shader.offset=self.trackOffset | |
pushMatrix() | |
translate(self.pos:unpack()) | |
rotate(self.angle,0,1,0) | |
--if self.player then self:Wobble(self.speed) end | |
m.wheels.shader.mModel=modelMatrix() | |
m.wheels.shader.camPos=camPos | |
m.wheels:draw() | |
m.track.shader.mModel=modelMatrix() | |
m.track.shader.camPos=camPos | |
m.track:draw() | |
m.body.shader.mModel=modelMatrix() | |
m.body.shader.camPos=camPos | |
m.body.shader.offset2=self.offset2 | |
m.body:draw() | |
rotate(self.turretAngle,0,1,0) | |
m.turret.shader.mModel=modelMatrix() | |
m.turret.shader.camPos=camPos | |
m.turret.shader.offset2=self.offset2 | |
m.turret:draw() | |
translate(self.startOfBarrel:unpack()) | |
rotate(-self.gunAngle,0,0,1) | |
m.barrel.shader.mModel=modelMatrix() | |
m.barrel.shader.camPos=camPos | |
m.barrel.shader.offset2=self.offset2 | |
m.barrel:draw() | |
--calculate position and angle of end of barrel | |
local mm=modelMatrix() | |
local a=vec3(mm[13],mm[14],mm[15]) | |
mm=mm:translate(self.endOfBarrel:unpack()) | |
self.muzzlePos=vec3(mm[13],mm[14],mm[15]) | |
self.muzzleAngle=(self.muzzlePos-a):normalize() | |
popMatrix() | |
end | |
function Tank:Drive() | |
if self.angleTarget==self.angle then | |
local dir=vec3(-math.cos(self.rad),0,math.sin(self.rad)) | |
local pos=self.pos+dir*self.speed*self.speedStatus*DeltaTime | |
if self.player then self.pos=pos return end | |
if pos.x<20 or pos.x>mapWidth-20 or pos.z>-20 or pos.z<20-mapHeight then | |
local a=math.random(75,105) | |
if math.random()<0.5 then self:rotate(-a) else self:rotate(a) end | |
else | |
if not self:CheckForCollisions(dir) then self.pos=pos end | |
end | |
end | |
end | |
--[[ | |
function Tank:CheckForCollisions(dir) | |
local ahead,ahead2,ahead3=self.pos+dir*Tank.collisionRange,self.pos+dir*Tank.collisionRange/2,self.pos | |
local minDist,minT=99999,nil | |
for _,t in pairs(T) do | |
if t~=self then | |
local r=Tank.shotRadius*2 | |
if ahead:dist(t.pos)<r or ahead2:dist(t.pos)<r or ahead3:dist(t.pos)<r then | |
local d=self.pos:dist(t.pos) | |
if d<minDist then minDist=d minT=t end | |
end | |
end | |
end | |
if minT then | |
local v=(ahead2-minT.pos) | |
self:rotate(math.atan(-v.z/v.x)*Tank.avoidStrength) | |
return true | |
end | |
end | |
function Tank:CheckForCollisions(dir) | |
local minDist,minP=Tank.collisionRange,nil | |
for _,t in pairs(T) do | |
if t~=self then | |
local d=self.pos:dist(t.pos) | |
if d<minDist then | |
local p=self.pos+d*dir-t.pos | |
if p:len()<Tank.shotRadius*2 then | |
minDist=d | |
minP=p | |
end | |
end | |
end | |
end | |
if minP then | |
self:rotate(math.atan(-minP.z/minP.x)*Tank.avoidStrength) | |
return true | |
end | |
end | |
--]] | |
function Tank:CheckForCollisions(dir) | |
local minDist,minP,minT=Tank.collisionRange | |
for _,t in pairs(T) do | |
if t~=self then | |
local d=self.pos:dist(t.pos) | |
if d<minDist then | |
local p=self.pos+d*dir-t.pos | |
if p:len()<Tank.shotRadius*2 then | |
minDist=d | |
--minP=p | |
minT=t.pos | |
end | |
end | |
end | |
end | |
if minT then | |
local p=minT-self.pos | |
local a=math.random(10,30)*dir:dot(p)/minDist | |
if math.atan2(-p.z/p.x)<self.angle then self:rotate(a) else self:rotate(-a) end | |
--math.atan(-minP.z/minP.x)*Tank.avoidStrength) | |
return true | |
end | |
end | |
function Tank:update(a1,s,a2,a3) | |
self:rotate(-a1/4) | |
local ss | |
if s<0 then ss=s*Tank.maxSpeed[1] else ss=s*Tank.maxSpeed[2] end | |
self.speed=self.speed+math.max(-Tank.maxSpeedChange,math.min(Tank.maxSpeedChange,ss-self.speed)) | |
self.turretAngle=math.max(-Tank.turretMaxAngle,math.min(Tank.turretMaxAngle,self.turretAngle-a2/2)) | |
self.gunAngle=math.max(Tank.barrelMaxAngles[1],math.min(Tank.barrelMaxAngles[2],self.gunAngle+a3/5)) | |
end | |
function Tank:rotate(a,NoLimit) | |
self.angleTarget=self.angleTarget+a | |
if NoLimit then | |
self.angle=self.angleTarget | |
else | |
self.angle=self.angle+Limit(self.angleTarget-self.angle,Tank.turnRate) | |
end | |
self.rad=math.rad(self.angle) | |
end | |
function Tank:rotateTurret(a) | |
self.turretAngle=self.turretAngle+a*self.rotationStatus | |
end | |
function Tank:rotateBarrel(a) | |
self.barrelAngle=self.barrelAngle+a | |
end | |
function Tank:SetAim() | |
local m=matrix(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1) | |
m=m:translate(self.pos:unpack()) | |
m=m:rotate(self.angle,0,1,0) | |
m=m:rotate(self.turretAngle,0,1,0) | |
m=m:translate(self.startOfBarrel:unpack()) | |
local b1=vec3(m[13],m[14],m[15]) | |
m=m:rotate(-self.gunAngle,0,0,1) | |
--calculate position and angle of end of barrel | |
m=m:translate(self.endOfBarrel:unpack()) | |
local b2=vec3(m[13],m[14],m[15]) | |
local a=(b2-b1):normalize() | |
self.muzzlePos,self.muzzleAngle=b2,a | |
return b1,b2,a | |
end | |
function Tank:Shoot() | |
if not self:ready() then return end | |
AddShot(self.muzzlePos,self.muzzleAngle,self.muzzleSpeed) | |
self.lastShot=ElapsedTime | |
end | |
function Tank:ready() | |
return ElapsedTime>self.lastShot+Tank.reloadTime | |
end | |
function Tank:Wobble(speed) | |
local f,e=math.abs(.2*speed/60),ElapsedTime+self.wobble | |
local a,b=e*f,e*math.pi*f | |
rotate(10*noise(a,b),1,0,0) | |
rotate(3*noise(b,a),0,0,1) | |
end | |
function Tank.LoadTextures() | |
Tank.Shot=MakeShell() | |
Tank.texBody=readImage("Dropbox:Tank_Body") | |
if not Tank.texBody then | |
local w,h,f=400,400,5 | |
local img1=image(w,h) | |
for i=1,w do | |
for j=1,h do | |
local m=(noise(i/w*f,j/h*f)+1)/2 | |
img1:set(i,j,Tank.colors[1]:mix(Tank.colors[2],m)) | |
end | |
end | |
saveImage("Dropbox:Tank_Body",img1) | |
Tank.texBody=img1 | |
end | |
--Track------- | |
Tank.texTrack=readImage("Dropbox:Tank_Track") | |
if not Tank.texTrack then | |
local img=image(50,5) | |
setContext(img) | |
pushStyle() | |
noSmooth() | |
background(123, 122, 122, 255) | |
stroke(25) | |
strokeWidth(0.5) | |
for i=1,img.width do | |
line(i,0,i,5) | |
end | |
popStyle() | |
setContext() | |
saveImage("Dropbox:Tank_Track",img) | |
Tank.texTrack=img | |
end | |
--Wheel------- | |
Tank.texWheel=readImage("Dropbox:Tank_Wheel") | |
if not Tank.texWheel then | |
img=image(100,10) | |
local col=Tank.colors[1]*2/3+Tank.colors[2]/3 col.a=255 | |
print(col) | |
setContext(img) | |
background(0) | |
pushStyle() | |
noSmooth() | |
fill(col) | |
stroke(Tank.colors[2]) | |
strokeWidth(2) | |
for i=1,4 do | |
ellipse(i*26-11,10,20) | |
end | |
fill(Tank.colors[2]) | |
for i=1,4 do | |
ellipse(i*26-11,10,4) | |
end | |
fill(col) | |
for i=1,3 do | |
ellipse(2+i*26,9,20) | |
end | |
fill(Tank.colors[2]) | |
for i=1,4 do | |
ellipse(2+i*26,9,4) | |
end | |
popStyle() | |
setContext() | |
saveImage("Dropbox:Tank_Wheel",img) | |
Tank.texWheel=img | |
end | |
end | |
function Tank.CreateModel() | |
local x1,x2,x3,y1,y2,y3,z1,z2,z3,z4,f1,f2 | |
--body of tank | |
local body=mesh() | |
local v,t={},{} | |
x1,x2,y,z1,z2=-8.25,8.25,4.5,3.5,-3.5 --deck | |
v[#v+1]=vec3(x1,y,z1) v[#v+1]=vec3(x2,y,z1) v[#v+1]=vec3(x2,y,z2) | |
v[#v+1]=vec3(x2,y,z2) v[#v+1]=vec3(x1,y,z2) v[#v+1]=vec3(x1,y,z1) | |
x,y1,y2,z1,z2=8.25,2,4.5,3.5,-3.5 --back | |
v[#v+1]=vec3(x,y1,z1) v[#v+1]=vec3(x,y1,z2) v[#v+1]=vec3(x,y2,z2) | |
v[#v+1]=vec3(x,y2,z2) v[#v+1]=vec3(x,y2,z1) v[#v+1]=vec3(x,y1,z1) | |
x1,x2,y1,y2,z1,z2,z3,z4=-11,-8.25,3,4.5,-4.5,4.5,-3.5,3.5 --front #2 angle | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x1,y1,z2) v[#v+1]=vec3(x2,y2,z4) | |
v[#v+1]=vec3(x2,y2,z4) v[#v+1]=vec3(x2,y2,z3) v[#v+1]=vec3(x1,y1,z1) | |
x1,x2,y1,y2,y3,z1,z2=-8.25,8.25,2,2,3.5,4.5,-4.5 --sides | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x2,y2,z1) v[#v+1]=vec3(x2,y3,z1) | |
v[#v+1]=vec3(x2,y3,z1) v[#v+1]=vec3(x1,y3,z1) v[#v+1]=vec3(x1,y1,z1) | |
v[#v+1]=vec3(x2,y2,z2) v[#v+1]=vec3(x1,y1,z2) v[#v+1]=vec3(x1,y3,z2) | |
v[#v+1]=vec3(x1,y3,z2) v[#v+1]=vec3(x2,y3,z2) v[#v+1]=vec3(x2,y2,z2) | |
x1,x2,y1,y2,z1,z2=-8.25,8.25,3.5,4.5,4.5,3.5 --sloping sides | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x2,y1,z1) v[#v+1]=vec3(x2,y2,z2) | |
v[#v+1]=vec3(x2,y2,z2) v[#v+1]=vec3(x1,y2,z2) v[#v+1]=vec3(x1,y1,z1) | |
x1,x2,y1,y2,z1,z2=8.25,-8.25,3.5,4.5,-4.5,-3.5 | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x2,y1,z1) v[#v+1]=vec3(x2,y2,z2) | |
v[#v+1]=vec3(x2,y2,z2) v[#v+1]=vec3(x1,y2,z2) v[#v+1]=vec3(x1,y1,z1) | |
x1,x2,y1,z1,z2=-10,7.25,1,-2.5,2.5 --underneath | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x2,y1,z1) v[#v+1]=vec3(x2,y1,z2) | |
v[#v+1]=vec3(x2,y1,z2) v[#v+1]=vec3(x1,y1,z2) v[#v+1]=vec3(x1,y1,z1) | |
x,y1,y2,z1,z2=-11,2,3,-4.5,4.5 --front bottom | |
v[#v+1]=vec3(x,y1,z1) v[#v+1]=vec3(x,y1,z2) v[#v+1]=vec3(x,y2,z2) | |
v[#v+1]=vec3(x,y2,z2) v[#v+1]=vec3(x,y2,z1) v[#v+1]=vec3(x,y1,z1) | |
x1,x2,y1,y2,z1,z2=-10,-11,1,2,-2.5,2.5 --front under slant | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x1,y1,z2) v[#v+1]=vec3(x2,y2,z2) | |
v[#v+1]=vec3(x2,y2,z2) v[#v+1]=vec3(x2,y2,z1) v[#v+1]=vec3(x1,y1,z1) | |
x1,x2,y1,y2,z1,z2=7.25,8.25,1,2,2.5,-2.5 --back under slant | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x1,y1,z2) v[#v+1]=vec3(x2,y2,z2) | |
v[#v+1]=vec3(x2,y2,z2) v[#v+1]=vec3(x2,y2,z1) v[#v+1]=vec3(x1,y1,z1) | |
x1,x2,x3,y1,y2,y3,z1,z2=-11,-8.25,-8.25,3,2,3.5,4.5,3.5 --fill the cracks | |
v[#v+1]=vec3(x,y1,z1) v[#v+1]=vec3(x2,y2,z1) v[#v+1]=vec3(x3,y3,z1) | |
z1=-4.5 | |
v[#v+1]=vec3(x,y1,z1) v[#v+1]=vec3(x3,y3,z1) v[#v+1]=vec3(x2,y2,z1) | |
x1,x2,x3,y1,y2,y3,z1=-11,-11,-8.25,3,2,2,4.5 | |
v[#v+1]=vec3(x,y1,z1) v[#v+1]=vec3(x2,y2,z1) v[#v+1]=vec3(x3,y3,z1) | |
z1=-4.5 | |
v[#v+1]=vec3(x,y1,z1) v[#v+1]=vec3(x3,y3,z1) v[#v+1]=vec3(x2,y2,z1) | |
x1,x2,y1,y2,y3,z1,z2=-11,-8.25,3,3.5,4.5,4.5,3.5 | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x2,y2,z1) v[#v+1]=vec3(x2,y3,z2) | |
v[#v+1]=vec3(x1,y1,-z1) v[#v+1]=vec3(x2,y3,-z2) v[#v+1]=vec3(x2,y2,-z1) | |
x1,y1,y2,z1,z2=8.25,5.5,6.5,3.5,-3.5 | |
x1,y1,y2,z1,z2=8.25,2,3.5,4.5,3.5 | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x1,y1,z2) v[#v+1]=vec3(x1,y2,z2) | |
v[#v+1]=vec3(x1,y2,z2) v[#v+1]=vec3(x1,y2,z1) v[#v+1]=vec3(x1,y1,z1) | |
v[#v+1]=vec3(x1,y1,-z1) v[#v+1]=vec3(x1,y2,-z2) v[#v+1]=vec3(x1,y1,-z2) | |
v[#v+1]=vec3(x1,y2,-z2) v[#v+1]=vec3(x1,y1,-z1) v[#v+1]=vec3(x1,y2,-z1) | |
x1,y1,y2,z1,z2=8.25,3.5,4.5,4.5,3.5 | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x1,y1,z2) v[#v+1]=vec3(x1,y2,z2) | |
z1,z2=-3.5,-4.5 | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x1,y1,z2) v[#v+1]=vec3(x1,y2,z1) | |
body.vertices=v | |
body.texture=Tank.texBody | |
body.name="body" | |
local testBody={v} | |
--tracks | |
local track,v,t=mesh(),{},{} | |
local f=.05 | |
x1,x2,y1,y2,z1,z2,f1,f2=8.25,8.25,1,2,4.5,2.5,0,f*2 --back right vert | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x1,y1,z2) v[#v+1]=vec3(x2,y2,z2) | |
v[#v+1]=vec3(x2,y2,z2) v[#v+1]=vec3(x2,y2,z1) v[#v+1]=vec3(x1,y1,z1) | |
t[#t+1]=vec2(f1,0) t[#t+1]=vec2(f1,1) t[#t+1]=vec2(f2,1) | |
t[#t+1]=vec2(f2,1) t[#t+1]=vec2(f2,0) t[#t+1]=vec2(f1,0) | |
z1,z2=-2.5,-4.5 --back left vert | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x1,y1,z2) v[#v+1]=vec3(x2,y2,z2) | |
v[#v+1]=vec3(x2,y2,z2) v[#v+1]=vec3(x2,y2,z1) v[#v+1]=vec3(x1,y1,z1) | |
t[#t+1]=vec2(f1,0) t[#t+1]=vec2(f1,1) t[#t+1]=vec2(f2,1) | |
t[#t+1]=vec2(f2,1) t[#t+1]=vec2(f2,0) t[#t+1]=vec2(f1,0) | |
x1,x2,y1,y2,z1,z2,f1,f2=7.25,8.25,0,1,4.5,2.5,f*2,f*4 --back right slant | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x1,y1,z2) v[#v+1]=vec3(x2,y2,z2) | |
v[#v+1]=vec3(x2,y2,z2) v[#v+1]=vec3(x2,y2,z1) v[#v+1]=vec3(x1,y1,z1) | |
t[#t+1]=vec2(f1,0) t[#t+1]=vec2(f1,1) t[#t+1]=vec2(f2,1) | |
t[#t+1]=vec2(f2,1) t[#t+1]=vec2(f2,0) t[#t+1]=vec2(f1,0) | |
z1,z2=-2.5,-4.5 --back left slant | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x1,y1,z2) v[#v+1]=vec3(x2,y2,z2) | |
v[#v+1]=vec3(x2,y2,z2) v[#v+1]=vec3(x2,y2,z1) v[#v+1]=vec3(x1,y1,z1) | |
t[#t+1]=vec2(f1,0) t[#t+1]=vec2(f1,1) t[#t+1]=vec2(f2,1) | |
t[#t+1]=vec2(f2,1) t[#t+1]=vec2(f2,0) t[#t+1]=vec2(f1,0) | |
x1,x2,y1,y2,z1,z2,f1,f2=-9,-11,0,2,2.5,4.5,0,f*2 --front right slant | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x1,y1,z2) v[#v+1]=vec3(x2,y2,z2) | |
v[#v+1]=vec3(x2,y2,z2) v[#v+1]=vec3(x2,y2,z1) v[#v+1]=vec3(x1,y1,z1) | |
t[#t+1]=vec2(f2,0) t[#t+1]=vec2(f2,1) t[#t+1]=vec2(f1,1) | |
t[#t+1]=vec2(f1,1) t[#t+1]=vec2(f1,0) t[#t+1]=vec2(f2,0) | |
z1,z2=-4.5,-2.5 --front left slant | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x1,y1,z2) v[#v+1]=vec3(x2,y2,z2) | |
v[#v+1]=vec3(x2,y2,z2) v[#v+1]=vec3(x2,y2,z1) v[#v+1]=vec3(x1,y1,z1) | |
t[#t+1]=vec2(f2,0) t[#t+1]=vec2(f2,1) t[#t+1]=vec2(f1,1) | |
t[#t+1]=vec2(f1,1) t[#t+1]=vec2(f1,0) t[#t+1]=vec2(f2,0) | |
x1,x2,y1,y2,z1,z2,f1,f2=-9,7.25,0,0,4.5,2.5,4*f,20.25*f --bottom right track | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x1,y1,z2) v[#v+1]=vec3(x2,y1,z2) | |
v[#v+1]=vec3(x2,y1,z2) v[#v+1]=vec3(x2,y1,z1) v[#v+1]=vec3(x1,y1,z1) | |
t[#t+1]=vec2(f1,0) t[#t+1]=vec2(f1,1) t[#t+1]=vec2(f2,1) | |
t[#t+1]=vec2(f2,1) t[#t+1]=vec2(f2,0) t[#t+1]=vec2(f1,0) | |
z1,z2=-2.5,-4.5 --bottom left track | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x1,y1,z2) v[#v+1]=vec3(x2,y1,z2) | |
v[#v+1]=vec3(x2,y1,z2) v[#v+1]=vec3(x2,y1,z1) v[#v+1]=vec3(x1,y1,z1) | |
t[#t+1]=vec2(f1,0) t[#t+1]=vec2(f1,1) t[#t+1]=vec2(f2,1) | |
t[#t+1]=vec2(f2,1) t[#t+1]=vec2(f2,0) t[#t+1]=vec2(f1,0) | |
track.vertices=v | |
track.texCoords=t | |
track.texture=Tank.texTrack | |
track.name="track" | |
wheels,v,t=mesh(),{},{} | |
x1,x2,y1,y2,z1=-11,-9,2,0,4 | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x2,y2,z1) v[#v+1]=vec3(x2,y1,z1) | |
x1,x2,y1,y2,z1=-9,7.25,2,0,4 | |
v[#v+1]=vec3(x1,y2,z1) v[#v+1]=vec3(x2,y2,z1) v[#v+1]=vec3(x2,y1,z1) | |
v[#v+1]=vec3(x2,y1,z1) v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x1,y2,z1) | |
x1,x2,y1,y2,y3,z1=7.25,8.25,0,1,2,4 | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x2,y2,z1) v[#v+1]=vec3(x1,y3,z1) | |
v[#v+1]=vec3(x2,y2,z1) v[#v+1]=vec3(x2,y3,z1) v[#v+1]=vec3(x1,y3,z1) | |
x1,x2,y1,y2,z1=-11,-9,2,0,-4 | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x2,y1,z1) v[#v+1]=vec3(x2,y2,z1) | |
x1,x2,y1,y2,z1=-9,7.25,2,0,-4 | |
v[#v+1]=vec3(x2,y2,z1) v[#v+1]=vec3(x1,y2,z1) v[#v+1]=vec3(x1,y1,z1) | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x2,y1,z1) v[#v+1]=vec3(x2,y2,z1) | |
x1,x2,y1,y2,y3,z1=7.25,8.25,0,1,2,-4 | |
v[#v+1]=vec3(x1,y1,z1) v[#v+1]=vec3(x1,y3,z1) v[#v+1]=vec3(x2,y2,z1) | |
v[#v+1]=vec3(x2,y2,z1) v[#v+1]=vec3(x1,y3,z1) v[#v+1]=vec3(x2,y3,z1) | |
for i=1,#v/2 do t[#t+1]=vec2((v[i].x+11)/19,v[i].y/2) end | |
for i=#v/2+1,#v do t[#t+1]=vec2(-(8.25-v[i].x)/19,v[i].y/2) end | |
wheels.vertices=v | |
wheels.texCoords=t | |
wheels.texture=Tank.texWheel | |
wheels.name="wheels" | |
local turret,v=mesh(),{} | |
local h,y,slope=2.5,4.5,0.5 | |
local p={vec3(-4,0,1.5),vec3(0,0,3.5),vec3(2,0,3),vec3(4,0,1),vec3(4,0,-1), | |
vec3(2,0,-3),vec3(0,0,-3.5),vec3(-4,0,-1.5)} | |
for i=1,#p do p[i].y=y+(p[i].x/8+0.5)*slope end | |
for i=1,#p do | |
local a,b=p[i],p[i+1] or p[1] | |
v[#v+1]=vec3(a.x,y,a.z) | |
v[#v+1]=vec3(b.x,y,b.z) | |
v[#v+1]=vec3(b.x,b.y+h,b.z) | |
v[#v+1]=vec3(b.x,b.y+h,b.z) | |
v[#v+1]=vec3(a.x,a.y+h,a.z) | |
v[#v+1]=vec3(a.x,y,a.z) | |
end | |
local norm={} | |
for i=1,#v do norm[i]=v[i]:normalize() end | |
--roof | |
for i=1,#p do | |
local a,b=p[i],p[i+1] or p[1] | |
v[#v+1]=vec3(a.x,a.y+h,a.z) | |
v[#v+1]=vec3(b.x,b.y+h,b.z) | |
v[#v+1]=vec3(0,y+h+slope/2,0) | |
for j=1,3 do norm[#norm+1]=vec3(0,1,0) end | |
end | |
turret.vertices=v | |
--turret.normals=norm | |
turret.texture=Tank.texBody | |
turret.name="turret" | |
local testTurret={v} | |
barrel,p,v,norm=mesh(),{},{},{} | |
local x1,x2,r1,r2,y=-4,-7,0.75,0.4,7.75 | |
for i=1,8 do p[i]=vec3(0,math.cos(i*math.pi/4),math.sin(i*math.pi/4)) end | |
for i=1,8 do | |
local p1,p2=p[i],p[i+1] or p[1] | |
v[#v+1]=vec3(x1,y+p1.y*r1,p1.z*r1) | |
v[#v+1]=vec3(x2,y+p1.y*r2,p1.z*r2) | |
v[#v+1]=vec3(x2,y+p2.y*r2,p2.z*r2) | |
v[#v+1]=vec3(x2,y+p2.y*r2,p2.z*r2) | |
v[#v+1]=vec3(x1,y+p2.y*r1,p2.z*r1) | |
v[#v+1]=vec3(x1,y+p1.y*r1,p1.z*r1) | |
end | |
local x1,x2,r1,r2,y=-7,-17,0.4,0.3,7.75 | |
for i=1,8 do p[i]=vec3(0,math.cos(i*math.pi/4),math.sin(i*math.pi/4)) end | |
for i=1,8 do | |
local p1,p2=p[i],p[i+1] or p[1] | |
v[#v+1]=vec3(x1,y+p1.y*r1,p1.z*r1) | |
v[#v+1]=vec3(x2,y+p1.y*r2,p1.z*r2) | |
v[#v+1]=vec3(x2,y+p2.y*r2,p2.z*r2) | |
v[#v+1]=vec3(x2,y+p2.y*r2,p2.z*r2) | |
v[#v+1]=vec3(x1,y+p2.y*r1,p2.z*r1) | |
v[#v+1]=vec3(x1,y+p1.y*r1,p1.z*r1) | |
end | |
for i=1,#v do norm[i]=(v[i]-vec3(v[i].x,y,0)):normalize() end | |
for i=1,#v do v[i].x,v[i].y=v[i].x+4,v[i].y-7.75 end | |
barrel.vertices=v | |
barrel.normals=norm | |
barrel.texture=Tank.texBody | |
barrel.name="barrel" | |
local tank={body=body,track=track,turret=turret,barrel=barrel,wheels=wheels} | |
for _,m in pairs(tank) do | |
local norm={} | |
local nn=m:buffer("normal") | |
if nn[1]==nil then | |
local v=m:buffer("position") | |
for i=1,m.size,3 do | |
local v1,v2=v[i]-v[i+2],v[i+1]-v[i+2] | |
local n=(v1:cross(v2)):normalize() | |
norm[i],norm[i+1],norm[i+2]=n,n,n | |
end | |
m.normals=norm | |
end | |
if m.texture==Tank.texBody then | |
local t={} | |
local v=m:buffer("position") | |
for i=1,m.size do | |
t[i]=vec2(v[i].x/8+v[i].y/5,v[i].z/6+v[i].y/9)/2 | |
end | |
m.texCoords=t | |
end | |
m=SetLighting(m) | |
m.shader.offset2=0 | |
end | |
--make tables for testing of penetration and damage | |
Tank.Damage={Speed=1,Rotation=2,Ammo=3,Accuracy=4,Visibility=5} | |
Tank.DamageList={"Speed","Rotation","Ammo","Accuracy","Visibility"} | |
Tank.model=tank | |
local vert,norm,armor,damage={},{},{},{} | |
local v,n=body:buffer("position"),body:buffer("normal") | |
for i=1,body.size do | |
vert[i]=v[i] | |
norm[i]=n[i] | |
if norm[i]:dot(vec3(-1,0,0))>0 then | |
armor[i]=40 | |
damage[i]=Tank.Damage.Visibility | |
else | |
armor[i]=20 | |
if norm[i]:dot(vec3(1,0,0))>0 then damage[i]=Tank.Damage.Speed | |
elseif norm[i]:dot(vec3(0,0,-1))>0 then damage[i]=Tank.Damage.Ammo | |
else damage[i]=Tank.Damage.Accuracy end | |
end | |
end | |
local testBody={vert,norm,armor,damage} | |
local vert,norm,armor,damage={},{},{},{} | |
local v,n=turret:buffer("position"),turret:buffer("normal") | |
for i=1,turret.size do vert[i]=v[i] norm[i]=n[i] armor[i]=40 end | |
for i=1,turret.size do damage[i]=Tank.Damage.Rotation end | |
local testTurret={vert,norm,armor,damage} | |
--create bounding box for tracks for penetration testing | |
local b=CreateBlock(19,2,2) | |
local v=b:buffer("position") | |
local vert,norm,armor,damage={},{},{},{} | |
for i=1,b.size do vert[#vert+1]=v[i]+vec3(0,1,-3.5) norm[#norm+1]=vec3(0,0,-1) end | |
for i=1,b.size do vert[#vert+1]=v[i]+vec3(0,1, 3.5) norm[#norm+1]=vec3(0,0, 1) end | |
for i=1,#vert do armor[i]=20 end | |
for i=1,#vert do damage[i]=Tank.Damage.Speed end | |
local testTrack={vert,norm,armor,damage} | |
Tank.Test={track=testTrack,turret=testTurret,body=testBody} | |
end | |
function Tank:PenetrationTest(pos,dir) | |
local min,h,a,ang,dam,nam=9999 | |
local v=Tank.Test.body | |
local name="body" | |
for i=1,#v[1],3 do | |
local d,p=LineIntersectsTriangle(pos,dir,v[1][i],v[1][i+1],v[1][i+2],v[2][i]) | |
if d and d<min then | |
ang=(-dir):dot(v[2][i]) | |
min,h,a,dam,nam=d,p,v[3][i],v[4][i],name | |
end | |
end | |
local v=Tank.Test.turret | |
local name="turret" | |
for i=1,#v[1],3 do | |
local d,p=LineIntersectsTriangle(pos,dir,v[1][i],v[1][i+1],v[1][i+2],v[2][i]) | |
if d and d<min then | |
ang=(-dir):dot(v[2][i]) | |
min,h,a,dam,nam=d,p,v[3][i],v[4][i],name | |
end | |
end | |
local v=Tank.Test.track | |
local name="track" | |
for i=1,#v[1],3 do | |
local d,p=LineIntersectsTriangle(pos,dir,v[1][i],v[1][i+1],v[1][i+2],v[2][i]) | |
if d and d<min then | |
ang=(-dir):dot(v[2][i]) | |
min,h,a,dam,nam=d,p,v[3][i],v[4][i],name | |
end | |
end | |
if h then self:CalcDamage(min,h,a,ang,dam,nam) end | |
return min,h,a,ang,dam,nam | |
end | |
function Tank:CalcDamage(min,h,a,ang,dam,nam) | |
if ang<Tank.bounceDot then print("bounce") return end | |
local f=(1-Tank.damageReductionWithDistance)*ang | |
if Tank.shotForce*f<a then print("no pen") return end | |
local d=0.25+0.75*math.random() | |
self.health=self.health-d/2 | |
if self.health<0 then self:Die() return | |
elseif dam==Tank.Damage.Speed then self.speedStatus=math.max(0,self.speedStatus-d) | |
elseif dam==Tank.Damage.Rotation then self.rotationStatus=math.max(0,self.rotationStatus-d) | |
elseif dam==Tank.Damage.Ammo then self.ammoStatus=math.max(0,self.ammoStatus-d) | |
elseif dam==Tank.Damage.Accuracy then self.aimStatus=math.max(0,self.aimStatus-d) | |
elseif dam==Tank.Damage.Visibility then self.visStatus=math.max(0,self.visStatus-d) | |
end | |
hitText="health="..self.health.."\nspeed="..self.speedStatus.."\nrotate="..self.rotationStatus.. | |
"\nammo="..self.ammoStatus.."\naim="..self.aimStatus.."\nvis="..self.visStatus | |
end | |
function Tank:Die() | |
T[self]=nil | |
hitText="Tank died" | |
end | |
--# Controls | |
--joystick | |
JoyStick = class() | |
local tick=60 | |
--Note all the options you can set below. Pass them through in a named table | |
function JoyStick:init(t) | |
t = t or {} | |
self.radius = t.radius or 100 --size of joystick on screen | |
self.stick = t.stick or 30 --size of inner circle | |
self.centre = t.centre or self.radius * vec2(1,1) + vec2(5,5) | |
self.damp=vec2(0.2,0.2) | |
self.position = vec2(0,0) --initial position of inner circle | |
self.target = vec2(0,0) --current position of inner circle (used when we interpolate movement) | |
self.value = vec2(0,0) | |
self.delta = vec2(0,0) | |
self.mspeed = 30 | |
self.moving = 0 | |
end | |
function JoyStick:draw() | |
ortho() | |
viewMatrix(matrix()) | |
pushStyle() | |
fill(160, 182, 191, 1) | |
stroke(0,0,0,50) | |
strokeWidth(3) | |
ellipse(self.centre.x,self.centre.y,2*self.radius) | |
fill(78, 131, 153, 1) | |
ellipse(self.centre.x+self.position.x, self.centre.y+self.position.y, self.stick*2) | |
popStyle() | |
end | |
function JoyStick:touched(t) | |
if t.state == BEGAN then | |
local v = vec2(t.x,t.y) | |
if v:dist(self.centre)<self.radius-self.stick then | |
self.touch = true | |
--else return false | |
end | |
end | |
if self.touch then | |
if t.state~=ENDED then | |
local v = vec2(t.x,t.y) | |
if v:dist(self.centre)>self.radius-self.stick then | |
v = (v - self.centre):normalize()*(self.radius - self.stick) + self.centre | |
end --set x,y values for joy based on touch | |
self.target=v - self.centre | |
else --reset joystick to centre when touch ends | |
self.target=vec2(0,0) | |
self.touch = false | |
end | |
else return false | |
end | |
return true | |
end | |
function JoyStick:update() | |
local p = self.target - self.position | |
if p:len() < tick * self.mspeed then | |
self.position = self.target | |
if not self.touch then | |
if self.moving ~= 0 then | |
self.moving = self.moving - 1 | |
end | |
else | |
self.moving = 2 | |
end | |
else | |
self.position = self.position + p:normalize() * tick * self.mspeed | |
self.moving = 2 | |
end | |
local v=self.position/(self.radius - self.stick) | |
return self:Dampen(v) | |
end | |
function JoyStick:Dampen(v) | |
if not self.damp then return v end | |
if v.x>0 then v.x=math.max(0,(v.x-self.damp.x)/(1-self.damp.x)) | |
else v.x=math.min(0,(v.x+self.damp.x)/(1-self.damp.x)) end | |
if v.y>0 then v.y=math.max(0,(v.y-self.damp.y)/(1-self.damp.y)) | |
else v.y=math.min(0,(v.y+self.damp.y)/(1-self.damp.y)) end | |
return v | |
end | |
function JoyStick:isMoving() | |
return self.moving | |
end | |
function JoyStick:isTouched() | |
return self.touch | |
end | |
--# Main | |
-- Tiger4 | |
displayMode(FULLSCREEN) | |
function setup() | |
-- backup("WoT","ver 104b","Shooting damage v3") | |
Settings() | |
end | |
function Settings() | |
backgroundColor=color(152, 180, 208, 255) | |
mapWidth,mapHeight=500,500 | |
fogRange=5000 | |
FOV=45 | |
MakeTerrain(mapWidth,mapHeight) | |
SetupTanks() | |
end | |
function SetupTanks() | |
player=Tank(true) | |
player.pos=vec3(450,0.5,-50) | |
player.turretAngle=0 | |
player:rotate(0) | |
player.gunAngle=0 | |
player.viewAngle=180 | |
T={} | |
for j=1,20 do | |
local t=Tank() | |
local keepGoing=true | |
while keepGoing do | |
keepGoing=false | |
t.pos=vec3((0.05+.9*math.random())*mapWidth,0.5,-(0.05+0.9*math.random())*mapHeight) | |
for _,tt in pairs(T) do | |
if t.pos:dist(tt.pos)<Tank.collisionRange then keepGoing=true break end | |
end | |
end | |
t:rotate(math.random(0,360),true) | |
t.turretAngle=0 | |
t.gunAngle=0 | |
t.turretAngleChange=0.25 | |
t.gunAngleChange=0.1 | |
T[t]=t | |
end | |
camAngle=math.pi/2 | |
speed=0.5 | |
FPS=60 | |
joy=JoyStick() | |
joyAiming=JoyStick{centre=vec2(310,105)} | |
firePos,fireRadius=vec2(WIDTH-100,105),100 | |
zoomPos,zoomRadius=vec2(WIDTH-105,600),50 | |
zoomLevels,currZoom={45,30,10},1 | |
end | |
function draw() | |
background(backgroundColor) | |
FPS=FPS*0.9+0.1/DeltaTime | |
perspective(zoomLevels[currZoom]) | |
local d=joy:update() | |
local aim=joyAiming:update() | |
player:update(d.x, d.y, aim.x, aim.y) | |
if joy:isTouched() or joyAiming:isTouched() then player.viewAngle=-90+player.angle+player.turretAngle end | |
SetCamera() | |
for _,t in pairs(T) do | |
t:update(0,0.5,0,0) | |
t:draw(camPos,camLook) | |
end | |
player:draw(camPos,camLook) | |
DrawShots(T) | |
DrawTerrain(camPos) | |
joy:draw() | |
joyAiming:draw() | |
DrawHUD() | |
end | |
function DrawHUD() | |
ortho() | |
viewMatrix(matrix()) | |
pushStyle() | |
if player:ready() then fill(109, 150, 205, 100) else fill(255,0,0,100) end | |
ellipse(firePos.x,firePos.y,fireRadius) | |
fill(0) | |
fontSize(18) | |
text("FPS: "..math.floor(FPS),50,HEIGHT-25) | |
text("Speed: "..math.floor(player.speed),50,HEIGHT-50) | |
if hitText then | |
fill(255) | |
text(hitText,WIDTH-100,HEIGHT-200) | |
end | |
fill(255,255,0,100) | |
if drawTouch then ellipse(drawTouch.x,drawTouch.y,25) drawTouch=nil end | |
if aiming then | |
pushStyle() | |
stroke(0) | |
strokeWidth(2) | |
line(WIDTH/2,0,WIDTH/2,HEIGHT) | |
line(0,HEIGHT/2,WIDTH,HEIGHT/2) | |
noFill() | |
ellipse(WIDTH/2,HEIGHT/2,200) | |
popStyle() | |
--draw zoom button | |
fill(196, 118, 193, 255) | |
ellipse(zoomPos.x,zoomPos.y,zoomRadius*2) | |
end | |
popStyle() | |
end | |
function SetCamera() | |
if aiming then | |
local b1,b2=player:SetAim() | |
local d=(b2-b1):normalize() | |
camPos=b2+d*2.3 | |
camLook=camPos+d | |
else | |
camPos=player.pos+RotateVector(vec3(0,20,-20),player.viewAngle,0) | |
camLook=player.pos+vec3(0,15,0) | |
end | |
camera(camPos.x,camPos.y,camPos.z,camLook.x,camLook.y,camLook.z) | |
end | |
function SetLighting(m,s) | |
s=s or TankShader | |
m.shader=shader(s.v,s.f) | |
m.shader.directDirection=vec4(-2,2,1,0):normalize() | |
m.shader.ambientColor=color(255)*0.5 | |
m.shader.directColor=color(255)*0.5 | |
m.shader.fog=fogRange | |
m.shader.mistColor=backgroundColor | |
m.shader.offset=0 | |
m.shader.mModel=modelMatrix() | |
return m | |
end | |
function touched(t) | |
drawTouch=vec2(t.x,t.y) | |
if joy:touched(t) or joyAiming:touched(t) or FireButtonTouched(t) or ZoomButtonTouched(t) then return | |
elseif t.state==MOVING then | |
player.viewAngle=player.viewAngle+2*t.deltaX/WIDTH*FOV | |
aiming=nil | |
currZoom=1 | |
end | |
end | |
function FireButtonTouched(t) | |
if t.state==ENDED and vec2(t.x,t.y):dist(firePos)<fireRadius then | |
if not aiming then | |
aiming=true | |
else | |
player:Shoot() | |
aiming=nil | |
currZoom=1 | |
end | |
return true | |
end | |
end | |
function ZoomButtonTouched(t) | |
if t.state==ENDED and vec2(t.x,t.y):dist(zoomPos)<zoomRadius then | |
currZoom=currZoom+1 | |
if currZoom>#zoomLevels then currZoom=1 end | |
return true | |
end | |
end | |
--# Test8 | |
--[[ | |
displayMode(NORMAL) | |
-- Tiger3 | |
displayMode(STANDARD) | |
function setup() | |
backgroundColor=color(255) | |
fogRange=5000 | |
FOV=45 | |
camPos=vec3(0,5,30) | |
camLook=camPos+vec3(0,0,-1) | |
ax,ay=0,0 | |
T={} | |
for j=1,1 do | |
T[j]=Tank() | |
T[j].pos=vec3(0,0.5,0) | |
T[j].turretAngle=0 | |
T[j]:rotate(180) | |
T[j].turretAngle=0--math.random(-35,35) | |
T[j].gunAngle=0--math.random(0,5) | |
T[j].turretAngleChange=0.25 | |
T[j].gunAngleChange=0.1 | |
end | |
T[1]:PenetrationTest(camPos,(camLook-camPos):normalize()) | |
end | |
function draw() | |
background(0) | |
perspective() | |
camera(camPos.x,camPos.y,camPos.z,camLook.x,camLook.y,camLook.z) | |
T[1]:draw(camPos,camLook) | |
end | |
function SetLighting(m,s) | |
s=s or TankShader | |
m.shader=shader(s.v,s.f) | |
m.shader.directDirection=vec4(-2,2,1,0):normalize() | |
m.shader.ambientColor=color(255)*0.5 | |
m.shader.directColor=color(255)*0.5 | |
m.shader.fog=fogRange | |
m.shader.mistColor=backgroundColor | |
m.shader.offset=0 | |
m.shader.mModel=modelMatrix() | |
return m | |
end | |
function touched(t) | |
if t.state~=ENDED then | |
ay,ax=ay+t.deltaX,ax+t.deltaY | |
elseif t.state==ENDED and vec2(WIDTH/2,HEIGHT/2):dist(vec2(t.x,t.y))<200 then | |
m=matrix(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1) | |
m=m:rotate(-ax,1,0,0) | |
m=m:rotate(-ay,0,1,0) | |
d=m*vec3(0,0,-1) | |
p=d*10 | |
T[1]:PenetrationTest(d,p) | |
end | |
end | |
function touched(t) | |
if t.state~=ENDED then | |
T[1]:PenetrationTest(vec3(0,0,10),vec3(0,0,-1)) | |
end | |
end | |
--]] | |
--# Backup | |
--backup | |
function backup(projectName,ver,comments,load) | |
local backupName=projectName..": "..ver..".txt" -- name of the backup file | |
local dds="/Documents/Dropbox.assetpack/" | |
local path = os.getenv("HOME").."/Documents/" | |
local file = os.getenv("HOME")..dds..backupName | |
local t=io.open(file,"r") | |
if t then | |
if load then | |
local txt=t:read('*a') | |
saveProjectTab("Recovered",txt) | |
end | |
io.close() | |
return | |
end | |
local wFd = io.open(file,"w") | |
local tabList=listProjectTabs() | |
data="--"..comments.."\n\n" | |
local txt | |
if #tabList>0 then | |
for _,tab in pairs(tabList) do | |
data=data..string.format("\n--# %s\n",tab) | |
local tt=readProjectTab(tab) | |
data=data..tt | |
if tab=="Versions" then txt=tt end | |
end | |
wFd:write(data) | |
end | |
wFd:close() | |
print(ver.." backup made") | |
--update version history | |
if not txt then txt="--[[VERSION HISTORY\n\n--]]" end | |
local i=txt:find("--]]") | |
txt=txt:sub(1,i-1)..""..ver.." ("..os.date("%c")..") - "..comments.."\n--]]" | |
saveProjectTab("Versions",txt) | |
end | |
--# Versions | |
--[[VERSION HISTORY | |
ver 101c (Sun Oct 25 17:18:22 2015) - Full controls working ver2 | |
ver 102 (Mon Oct 26 07:37:20 2015) - Aiming v1 | |
ver 102b (Mon Oct 26 08:15:19 2015) - Aiming v3 | |
ver 102c (Mon Oct 26 08:35:48 2015) - Aiming blue/red v4 | |
ver 103 (Mon Oct 26 14:23:31 2015) - Shooting seems to work v1 | |
ver 103a (Mon Oct 26 21:47:14 2015) - Shooting with angles v2 | |
ver 104 (Tue Oct 27 14:55:57 2015) - Shooting damage | |
ver 104a (Tue Oct 27 20:08:11 2015) - Shooting damage v2 | |
ver 104b (Sat Oct 31 14:39:31 2015) - Shooting damage v3 | |
--]] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment