Untitled diff

Created Diff never expires
15 removals
246 lines
8 additions
239 lines
local BaseHassler = Class(function(self, inst)
local BaseHassler = Class(function(self, inst)
self.inst = inst
self.inst = inst
self.warning = false
self.warning = false
self.timetoattack = nil
self.timetoattack = nil
self.warnduration = 60
self.warnduration = 60
self.timetonextwarningsound = 0
self.timetonextwarningsound = 0
self.announcewarningsoundinterval = 4
self.announcewarningsoundinterval = 4
self.hasslerprefab = "deerclops"
self.hasslerprefab = "deerclops"
self.warningsound = "dontstarve/creatures/deerclops/distant"
self.warningsound = "dontstarve/creatures/deerclops/distant"
self.attacksperwinter = 1
self.attacksperwinter = 1
self.attackduringsummer = false
self.attackduringsummer = false
self.attackdelay = nil
self.attackdelay = nil
self.attackrandom = nil
self.attackrandom = nil
self.inst:ListenForEvent("snowcoverchange", function(inst)
self.inst:ListenForEvent("snowcoverchange", function(inst, data)
local snow_cover = GetSeasonManager() and GetSeasonManager():GetSnowPercent() or 0
if data and data.snow >= 0.2 then
if snow_cover >= 0.2 then
if not self.timetoattack then
if not self.timetoattack then
self:StartAttacks()
self:StartAttacks()
end
end
elseif snow_cover <= 0 and self.attackduringsummer and not self.timetoattack then
elseif data and data.snow <= 0 and self.attackduringsummer and not self.timetoattack then
self:StartAttacks()
self:StartAttacks()
else
else
self:CeaseAttacks()
self:CeaseAttacks()
end
end
end, GetWorld() )
end, GetWorld() )
end)
end)
local HASSLER_SPAWN_DIST = 40
local HASSLER_SPAWN_DIST = 40
local WANDER_AWAY_DIST = 100
local WANDER_AWAY_DIST = 100
function BaseHassler:SetHasslerPrefab(prefab)
function BaseHassler:SetHasslerPrefab(prefab)
self.hasslerprefab = prefab
self.hasslerprefab = prefab
end
end
function BaseHassler:SetWarningSound(sound)
function BaseHassler:SetWarningSound(sound)
self.warningsound = sound
self.warningsound = sound
end
end
function BaseHassler:SetAttacksPerWinter(attacks)
function BaseHassler:SetAttacksPerWinter(attacks)
self.attacksperwinter = attacks
self.attacksperwinter = attacks
end
end
function BaseHassler:SetAttackDuringSummer(attack)
function BaseHassler:SetAttackDuringSummer(attack)
self.attackduringsummer = attack
self.attackduringsummer = attack
end
end
function BaseHassler:OnSave()
function BaseHassler:OnSave()
if not self.noserial then
if not self.noserial then
return
return
{
{
warning = self.warning,
warning = self.warning,
timetoattack = self.timetoattack,
timetoattack = self.timetoattack,
attackdelay = self.attackdelay,
attackdelay = self.attackdelay,
attackrandom = self.attackrandom,
attackrandom = self.attackrandom,
}
}
end
end
self.noserial = false
self.noserial = false
end
end
function BaseHassler:OnLoad(data)
function BaseHassler:OnLoad(data)
self.warning = data.warning or false
self.warning = data.warning or false
self.timetoattack = data.timetoattack
self.timetoattack = data.timetoattack
self.attackdelay = data.attackdelay
self.attackdelay = data.attackdelay
self.attackrandom = data.attackrandom
self.attackrandom = data.attackrandom
if self.timetoattack then
if self.timetoattack then
self.inst:StartUpdatingComponent(self)
self.inst:StartUpdatingComponent(self)
end
end
end
end
function BaseHassler:OnProgress()
function BaseHassler:OnProgress()
self.noserial = true
self.noserial = true
end
end
function BaseHassler:GetDebugString()
function BaseHassler:GetDebugString()
if not self.timetoattack then
if not self.timetoattack then
return "DORMANT"
return "DORMANT"
elseif self.timetoattack > 0 then
elseif self.timetoattack > 0 then
return string.format("%s Deerclops is coming in %2.2f", self.warning and "WARNING" or "WAITING", self.timetoattack)
return string.format("%s Deerclops is coming in %2.2f", self.warning and "WARNING" or "WAITING", self.timetoattack)
else
else
return string.format("ATTACKING!!!")
return string.format("ATTACKING!!!")
end
end
end
end
function BaseHassler:LongUpdate(dt)
self:OnUpdate(dt)
end
function BaseHassler:OnUpdate(dt)
function BaseHassler:OnUpdate(dt)
if not self.timetoattack then
if not self.timetoattack then
self:CeaseAttacks()
self:CeaseAttacks()
return
return
end
end
self.timetoattack = self.timetoattack - dt
self.timetoattack = self.timetoattack - dt
if self.timetoattack <= 0 then
if self.timetoattack <= 0 then
self.warning = false
self.warning = false
self:ReleaseHassler()
self:ReleaseHassler()
self:CeaseAttacks()
self:CeaseAttacks()
else
else
if not self.warning and self.timetoattack < self.warnduration then
if not self.warning and self.timetoattack < self.warnduration then
self.warning = true
self.warning = true
self.timetonextwarningsound = 0
self.timetonextwarningsound = 0
end
end
end
end
if self.warning then
if self.warning then
self.timetonextwarningsound = self.timetonextwarningsound - dt
self.timetonextwarningsound = self.timetonextwarningsound - dt
if self.timetonextwarningsound <= 0 then
if self.timetonextwarningsound <= 0 then
self.announcewarningsoundinterval = self.announcewarningsoundinterval - 1
self.announcewarningsoundinterval = self.announcewarningsoundinterval - 1
if self.announcewarningsoundinterval <= 0 then
if self.announcewarningsoundinterval <= 0 then
self.announcewarningsoundinterval = 10 + math.random(5)
self.announcewarningsoundinterval = 10 + math.random(5)
GetPlayer().components.talker:Say(GetString(GetPlayer().prefab, "ANNOUNCE_DEERCLOPS"))
self.inst.components.talker:Say(GetString(self.inst.prefab, "ANNOUNCE_DEERCLOPS"))
end
end
local inst = CreateEntity()
local inst = CreateEntity()
inst.entity:AddTransform()
inst.entity:AddTransform()
inst.entity:AddSoundEmitter()
inst.entity:AddSoundEmitter()
inst.persists = false
inst.persists = false
local theta = math.random() * 2 * PI
local theta = math.random() * 2 * PI
local radius = 5
local radius = 5
self.timetonextwarningsound = 15 + math.random(4)
self.timetonextwarningsound = 15 + math.random(4)
if self.timetoattack < 30 then
if self.timetoattack < 30 then
self.timetonextwarningsound = 10 + math.random(1)
self.timetonextwarningsound = 10 + math.random(1)
radius = radius
radius = radius
elseif self.timetoattack < 60 then
elseif self.timetoattack < 60 then
radius = radius + 10
radius = radius + 10
elseif self.timetoattack < 90 then
elseif self.timetoattack < 90 then
radius = radius + 15
radius = radius + 15
else
else
radius = radius + 20
radius = radius + 20
end
end
local offset = Vector3(GetPlayer().Transform:GetWorldPosition()) + Vector3(radius * math.cos( theta ), 0, -radius * math.sin( theta ))
local offset = Vector3(self.inst.Transform:GetWorldPosition()) + Vector3(radius * math.cos( theta ), 0, -radius * math.sin( theta ))
inst.Transform:SetPosition(offset.x,offset.y,offset.z)
inst.Transform:SetPosition(offset.x,offset.y,offset.z)
inst.SoundEmitter:PlaySound(self.warningsound)
inst.SoundEmitter:PlaySound(self.warningsound)
inst:DoTaskInTime(1.5, function() inst:Remove() end)
inst:DoTaskInTime(1.5, function() inst:Remove() end)
end
end
end
end
end
end
function BaseHassler:StartAttacks()
function BaseHassler:StartAttacks()
local timeLeftInSeason = GetSeasonManager():GetDaysLeftInSeason() * TUNING.TOTAL_DAY_TIME
local timeLeftInSeason = GetSeasonManager():GetDaysLeftInSeason() * TUNING.TOTAL_DAY_TIME
if self.attacksperwinter > 0 then
if self.attacksperwinter > 0 then
if self.attacksperwinter < 1 then
if self.attacksperwinter < 1 then
--special case: plan attack for NEXT season
--special case: plan attack for NEXT season
local summersToSkip = math.floor( (1 / self.attacksperwinter) - 1 )
local summersToSkip = math.floor( (1 / self.attacksperwinter) - 1 )
local wintersToSkip = math.max(0, summersToSkip-1)
local wintersToSkip = math.max(0, summersToSkip-1)
self.attackdelay = 0.5*timeLeftInSeason + TUNING.TOTAL_DAY_TIME*(summersToSkip*GetSeasonManager().summerlength + wintersToSkip*GetSeasonManager().winterlength)
self.attackdelay = 0.5*timeLeftInSeason + TUNING.TOTAL_DAY_TIME*(summersToSkip*GetSeasonManager().summerlength + wintersToSkip*GetSeasonManager().winterlength)
self.attackrandom = 0.25*timeLeftInSeason
self.attackrandom = 0.25*timeLeftInSeason
else
else
self.attackdelay = timeLeftInSeason / self.attacksperwinter
self.attackdelay = timeLeftInSeason / self.attacksperwinter
self.attackrandom = 0.25*self.attackdelay
self.attackrandom = 0.25*self.attackdelay
end
end
self:PlanNextAttack()
self:PlanNextAttack()
self.inst:StartUpdatingComponent(self)
self.inst:StartUpdatingComponent(self)
end
end
end
end
function BaseHassler:PlanNextAttack()
function BaseHassler:PlanNextAttack()
if (not GetSeasonManager():IsWinter() and not self.attackduringsummer) or not self.attackdelay then
if (not GetSeasonManager():IsWinter() and not self.attackduringsummer) or not self.attackdelay then
self:CeaseAttacks()
self:CeaseAttacks()
return
return
end
end
self.timetoattack = GetRandomWithVariance(self.attackdelay, self.attackrandom or 0)
self.timetoattack = GetRandomWithVariance(self.attackdelay, self.attackrandom or 0)
end
end
function BaseHassler:CeaseAttacks()
function BaseHassler:CeaseAttacks()
self.timetoattack = nil
self.timetoattack = nil
self.warning = false
self.warning = false
self.inst:StopUpdatingComponent(self)
self.inst:StopUpdatingComponent(self)
end
end
function BaseHassler:GetSpawnPoint(pt)
function BaseHassler:GetSpawnPoint(pt)
local theta = math.random() * 2 * PI
local theta = math.random() * 2 * PI
local radius = HASSLER_SPAWN_DIST
local radius = HASSLER_SPAWN_DIST
local offset = FindWalkableOffset(pt, theta, radius, 12, true)
local offset = FindWalkableOffset(pt, theta, radius, 12, true)
if offset then
if offset then
return pt+offset
return pt+offset
end
end
end
end
function BaseHassler:GetWanderAwayPoint(pt)
function BaseHassler:GetWanderAwayPoint(pt)
local theta = math.random() * 2 * PI
local theta = math.random() * 2 * PI
local radius = WANDER_AWAY_DIST
local radius = WANDER_AWAY_DIST
local ground = GetWorld()
local ground = GetWorld()
-- Walk the circle trying to find a valid spawn point
-- Walk the circle trying to find a valid spawn point
local steps = 12
local steps = 12
for i = 1, 12 do
for i = 1, 12 do
local offset = Vector3(radius * math.cos( theta ), 0, -radius * math.sin( theta ))
local offset = Vector3(radius * math.cos( theta ), 0, -radius * math.sin( theta ))
local wander_point = pt + offset
local wander_point = pt + offset
if ground.Map and ground.Map:GetTileAtPoint(wander_point.x, wander_point.y, wander_point.z) ~= GROUND.IMPASSABLE
if ground.Map and ground.Map:GetTileAtPoint(wander_point.x, wander_point.y, wander_point.z) ~= GROUND.IMPASSABLE
and ground.Pathfinder:IsClear(pt.x, pt.y, pt.z, wander_point.x, wander_point.y, wander_point.z, {ignorewalls = true} ) then
and ground.Pathfinder:IsClear(pt.x, pt.y, pt.z, wander_point.x, wander_point.y, wander_point.z, {ignorewalls = true} ) then
return wander_point
return wander_point
end
end
theta = theta - (2 * PI / steps)
theta = theta - (2 * PI / steps)
end
end
end
end
function BaseHassler:ReleaseHassler()
function BaseHassler:ReleaseHassler()
local pt = Vector3(GetPlayer().Transform:GetWorldPosition())
local pt = Vector3(self.inst.Transform:GetWorldPosition())
local spawn_pt = self:GetSpawnPoint(pt)
local spawn_pt = self:GetSpawnPoint(pt)
if spawn_pt then
if spawn_pt then
local hassler = TheSim:FindFirstEntityWithTag(self.hasslerprefab)
local hassler = TheSim:FindFirstEntityWithTag(self.hasslerprefab)
if not hassler then
if not hassler then
hassler = SpawnPrefab(self.hasslerprefab)
hassler = SpawnPrefab(self.hasslerprefab)
end
end
if hassler then
if hassler then
hassler.Physics:Teleport(spawn_pt:Get())
hassler.Physics:Teleport(spawn_pt:Get())
local target = GetClosestInstWithTag("structure", GetPlayer(), 40)
local target = GetClosestInstWithTag("structure", self.inst, 40)
if target then
if target then
local targetPos = Vector3(target.Transform:GetWorldPosition() )
local targetPos = Vector3(target.Transform:GetWorldPosition() )
hassler.components.knownlocations:RememberLocation("targetbase", targetPos)
hassler.components.knownlocations:RememberLocation("targetbase", targetPos)
local wanderAwayPoint = self:GetWanderAwayPoint(targetPos)
local wanderAwayPoint = self:GetWanderAwayPoint(targetPos)
if wanderAwayPoint then
if wanderAwayPoint then
hassler.components.knownlocations:RememberLocation("home", wanderAwayPoint)
hassler.components.knownlocations:RememberLocation("home", wanderAwayPoint)
end
end
else
else
hassler.components.combat:SetTarget(GetPlayer())
hassler.components.combat:SetTarget(self.inst)
end
end
end
end
end
end
end
end
return BaseHassler
return BaseHassler