All pastes #2977125 Raw Edit

Archatrep

public unlisted lua v1 · immutable
#2977125 ·published 2015-04-20 04:30 UTC
rendered paste body
-- Archatrep for Ludum Dare by GlowingSnail, 19/04/2015-- See http://ludumdare.com/compo/ludum-dare-32/?action=preview&uid=36700-- Coordinates:----  +------->--  |      x--  |--  |y--  v----        ^ 270°--        |--  180°  |    0°--   <----+---->--        |--        |--        v 90°-- Globalstau=2*math.piFonts={}Pics={}Sounds={}Level={}HeroIndex=-1TX,TY=0,0-- Tile Size in PixelZoo={}Bullets={}GravGens={}InkClouds={}GameOver=falseShowGravField=falsePaused=false-- Localslocal FullScreen=false-- Change to true to start directly in fullscreen, without having to press Flocal ScreenShotting=false-- Make the game go slower, make right click aim at a given height, and fix window size to 548x548local function CoinFlip()	return math.random()>0.5endlocal function sign(x)	if x>0 then		return 1	elseif x<0 then		return -1	else		return 0	endendlocal function InvertAngle(r)	local r=(r+tau/4)%tau	if r>tau/2 then		return (7*tau/4-r)%tau	else		return (7*tau/4-r)%tau	endendlocal function Rotate(x,y,angle)	return	x*math.cos(angle)-y*math.sin(angle),			x*math.sin(angle)+y*math.cos(angle)endlocal function DrawCenteredText(font,text,x,y)	love.graphics.setFont(Fonts[48])	love.graphics.print(text,x-font:getWidth(text)/2,y-font:getLineHeight()/2)endlocal function NewAnimal(kind,x,y,size)	local pic=Pics[kind]	assert(pic,"Cannot find graphic for \""..kind.."\"")	local sx,sy=pic:getDimensions()	local scale=math.min(size/sx,size/sy)	return table.insert(Zoo,{kind=kind,pic=pic,x=x,y=y,r=math.random()*tau,size=size,scale=scale,ox=sx/2,oy=sy/2,dx=0,dy=0,speed=0})endlocal function NewBullet(kind,x,y,dx,dy,ttl)	local pic=Pics[kind]	assert(pic,"Cannot find graphic for \""..kind.."\"")	local sx,sy=pic:getDimensions()	return table.insert(Bullets,{kind=kind,pic=pic,x=x,y=y,size=size,scale,ox=sx/2,oy=sy/2,dx=dx or 0,dy=dy or 0,ttl=ttl})endlocal function UpdateScreenMode()	if FullScreen then		-- Fullscreen		local AvailableFullScreenModes=love.window.getFullscreenModes()		table.sort(AvailableFullScreenModes,function(a,b) return a.width*a.height>b.width*b.height end)		local Biggest=AvailableFullScreenModes[1]		love.window.setMode(Biggest.width,Biggest.height,{fullscreen=true,fullscreentype=(false and "desktop" or "normal"),})	else		-- Windowed		local dsx,dsy=love.window.getDesktopDimensions()		if ScreenShotting then			love.window.setMode(548,548,{resizable=false})		else			love.window.setMode(dsx*4/5,dsy*4/5,{resizable=true, minwidth=200, minheight=200})		end	endendlocal function InitBubbling()	Bubbles=love.graphics.newParticleSystem(Pics.bubble,100)-- Second param: Max particle count	Bubbles:setColors(255,255,255,255, 255,255,255,0)-- Up to eight r,g,b,a that particle will go through	Bubbles:setSizes(0.2,1.5)-- A series of up to eight size	Bubbles:setSizeVariation(0)	Bubbles:setPosition(Zoo[HeroIndex].x,Zoo[HeroIndex].y)-- Position of emitter	Bubbles:setAreaSpread("normal",10,10)-- The area particles are created in	Bubbles:setDirection(0)-- Doesn't matter with a 360° spread	Bubbles:setSpread(tau)-- Spread their direction all around	Bubbles:setEmissionRate(0)-- How many particles per second	Bubbles:setEmitterLifetime(-1)-- Emit forever	Bubbles:setParticleLifetime(1,3)-- min and max in seconds	Bubbles:setSpeed(2,5)-- Min and max speed	Bubbles:setLinearAcceleration(0,-8,0,-8)-- xmin,ymin,xmax,ymax 	Bubbles:setInsertMode("random")	Bubbles:setRotation(0,0)-- Min and max image rotation of particle in radians	Bubbles:start()	--echo("Bubbling activated")endlocal function InitBleeding()	BloodBlots=love.graphics.newParticleSystem(Pics.droplet,4321)-- Second param: Max particle count	BloodBlots:setColors(255,0,0,255, 91,29,29,255, 91,29,29,0)-- Up to eight r,g,b,a that particle will go through	BloodBlots:setSizes(1,0.2)-- A series of up to eight size	BloodBlots:setSizeVariation(1)	BloodBlots:setPosition(0,0)-- Position of emitter	BloodBlots:setAreaSpread("normal",7,7)-- The area particles are created in	BloodBlots:setDirection(0)-- Doesn't matter with a 360° spread	BloodBlots:setSpread(tau)-- Spread their direction all around	BloodBlots:setEmissionRate(0)-- How many particles per second	BloodBlots:setEmitterLifetime(-1)-- Emit forever	BloodBlots:setParticleLifetime(2,4)-- min and max in seconds	BloodBlots:setSpeed(5,50)-- Min and max speed	BloodBlots:setLinearAcceleration(0,40,0,40)-- xmin,ymin,xmax,ymax 	BloodBlots:setInsertMode("random")	BloodBlots:setRotation(0,tau)-- Min and max image rotation of particle in radians	BloodBlots:start()	--echo("Bleeding activated")endlocal function InitInking()	InkBlots=love.graphics.newParticleSystem(Pics.droplet,4321)-- Second param: Max particle count	InkBlots:setColors(0,0,64,255, 0,62,55,255, 26,62,0,0)-- Up to eight r,g,b,a that particle will go through	InkBlots:setSizes(0.2,1)-- A series of up to eight size	InkBlots:setSizeVariation(1)	InkBlots:setPosition(0,0)-- Position of emitter	InkBlots:setAreaSpread("normal",8,8)-- The area particles are created in	InkBlots:setDirection(0)-- Doesn't matter with a 360° spread	InkBlots:setSpread(tau)-- Spread their direction all around	InkBlots:setEmissionRate(0)-- How many particles per second	InkBlots:setEmitterLifetime(-1)-- Emit forever	InkBlots:setParticleLifetime(12,12)-- min and max in seconds	InkBlots:setSpeed(0,200)-- Min and max speed	InkBlots:setLinearDamping(0.9,2.2)-- min,max	InkBlots:setInsertMode("top")	InkBlots:setRotation(0,tau)-- Min and max image rotation of particle in radians	InkBlots:start()	--echo("Inking activated")endlocal function InitSpinnyShards()	SpinnyShards=love.graphics.newParticleSystem(Pics.spinnyshard,50)-- Second param: Max particle count	SpinnyShards:setColors(255,255,255,255, 255,255,255,0)-- Up to eight r,g,b,a that particle will go through	SpinnyShards:setSizes(0.12,0.12)	SpinnyShards:setPosition(0,0)-- Position of emitter	SpinnyShards:setAreaSpread("normal",20,20)-- The area particles are created in	SpinnyShards:setDirection(0)-- Doesn't matter with a 360° spread	SpinnyShards:setSpread(tau)-- Spread their direction all around	SpinnyShards:setEmissionRate(0)-- How many particles per second	SpinnyShards:setEmitterLifetime(-1)-- Emit forever	SpinnyShards:setParticleLifetime(4,8)-- min and max in seconds	SpinnyShards:setSpeed(90,500)-- Min and max speed	SpinnyShards:setInsertMode("random")	SpinnyShards:setRotation(0,tau)-- Min and max image rotation of particle in radians	SpinnyShards:setSpin(-3*tau,-tau/4)	SpinnyShards:start()endlocal function InitSquidShards()	SquidShards=love.graphics.newParticleSystem(Pics.squidshard,50)-- Second param: Max particle count	SquidShards:setColors(255,255,255,255, 255,255,255,0)-- Up to eight r,g,b,a that particle will go through	SquidShards:setSizes(0.07,0.13)	SquidShards:setPosition(0,0)-- Position of emitter	SquidShards:setAreaSpread("normal",20,20)-- The area particles are created in	SquidShards:setDirection(0)-- Doesn't matter with a 360° spread	SquidShards:setSpread(tau)-- Spread their direction all around	SquidShards:setEmissionRate(0)-- How many particles per second	SquidShards:setEmitterLifetime(-1)-- Emit forever	SquidShards:setParticleLifetime(3,6)-- min and max in seconds	SquidShards:setSpeed(70,400)-- Min and max speed	SquidShards:setInsertMode("random")	SquidShards:setRotation(0,tau)-- Min and max image rotation of particle in radians	SquidShards:setSpin(-2*tau,2*tau)	SquidShards:start()endlocal function InitCrabShards()	CrabShards=love.graphics.newParticleSystem(Pics.crabshard,50)-- Second param: Max particle count	CrabShards:setColors(255,255,255,255, 255,255,255,0)-- Up to eight r,g,b,a that particle will go through	CrabShards:setSizes(0.14,0.14)	CrabShards:setPosition(0,0)-- Position of emitter	CrabShards:setAreaSpread("normal",20,20)-- The area particles are created in	CrabShards:setDirection(0)-- Doesn't matter with a 360° spread	CrabShards:setSpread(tau)-- Spread their direction all around	CrabShards:setEmissionRate(0)-- How many particles per second	CrabShards:setEmitterLifetime(-1)-- Emit forever	CrabShards:setParticleLifetime(4,8)-- min and max in seconds	CrabShards:setSpeed(90,500)-- Min and max speed	CrabShards:setInsertMode("random")	CrabShards:setRotation(0,tau)-- Min and max image rotation of particle in radians	CrabShards:setSpin(-3*tau,-tau/4)	CrabShards:start()endlocal function Emit(particles,x,y,quantity)	if love.getVersion then		particles:moveTo(x,y)	else		particles:setPosition(x,y)	end	particles:emit(quantity)endlocal function InitWaving()	Waves=love.graphics.newParticleSystem(Pics.wave,4321)	Waves:setColors(255,255,255,255, 255,255,255,0)-- Up to eight r,g,b,a that particle will go through	Waves:setSizes(0.2,3)-- A series of up to eight size	Waves:setSizeVariation(0)	Waves:setPosition(0,0)-- Position of emitter	Waves:setAreaSpread("normal",1,1)-- The area particles are created in	Waves:setDirection(0)-- Doesn't matter with a 360° spread	Waves:setSpread(tau)-- Spread their direction all around	Waves:setRelativeRotation(true)	Waves:setEmissionRate(0)-- How many particles per second	Waves:setEmitterLifetime(-1)-- Emit forever	Waves:setParticleLifetime(2,3)-- min and max in seconds	Waves:setSpeed(70,90)-- Min and max speed	Waves:setInsertMode("top")	Waves:setRotation(0,0)-- Min and max image rotation of particle in radians	Waves:start()	--echo("Waving activated")endlocal function GetLocalGravity(x,y)	local lgx,lgy=0,0	for _,gg in ipairs(GravGens) do		if gg.ttl~=-1 and (x-gg.x)^2+(y-gg.y)^2>20^2 then-- ignore brand new fields, and ignore self-centered fields			local dx,dy=gg.x-x,gg.y-y			local nrm=math.sqrt(dx^2+dy^2)			--lgx,lgy=lgx+dx/nrm*gg.g,lgy+dy/nrm*gg.g -- SE type gravity			--lgx,lgy=lgx+gg.g*dx/nrm^2,lgy+gg.g*dy/nrm^2 -- Linearly decreasing gravity			lgx,lgy=lgx+gg.g*dx/nrm^3,lgy+gg.g*dy/nrm^3 -- Inverse square low, like real gravity		end	end	return lgx,lgyendlocal function IsSolidTileCoord(i,j)	if i<1 or i>Level.u or j<1 or j>Level.v then		return true	else		return Level[i][j]	endendlocal function IsSolidWorldCoord(x,y)	return IsSolidTileCoord(math.floor(x/TX),math.floor(y/TY))endlocal function GetTileIndex(i,j)	local function GetSN(n,i,j)		return IsSolidTileCoord(i,j) and n or 0	end	return GetSN(1,i,j)*(1+GetSN(2,i+1,j)+GetSN(4,i-1,j)+GetSN(8,i,j+1)+GetSN(16,i,j-1))endlocal function CreateLevelSpriteBatch()	-- Create the quads over the rock texture	local quads={}	for n=0,31 do		quads[n]=love.graphics.newQuad(n*TX,0,TX,TY,TX*32,TY)	end	-- Create the level sprite batch	local bx,by=50,50-- Number of extra tiles at the border outside level	local LevelBatch=love.graphics.newSpriteBatch(Pics.rocks,(Level.u+2*bx)*(Level.v+2*by)-(Level.u*Level.v-Level.WallCount))	for x=1-bx,Level.u+bx do		for y=1-by,Level.v+by do			local n=GetTileIndex(x,y)			if n>0 then				LevelBatch:add(quads[n],x*TX,y*TY,0,1.01,1.01)			end		end	end	Level.Batch=LevelBatchendlocal function LoadLevel()	local imgdata=love.image.newImageData("level.png")	local WallCount=0	Level={u=imgdata:getWidth(),v=imgdata:getHeight()}	for x=1,imgdata:getWidth() do		Level[x]={}		for y=1,imgdata:getHeight() do			local r,g,b,a = imgdata:getPixel(x-1,y-1)			Level[x][y]=(r+g+b==0)			if Level[x][y] then				WallCount=WallCount+1			end			if r==255 and g==0 then				if b==10 then					NewAnimal("spinny",TX*(x+0.5),TY*(y+0.5),80+math.floor(20*math.random()))				elseif b==20 then					NewAnimal("squid",TX*(x+0.5),TY*(y+0.5),70+math.floor(50*math.random()))				elseif b==30 then					NewAnimal("jelly",TX*(x+0.5),TY*(y+0.5),60+math.floor(40*math.random()))				elseif b==40 then					NewAnimal("crab",TX*(x+0.5),TY*(y+0.5),110)				end			end			if r==0 and g==255 and b==0 then				Zoo[HeroIndex].x=TX*(x+0.5)				Zoo[HeroIndex].y=TY*(y+0.5)			end		end	end	Level.WallCount=WallCount	CreateLevelSpriteBatch()endlocal function LoadImages()	love.graphics.setDefaultFilter("linear","linear",2)	local LoadImg=love.graphics.newImage	Pics={}	Pics.hero=LoadImg("hero.png")	Pics.spinny=LoadImg("spinny.png")	Pics.squid=LoadImg("squid.png")	Pics.jelly=LoadImg("jelly.png")	Pics.crab=LoadImg("crab.png")	Pics.bubble=LoadImg("bubble.png")	Pics.droplet=LoadImg("droplet.png")	Pics.spinnyshard=LoadImg("spinnyshard.png")	Pics.squidshard=LoadImg("squidshard.png")	Pics.crabshard=LoadImg("crabshard.png")	Pics.bullet=LoadImg("shot.png")	Pics.bulletexp={LoadImg("shotex1.png"),LoadImg("shotex2.png")}	Pics.wave=LoadImg("wave.png")	--- Load rocks	Pics.rocks={}	local earth=love.image.newImageData("earth.png")	local sand=love.image.newImageData("sand.png")	local msk=love.image.newImageData("r31.png")	TX,TY=msk:getWidth(),msk:getHeight()	local function IsWaterPixel(x,y)		if x<0 or x>TX-1 or y<0 or y>TY-1 then			return true		else			local r,g,b=msk:getPixel(x,y)			return r+g+b>700		end	end	local function IsRockPixel(x,y)		local r,g,b=msk:getPixel(math.max(0,math.min(TX-1,x)),math.max(0,math.min(TY-1,y)))		return r+g+b<10	end	local tiles=love.image.newImageData(TX*32,TY)	for n=1,31,2 do		msk=love.image.newImageData("r"..n..".png")		local offset=n*TX		for x=0,TX-1 do			for y=0,TY-1 do				local r,g,b=msk:getPixel(x,y)				if r+g+b<10 then--black					local inside=true					for i=-1,1 do						for j=-1,1 do							if not IsRockPixel(x+i,y+j) then								inside=false							end						end					end					if inside then						local r,g,b=earth:getPixel(x,y)						tiles:setPixel(offset+x,y,r,g,b,255)-- as rock texture					else						tiles:setPixel(offset+x,y,0,0,0,255)-- as rock border					end				elseif r+g+b>700 then-- white					local r,g,b=sand:getPixel(x,y)					tiles:setPixel(offset+x,y,r,g,b,0)-- transparent sand					local border=false					for i=-1,1 do						for j=-1,1 do							if not IsWaterPixel(x+i,y+j) then								border=true							end						end					end					tiles:setPixel(offset+x,y,0,0,0,border and 255 or 0)				else-- yellow					local r,g,b=sand:getPixel(x,y)					tiles:setPixel(offset+x,y,r,g,b,255)-- opaque sand				end			end		end		Pics.rocks=love.graphics.newImage(tiles)	endendlocal function LoadSounds()	love.audio.setDistanceModel("linear")	local LoadSound=function(src,vol)		local snd=love.audio.newSource(src,"static")		snd:setAttenuationDistances(500,1000)		if vol then			snd:setVolume(vol)		end		return snd	end	Sounds={}	Sounds.GravGen={LoadSound("gravgen1.wav"),LoadSound("gravgen2.wav"),LoadSound("gravgen3.wav"),		LoadSound("gravgen1.wav"),LoadSound("gravgen2.wav"),LoadSound("gravgen3.wav")}	Sounds.Plick={LoadSound("plick1.wav"),LoadSound("plick2.wav")}	Sounds.Splode={LoadSound("claplow1.wav"),LoadSound("claplow2.wav"),LoadSound("tube.wav")}	Sounds.Knell=LoadSound("knell.wav",0.1)	Sounds.Knife=LoadSound("knife.wav",0.1)endlocal function IsOnScreen(x,y)	local wx,wy=love.window.getWidth(),love.window.getHeight()	local cx,cy=Zoo[HeroIndex].x,Zoo[HeroIndex].y	return (x-cx)<wx/2 and (y-cy)<wy/2endlocal function PlaySoundAt(sound,x,y)	sound:setPosition(x,y,0)	love.audio.play(sound)endlocal function HasSight(oe,tx,ty,fov,dst)	-- ao: angle, absolute, of line from e to target	-- ar: angle, relative to the direction of e, of the line from e to target	local ao=math.atan2(ty-oe.y,tx-oe.x)	local ar=(ao-oe.r+4*tau)%tau	if ar>tau/2 then		ar=ar-tau	end	if fov and math.abs(ar)>fov/2 then		return false	end	local d=math.sqrt((oe.x-tx)^2+(oe.y-ty)^2)	if dst and d>dst then		return false	end	for ds=0,d,math.min(TX,TY)/2 do		-- Check points along the line, spaced by half a tile		local x,y=oe.x+ds*math.cos(ao),oe.y+ds*math.sin(ao)		-- Check if point is in a wall		if IsSolidWorldCoord(x,y) then			return false		end		-- Check if point is in an ink cloud		for _,ic in ipairs(InkClouds) do			if (x-ic.x)^2+(y-ic.y)^2<ic.r^2 then				if not dst then					InkBlocked=true				end				return false			end		end	end	return trueendlocal function WouldHitWallAt(e,d)	local hs=math.ceil(e.size*0.3)	for i=-hs,hs,hs do		for j=-hs,hs,hs do			if IsSolidWorldCoord(i+e.x+d*math.cos(e.r),j+e.y+d*math.sin(e.r)) then				return true			end		end	end	return falseendfunction love.load(arguments)	love.window.setTitle("Archatrep")	UpdateScreenMode()	Fonts[12] = love.graphics.newFont(12)	Fonts[24] = love.graphics.newFont(24)	Fonts[48] = love.graphics.newFont(48)	love.mouse.setVisible(false)	love.mouse.setGrabbed(false)	love.keyboard.setKeyRepeat(true)	LoadImages()	LoadSounds()	Zoo={}	Bullets={}	GravGens={}	InkClouds={}	NewAnimal("hero",3520,288,50)	HeroIndex=#Zoo	InitBubbling()	InitBleeding()	InitInking()	InitSpinnyShards()	InitSquidShards()	InitCrabShards()	InitWaving()	LoadLevel()endfunction love.draw()	--love.graphics.setBlendMode("additive")	--love.graphics.setBlendMode("alpha")	love.graphics.setBlendMode("alpha")	love.graphics.origin()	local wx,wy=love.window.getWidth(),love.window.getHeight()	love.graphics.translate(wx/2-Zoo[HeroIndex].x,wy/2-Zoo[HeroIndex].y)	-- Never seen, except far outside the level	love.graphics.setBackgroundColor(190,170,80,255)	-- Water	for darkness=1,256,5 do		local factor=65536/(Level.v+2)^2		local y1,y2=(darkness-5)^2/factor,darkness^2/factor		love.graphics.setColor(0,128-darkness/2,256-darkness,255)		love.graphics.polygon("fill",Zoo[HeroIndex].x-wx,y1,Zoo[HeroIndex].x+wx,y1,Zoo[HeroIndex].x+wx,y2,Zoo[HeroIndex].x-wx,y2)	end	-- Particles systems, hidden by level walls	love.graphics.draw(BloodBlots)	love.graphics.draw(SquidShards)	love.graphics.draw(SpinnyShards)	love.graphics.draw(CrabShards)	-- Level	love.graphics.setColor(255,255,255,255)	love.graphics.draw(Level.Batch)	-- Particles systems, drawn over walls	love.graphics.draw(Bubbles)	love.graphics.draw(Waves)	-- Animals	for ie=#Zoo,1,-1 do		local e=Zoo[ie]		if e.drawn then			local xmouth,ymouth=10,15			love.graphics.setColor(0,0,0,255)			love.graphics.setLineWidth(4)			for xms=-1,1,2 do				local ymouth=ymouth-1				love.graphics.line(					e.x+ymouth*math.cos(e.r)-xmouth*xms*math.sin(e.r),					e.y+ymouth*math.sin(e.r)+xmouth*xms*math.cos(e.r),					e.x+math.max(e.drawn,ymouth)*math.cos(e.r)-xmouth*xms*math.sin(e.r),					e.y+math.max(e.drawn,ymouth)*math.sin(e.r)+xmouth*xms*math.cos(e.r))			end			love.graphics.setColor(152,26,26,255)			love.graphics.setLineWidth(2)			for xms=-1,1,2 do				love.graphics.line(					e.x+ymouth*math.cos(e.r)-xmouth*xms*math.sin(e.r),					e.y+ymouth*math.sin(e.r)+xmouth*xms*math.cos(e.r),					e.x+math.max(e.drawn,ymouth)*math.cos(e.r)-xmouth*xms*math.sin(e.r),					e.y+math.max(e.drawn,ymouth)*math.sin(e.r)+xmouth*xms*math.cos(e.r))			end			love.graphics.setColor(255,255,255,255)			love.graphics.setLineWidth(1)		end		love.graphics.draw(e.pic,e.x,e.y,e.r+tau/4,e.scale,e.scale,e.ox,e.oy)	end	-- Bullets	for _,b in ipairs(Bullets) do		love.graphics.draw(b.pic,b.x,b.y,0,1,1,b.ox,b.oy)	end	-- Particles hiding everything	love.graphics.draw(InkBlots)	if ShowGravField then		-- Fade screen to white a bit		love.graphics.setColor(255,255,255,128)		love.graphics.rectangle("fill",Zoo[HeroIndex].x-0.6*wx,Zoo[HeroIndex].y-0.6*wy,1.2*wx,1.2*wy)		-- Draw the grav gen		love.graphics.setColor(128,128,255,255)-- Blueish		for _,gg in ipairs(GravGens) do			love.graphics.circle("fill",gg.x,gg.y,2.5,16)		end		-- Draw the gravity field		local GridSpacing=20		local VectorLength=30		for x=GridSpacing*(math.floor((Zoo[HeroIndex].x-wx/2)/GridSpacing)-1.5),			GridSpacing*(math.floor((Zoo[HeroIndex].x+wx/2)/GridSpacing)+1.5),GridSpacing do		for y=GridSpacing*(math.floor((Zoo[HeroIndex].y-wy/2)/GridSpacing)-1.5),			GridSpacing*(math.floor((Zoo[HeroIndex].y+wy/2)/GridSpacing)+1.5),GridSpacing do				local lgx,lgy=GetLocalGravity(x,y)--local gravity vector				local ng=math.sqrt(lgx^2+lgy^2)--local gravity norm				if ng>0.1 then-- Don't show neglible one					if ng*VectorLength>40 then						-- Too strong gravity to be shown						-- Draw black dots						love.graphics.setColor(0,0,64,255)-- Near black dots						love.graphics.circle("fill",x,y,3.2,35)					else						-- Draw dots						love.graphics.setColor(85,51,7,255)-- Dark Brown						love.graphics.circle("fill",x,y,1.9,35)						-- Draw vectors						love.graphics.setColor(255,64,64,255)-- Red						love.graphics.line(x,y,x+VectorLength*lgx,y+VectorLength*lgy)						-- Draw the side wings of the vector arrow						local ngx,ngy=lgx/ng,lgy/ng						local a1x,a1y=Rotate(ngx,ngy,tau/10)						local a2x,a2y=Rotate(ngx,ngy,-tau/10)						local tipx,tipy=x+VectorLength*lgx,y+VectorLength*lgy						love.graphics.line(tipx,tipy,tipx-a1x*5,tipy-a1y*5)						love.graphics.line(tipx,tipy,tipx-a2x*5,tipy-a2y*5)					end				end			end		end		-- Draw the ink clouds		love.graphics.setColor(61,206,16,255)		for _,ic in ipairs(InkClouds) do			love.graphics.circle("line",ic.x,ic.y,ic.r,64)		end	end	-- Instructions and other textual info	love.graphics.origin()	love.graphics.setFont(Fonts[12])	love.graphics.setColor(240,255,240,255)	if InkBlocked then		love.graphics.print("Blinded!",120,wy-46)	end	if Zoo[HeroIndex].HasEnemyInSight then		love.graphics.print("Enemy in Sight!",4,wy-46)	end	love.graphics.print("Swim with WASD or arrow keys, fire with space, E or A key.",4,wy-30)	love.graphics.print("Press F to toggle fullscreen, B for debug, P for pause, N for pic dump.",4,wy-14)	--love.graphics.print("Hero position: x="..math.floor(0.5+Zoo[HeroIndex].x)..", y="..math.floor(0.5+Zoo[HeroIndex].y).." r:"..math.floor(0.5+Zoo[HeroIndex].r*360/tau).."°",4,wy-78)	--love.graphics.print("Blood particle count: "..BloodBlots:getCount(),4,wy-94)	love.graphics.setFont(Fonts[24])	--love.graphics.print("Score: "..Score,wx-89-Fonts[24]:getWidth(Score),5)	if GameOver then		local fading=math.min(9,(love.timer.getTime()-GameOver.since))/9		love.graphics.setColor(0,0,0,255*(1-(1-fading)^2))		love.graphics.rectangle("fill",0,0,wx,wy)		love.graphics.setColor(255,50,50,255)		DrawCenteredText(Fonts[48],"Game Over",wx/2,wy/2)	end	if #Zoo==1 then		love.graphics.setColor(50,255,50,255)		DrawCenteredText(Fonts[48],"You Won!",wx/2,wy/2)	end	if Paused then		love.graphics.setColor(10,10,185,255)		DrawCenteredText(Fonts[48],"Paused",wx/2,wy*0.3)	endendfunction love.keypressed(key)	if key=="f" then		FullScreen=not FullScreen		UpdateScreenMode()	end	if key=="n" then		ScreenShotting=not ScreenShotting		UpdateScreenMode()	end	if key=="p" or key=="pause" then		Paused=not Paused	end	if false and key=="x" then		--echo("Fire!!")		local h=Zoo[HeroIndex]		local dx,dy=math.cos(h.r),math.sin(h.r)		NewBullet("bullet",h.x+50*dx,h.y+50*dy,h.dx+3*dx,h.dy+3*dy,30)	end	if key=="a" or key=="e" or key==" " then		if not Zoo[HeroIndex].drawn and not Zoo[HeroIndex].carrying then			Zoo[HeroIndex].drawn=-1		end		if Zoo[HeroIndex].carrying then			Zoo[HeroIndex].carrying.wriggle=true		end	end	if key=="b" then		ShowGravField=not ShowGravField	end	if GameOver then		if love.timer.getTime()-GameOver.since>2 then			GameOver=false			love.load()-- Let's hope reloading everything at each death won't leak too much memory...		end	end	if key=="escape" then		if love.event.quit then			love.event.quit() -- 0.8.0		else			love.event.push('q') -- 0.7.2		end	endendfunction DecayGravGens(dt)	for igg,gg in pairs(GravGens) do		gg.ttl=gg.ttl-dt		if gg.ttl<0 then			table.remove(GravGens,igg)		end	endendfunction DecayInkClouds(dt)	for iic,ic in pairs(InkClouds) do		ic.ttl=ic.ttl-dt		if ic.ttl<=0 then			table.remove(InkClouds)		end	endendfunction UpdateBullets(dt)	for ib,b in pairs(Bullets) do		b.ttl=b.ttl-dt-- Where ttl is TimeToLive		lgx,lgy=GetLocalGravity(b.x,b.y)		b.dx,b.dy=b.dx+lgx,b.dy+lgy		b.x=b.x+b.dx		b.y=b.y+b.dy		if IsSolidWorldCoord(b.x,b.y) then			b.ttl=math.min(0,b.ttl)			b.dx=0			b.dy=0		end		if b.ttl<=0 and b.ttl+dt>0 then			PlaySoundAt(Sounds.Plick[2],b.x,b.y)		end		if b.ttl<-0.9 then			table.remove(Bullets,ib)		elseif b.ttl<-0.6 then			b.pic=Pics.bulletexp[2]		elseif b.ttl<-0.3 then			b.pic=Pics.bulletexp[1]		end	endendfunction UpdateZoo(dt)	-- For all animals	for ie,e in ipairs(Zoo) do		----------------------------		-- Before position update --		----------------------------		-- Default of constant values		local maxspeed=10		local accel=1		local decel=3		if e.kind=="hero" then			-- Player react to keyboard			local wdx,wdy=0,0			if love.keyboard.isDown("up") or love.keyboard.isDown("w") then				wdy=wdy-1			end			if love.keyboard.isDown("down") or love.keyboard.isDown("s") then				wdy=wdy+1			end			if love.keyboard.isDown("left") or love.keyboard.isDown("a") then				wdx=wdx-1			end			if love.keyboard.isDown("right") or love.keyboard.isDown("d") then				wdx=wdx+1			end			e.dx=e.dx*0.95			e.dy=e.dy*0.95			e.dx=e.dx+accel*wdx			e.dy=e.dy+accel*wdy			if e.drawn then				if e.drawn==-1 then					e.drawn=100					PlaySoundAt(Sounds.Knife,e.x,e.y)					local x,y=e.x+e.drawn*math.cos(e.r),e.y+e.drawn*math.sin(e.r)					for ic,c in ipairs(Zoo) do						if ic~=ie and (c.x-x)^2+(c.y-y)^2<=(e.size*0.6+18)^2 then							if c.kind~="crab" then								c.caught={c=e,d=math.sqrt((c.x-e.x)^2+(c.y-e.y)^2),a=math.atan2(c.y-e.y,c.x-e.x)-e.r,r=c.r-e.r}								e.carrying=c								break							end						end					end				end				e.drawn=e.drawn-3				if e.drawn<=0 then					e.drawn=nil				end			end			if e.carrying then				e.carrying.caught.d=math.max(0,e.carrying.caught.d-3)				if e.carrying.kind=="jelly" and					love.keyboard.isDown("a") or love.keyboard.isDown("e") or love.keyboard.isDown(" ") then						e.carrying.wriggle=true				end			end		elseif e.kind=="spinny" then			maxspeed=5			-- Spin			e.r=e.r-tau*dt/5			-- Brownian drift			e.dx=e.dx+0.5*(2*math.random()-1)			e.dy=e.dy+0.5*(2*math.random()-1)			-- Attack if hero come close			if HasSight(e,Zoo[HeroIndex].x,Zoo[HeroIndex].y,false,384) then				e.attack=(e.attack or 0)+dt				e.dx=0				e.dy=0				e.speed=0				e.r=e.r-4*e.attack/3*tau*dt/5				if e.attack>3 then					local at=math.atan2(Zoo[HeroIndex].y-e.y,Zoo[HeroIndex].x-e.x)					for a=at-tau/8,at+tau/8+tau/64,tau/12 do						NewBullet("bullet",e.x,e.y,5*math.cos(a),5*math.sin(a),30)					end					e.attack=nil					PlaySoundAt(Sounds.Plick[1],e.x,e.y)				end			else				e.attack=nil			end		elseif e.kind=="squid" then			-- Squid movement			maxspeed=20			e.dx,e.dy=e.dx*0.9,e.dy*0.9			if e.dr then				e.r=e.r+e.dr				e.dr=e.dr*0.97				if e.dr<tau*dt/9 then					e.dr=nil				end			end			local free,ilx,ily=true,math.floor(e.x/TX),math.floor(e.y/TY)			for i=ilx-1,ilx+1 do				for j=ily-1,ily+1 do					if IsSolidTileCoord(i,j) then						free=false					end				end			end			if not free then				e.dr=e.dr or (CoinFlip() and tau or -tau)*dt/(2+math.random())			end			e.pulse=((e.pulse or 2*math.random())+dt)%2			if e.pulse%2<(e.pulse-dt)%2 then				e.dx=11*math.cos(e.r)				e.dy=11*math.sin(e.r)			end			-- Drop ink cloud if hero come close			e.inkload=(e.inkload or 30)-- Start with full charge			if e.caught and e.inkload>0 then				e.inkload=9999-- insta recharge when caught			end			if e.inkload<0 then				Emit(InkBlots,e.x,e.y,math.ceil(300*dt))-- How many blot per seconds				table.insert(InkClouds,{x=e.x,y=e.y,r=160,ttl=12})			end			if e.inkload>=30 then-- Recharge time				if ((HasSight(e,Zoo[HeroIndex].x,Zoo[HeroIndex].y,false,256) and not e.caught)					or e.wriggle) then					e.inkload=-2 -- Duration of emission					table.insert(InkClouds,{x=e.x,y=e.y,r=160,ttl=12})				end			end			if e.inkload<5000 then-- Just a big number to avoid increasing inkload indefinitly				e.inkload=e.inkload+dt			end		elseif e.kind=="jelly" then			-- Jelly movement			maxspeed=3			e.dx,e.dy=e.dx*0.9,e.dy*0.9			if e.dr then				e.r=e.r+e.dr				e.dr=e.dr*0.97				if e.dr<tau*dt/9 then					e.dr=nil				end			end			local free,ilx,ily=true,math.floor(e.x/TX),math.floor(e.y/TY)			for i=ilx-1,ilx+1 do				for j=ily-1,ily+1 do					if IsSolidTileCoord(i,j) then						free=false					end				end			end			if not free then				e.dr=e.dr or (CoinFlip() and tau or -tau)*dt/(2+math.random())			end			e.dx=2*math.cos(e.r)			e.dy=2*math.sin(e.r)			-- Wait randomly 5s to 10s between every burst			e.ttn=(e.ttn or 5+10*math.random())-dt			if (not e.caught and (e.ttn<0 and not e.caught)) or e.wriggle then-- Then fire				e.sometimes=(e.sometimes or 0)+1				table.insert(GravGens,{x=e.x,y=e.y,r=150,g=1000,ttl=0.05})				if e.sometimes%5==1 then					if IsOnScreen(e.x,e.y) then						PlaySoundAt(Sounds.GravGen[e.caught and math.random(1,3) or math.random(3,6)],e.x,e.y)					end					Emit(Waves,e.x,e.y,1)				end				if e.ttn<-5 then-- Duration of burst					e.ttn=nil				end			elseif e.sometimes then				e.sometimes=0			end		elseif e.kind=="crab" then			e.r=tau/4			if e.dx==0 then				e.dx=CoinFlip() and -1 or 1			end			local i,j=math.floor((e.x+50*e.dx)/TX),math.floor(e.y/TY)			if IsSolidTileCoord(i,j) or not IsSolidTileCoord(i,j-1) then				e.dx=-e.dx			end			e.sometimes=(e.sometimes or 0)+1			if HasSight(e,Zoo[HeroIndex].x,Zoo[HeroIndex].y,3*tau/8,false) then				if e.sometimes%5==1 then					local at=math.atan2(Zoo[HeroIndex].y-e.y,Zoo[HeroIndex].x-e.x)					for claw=-40,40,80 do						NewBullet("bullet",e.x+claw,e.y+40,15*math.cos(at),15*math.sin(at),10)					end				end			end		end		---------------------		-- Update position --		---------------------		if not (e.kind=="crab") then-- Crab cling to wall, so no gravity pull and wall check for them			-- Add Gravity Fields Effect			lgx,lgy=GetLocalGravity(e.x,e.y)			e.dx,e.dy=e.dx+lgx,e.dy+lgy			-- Limit to max speed			e.speed=math.sqrt(e.dx^2+e.dy^2)			if e.speed>maxspeed then				local a=math.atan2(e.dy,e.dx)				e.dx=maxspeed*math.cos(a)				e.dy=maxspeed*math.sin(a)				e.speed=maxspeed			end			-- Check for wall collision			local hs=math.ceil(e.size*0.3)			for i=-hs,hs,hs do				for j=-hs,hs,hs do					if IsSolidWorldCoord(i+e.x+e.dx,j+e.y+e.dy) and not (e.kind=="crab") then						e.dx=0						e.dy=0						e.speed=0					end				end			end		end		-- Proceed to new position		e.x=e.x+e.dx		e.y=e.y+e.dy		-- If attached, disregard all that and place relative to carrier		if e.caught then			local c=e.caught.c			local d,a,r=e.caught.d,e.caught.a,e.caught.r			e.x=c.x+d*math.cos(c.r+a)			e.y=c.y+d*math.sin(c.r+a)			if e.kind~="spinny" then				e.r=c.r+r			end			e.wriggle=false		end		---------------------------		-- After position update --		---------------------------		-- Check for collision against bullets		for _,b in ipairs(Bullets) do			if b.ttl>0 and (e.x-b.x)^2+(e.y-b.y)^2<=(e.size*0.4+8)^2 then				if ie==HeroIndex then					PlaySoundAt(Sounds.Knell,e.x,e.y)					GameOver={since=love.timer.getTime()}					--echo("Touched a deadly bullet!")				elseif e.kind=="spinny" then					local a=(math.atan2(b.dy,b.dx)-math.atan2(e.y-b.y,e.x-b.x)+4*tau)%tau					if (a<tau/4 or a>3*tau/4) and (e.y-b.y)^2+(e.y-b.y)^2>3^2 then						if e.caught then							e.caught.c.carrying=nil						end						b.ttl,b.dx,b.dy=0,0,0						Emit(SpinnyShards,e.x,e.y,7)						table.remove(Zoo,ie)						PlaySoundAt(Sounds.Splode[1],e.x,e.y)					end				elseif e.kind=="squid" or e.kind=="jelly" then					if e.caught then						e.caught.c.carrying=nil					end					b.ttl,b.dx,b.dy=0,0,0					Emit(SquidShards,e.x,e.y,7)					table.remove(Zoo,ie)					PlaySoundAt(Sounds.Splode[3],e.x,e.y)				elseif e.kind=="crab" then					local a=(math.atan2(b.dy,b.dx)-math.atan2(e.y-b.y,e.x-b.x)+4*tau)%tau					if (a<0.8*tau/4 or a>tau-0.8*tau/4) and (e.y-b.y)^2+(e.y-b.y)^2>3^2 then						b.ttl,b.dx,b.dy=0,0,0						Emit(CrabShards,e.x,e.y,11)						table.remove(Zoo,ie)						PlaySoundAt(Sounds.Splode[1],e.x,e.y)					end				end			end		end		if e.kind=="hero" then			--Make it face where it is going			if e.speed>0.5 then				e.r=(math.atan2(e.dy,e.dx)+4*tau)%tau			end			-- Position sound listener			love.audio.setPosition(e.x,e.y,0)			-- Emit bubbles			if love.getVersion then				Bubbles:moveTo(e.x,e.y)			else				Bubbles:setPosition(e.x,e.y)			end			Bubbles:setEmissionRate(e.speed)			-- Check for collision against enemy			e.HasEnemyInSight=false			InkBlocked=false			for ic,c in ipairs(Zoo) do				if ic~=ie and (not (c.caught and c.caught.c==e) or c.kind=="spinny") then					if HasSight(e,c.x,c.y,tau/4) then						e.HasEnemyInSight=true					end					if (e.x-c.x)^2+(e.y-c.y)^2<=(e.size*0.3+c.size*0.3)^2 then						PlaySoundAt(Sounds.Knell,e.x,e.y)						GameOver={since=love.timer.getTime()}						--echo("Touched a deadly enemy!")					end				end			end		end	endendlocal function WarnParticuleFull()	for _,p in ipairs({{Waves,"waves"},{Bubbles,"bubbles"},		{BloodBlots,"blood blots"},{InkBlots,"ink blots"},		{SpinnyShards,"spinny hards"},{SquidShards,"squid shards"},{CrabShards,"crab shards"}}) do		if p[1]:getCount()==p[1]:getBufferSize() then			--echo("Too many "..p[2])		end	endendfunction love.update(dt)	local ForcedUpdateRate=0.030	if (not GameOver) and (not Paused) then		WarnParticuleFull()		UpdateZoo(ForcedUpdateRate)		UpdateBullets(ForcedUpdateRate)		DecayGravGens(ForcedUpdateRate)		DecayInkClouds(ForcedUpdateRate)		Bubbles:update(ForcedUpdateRate)		BloodBlots:update(ForcedUpdateRate)		InkBlots:update(ForcedUpdateRate)		SpinnyShards:update(ForcedUpdateRate)		SquidShards:update(ForcedUpdateRate)		CrabShards:update(ForcedUpdateRate)		Waves:update(ForcedUpdateRate)	end	if ScreenShotting then		FrameCount=(FrameCount or 0)+1		love.graphics.newScreenshot():encode(string.format("Archatrep%04d.png",FrameCount))	else		love.timer.sleep(ForcedUpdateRate) -- 0.8.0	endend