1 // ============================================================================
2 // File Name: bpc_heartbeat
3 // The "Sense" phase of the Generative AI Loop OnHeartbeat obecjt event
4 // ============================================================================
6 #include "nwnx_redis_lib"
10 object oNPC = OBJECT_SELF;
12 object oNearestPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC, oNPC);
13 if (!GetIsObjectValid(oNearestPC) || GetDistanceBetween(oNPC, oNearestPC) > 25.0f) {
17 int nTick = GetLocalInt(oNPC, "LLM_RADAR_TICK") + 1;
18 if (nTick >= 10) nTick = 0;
19 SetLocalInt(oNPC, "LLM_RADAR_TICK", nTick);
21 if (nTick != 0) return;
23 int nMyMaxHP = GetMaxHitPoints(oNPC);
24 int nMyCurHP = GetCurrentHitPoints(oNPC);
25 string sMyHealth = "Healthy and uninjured.";
27 if (nMyCurHP < nMyMaxHP) {
28 if (nMyCurHP <= (nMyMaxHP / 4)) sMyHealth = "Critically wounded, bleeding heavily, and near death! I need to REST immediately.";
29 else if (nMyCurHP <= (nMyMaxHP / 2)) sMyHealth = "Injured, bruised, and exhausted.";
30 else sMyHealth = "Slightly hurt, with a few cuts and scratches.";
33 int nStrategy = GetLocalInt(oNPC, "LLM_STRATEGY");
34 if (nStrategy == 0) nStrategy = 1;
36 string sObservation = "You are observing your surroundings.";
38 sObservation += " " + GetName(oNearestPC) + " is standing nearby.";
41 string sNearbyNPCs = "";
43 object oNearbyNPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_NOT_PC, oNPC, nNPCScan);
45 while (GetIsObjectValid(oNearbyNPC) && GetDistanceBetween(oNPC, oNearbyNPC) <= 15.0f) {
46 if (oNearbyNPC != oNPC) sNearbyNPCs += GetName(oNearbyNPC) + ", ";
48 oNearbyNPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_NOT_PC, oNPC, nNPCScan);
52 object oProp = GetNearestObject(OBJECT_TYPE_PLACEABLE, oNPC, nPropScan);
54 while (GetIsObjectValid(oProp) && GetDistanceBetween(oNPC, oProp) < 15.0f && nPropScan <= 3) {
55 if (GetUseableFlag(oProp) || GetLocalInt(oProp, "NW_INTERACTIVE") == TRUE) {
56 sObservation += " You notice a " + GetName(oProp) + " nearby.";
60 oProp = GetNearestObject(OBJECT_TYPE_PLACEABLE, oNPC, nPropScan);
63 string sLocationLore = "";
64 object oArea = GetArea(oNPC);
65 string sAreaLore = GetLocalString(oArea, "LLM_LOCATION_CONTEXT");
66 if (sAreaLore != "") sLocationLore = sAreaLore;
68 object oWaypoint = GetNearestObjectByTag("WP_LLM_LORE", oNPC);
69 if (GetIsObjectValid(oWaypoint) && GetDistanceBetween(oNPC, oWaypoint) <= 15.0f) {
70 string sWPLore = GetLocalString(oWaypoint, "LLM_LOCATION_CONTEXT");
71 if (sWPLore != "") sLocationLore += " SPECIFIC SURROUNDINGS: " + sWPLore;
74 json jData = JsonObject();
75 jData = JsonObjectSet(jData, "npc_tag", JsonString(GetTag(oNPC)));
76 jData = JsonObjectSet(jData, "target_player", JsonString("Environment"));
77 jData = JsonObjectSet(jData, "message", JsonString(sObservation));
78 jData = JsonObjectSet(jData, "persona", JsonString(GetLocalString(oNPC, "LLM_PERSONA")));
79 jData = JsonObjectSet(jData, "profession", JsonString(GetLocalString(oNPC, "LLM_PROFESSION")));
80 jData = JsonObjectSet(jData, "npc_health", JsonString(sMyHealth));
81 jData = JsonObjectSet(jData, "llm_strategy", JsonInt(nStrategy));
83 if (sLocationLore != "") jData = JsonObjectSet(jData, "location_context", JsonString(sLocationLore));
84 if (sNearbyNPCs != "") jData = JsonObjectSet(jData, "nearby_npcs", JsonString(sNearbyNPCs));
86 string sQuests = GetLocalString(oNPC, "LLM_QUESTS");
87 if (sQuests != "") jData = JsonObjectSet(jData, "available_quests", JsonString(sQuests));
89 string sProps = GetLocalString(oNPC, "LLM_PROPS");
90 if (sProps != "") jData = JsonObjectSet(jData, "available_props", JsonString(sProps));
92 NWNX_Redis_RPush("nwn_to_llm", JsonDump(jData));