Files
security-game/src/core/npc_profile.gd
T
zwitschi a3768c2ce4
Full build / Import + headless tests (push) Successful in 2m25s
Full build / Full desktop build + optional release (push) Skipped
Remove dialogue tone-of-voice system, add Gitea Actions full build, add venue cycling
- Delete Rules.Tone/TONE_NAMES, NpcProfile.tone_modifiers, tone params on
  NPCController.roll_reaction/apply_player_action; dialogue_lines flattened
  to Array[String]; HUD tone label, T binding, and hud.tone_prefix removed
- Docs updated everywhere (PLAN, README, DONE, MEMORY); PLAN section heading
  'Tone and Design Direction' -> 'Design Direction'
- Add .gitea/workflows/build.yml: checks job (import+tests) on push/PR; full
  build job (export templates, Linux/Windows/macOS exports, artifact upload,
  and tag-based Gitea release publish via GITEA_TOKEN) on tags/dispatch
- Remove old .github/workflows/ci.yml to avoid double runs
- Add venue cycling (V) across all three scenarios with hud.venue_switched
- Refresh README screenshots to match the new HUD
- 53/53 tests pass, boot smoke clean
2026-10-31 13:08:20 +01:00

72 lines
2.4 KiB
GDScript

class_name NpcProfile
extends Resource
const Rules := preload("res://src/core/rules.gd")
## Authorable behavior profile for one kind of NPC (PLAN.md: NPC Behavior Types).
@export var id: String = ""
@export var display_name: String = ""
@export var npc_type: int = Rules.NpcType.TOURIST
## How difficult temperament is (0 = very calm, 1 = very hot-headed).
## Affects how likely the NPC is to argue, point, or threaten at each level.
@export_range(0.0, 1.0) var temperament: float = 0.25
## Probability the NPC obeys a level-1 polite direction.
@export_range(0.0, 1.0) var base_compliance: float = 0.8
## Probability that an NPC directed away will circle back later in the shift
## (returning-trespasser behavior). 0 = never returns. Trespassers and people
## in crisis are more likely to return than tourists.
@export_range(0.0, 1.0) var return_probability: float = 0.0
## Probability weighting of the NP C's reaction per player escalation level.
## keys: escalation level int -> { ReactionEnum: weight }
## Missing entries fall back to the profile's `default_reactions`.
@export var reactions_by_level: Dictionary = {}
@export var default_reactions: Dictionary = {
Rules.Level.SIMPLE_DETERRENCE: {
Rules.Reaction.COMPLY: 80,
Rules.Reaction.IGNORE: 12,
Rules.Reaction.ARGUE: 8,
},
Rules.Level.FORMAL_WARNING: {
Rules.Reaction.COMPLY: 70,
Rules.Reaction.LEAVE: 10,
Rules.Reaction.ARGUE: 12,
Rules.Reaction.RECORD: 8,
},
Rules.Level.BLOCKING: {
Rules.Reaction.COMPLY: 55,
Rules.Reaction.LEAVE: 15,
Rules.Reaction.ARGUE: 20,
Rules.Reaction.RECORD: 10,
},
Rules.Level.RADIO_ASSIST: {
Rules.Reaction.LEAVE: 65,
Rules.Reaction.ARGUE: 15,
Rules.Reaction.FORCE_ENTRY: 20,
},
Rules.Level.EMERGENCY: {
Rules.Reaction.FORCE_ENTRY: 30,
Rules.Reaction.THREATEN: 40,
Rules.Reaction.LEAVE: 30,
},
}
## Delay the NPC waits before reacting to the player (frames/seconds scaled).
@export_range(0.1, 5.0) var reaction_delay: float = 0.6
## Dialogue lines this NPC uses. Falls back to generic lines when empty.
@export var dialogue_lines: Array[String] = []
## Items the NPC is carrying, used by the handheld wand (The Joke Shop).
## Values are item names: "keys", "belt", "phone", "weapon", "knife", etc.
@export var carried_items: Array[String] = []
func pickup_reactions_per_level(level: int) -> Dictionary:
if reactions_by_level.has(level):
return reactions_by_level[level]
return default_reactions.get(level, {})