# Defensive Firing Habits

This time the goal will be to control the firing habits of defense elements. There are several factors that can be used to control the firing habits of defenses.

## Distance to enemies

The distance to an enemy can be set for defense. This gives the option choosing defenses with greater or lesser range.

## Column position

In Plants vs Zombies defenses only fire at enemies in the same column and distance is not a factor. Though, this could be combined with distance for more variation.

## Firing angle

In the original experiments the defenses only fire straight up the screen. There’s nothing to stop us from having defenses fire at an enemy in any column, except a few lines of code.

# Firing at enemies in same column

In this experiment we’ll make the defenses fire only when there is an enemy in their column. To make this happen defenses and enemies will have to know their column number.

### make_alien()

When enemies are created I choose a random column and position the enemy in that column. At this time we need to add a property variable to the enemy and set it to the column number. The place for this to happen is make_alien().

NOTE: I realized when I created the earlier version I used the name row, when I setting the column! So I changed this variable name to col.

```local function make_alien()
local alien = display.newRect( 0, 0, 32, 32 )
alien:setFillColor( 0, 200, 0 )

local col = math.random( 1, TILE_COLS ) -- Oops! I had previously used row instead of col here!
alien.col = col 			-- Assign this alien his col position
alien.x = col * ( TILE_SIZE  + TILE_MARGIN )
alien.y = 0
alien.life = 5

local target_y = ( TILE_SIZE + TILE_MARGIN ) * TILE_ROWS
local t = ( TILE_ROWS + 1 ) * 2000
alien.transition = transition.to( alien, {y=target_y, time=t, onComplete=remove_alien} )
alien_group:insert( alien )
table.insert( alien_array, alien )
end```

### make_grid()

When creating defense elements we tap on a tile. The tile positions the new defense at the tile’s location. Since defense will no need to know their column number, the tile will also need to know it’s column number so it can pass it along to the defense that is created. Tiles are created in the make_grid() function. Here I added one line of code assigning a new property variable, col, to each tile.

```local function make_grid()
for row = 1, TILE_ROWS, 1 do
for col = 1, TILE_COLS, 1 do
local tile = display.newRect( 0, 0, TILE_SIZE, TILE_SIZE )
tile.x = ( TILE_SIZE + TILE_MARGIN ) * col
tile.y = ( TILE_SIZE + TILE_MARGIN ) * row

tile.col = col -- Tiles need to know col

tile.has_defense = false

tile_group:insert( tile )
end
end
end```

### touch_tile()

Touching a tile creates a defense. Touch events on tiles are handled in the touch_tile() handler. Here we need to get the column number from the tile and assign it to the new defense that is created. Earlier I had just made a defense with getting a reference to the defense. Note that the make_defense() function does return the new defense created! I had ignored the return value previously. We’ll use it now. Here we get a reference to the new defense and assign it a new property variable.

```local function touch_tile( event )
local phase = event.phase
if phase == "began" then
local tile = event.target
local tile_x = tile.x
local tile_y = tile.y

local defense = make_defense( tile_x, tile_y ) -- Get a reference to the new defense defense.col = tile.col -- assign the col to the new defense
end
end```

### make_defense()

Next we need defenses to think before they shoot. Each defense will need to look at all of the enemies on the screen and check their col agains the col of the defense. If cols match the defense fires, others not. To do this I added an intermediate function call from the timer, rather than call make_bullet() directly from the timer.

The new function, defense_defend( defense ), takes the defense as a parameter.

The defense timer is created when the defense is created, in make_defense(). Here’s an updated version of make_defense():

```local function make_defense( x, y )
local defense = display.newRect( 0, 0, 32, 32 )
defense:setFillColor( 200, 0, 0 )

defense_group:insert( defense )

defense.x = x
defense.y = y

table.insert( defense_array, defense )

-- Change the timer handler to call defense_defend( defense )
defense.timer = timer.performWithDelay( 1000, function() defense_defend( defense ) end, -1 )

return defense
end```

### defene_defend()

The defense_defend( defense ) function needs to examine all of the enemies and check their col. Here I will use a loop to look at each of the enemies in the alien_array. Since defenses can only fire once, if we find a target we break the loop, and stop checking for new enemies.

```-- This new function will look at the board and find eligible targets
local function defense_defend( defense )
for i = 1, #alien_array, 1 do 		-- Loop through alien_array
local alien = alien_array[i]	-- get the alien
if alien.col == defense.col then -- check column
make_bullet( defense.x, defense.y )
break
end
end
end```

Time to test! If everything is working correctly defense should now only fire when there is an enemy in their column.

Here’s a listing the entire code for this version:

```-----------------------------------------------------------------------------------------
--
-- main.lua
--
-----------------------------------------------------------------------------------------

-- Experiments controlling the firing habits of defense elements
-- This time defense elements only fire at enemies in their row

display.setStatusBar( display.HiddenStatusBar )

local TILE_ROWS = 9
local TILE_COLS = 5
local TILE_SIZE = 48
local TILE_MARGIN = 1
local BULLET_SPEED = 1000 / 400

local defense_array = {}
local alien_array = {}
local bullet_array = {}

local game_group = display.newGroup()
local defense_group = display.newGroup()
local alien_group = display.newGroup()
local tile_group = display.newGroup()

game_group:insert( tile_group )
game_group:insert( defense_group )
game_group:insert( alien_group )

local function remove_bullet( bullet )
local index = table.indexOf( bullet_array, bullet )
transition.cancel( bullet.transition )
table.remove( bullet_array, index )
display.remove( bullet )
end

local function make_bullet( x, y )
local bullet = display.newCircle( 0, 0, 5 )
bullet:setFillColor( 0, 0, 0 )
bullet.x = x
bullet.y = y
table.insert( bullet_array, bullet )

local bt = y * BULLET_SPEED
bullet.transition = transition.to( bullet, {y=0, time=bt, onComplete=remove_bullet} )
end

-- This new function will look at the board and find eligible targets
local function defense_defend( defense )
for i = 1, #alien_array, 1 do 		-- Loop through alien_array
local alien = alien_array[i]	-- get the alien
if alien.col == defense.col then -- check column
make_bullet( defense.x, defense.y )
break
end
end
end

local function remove_defense( defense )
local index = table.indexOf( defense_array, defense )
table.remove( defense_array, index )
display.remove( defense )
end

local function make_defense( x, y )
local defense = display.newRect( 0, 0, 32, 32 )
defense:setFillColor( 200, 0, 0 )

defense_group:insert( defense )

defense.x = x
defense.y = y

table.insert( defense_array, defense )

-- Change the timer handler to call defense_defend( defense )
defense.timer = timer.performWithDelay( 1000, function() defense_defend( defense ) end, -1 )

return defense
end

local function touch_tile( event )
local phase = event.phase
if phase == "began" then
local tile = event.target
local tile_x = tile.x
local tile_y = tile.y

local defense = make_defense( tile_x, tile_y ) -- Get a reference to the new defense
defense.col = tile.col 						   -- assign the col to the new defense
end
end

local function make_grid()
for row = 1, TILE_ROWS, 1 do
for col = 1, TILE_COLS, 1 do
local tile = display.newRect( 0, 0, TILE_SIZE, TILE_SIZE )
tile.x = ( TILE_SIZE + TILE_MARGIN ) * col
tile.y = ( TILE_SIZE + TILE_MARGIN ) * row

tile.col = col -- Tiles need to know col

tile.has_defense = false

tile_group:insert( tile )
end
end
end

local function remove_alien( alien )
local index = table.indexOf( alien_array, alien )
transition.cancel( alien.transition )
table.remove( alien_array, index )
display.remove( alien )
end

local function make_alien()
local alien = display.newRect( 0, 0, 32, 32 )
alien:setFillColor( 0, 200, 0 )

local col = math.random( 1, TILE_COLS ) -- Oops! I had previously used row instead of col here!
alien.col = col 						-- Assign this alien his col position
alien.x = col * ( TILE_SIZE  + TILE_MARGIN )
alien.y = 0
alien.life = 5

local target_y = ( TILE_SIZE + TILE_MARGIN ) * TILE_ROWS
local t = ( TILE_ROWS + 1 ) * 2000
alien.transition = transition.to( alien, {y=target_y, time=t, onComplete=remove_alien} )
alien_group:insert( alien )
table.insert( alien_array, alien )
end

local function hit_test( x, y, bounds )
if x > bounds.xMin and x < bounds.xMax and y > bounds.yMin and y < bounds.yMax then
return true
else
return false
end
end

local function on_enterframe( event )
for b = 1, #bullet_array, 1 do
local bullet = bullet_array[b]

if b > #bullet_array then
return
end

for a = 1, #alien_array, 1 do
local alien = alien_array[a]

if hit_test( bullet.x, bullet.y, alien.contentBounds ) then
if alien.life > 0 then
alien.life = alien.life - 1
else
remove_alien( alien )
end
remove_bullet( bullet )
break
end
end
end
end

make_grid()
local alien_timer = timer.performWithDelay( 5300, make_alien, -1 )

local memory_text = display.newText( "Hello", 5, 5, native.systemFont, 16 )
memory_text:setTextColor( 255, 0, 0 )
memory_text.x = display.contentCenterX

local monitorMem = function()
collectgarbage()
local textMem = system.getInfo( "textureMemoryUsed" ) / 1000000
memory_text.text = "Mem:"..collectgarbage("count") .. " tex:".. textMem
end