diff --git a/src/core/landscape.jl b/src/core/landscape.jl
index 959aef5f0c4d4277a56652d10a24189b73d6773d..665e94fe981f7610b4270ddf891c5e07dcfcc1a0 100644
--- a/src/core/landscape.jl
+++ b/src/core/landscape.jl
@@ -1,14 +1,14 @@
-
 ### Persephone - a socio-economic-ecological model of European agricultural landscapes.
 ###
 ### This file manages the landscape maps that underlie the model.
 ###
 
-## The land cover classes encoded in the Mundialis Sentinel data.
-## Do not change the order of this enum, or initlandscape() will break!
+"The land cover classes encoded in the Mundialis Sentinel data."
 @enum LandCover nodata forest grass water builtup soil agriculture
+## Do not change the order of this enum, or initlandscape() will break!
 
-@enum LandscapeEvent test tillage fertiliser pesticide harvest
+"The types of landscape event that can be simulated"
+@enum EventType tillage sowing fertiliser pesticide harvest
 
 """
     Pixel
@@ -20,7 +20,19 @@ in a single object. The model landscape consists of a matrix of pixels.
 mutable struct Pixel
     landcover::LandCover
     fieldid::Union{Missing,Int64}
-    events::Vector{Tuple{LandscapeEvent,Int64}}
+    events::Vector{EventType}
+end
+
+"""
+    FarmEvent
+
+A data structure to define a landscape event, giving its type,
+spatial extent, and duration.
+"""
+mutable struct FarmEvent
+    type::EventType
+    pixels::Vector{Tuple{Int64,Int64}}
+    duration::Int64
 end
 
 """
@@ -51,25 +63,37 @@ function initlandscape()
 end
 
 """
-    updatelandscape!(model)
+    updateevents!(model)
 
-Update the model landscape. (Currently only removes old events.)
+Cycle through the list of events, removing those that have expired.
 """
-function updatelandscape!(model::AgentBasedModel)
-    #FIXME This is really slow?!
-    # Instead of cycling through every pixel every time, I should keep a list of active
-    # events, and cycle through those
-    width, height = size(model.landscape)
-    for x in 1:width
-        for y in 1:height
-            newevents::Vector{Tuple{LandscapeEvent,Int64}} = []
-            for e in model.landscape[x,y].events
-                # only keep events that still have a duration > 1 day
-                (e[2] > 1) && push!(newevents, (e[1], e[2]-1))
+function updateevents!(model::AgentBasedModel)
+    expiredevents = []
+    for e in 1:length(model.events)
+        event = model.events[e]
+        event.duration -= 1
+        if event.duration <= 0
+            push!(expiredevents, e)
+            for p in event.pixels
+                i = findnext(x -> x == event.type, model.landscape[p...].events, 1)
+                deleteat!(model.landscape[p...].events, i)
             end
-            model.landscape[x,y].events = newevents
         end
     end
+    deleteat!(model.events, expiredevents)
+end
+
+"""
+    createevent(model, pos, name, duration=1)
+
+Add a farm event to the specified pixels for a given duration.
+"""
+function createevent!(model::AgentBasedModel, pixels::Vector{Tuple{Int64,Int64}},
+                     name::EventType, duration::Int64=1)
+    push!(model.events, FarmEvent(name, pixels, duration))
+    for p in pixels
+        push!(model.landscape[p...].events, name)
+    end
 end
 
 """
@@ -90,13 +114,3 @@ function farmplot(model::AgentBasedModel, pos::Tuple{Int64,Int64})
     model[model.landscape[pos...].fieldid]
 end
 
-"""
-    createevent(model, pos, name, duration=1)
-
-Add a landscape event to the pixel at the specified site.
-"""
-function createevent(model::AgentBasedModel, pos::Tuple{Int64,Int64},
-                     name::LandscapeEvent, duration::Int64=1)
-    push!(model.landscape[pos...].events, (name, duration))
-end
-
diff --git a/src/core/simulation.jl b/src/core/simulation.jl
index 525e76e2b0ce06a475d6186f62a9bb55375de12e..6440f5792126fba23de45fa625b6451b07ed02c6 100644
--- a/src/core/simulation.jl
+++ b/src/core/simulation.jl
@@ -4,21 +4,24 @@
 ###
 
 """
-    initialise(config)
+    initialise(config, seed)
 
 Initialise the model: read in parameters, create the output data directory,
 and instantiate the AgentBasedModel object.
 """
 function initialise(config::String=PARAMFILE)
+    #XXX add a seed parameter?
     # do some housekeeping
     initsettings(config)
     Random.seed!(param("core.seed"))
     setupdatadir()
     # initialise world-level properties
     landscape = initlandscape()
+    events = Vector{FarmEvent}()
     space = GridSpace(size(landscape), periodic=false)
     properties = Dict{Symbol,Any}(:date=>param("core.startdate"),
-                                  :landscape=>landscape)
+                                  :landscape=>landscape,
+                                  :events=>events)
     @debug "Setting up model now"
     model = AgentBasedModel(Union{Farmer,Animal,FarmPlot}, space, properties=properties,
                             rng=Random.Xoshiro(param("core.seed")), warn=false)
@@ -41,7 +44,7 @@ function stepsimulation!(model::AgentBasedModel)
         #The animal may have been killed, so we need a try/catch
         try stepagent!(model[a], model) catch keyerror end
     end
-    #updatelandscape!(model)
+    updateevents!(model)
     outputdata(model)
     model.date += Day(1)
 end
@@ -54,8 +57,8 @@ Wrap up the simulation. Output all remaining data and exit.
 function finalise(model::AgentBasedModel)
     @info "Simulated $(model.date-param("core.startdate"))."
     @info "Simulation completed at $(Dates.now()),\nwrote output to $(param("core.outdir"))."
-    #TODO
-    genocide!(model)
+    #XXX is there anything to do here?
+    #genocide!(model)
 end
 
 """
diff --git a/src/crop/crops.jl b/src/crop/crops.jl
index 709a203d4d9aec05329742a498d3acea3184a8fe..ccae89b5abe3497475fbf1dbb4d7f1fa58ab496b 100644
--- a/src/crop/crops.jl
+++ b/src/crop/crops.jl
@@ -86,16 +86,3 @@ function averagefieldsize(model::AgentBasedModel)
     round(sum(sizes)/size(sizes)[1], digits=2)
     #sizes
 end
-
-
-"""
-    applyevent(model, farmplotid, name, duration=1)
-
-Apply an event to all pixels in a farm plot.
-"""
-function applyevent(model::AgentBasedModel, farmplotid::Int64,
-                    name::LandscapeEvent, duration::Int64=1)
-    for p in model[farmplotid].pixels
-        createevent(model, p, name, duration)
-    end
-end
diff --git a/src/nature/lifehistory.jl b/src/nature/lifehistory.jl
index c15f7593a07d1792d79231dc7db10726b1671b56..c48618a11b1a3a206d0c779eece24611fb0db2fa 100644
--- a/src/nature/lifehistory.jl
+++ b/src/nature/lifehistory.jl
@@ -36,7 +36,7 @@ function reproduce!(animal::Animal, model::AgentBasedModel, n::Int64=1)
     # XXX at the moment we don't have intra-specific variation, so currently we
     # don't need sexual recombination here
     for i in 1:n
-        sex = animal.sex == hermaphrodite ? hermaphrodite : rand([male, female])
+        sex = (animal.sex == hermaphrodite) ? hermaphrodite : rand([male, female])
         add_agent!(animal.pos, Animal, model, animal.traits, sex, 0)
     end
     @debug "$(animalid(animal)) has reproduced."
diff --git a/src/nature/nature.jl b/src/nature/nature.jl
index 9fe3f273b312c6622651b811045a9c9cff547825..a13faefafcf0d20213f1b61cbfdc654b6aca6b74 100644
--- a/src/nature/nature.jl
+++ b/src/nature/nature.jl
@@ -123,7 +123,7 @@ This can only be used nested within `@phase`.
 macro respond(eventname, body)
     quote
         #TODO test this
-        if any(e -> e[1] == $(esc(eventname)), @here(events))
+        if $(esc(eventname)) in @here(events)
             $body
         end
     end