Created
February 17, 2016 03:13
-
-
Save dermotbalson/eddcd793534eedf34c03 to your computer and use it in GitHub Desktop.
Tank 7b
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
--# Main | |
-- Tiger7b | |
displayMode(FULLSCREEN) | |
function setup() | |
backup("WoT","ver 107b","visibility") | |
Settings() | |
end | |
function Settings() | |
backgroundColor=color(152, 180, 208, 255) | |
mapWidth,mapHeight=2000,2000 | |
fogRange=150000 | |
LoadImages() | |
MakeTerrain(mapWidth,mapHeight) | |
SetupTanks() | |
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,zoomDefault={90,60,45,30,10},3,3 | |
SetZoom(zoomDefault) | |
end | |
function SetupTanks() | |
local player=Tank(true) | |
player.pos=vec3(350,0.5,-50) | |
player.turretAngle=0 | |
player:rotate(0) | |
player.gunAngle=0 | |
player.viewAngle=180 | |
player.team="A" | |
T={player=player} | |
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.team="B" | |
T[t]=t | |
end | |
---for _,t in pairs(T) do | |
-- if t.team=="B" then t.shader.teamColour=color(102, 220, 37, 50) end | |
--end | |
end | |
function draw() | |
background(backgroundColor) | |
FPS=FPS*0.8+0.2/DeltaTime | |
perspective(zoomLevels[currZoom]) | |
local d=joy:update() | |
local aim=joyAiming:update() | |
--T.player:update(d.x, d.y, aim.x, aim.y) | |
if joy:isTouched() or joyAiming:isTouched() then | |
T.player.viewAngle=-90+T.player.angle+T.player.turretAngle | |
end | |
SetCamera() | |
qq=0 | |
--for i=1,#Terrain.trees do Terrain.trees[i].occ=1 end | |
for _,t in pairs(T) do | |
if t.player then t:update(d.x, d.y, aim.x, aim.y) | |
else t:update(0,0.5,0,0) end | |
t:draw(camPos,camLook,camDir,T) | |
end | |
--T.player:draw(camPos,camLook,camDir) | |
DrawShots(T) | |
DrawTerrain(camPos) | |
joy:draw() | |
joyAiming:draw() | |
DrawHUD() | |
end | |
function DrawHUD() | |
ortho() | |
viewMatrix(matrix()) | |
pushStyle() | |
if T.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(T.player.speed),50,HEIGHT-50) | |
text("Tanks:"..qq,50,HEIGHT-75) | |
--text("Viz: "..math.floor(viz*100),50,HEIGHT-100) | |
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=T.player:SetAim() | |
local d=(b2-b1):normalize() | |
camPos=b2+d*2.3 | |
camLook=camPos+d | |
else | |
camPos=T.player.pos+RotateVector(vec3(0,20,-20),T.player.viewAngle,0) | |
camLook=T.player.pos+vec3(0,17,0) | |
end | |
camDir=(camLook-camPos):normalize() | |
--camPos=TTT.pos+vec3(0,9,0) ---zzzz | |
--camLook=(player.pos-camPos):normalize()+camPos | |
camera(camPos.x,camPos.y,camPos.z,camLook.x,camLook.y,camLook.z) | |
PrepareVisibilityTests(camPos,camDir) | |
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.4 | |
m.shader.directColor=color(255)*0.5 | |
m.shader.fog=fogRange | |
m.shader.mistColor=backgroundColor | |
m.shader.offset=0 | |
m.shader.mModel=modelMatrix() | |
m.shader.teamColour=color(102, 220, 37, 50) --tint to apply to tank colour, default is nil color(102, 220, 37, 50) | |
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 | |
T.player.viewAngle=T.player.viewAngle+2*t.deltaX/WIDTH*zoomLevels[currZoom] | |
aiming=nil | |
SetZoom(zoomDefault) | |
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 | |
T.player:Shoot() | |
aiming=nil | |
SetZoom(zoomDefault) | |
end | |
return true | |
end | |
end | |
function ZoomButtonTouched(t) | |
if t.state==ENDED and vec2(t.x,t.y):dist(zoomPos)<zoomRadius then | |
local n=currZoom+1 | |
if n>#zoomLevels then n=1 end | |
SetZoom(n) | |
return true | |
end | |
end | |
function SetZoom(n) | |
currZoom=n | |
--cosFOV=math.cos(math.rad(zoomLevels[n]*1.34/2)) | |
cosFOV=math.cos(math.rad(zoomLevels[n]/2)) | |
cosFOV2=cosFOV*cosFOV | |
sinFOV=math.sin(math.rad(zoomLevels[n]/2)) | |
end | |
--# Model | |
--Model | |
local sin,cos,atan2=math.sin,math.cos,math.atan2 | |
local min,max,pi,abs,rad,deg=math.min,math.max,math.pi,math.abs,math.rad,math.deg | |
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=30 | |
--Tank.avoidStrength=50 | |
Tank.colors={color(158, 171, 48, 255),color(74, 81, 45, 255)} | |
Tank.minRange=30 | |
Tank.maxRange=200 | |
Tank.minShootFOV=0.1 | |
--visibility checks | |
Tank.tgtFOV=rad(45/2) --the field of view used for detecting other tanks | |
Tank.tgtFOV_centre,Tank.tgtFOV_edge=.05,.1 | |
--Tank.tgtMinAngle=0.1 | |
Tank.movingAdjust=0.5 --adjustment if other tank is moving and easier to see | |
function Tank:init(p) | |
if not Tank.model then | |
--Tank.LoadTextures() | |
Tank.Shot=MakeShell() | |
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,camDir,tanks) | |
self.trackOffset=self.trackOffset-self.speed/200 | |
self:Drive() | |
if not self.player then | |
self:Detect(tanks) | |
if IsVisible(self.pos,camPos,camDir,Tank.width/2,sinFOV)==false then return end | |
end | |
qq=qq+1 | |
local m=Tank.model | |
m.track.shader.offset=self.trackOffset | |
pushMatrix() | |
translate(self.pos:unpack()) | |
rotate(self.angle,0,1,0) | |
if not 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() | |
--update position | |
local dir=vec3(-math.cos(self.rad),0,math.sin(self.rad)) | |
--temporary position value | |
local pos=self.pos+dir*self.speed*self.speedStatus*DeltaTime | |
--move player if not turning | |
if self.player then | |
if self.angleTarget==self.angle then self.pos=pos end | |
return | |
end | |
--manage the bot tanks | |
--if we are turning or hit the edge of the map, don't move | |
if self.angleTarget==self.angle then --not turning | |
--turn at the edges of the map | |
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:Detect(tanks) --check if the bot can see any enemies | |
local enemies={} | |
--now check the other tanks | |
for i,t in pairs(tanks) do | |
if t.team~=self.team then --only check enemies | |
--are they in our FOV? | |
local a=AngleBetweenVec(self.pos-t.pos,self.dir) | |
--if in FOV, get the occlusion % and target fov (width of target, as an angle) | |
if abs(a)<Tank.tgtFOV then | |
local percent,fov=self:GetOcclusion(t) | |
--is tank visible? This depends on percent, whether the other tank is moving, and where it | |
--is in our FOV | |
--calculate what percent needs to be for us to see the other tank | |
local tgtPercent=Tank.tgtFOV_centre+(Tank.tgtFOV_edge-Tank.tgtFOV_centre)*a/22.5 | |
if t.speed~=0 then tgtPercent=tgtPercent*Tank.movingAdjust end | |
if percent*fov>=tgtPercent then --we see the tank | |
self:rotate(deg(a),false,true) | |
end | |
end | |
end | |
end | |
end | |
function Tank:CheckForCollisions(dir) | |
local minDist,minP,minT=Tank.collisionRange | |
local r=Tank.shotRadius*2 | |
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 | |
if p:dist(t.pos)<r then | |
minDist=d | |
minP=p | |
minT=t.pos | |
end | |
end | |
end | |
end | |
if minT then | |
if (minP.x-self.pos.x)*(minT.z-self.pos.z)-(minP.z-self.pos.z)*(minT.x-self.pos.x)>0 then | |
self:rotate(0.5) else self:rotate(-0.5) end | |
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 | |
--local testing=true | |
function Tank:GetOcclusion(t) | |
local tiles=GetTiles(self.pos,t.pos,tileSize,treeTiles) | |
local trees={} | |
for i,tt in pairs(tiles) do | |
for j=1,#tt do | |
local jj=tt[j] | |
if not trees[jj] then | |
---treesID[jj].occ=0.75 | |
trees[jj]=treesID[jj] | |
end | |
end | |
end | |
if testing then print("self=",self.pos) print("player=",T.player.pos) end | |
local left,right=self:GetTankEdges(T.player) if testing then print(left,right) end | |
local w=right-left | |
local points={} --create table of test points | |
for i=1,19 do | |
points[i]=left+i*0.05*w | |
end | |
local n=19 | |
--test trees in our list | |
for _,c in pairs(trees) do | |
local t1,t2=GetTangentAngles(c.pos,c.r*0.9,self.pos,t.pos) if testing then print(c.pos,c.r,t1,t2) end | |
---print(t1,t2) | |
if t2<left or t1>right then --c.occ=1 | |
else | |
for i=#points,1,-1 do | |
if points[i]>t1 and points[i]<t2 then table.remove(points,i) n=n-1 end | |
end | |
end | |
end | |
testing=false | |
return n/19,w --return occlusion % and width of tank as an angle | |
end | |
local tankAngles={true,true,true,true} | |
function Tank:GetTankEdges(t) | |
local s,c=sin(t.rad),cos(t.rad) | |
local minA,maxA=999,-999 | |
local corners=Tank.Test.corners | |
local px,pz=t.pos.x,t.pos.z | |
if testing then print("angle",t.rad) end | |
local vx,vz=t.pos.x-self.pos.x,t.pos.z-self.pos.z | |
for i=1,4 do | |
local x,z=corners[i].x,corners[i].z | |
local xx,zz=px+c*x-s*z,pz+s*x+c*z | |
local dx,dz=xx-self.pos.x,zz-self.pos.z | |
a=AngleBetween(vx,vz,dx,dz) | |
minA=min(minA,a) | |
maxA=max(maxA,a) | |
end | |
if testing then print("min,max=",minA,maxA) end | |
return minA,maxA | |
end | |
function Tank:rotate(a,NoLimit,ignoreTarget) | |
if ignoreTarget then self.angleTarget=self.angle+a | |
else self.angleTarget=self.angleTarget+a end | |
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) | |
--calculate our direction vector | |
self.dir=vec3(cos(self.rad),0,-sin(self.rad)) | |
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) | |
if speed==0 then return end | |
local f,e=math.abs(1.5*speed/60),ElapsedTime+self.wobble | |
local a,b=e,e*math.pi | |
rotate(10*noise(a,b)*f,1,0,0) | |
rotate(3*noise(b,a)*f,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} | |
local corners={vec3(-11,0,-4.5),vec3(-11,0,4.5),vec3(8.25,0,-4.5),vec3(-8.25,0,4.5)} | |
Tank.Test={track=testTrack,turret=testTurret,body=testBody,corners=corners} | |
end | |
function Tank:PenetrationTest(pos,dir) | |
hitText="" | |
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 hitText="Bounce" return end | |
local f=(1-Tank.damageReductionWithDistance)*ang | |
if Tank.shotForce*f<a then hitText="No penetration" 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 | |
local f=function(a) return string.format("%.f%%", 100*a) end | |
hitText="health="..f(self.health).."\nspeed="..f(self.speedStatus).."\nrotate="..f(self.rotationStatus).. | |
"\nammo="..f(self.ammoStatus).."\naim="..f(self.aimStatus).."\nvis="..f(self.visStatus) | |
end | |
function Tank:Die() | |
T[self]=nil | |
hitText="Tank died" | |
end | |
--# Terrain | |
--Terrain2 | |
local atan,rand,min,max,cos=math.atan,math.random,math.min,math.max,math.cos | |
local rad=math.pi/180 | |
Terrain={} | |
function MakeTerrain(w,d,pic,f,t) | |
local w,d,img,f=w or 1000,d or 1000,pic or grassImage,f or .02 | |
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) | |
tileSize=t or 40 | |
local trees={} | |
treesID={} | |
for i=1,200 do | |
trees[i]=AddBillboard(i,0,0,w,-d,treeImages,0.25) | |
treesID[i]=trees[i] | |
end | |
treeTiles=AllocateTrees(trees,tileSize) | |
Terrain={floor=floor,trees=trees} | |
end | |
local allocSeq={{-1,0},{0,0},{1,0},{0,-1},{0,1}} | |
function AllocateTrees(t,tileSize) --objects need a vec3 pos property and r(adius) value | |
local L={} | |
for p=1,#t do | |
local b=t[p] | |
local a={} --prevents dups | |
for u=1,#allocSeq do | |
local i,j=allocSeq[u][1],allocSeq[u][2] | |
local x,z=(b.pos.x+i*b.r)//tileSize,(-b.pos.z+j*b.r)//tileSize | |
L[x]=L[x] or {} | |
L[x][z]=L[x][z] or {} | |
local tt=L[x][z] | |
if not a[tt] then | |
a[tt]=tt | |
tt[#tt+1]=b.id | |
end | |
end | |
end | |
return L | |
end | |
function LoadImages() | |
local img=readImage("Dropbox:TankSheet") | |
treeImages={img:copy(409,287,211,141),img:copy(432,169,139,104),img:copy(427,54,140,107), | |
img:copy(641,219,188,175),img:copy(587,57,141,104)} | |
Tank.texBody=img:copy(1,35,400,398) | |
setContext(Tank.texBody) | |
fill(198, 85, 108, 100) | |
rect(0,0,400,400) | |
setContext() | |
Tank.texWheel=img:copy(1,9,200,19) | |
setContext(Tank.texWheel) | |
fill(198, 85, 108, 75) | |
rect(0,0,200,19) | |
setContext() | |
Tank.texTrack=img:copy(210,9,100,9) | |
grassImage=img:copy(849,68,342,341) | |
end | |
function AddBillboard(id,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/2*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 --zzz | |
return {m=m,pos=pos,r=w/2,id=id} | |
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 | |
--local testing1=true | |
function DrawBillboards(billboards) | |
local PM1=projectionMatrix()[1] | |
for _,b in pairs(billboards) do | |
b.pov=IsVisible(b.pos,camPos,camDir,b.r,sinFOV) | |
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 | |
if b.lastAng and math.abs(ang-b.lastAng)>90 and ElapsedTime<b.lastDraw+1 then ang=ang+180 end | |
b.lastAng=ang | |
b.lastDraw=ElapsedTime | |
--turn and draw | |
rotate(ang,0,1,0) | |
b.m.shader.visibility=b.visibility -- *(b.occ or 1) | |
b.m:draw() | |
popMatrix() | |
end | |
end | |
--testing1=false | |
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 | |
--# 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 | |
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-a)) | |
end | |
local atan2=math.atan2 | |
function GetTangentAngles(centre,radius,camPos,targetPos) | |
local cx,cz,cpx,cpz=centre.x,centre.z,camPos.x,camPos.z | |
--calculate offset from centre | |
local ax,az=cx-cpx,cz-cpz | |
local d=radius*(ax*ax+az*az)^-0.5 | |
local dx,dz=ax*d,az*d --print("dx,dz",dx,dz) | |
local t1,t2=vec3(cx-dz,0,cz+dx),vec3(cx+dz,0,cz-dx) --print("t1",t1) print("t2",t2) | |
local d1,d2=t1-camPos,t2-camPos | |
local p=targetPos-camPos | |
--local ta1,ta2=atan2(-d1.z,d1.x),atan2(-d2.z,d2.x) | |
local ta1,ta2=AngleBetweenVec(p,d1),AngleBetweenVec(p,d2) | |
if ta2<ta1 then ta1,ta2=ta2,ta1 end | |
return ta1,ta2 | |
end | |
function AngleBetween(x1,y1,x2,y2) | |
return atan2( x1*y2 - y1*x2, x1*x2 + y1*y2 ) | |
end | |
function AngleBetweenVec(v1,v2) | |
return atan2( v1.x*v2.z - v1.z*v2.x, v1.x*v2.x + v1.z*v2.z ) | |
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 vec4 teamColour; | |
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 = mix(diffuse * directColor + ambientColor,teamColour,teamColour.a); | |
} | |
]], | |
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); | |
} | |
]]} | |
--# 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!" end | |
--[[ | |
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 | |
--# 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 | |
ver 105 (Thu Nov 5 13:40:13 2015) - Collision | |
ver 106 (Mon Nov 16 23:00:11 2015) - culling 2 | |
ver 107 (Tue Nov 24 11:21:27 2015) - visibility | |
ver 107b (Wed Feb 17 11:12:28 2016) - visibility | |
--]] | |
--# Visibility | |
--visibility | |
local dx,dz,f1x,f1z,f2x,f2z,PM1,far | |
function PrepareVisibilityTests(camPos,camDir,f) | |
far=f or 2000 | |
PM1=1/projectionMatrix()[1] | |
dx,dz=camPos.x+camDir.x*far,camPos.z+camDir.z*far | |
local wx,wz=-far*PM1*camDir.z,far*PM1*camDir.x | |
f1x,f1z=camPos.x+camDir.x*far+wx,camPos.z+camDir.z*far+wz --far left of frustum | |
f2x,f2z=camPos.x+camDir.x*far-wx,camPos.z+camDir.z*far-wz --far right of frustum | |
end | |
function IsVisible(pos,camPos,camDir,radius,sinFOV) | |
local px,pz,pcx,pcz,pdx,pdz=pos.x,pos.z,camPos.x,camPos.z,camDir.x,camDir.z | |
--get tangent point | |
local u=radius/sinFOV | |
local tx,tz=px+pdx*u,pz+pdz*u --tangent point | |
--which side of LOS is our circle centre | |
local IsOnLeft=(px-pcx)*(dz-pcz)-(pz-pcz)*(dx-pcx)<0 | |
local wx,wz=-far*PM1*pdz,far*PM1*pdx | |
if IsOnLeft then return (tx-pcx)*(f1z-pcz)-(tz-pcz)*(f1x-pcx)>0 | |
else return (tx-pcx)*(f2z-pcz)-(tz-pcz)*(f2x-pcx)<0 end | |
end | |
function GetTangents(centre,radius,camPos) | |
local cx,cy,cpx,cpy=centre.x,centre.y,camPos.x,camPos.y | |
--calculate offset from centre | |
local ax,ay=cx-cpx,cy-cpy | |
local d=radius*(ax*ax+ay*ay)^-0.5 | |
local dx,dy=ax*d,ay*d | |
return vec2(cx-dy,cy+dx),vec2(cx+dy,cy-dx) | |
end | |
--given a 2D table "tiles" with a cell for each tile on the ground, and each cell containing a list of objects | |
--in that cell,find all cells which are in line of sight between points A and T | |
--s is cellsize (square) | |
local floor,abs=math.floor,math.abs | |
--local testing=true | |
function GetTiles(T,A,s,tiles) | |
local x0,y0,x1,y1=T.x/s,-T.z/s,A.x/s,-A.z/s --tile positions of the two points | |
if testing then print("==================") print(T) print(x0,y0) print(A) print(x1,y1) end | |
local dx = abs(x1-x0) | |
local dy = abs(y1-y0) | |
local huge=99999 | |
local tt={} | |
local x = floor(x0) | |
local y = floor(y0) | |
local n = 1 | |
local x_inc, y_inc, error | |
if dx == 0 then | |
x_inc = 0 | |
error = huge | |
elseif x1 > x0 then | |
x_inc = 1 | |
n = n + floor(x1) - x | |
error = (floor(x0) + 1 - x0) * dy | |
else | |
x_inc = -1 | |
n = n + x - floor(x1) | |
error = (x0 - floor(x0)) * dy | |
end | |
if dy == 0 then | |
y_inc = 0 | |
error =error - huge | |
elseif (y1 > y0) then | |
y_inc = 1 | |
n = n + floor(y1) - y | |
error = error - (floor(y0) + 1 - y0) * dx | |
else | |
y_inc = -1; | |
n = n + y - floor(y1) | |
error = error - (y0 - floor(y0)) * dx | |
end | |
for i = n,1,-1 do | |
--if testing then print("t=",x,y) end | |
if tiles[x] and tiles[x][y] then | |
tt[#tt+1]=tiles[x][y] | |
--if testing then print("a=",x,y,tiles[x][y][1]) end | |
end | |
if error > 0 then | |
y = y + y_inc | |
error = error - dx | |
else | |
x = x + x_inc | |
error = error + dy | |
end | |
end | |
testing=nil | |
return tt | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment