local Vector3 = require "types.vector3" local Signal = require "types.signal" local TEXTURE = "images/duckie.png" local States = { IDLE = 1, WANDER = 2, CHASE = 3, } local WANDER_TIME = 1.0 local IDLE_TIME = 1.0 local Duck = {} Duck.__index = Duck function Duck.new(position) -- local d = util.shallow_copy(Duck) local d = { position = position:copy(), velocity = Vector3(), direction = Vector3(0, 0, -1):rotated(Vector3.UP, util.random_float(-math.pi, math.pi)), target = nil, state = States.IDLE, wander_timer = 0, speed = 0.02, chase_speed = 0.04, accel = 0.5, STATES = States, AteFeed = Signal.new(), SeekFeed = Signal.new(), index = 1, } d.position.y = 0.4 return setmetatable(d, Duck) end function Duck:tick(ctx) if self.state == States.IDLE then self:idle(ctx.frame_duration) elseif self.state == States.WANDER then self:wander(ctx.frame_duration) elseif self.state == States.CHASE then self:chase(ctx.frame_duration) end draw_billboard{position = self.position, size = {x = 0.4, y = 0.4}, texture = TEXTURE} end function Duck:idle(delta) self.wander_timer = self.wander_timer + delta if self.wander_timer >= IDLE_TIME then self.state = States.WANDER self.wander_timer = 0 self.direction = Vector3(0, 0, -1):rotated(Vector3.UP, math.random() * (math.pi * 2.0)) return end end function Duck:wander(delta) self.wander_timer = self.wander_timer + delta if self.wander_timer >= WANDER_TIME then self.state = States.IDLE self.wander_timer = 0 self.SeekFeed:emit(self) return end local target_vel = self.direction * self.speed self.velocity = self.velocity:lerp(target_vel, self.accel) self.position = self.position + self.velocity end function Duck:chase(delta) local hpos = self.position:horizontal() local fhpos = self.target.position:horizontal() local dist = hpos:distance_to(fhpos) if dist <= 0.1 then self.AteFeed:emit(self.target) self.wander_timer = 0 self.state = States.IDLE self.target = nil else local dir = self.position:horizontal():direction_to(self.target.position:horizontal()) local target_vel = dir * self.chase_speed self.velocity = self.velocity:lerp(target_vel, self.accel) self.position = self.position + self.velocity end end function Duck:start_chase(feed) if self.state == States.CHASE then return end -- print("duck " .. self.index .. " starting chase") self.state = States.CHASE self.target = feed feed.occupied = true end return Duck