NPC fear: how bears react to Naughty
Bears don’t watch Naughty directly. Every bear has a fear component that holds its fear level, and a fear generator that gives off fear events for other bears to pick up. Being hit, seeing another bear get hit or hurt, and seeing a kill all go through this one system.
Where it lives
| Container | Script | What’s in it |
|---|---|---|
global.lu |
globaldictionnary |
The fear event constants (FEAR_ATTACKED, FEAR_WIT_ATTACKED, …) |
global.lu |
npc |
NPC:AddFear, NPC:InitFears (the fear table), NPC:InitFearEvents, threshold setters |
naughtyisland.lu |
naughtyisland_npcbase |
What a bear does when it’s hit (HealthChangedCB) or dies (DeathCB); applies each bear’s thresholds |
naughtyisland.lu |
naughtyisland_npcs |
Per-bear definitions (NPCDef), including max fear and thresholds |
Episode and challenge containers carry their own *_npcs definitions (for
example ep10story.lu → ep10story_npcs) with different values for the same
bears.
Fear events
Each event is a hashed name. The TEXT is the game’s own debug description
(demo_fearevent_* strings). From globaldictionnary (selected lines):
FEAR_TORMENTOR = --[[HASH:"demo_fearevent_see_tormentor" TEXT:"sees Naughty!"]]0x972502d2
FEAR_WITNESS_KILL = --[[HASH:"demo_fearevent_witness_kill" TEXT:"witnesses a Bear being killed!!!"]]0xfedbeab9
FEAR_ATTACKED = --[[HASH:"demo_fearevent_attacked" TEXT:"is attacked!"]]0x538405ab
FEAR_WIT_ATTACKED = --[[HASH:"demo_fearevent_witness_attacked" TEXT:"witnesses an attacked Bear!"]]0x621aabbb
FEAR_WIT_INJURED = --[[HASH:"demo_fearevent_witness_injured" TEXT:"witnesses an injured Bear!"]]0xadb546f0
“Tormentor” is the scripts’ word for Naughty.
What happens when Naughty hits a bear
naughtyisland_npcbase, complete function:
function NaughtyIsland_NPCBase:HealthChangedCB()
local message = self.healthChangedMsgListener:GetMessage()
if not message:IsDamaging() then
return
end
local damageData = message:GetDamageData()
if self.className == CHEERLEADER or self.className == SMART then
local generateAttribute = self:GenerateAttribute(STATE_ATTACK_CHARGED, false)
self.objectDescriptor:SetVisibleAttribute(generateAttribute)
end
local fearGeneratorComponent = GetComponent(self.gameObjectEnvironment, "Fear generator Component")
local fearComponent = GetComponent(self.gameObjectEnvironment, "Fear Component")
if GetCharacterModelAttributes(self.gameObjectEnvironment):IsInjured() and self.isInjured == false then
local modeledObject = GetModeledObject(self.gameObjectEnvironment)
self.isInjured = true
fearGeneratorComponent:ActivateFearEvent(FEAR_WIT_INJURED)
local fearInjured = engine.FearEvent_Create(FEAR_INJURED, true, 0.0, nil)
fearComponent:ApplyFearEvent(fearInjured)
local soundManagerManipulator = engine.SoundManagerManipulator()
modeledObject:GetSoundManager(soundManagerManipulator)
GlobalPlayCustomSound("VO_SCARED_SCREAM", soundManagerManipulator, self.soundSuffix)
end
if damageData ~= nil and damageData:GetDamageType() ~= DAMAGE_TYPE_TRAP then
fearGeneratorComponent:ActivateFearEvent(FEAR_WIT_ATTACKED)
local fearAttacked = engine.FearEvent_Create(FEAR_ATTACKED, true, 0.0, nil)
fearComponent:ApplyFearEvent(fearAttacked)
local attacker = damageData:GetAttacker()
if attacker ~= nil then
AchievementUsedWeapon(attacker)
end
end
end
Confirmed, step by step:
- Only damaging messages count.
- The first time the bear becomes injured, its generator switches on
FEAR_WIT_INJURED, the bear appliesFEAR_INJUREDto itself, and it playsVO_SCARED_SCREAM. - Any hit that isn’t trap damage switches on
FEAR_WIT_ATTACKEDon the victim’s generator and appliesFEAR_ATTACKEDto the victim. - Cheerleader and Smart class bears also lose their “attack charged” state when hit.
When the bear dies, DeathCB in the same script switches its generator from
FEAR_WIT_INJURED to FEAR_WITNESS_KILL.
NPC:InitFearEvents in npc registers these events on every bear’s generator,
so every bear can also be the source. The third argument is 3.0 for
FEAR_WIT_ATTACKED and 0.0 for FEAR_WIT_INJURED (selected lines):
fearEvent = engine.FearEvent_Create(FEAR_WIT_ATTACKED, false, 3.0, GetModeledObject(self.gameObjectEnvironment))
fearGeneratorComponent:AddFearEvent(fearEvent)
fearEvent = engine.FearEvent_Create(FEAR_WIT_INJURED, false, 0.0, GetModeledObject(self.gameObjectEnvironment))
fearGeneratorComponent:AddFearEvent(fearEvent)
Interpretation: the 3.0 is how many seconds the event stays active. Which bears perceive an event (range, line of sight) is decided by the engine, not the scripts.
How much each event scares a bear
NPC:InitFears in npc is the fear table: one AddFear call per event. The
first 13 lines of the function:
function NPC:InitFears()
self:AddFear(FEAR_SONAR, FEARTYPE_PLAYER, 0.0, 0.0, 0.0, 0.0, 0.0, false, 0.0, {}, 1.5)
self:AddFear(FEAR_TORMENTOR, FEARTYPE_PLAYER, 30.0, 20.0, 10.0, 0.0, 0.0, false)
self:AddFear(FEAR_NBSCREAM, FEARTYPE_PLAYER, 30.0, 20.0, 10.0, 5.0, 5.0, false)
self:AddFear(FEAR_TERRORIZE, FEARTYPE_PLAYER, 100.0, 75.0, 50.0, 25.0, 10.0, false)
self:AddFear(FEAR_WITNESS_KILL, FEARTYPE_TORMENT, 100.0, 75.0, 50.0, 25.0, 25.0, true)
self:AddFear(FEAR_ATTACKED, FEARTYPE_AGGRESSION, 50.0, 25.0, 15.0, 10.0, 5.0, false)
self:AddFear(FEAR_CORNERED, FEARTYPE_PLAYER, 30.0, 20.0, 10.0, 5.0, 0.0, false)
self:AddFear(FEAR_INJURED, FEARTYPE_AGGRESSION, 100.0, 0.0, 0.0, 0.0, 0.0, false)
self:AddFear(FEAR_WIT_ATTACKED, FEARTYPE_AGGRESSION, 40.0, 20.0, 10.0, 5.0, 0.0, false)
self:AddFear(FEAR_WIT_INJURED, FEARTYPE_TORMENT, 50.0, 40.0, 20.0, 10.0, 5.0, false)
self:AddFear(FEAR_SCREAM, FEARTYPE_TORMENT, 25.0, 20.0, 10.0, 5.0, 0.0, false)
self:AddFear(FEAR_HIDDEN_TERRORIZE, FEARTYPE_PLAYER, 200.0, 150.0, 100.0, 50.0, 25.0, false)
self:AddFear(FEAR_STEALTH_SCARE, FEARTYPE_PLAYER, 200.0, 150.0, 100.0, 50.0, 25.0, false)
The rest of the table follows the same pattern: one entry per object for
scares (FEAR_SCARE_*), seen kills (FEAR_SEE_KILL_*), destroyed
(FEAR_SEEDESTROY_*) and disabled (FEAR_SEEDISABLED_*) objects, plus
kill types (FEAR_KILLED_*).
AddFear’s arguments, confirmed from its body (Fear_Create,
SetFearLevels, SetShocking, …):
| Argument | Meaning |
|---|---|
| 1 | The fear event |
| 2 | Its category: FEARTYPE_PLAYER, _AGGRESSION, _TORMENT, _STRANGE, _TRAP |
| 3–7 | Five fear values, passed to SetFearLevels |
| 8 | “Shocking” |
| 9 | Optional: how many shocks are permitted (default -1) |
| 11 | Optional: immune time |
Interpretation: the five values are the amount of fear added at each of the bear’s fear stages, getting smaller as the bear is already more scared. “Shocking” is set on the kill-type events and probably triggers a stronger reaction. Neither has been tested in-game yet.
What the table shows either way: seeing a bear get hit (40) scares less than being hit (50); seeing a kill (100, shocking) scares more; seeing an object kill (200 or more, shocking) scares most.
Fear thresholds
Each bear type is defined with a maximum fear level and three thresholds. From
naughtyisland_npcs:
STARDUST = NPCDef("Stardust", --[[HASH:"npc_names_Stardust" TEXT:"Stardust"]]0x4b5fa30b, CHEERLEADER, 0, FACTION_NORMAL_BEAR_HASH, 20, false, {0.79, 1.0, 0.82}, 90, 1500, 1.5, {0.2, 0.5, 1.0}, NORMALBEAR2_SOUNDSUFFIX, "")
The arguments follow NPCDef.__init in npc: …, color {0.79, 1.0, 0.82},
maxHP 90, maxFearLevel 1500, shootingSpeed 1.5, fearThresholds
{0.2, 0.5, 1.0}, … . naughtyisland_npcbase applies them:
v:SetMaxFearLevel(maxFearLevel)
v:SetScaredThreshold(fearThresholds[1])
v:SetTerrifiedThreshold(fearThresholds[2])
v:SetPanicThreshold(fearThresholds[3])
Interpretation: the thresholds are fractions of the maximum, so Stardust becomes scared at 20% of 1500 (300), terrified at 50% (750) and panics at 100%.
Changing it
- How strongly bears react: edit the values in
NPC:InitFears(global.lu→npc) and ship. Keep the.0on these values (40.0, not40): Ship treats a number without a decimal point as the game’s integer type, and these are floating-point. - How easily one bear type breaks: edit its
maxFearLevelorfearThresholdsin the*_npcsscript of the level you’re changing. - What counts as an attack:
HealthChangedCBskips trap damage; that’s theDAMAGE_TYPE_TRAPcheck.
Verified in-game: a global.lu with NPC:InitFears multiplied by 10, read and
shipped with Seam Ripper, boots and plays correctly in the tutorial and in
gameplay (the 2026-09-24 scare test).