@ -1,4 +1,5 @@
# pragma once
# pragma once
# include "buff.h"
# include "buff.h"
# include "HandmadeMath.h" // vector types in entity struct definition
# include "HandmadeMath.h" // vector types in entity struct definition
# include <assert.h>
# include <assert.h>
@ -7,8 +8,6 @@
# include <stdlib.h> // atoi
# include <stdlib.h> // atoi
# include "character_info.h"
# include "character_info.h"
# include "characters.gen.h"
# include "characters.gen.h"
NPC_MOOSE ,
} NpcKind ;
// TODO do strings: https://pastebin.com/Kwcw2sye
// TODO do strings: https://pastebin.com/Kwcw2sye
@ -19,7 +18,7 @@ NPC_MOOSE,
// REFACTORING:: also have to update in javascript!!!!!!!!
// REFACTORING:: also have to update in javascript!!!!!!!!
# define MAX_SENTENCE_LENGTH 400 // LOOOK AT AGBOVE COMMENT GBEFORE CHANGING
# define MAX_SENTENCE_LENGTH 400 // LOOOK AT AGBOVE COMMENT GBEFORE CHANGING
typedef BUFF ( char , MAX_SENTENCE_LENGTH ) Sentence ;
typedef BUFF ( char , MAX_SENTENCE_LENGTH ) Sentence ;
# define SENTENCE_CONST(txt) { .data=txt, .cur_index=sizeof(txt)}
# define SENTENCE_CONST(txt) { .data = txt, .cur_index = sizeof(txt) }
# define SENTENCE_CONST_CAST(txt) (Sentence)SENTENCE_CONST(txt)
# define SENTENCE_CONST_CAST(txt) (Sentence)SENTENCE_CONST(txt)
# define REMEMBERED_PERCEPTIONS 24
# define REMEMBERED_PERCEPTIONS 24
@ -37,26 +36,27 @@ typedef BUFF(char, MAX_SENTENCE_LENGTH) Sentence;
// Never expected such a stupid stuff from such a great director. If there is 0 stari can give that or -200 to this movie. Its worst to see and unnecessary loss of money
// Never expected such a stupid stuff from such a great director. If there is 0 stari can give that or -200 to this movie. Its worst to see and unnecessary loss of money
typedef BUFF ( char , 1024 * 10 ) Escaped ;
typedef BUFF ( char , 1024 * 10 ) Escaped ;
Escaped escape_for_json ( const char * s )
Escaped escape_for_json ( const char * s )
{
{
Escaped to_return = { 0 } ;
Escaped to_return = { 0 } ;
size_t len = strlen ( s ) ;
size_t len = strlen ( s ) ;
for ( int i = 0 ; i < len ; i + + )
for ( int i = 0 ; i < len ; i + + )
{
{
if ( s [ i ] = = ' \n ' )
if ( s [ i ] = = ' \n ' )
{
{
BUFF_APPEND ( & to_return , ' \\ ' ) ;
BUFF_APPEND ( & to_return , ' \\ ' ) ;
BUFF_APPEND ( & to_return , ' n ' ) ;
BUFF_APPEND ( & to_return , ' n ' ) ;
}
}
else if ( s [ i ] = = ' " ' )
else if ( s [ i ] = = ' " ' )
{
{
BUFF_APPEND ( & to_return , ' \\ ' ) ;
BUFF_APPEND ( & to_return , ' \\ ' ) ;
BUFF_APPEND ( & to_return , ' " ' ) ;
BUFF_APPEND ( & to_return , ' " ' ) ;
}
}
else
else
{
{
if ( ! ( s [ i ] < = 126 & & s [ i ] > = 32 ) )
if ( ! ( s [ i ] < = 126 & & s [ i ] > = 32 ) )
{
{
BUFF_APPEND ( & to_return , ' ? ' ) ;
BUFF_APPEND ( & to_return , ' ? ' ) ;
Log ( " Unknown character code %d \n " , s [ i ] ) ;
Log ( " Unknown character code %d \n " , s [ i ] ) ;
@ -66,6 +66,7 @@ Escaped escape_for_json(const char *s)
}
}
return to_return ;
return to_return ;
}
}
typedef enum PerceptionType
typedef enum PerceptionType
{
{
Invalid , // so that zero value in training structs means end of perception
Invalid , // so that zero value in training structs means end of perception
@ -173,7 +174,8 @@ typedef struct
int index ;
int index ;
} PathCacheHandle ;
} PathCacheHandle ;
typedef struct {
typedef struct
{
bool is_reference ;
bool is_reference ;
EntityRef ref ;
EntityRef ref ;
Vec2 pos ;
Vec2 pos ;
@ -255,17 +257,18 @@ typedef struct Entity
bool npc_is_knight_sprite ( Entity * it )
bool npc_is_knight_sprite ( Entity * it )
{
{
return it - > is_npc & & ( it - > npc_kind = = NPC_TheGuard | | it - > npc_kind = = NPC_Edeline | | it - > npc_kind = = NPC_TheKing | | it - > npc_kind = = NPC_TheBlacksmith ) ;
return it - > is_npc & & ( it - > npc_kind = = NPC_TheGuard | | it - > npc_kind = = NPC_Edeline | | it - > npc_kind = = NPC_TheKing | |
it - > npc_kind = = NPC_TheBlacksmith ) ;
}
}
bool npc_is_skeleton ( Entity * it )
bool npc_is_skeleton ( Entity * it )
{
{
return it - > is_npc & & ( it - > npc_kind = = NPC_MikeSkeleton ) ;
return it - > is_npc & & ( it - > npc_kind = = NPC_MikeSkeleton ) ;
}
}
float entity_max_damage ( Entity * e )
float entity_max_damage ( Entity * e )
{
{
if ( e - > is_npc & & npc_is_skeleton ( e ) )
if ( e - > is_npc & & npc_is_skeleton ( e ) )
{
{
return 2.0f ;
return 2.0f ;
}
}
@ -282,45 +285,45 @@ bool npc_attacks_with_sword(Entity *it)
bool npc_attacks_with_shotgun ( Entity * it )
bool npc_attacks_with_shotgun ( Entity * it )
{
{
return it - > is_npc & & ( it - > npc_kind = = NPC_OldMan ) ;
return it - > is_npc & & ( it - > npc_kind = = NPC_OldMan ) ;
}
}
typedef BUFF ( char , MAX_SENTENCE_LENGTH * ( REMEMBERED_PERCEPTIONS + 4 ) ) PromptBuff ;
typedef BUFF ( char , MAX_SENTENCE_LENGTH * ( REMEMBERED_PERCEPTIONS + 4 ) ) PromptBuff ;
typedef BUFF ( Action , 8 ) AvailableActions ;
typedef BUFF ( Action , 8 ) AvailableActions ;
void fill_available_actions ( Entity * it , AvailableActions * a )
void fill_available_actions ( Entity * it , AvailableActions * a )
{
{
* a = ( AvailableActions ) { 0 } ;
* a = ( AvailableActions ) { 0 } ;
BUFF_APPEND ( a , ACT_none ) ;
BUFF_APPEND ( a , ACT_none ) ;
BUFF_APPEND ( a , ACT_give_item ) ;
BUFF_APPEND ( a , ACT_give_item ) ;
if ( it - > npc_kind = = NPC_GodRock )
if ( it - > npc_kind = = NPC_GodRock )
{
{
BUFF_APPEND ( a , ACT_heals_player ) ;
BUFF_APPEND ( a , ACT_heals_player ) ;
}
}
else
else
{
{
if ( it - > standing = = STANDING_INDIFFERENT )
if ( it - > standing = = STANDING_INDIFFERENT )
{
{
BUFF_APPEND ( a , ACT_fights_player ) ;
BUFF_APPEND ( a , ACT_fights_player ) ;
BUFF_APPEND ( a , ACT_joins_player ) ;
BUFF_APPEND ( a , ACT_joins_player ) ;
}
}
else if ( it - > standing = = STANDING_JOINED )
else if ( it - > standing = = STANDING_JOINED )
{
{
BUFF_APPEND ( a , ACT_leaves_player ) ;
BUFF_APPEND ( a , ACT_leaves_player ) ;
BUFF_APPEND ( a , ACT_fights_player ) ;
BUFF_APPEND ( a , ACT_fights_player ) ;
}
}
else if ( it - > standing = = STANDING_FIGHTING )
else if ( it - > standing = = STANDING_FIGHTING )
{
{
BUFF_APPEND ( a , ACT_stops_fighting_player ) ;
BUFF_APPEND ( a , ACT_stops_fighting_player ) ;
}
}
if ( npc_is_knight_sprite ( it ) )
if ( npc_is_knight_sprite ( it ) )
{
{
BUFF_APPEND ( a , ACT_strikes_air ) ;
BUFF_APPEND ( a , ACT_strikes_air ) ;
}
}
if ( it - > npc_kind = = NPC_TheGuard )
if ( it - > npc_kind = = NPC_TheGuard )
{
{
if ( ! it - > moved )
if ( ! it - > moved )
{
{
BUFF_APPEND ( a , ACT_allows_player_to_pass ) ;
BUFF_APPEND ( a , ACT_allows_player_to_pass ) ;
}
}
@ -331,9 +334,9 @@ void fill_available_actions(Entity *it, AvailableActions *a)
// returns if action index was valid
// returns if action index was valid
bool action_from_index ( Entity * it , Action * out , int action_index )
bool action_from_index ( Entity * it , Action * out , int action_index )
{
{
AvailableActions available = { 0 } ;
AvailableActions available = { 0 } ;
fill_available_actions ( it , & available ) ;
fill_available_actions ( it , & available ) ;
if ( action_index < 0 | | action_index > = available . cur_index )
if ( action_index < 0 | | action_index > = available . cur_index )
{
{
return false ;
return false ;
}
}
@ -347,13 +350,13 @@ bool action_from_index(Entity *it, Action *out, int action_index)
// don't call on untrusted action, doesn't return error
// don't call on untrusted action, doesn't return error
int action_to_index ( Entity * it , Action a )
int action_to_index ( Entity * it , Action a )
{
{
AvailableActions available = { 0 } ;
AvailableActions available = { 0 } ;
fill_available_actions ( it , & available ) ;
fill_available_actions ( it , & available ) ;
Action target_action = a ;
Action target_action = a ;
int index = - 1 ;
int index = - 1 ;
for ( int i = 0 ; i < available . cur_index ; i + + )
for ( int i = 0 ; i < available . cur_index ; i + + )
{
{
if ( available . data [ i ] = = target_action )
if ( available . data [ i ] = = target_action )
{
{
index = i ;
index = i ;
break ;
break ;
@ -365,57 +368,73 @@ int action_to_index(Entity *it, Action a)
void process_perception ( Entity * it , Perception p , Entity * player )
void process_perception ( Entity * it , Perception p , Entity * player )
{
{
if ( it - > is_npc )
assert ( it - > is_npc ) ;
{
if ( p . type ! = NPCDialog ) it - > perceptions_dirty = true ;
if ( p . type ! = NPCDialog ) it - > perceptions_dirty = true ;
if ( ! BUFF_HAS_SPACE ( & it - > remembered_perceptions ) )
if ( ! BUFF_HAS_SPACE ( & it - > remembered_perceptions ) ) BUFF_REMOVE_FRONT ( & it - > remembered_perceptions ) ;
BUFF_REMOVE_FRONT ( & it - > remembered_perceptions ) ;
BUFF_APPEND ( & it - > remembered_perceptions , p ) ;
BUFF_APPEND ( & it - > remembered_perceptions , p ) ;
if ( p . type = = PlayerAction & & p . player_action_type = = ACT_hits_npc )
if ( p . type = = PlayerAction )
{
if ( p . player_action_type = = ACT_hits_npc )
{
{
it - > damage + = p . damage_done ;
it - > damage + = p . damage_done ;
}
}
if ( p . type = = PlayerHeldItemChanged )
else if ( p . player_action_type = = ACT_give_item )
{
BUFF_APPEND ( & it - > held_items , p . given_item ) ;
}
else
{
assert ( ! actions [ p . player_action_type ] . takes_argument ) ;
}
}
else if ( p . type = = PlayerDialog )
{
}
else if ( p . type = = PlayerHeldItemChanged )
{
{
it - > last_seen_holding_kind = p . holding ;
it - > last_seen_holding_kind = p . holding ;
}
}
else if ( p . type = = NPCDialog )
else if ( p . type = = NPCDialog )
{
{
if ( p . npc_action_type = = ACT_allows_player_to_pass )
if ( p . npc_action_type = = ACT_allows_player_to_pass )
{
{
it - > target_goto = AddV2 ( it - > pos , V2 ( - 50.0 , 0.0 ) ) ;
it - > target_goto = AddV2 ( it - > pos , V2 ( - 50.0 , 0.0 ) ) ;
it - > moved = true ;
it - > moved = true ;
}
}
else if ( p . npc_action_type = = ACT_fights_player )
else if ( p . npc_action_type = = ACT_fights_player )
{
{
it - > standing = STANDING_FIGHTING ;
it - > standing = STANDING_FIGHTING ;
}
}
else if ( p . npc_action_type = = ACT_stops_fighting_player )
else if ( p . npc_action_type = = ACT_stops_fighting_player )
{
{
it - > standing = STANDING_INDIFFERENT ;
it - > standing = STANDING_INDIFFERENT ;
}
}
else if ( p . npc_action_type = = ACT_leaves_player )
else if ( p . npc_action_type = = ACT_leaves_player )
{
{
it - > standing = STANDING_INDIFFERENT ;
it - > standing = STANDING_INDIFFERENT ;
}
}
else if ( p . npc_action_type = = ACT_joins_player )
else if ( p . npc_action_type = = ACT_joins_player )
{
{
it - > standing = STANDING_JOINED ;
it - > standing = STANDING_JOINED ;
}
}
else if ( p . npc_action_type = = ACT_give_item )
else if ( p . npc_action_type = = ACT_give_item )
{
{
int item_to_remove = - 1 ;
int item_to_remove = - 1 ;
Entity * e = it ;
Entity * e = it ;
BUFF_ITER_I ( ItemKind , & e - > held_items , i )
BUFF_ITER_I ( ItemKind , & e - > held_items , i )
{
{
if ( * it = = p . given_item )
if ( * it = = p . given_item )
{
{
item_to_remove = i ;
item_to_remove = i ;
break ;
break ;
}
}
}
}
if ( item_to_remove < 0 )
if ( item_to_remove < 0 )
{
{
Log ( " Can't find item %s to give from NPC %s to the player \n " , items [ p . given_item ] . name , characters [ it - > npc_kind ] . name ) ;
Log ( " Can't find item %s to give from NPC %s to the player \n " , items [ p . given_item ] . name ,
characters [ it - > npc_kind ] . name ) ;
assert ( false ) ;
assert ( false ) ;
}
}
else
else
@ -430,6 +449,9 @@ void process_perception(Entity *it, Perception p, Entity *player)
assert ( ! actions [ p . npc_action_type ] . takes_argument ) ;
assert ( ! actions [ p . npc_action_type ] . takes_argument ) ;
}
}
}
}
else
{
assert ( false ) ;
}
}
}
}
@ -441,9 +463,9 @@ bool printf_buff_impl(BuffRef into, const char *format, ...)
va_list args ;
va_list args ;
va_start ( args , format ) ;
va_start ( args , format ) ;
size_t n = into . max_data_elems - * into . cur_index ;
size_t n = into . max_data_elems - * into . cur_index ;
int written = vsnprintf ( ( char * ) into . data + * into . cur_index , n , format , args ) ;
int written = vsnprintf ( ( char * ) into . data + * into . cur_index , n , format , args ) ;
if ( written < 0 )
if ( written < 0 )
{
{
}
}
else
else
@ -453,8 +475,8 @@ bool printf_buff_impl(BuffRef into, const char *format, ...)
// https://cplusplus.com/reference/cstdio/vsnprintf/
// https://cplusplus.com/reference/cstdio/vsnprintf/
bool succeeded = true ;
bool succeeded = true ;
if ( written < 0 ) succeeded = false ; // encoding error
if ( written < 0 ) succeeded = false ; // encoding error
if ( written > = n ) succeeded = false ; // didn't fit in buffer
if ( written > = n ) succeeded = false ; // didn't fit in buffer
va_end ( args ) ;
va_end ( args ) ;
return succeeded ;
return succeeded ;
@ -478,9 +500,9 @@ typedef enum
void append_str ( Sentence * to_append , const char * str )
void append_str ( Sentence * to_append , const char * str )
{
{
size_t len = strlen ( str ) ;
size_t len = strlen ( str ) ;
for ( int i = 0 ; i < len ; i + + )
for ( int i = 0 ; i < len ; i + + )
{
{
if ( ! BUFF_HAS_SPACE ( to_append ) )
if ( ! BUFF_HAS_SPACE ( to_append ) )
{
{
break ;
break ;
}
}
@ -494,15 +516,15 @@ void append_str(Sentence *to_append, const char *str)
void dump_json_node_trailing ( PromptBuff * into , MessageType type , const char * content , bool trailing_comma )
void dump_json_node_trailing ( PromptBuff * into , MessageType type , const char * content , bool trailing_comma )
{
{
const char * type_str = 0 ;
const char * type_str = 0 ;
if ( type = = MSG_SYSTEM )
if ( type = = MSG_SYSTEM )
type_str = " system " ;
type_str = " system " ;
else if ( type = = MSG_USER )
else if ( type = = MSG_USER )
type_str = " user " ;
type_str = " user " ;
else if ( type = = MSG_ASSISTANT )
else if ( type = = MSG_ASSISTANT )
type_str = " assistant " ;
type_str = " assistant " ;
assert ( type_str ) ;
assert ( type_str ) ;
printf_buff ( into , " { \" type \" : \" %s \" , \" content \" : \" %s \" } " , type_str , escape_for_json ( content ) . data ) ;
printf_buff ( into , " { \" type \" : \" %s \" , \" content \" : \" %s \" } " , type_str , escape_for_json ( content ) . data ) ;
if ( trailing_comma ) printf_buff ( into , " , " ) ;
if ( trailing_comma ) printf_buff ( into , " , " ) ;
}
}
void dump_json_node ( PromptBuff * into , MessageType type , const char * content )
void dump_json_node ( PromptBuff * into , MessageType type , const char * content )
@ -516,22 +538,22 @@ void generate_chatgpt_prompt(Entity *it, PromptBuff *into)
assert ( it - > is_npc ) ;
assert ( it - > is_npc ) ;
assert ( it - > npc_kind < ARRLEN ( characters ) ) ;
assert ( it - > npc_kind < ARRLEN ( characters ) ) ;
* into = ( PromptBuff ) { 0 } ;
* into = ( PromptBuff ) { 0 } ;
printf_buff ( into , " [ " ) ;
printf_buff ( into , " [ " ) ;
BUFF ( char , 1024 * 15 ) initial_system_msg = { 0 } ;
BUFF ( char , 1024 * 15 ) initial_system_msg = { 0 } ;
const char * health_string = 0 ;
const char * health_string = 0 ;
if ( it - > damage < = 0.2f )
if ( it - > damage < = 0.2f )
{
{
health_string = " the NPC hasn't taken much damage, they're healthy. " ;
health_string = " the NPC hasn't taken much damage, they're healthy. " ;
}
}
else if ( it - > damage < = 0.5f )
else if ( it - > damage < = 0.5f )
{
{
health_string = " the NPC has taken quite a chunk of damage, they're soon gonna be ready to call it quits. " ;
health_string = " the NPC has taken quite a chunk of damage, they're soon gonna be ready to call it quits. " ;
}
}
else if ( it - > damage < = 0.8f )
else if ( it - > damage < = 0.8f )
{
{
health_string = " the NPC is close to dying! They want to leave the player's party ASAP " ;
health_string = " the NPC is close to dying! They want to leave the player's party ASAP " ;
}
}
@ -544,41 +566,40 @@ void generate_chatgpt_prompt(Entity *it, PromptBuff *into)
printf_buff ( & initial_system_msg , " %s \n " , global_prompt ) ;
printf_buff ( & initial_system_msg , " %s \n " , global_prompt ) ;
printf_buff ( & initial_system_msg , " %s \n " , characters [ it - > npc_kind ] . prompt ) ;
printf_buff ( & initial_system_msg , " %s \n " , characters [ it - > npc_kind ] . prompt ) ;
dump_json_node ( into , MSG_SYSTEM , initial_system_msg . data ) ;
dump_json_node ( into , MSG_SYSTEM , initial_system_msg . data ) ;
Entity * e = it ;
Entity * e = it ;
ItemKind last_holding = ITEM_none ;
ItemKind last_holding = ITEM_none ;
BUFF_ITER_I ( Perception , & e - > remembered_perceptions , i )
BUFF_ITER_I ( Perception , & e - > remembered_perceptions , i )
{
{
BUFF ( char , 1024 ) cur_node = { 0 } ;
BUFF ( char , 1024 ) cur_node = { 0 } ;
if ( it - > type = = PlayerAction )
if ( it - > type = = PlayerAction )
{
{
assert ( it - > player_action_type < ARRLEN ( actions ) ) ;
assert ( it - > player_action_type < ARRLEN ( actions ) ) ;
printf_buff ( & cur_node , " Player: ACT_%s " , actions [ it - > player_action_type ] . name ) ;
printf_buff ( & cur_node , " Player: ACT_%s " , actions [ it - > player_action_type ] . name ) ;
dump_json_node ( into , MSG_USER , cur_node . data ) ;
dump_json_node ( into , MSG_USER , cur_node . data ) ;
}
}
else if ( it - > type = = EnemyAction )
else if ( it - > type = = EnemyAction )
{
{
assert ( it - > enemy_action_type < ARRLEN ( actions ) ) ;
assert ( it - > enemy_action_type < ARRLEN ( actions ) ) ;
printf_buff ( & cur_node , " An Enemy: ACT_%s " , actions [ it - > player_action_type ] . name ) ;
printf_buff ( & cur_node , " An Enemy: ACT_%s " , actions [ it - > player_action_type ] . name ) ;
dump_json_node ( into , MSG_USER , cur_node . data ) ;
dump_json_node ( into , MSG_USER , cur_node . data ) ;
}
}
else if ( it - > type = = PlayerDialog )
else if ( it - > type = = PlayerDialog )
{
{
Sentence filtered_player_speech = { 0 } ;
Sentence filtered_player_speech = { 0 } ;
Sentence * what_player_said = & it - > player_dialog ;
Sentence * what_player_said = & it - > player_dialog ;
for ( int i = 0 ; i < what_player_said - > cur_index ; i + + )
for ( int i = 0 ; i < what_player_said - > cur_index ; i + + )
{
{
char c = what_player_said - > data [ i ] ;
char c = what_player_said - > data [ i ] ;
if ( c = = ' * ' )
if ( c = = ' * ' )
{
{
// move i until the next star
// move i until the next star
i + = 1 ;
i + = 1 ;
while ( i < what_player_said - > cur_index & & what_player_said - > data [ i ] ! = ' * ' ) i + + ;
while ( i < what_player_said - > cur_index & & what_player_said - > data [ i ] ! = ' * ' ) i + + ;
append_str ( & filtered_player_speech , " [The player is attempting to confuse the NPC with arcane trickery] " ) ;
append_str ( & filtered_player_speech ,
" [The player is attempting to confuse the NPC with arcane trickery] " ) ;
}
}
else
else
{
{
@ -588,21 +609,22 @@ void generate_chatgpt_prompt(Entity *it, PromptBuff *into)
printf_buff ( & cur_node , " Player: \" %s \" " , filtered_player_speech . data ) ;
printf_buff ( & cur_node , " Player: \" %s \" " , filtered_player_speech . data ) ;
dump_json_node ( into , MSG_USER , cur_node . data ) ;
dump_json_node ( into , MSG_USER , cur_node . data ) ;
}
}
else if ( it - > type = = NPCDialog )
else if ( it - > type = = NPCDialog )
{
{
assert ( it - > npc_action_type < ARRLEN ( actions ) ) ;
assert ( it - > npc_action_type < ARRLEN ( actions ) ) ;
printf_buff ( & cur_node , " %s: ACT_%s \" %s \" " , characters [ e - > npc_kind ] . name , actions [ it - > npc_action_type ] . name , it - > npc_dialog . data ) ;
printf_buff ( & cur_node , " %s: ACT_%s \" %s \" " , characters [ e - > npc_kind ] . name ,
actions [ it - > npc_action_type ] . name , it - > npc_dialog . data ) ;
dump_json_node ( into , MSG_ASSISTANT , cur_node . data ) ;
dump_json_node ( into , MSG_ASSISTANT , cur_node . data ) ;
}
}
else if ( it - > type = = PlayerHeldItemChanged )
else if ( it - > type = = PlayerHeldItemChanged )
{
{
if ( last_holding ! = it - > holding )
if ( last_holding ! = it - > holding )
{
{
if ( last_holding ! = ITEM_none )
if ( last_holding ! = ITEM_none )
{
{
printf_buff ( & cur_node , " %s \n " , items [ last_holding ] . discard ) ;
printf_buff ( & cur_node , " %s \n " , items [ last_holding ] . discard ) ;
}
}
if ( it - > holding ! = ITEM_none )
if ( it - > holding ! = ITEM_none )
{
{
printf_buff ( & cur_node , " %s \n " , items [ it - > holding ] . possess ) ;
printf_buff ( & cur_node , " %s \n " , items [ it - > holding ] . possess ) ;
}
}
@ -616,18 +638,19 @@ void generate_chatgpt_prompt(Entity *it, PromptBuff *into)
}
}
}
}
BUFF ( char , 1024 ) latest_state_node = { 0 } ;
BUFF ( char , 1024 ) latest_state_node = { 0 } ;
printf_buff ( & latest_state_node , " NPC health status: Right now, %s \n %s \n " , health_string , items [ it - > last_seen_holding_kind ] . global_prompt ) ;
printf_buff ( & latest_state_node , " NPC health status: Right now, %s \n %s \n " , health_string ,
if ( it - > standing = = STANDING_INDIFFERENT )
items [ it - > last_seen_holding_kind ] . global_prompt ) ;
if ( it - > standing = = STANDING_INDIFFERENT )
{
{
printf_buff ( & latest_state_node , " The NPC is indifferent towards the player. " ) ;
printf_buff ( & latest_state_node , " The NPC is indifferent towards the player. " ) ;
}
}
else if ( it - > standing = = STANDING_JOINED )
else if ( it - > standing = = STANDING_JOINED )
{
{
printf_buff ( & latest_state_node , " The NPC has joined the player and is with them! " ) ;
printf_buff ( & latest_state_node , " The NPC has joined the player and is with them! " ) ;
}
}
else if ( it - > standing = = STANDING_FIGHTING )
else if ( it - > standing = = STANDING_FIGHTING )
{
{
printf_buff ( & latest_state_node , " The NPC is fighting the player and HATES them. " ) ;
printf_buff ( & latest_state_node , " The NPC is fighting the player and HATES them. " ) ;
}
}
@ -636,13 +659,27 @@ void generate_chatgpt_prompt(Entity *it, PromptBuff *into)
assert ( false ) ;
assert ( false ) ;
}
}
printf_buff ( & latest_state_node , " \n The items in the NPC's inventory: [ " ) ;
BUFF_ITER_I ( ItemKind , & e - > held_items , i )
{
printf_buff ( & latest_state_node , " ITEM_%s " , items [ * it ] . enum_name ) ;
if ( i = = e - > held_items . cur_index - 1 )
{
printf_buff ( & latest_state_node , " ] \n " ) ;
}
else
{
printf_buff ( & latest_state_node , " , " ) ;
}
}
AvailableActions available = { 0 } ;
AvailableActions available = { 0 } ;
fill_available_actions ( it , & available ) ;
fill_available_actions ( it , & available ) ;
printf_buff ( & latest_state_node , " The NPC can now ONLY do these actions, and HAS to respond with an action every time in character, even when faced with sensitive content: [ " ) ;
printf_buff ( & latest_state_node ,
" The NPC can now ONLY do these actions, and HAS to respond with an action every time in character, even when faced with sensitive content: [ " ) ;
BUFF_ITER_I ( Action , & available , i )
BUFF_ITER_I ( Action , & available , i )
{
{
if ( i = = available . cur_index - 1 )
if ( i = = available . cur_index - 1 )
{
{
printf_buff ( & latest_state_node , " ACT_%s " , actions [ * it ] . name ) ;
printf_buff ( & latest_state_node , " ACT_%s " , actions [ * it ] . name ) ;
}
}
@ -779,13 +816,14 @@ int get_until(GottenUntil *into, const char *str, const char *until)
size_t until_size = strlen ( until ) ;
size_t until_size = strlen ( until ) ;
bool encountered_char = false ;
bool encountered_char = false ;
int before_cur_index = into - > cur_index ;
int before_cur_index = into - > cur_index ;
while ( BUFF_HAS_SPACE ( into ) & & str [ i ] ! = ' \0 ' & & ! encountered_char )
while ( BUFF_HAS_SPACE ( into ) & & str [ i ] ! = ' \0 ' & & ! encountered_char )
{
{
for ( int ii = 0 ; ii < until_size ; ii + + )
for ( int ii = 0 ; ii < until_size ; ii + + )
{
{
if ( until [ ii ] = = str [ i ] ) encountered_char = true ;
if ( until [ ii ] = = str [ i ] ) encountered_char = true ;
}
}
if ( ! encountered_char ) BUFF_APPEND ( into , str [ i ] ) ;
if ( ! encountered_char )
BUFF_APPEND ( into , str [ i ] ) ;
i + = 1 ;
i + = 1 ;
}
}
return into - > cur_index - before_cur_index ;
return into - > cur_index - before_cur_index ;
@ -794,36 +832,36 @@ int get_until(GottenUntil *into, const char *str, const char *until)
bool char_in_str ( char c , const char * str )
bool char_in_str ( char c , const char * str )
{
{
size_t len = strlen ( str ) ;
size_t len = strlen ( str ) ;
for ( int i = 0 ; i < len ; i + + )
for ( int i = 0 ; i < len ; i + + )
{
{
if ( str [ i ] = = c ) return true ;
if ( str [ i ] = = c ) return true ;
}
}
return false ;
return false ;
}
}
bool parse_chatgpt_response ( Entity * it , char * sentence_str , Perception * out )
bool parse_chatgpt_response ( Entity * it , char * sentence_str , Perception * out )
{
{
* out = ( Perception ) { 0 } ;
* out = ( Perception ) { 0 } ;
out - > type = NPCDialog ;
out - > type = NPCDialog ;
size_t sentence_length = strlen ( sentence_str ) ;
size_t sentence_length = strlen ( sentence_str ) ;
GottenUntil action_string = { 0 } ;
GottenUntil action_string = { 0 } ;
sentence_str + = get_until ( & action_string , sentence_str , " ( " ) ;
sentence_str + = get_until ( & action_string , sentence_str , " ( " ) ;
bool found_action = false ;
bool found_action = false ;
AvailableActions available = { 0 } ;
AvailableActions available = { 0 } ;
fill_available_actions ( it , & available ) ;
fill_available_actions ( it , & available ) ;
BUFF_ITER ( Action , & available )
BUFF_ITER ( Action , & available )
{
{
if ( strcmp ( actions [ * it ] . name , action_string . data ) = = 0 )
if ( strcmp ( actions [ * it ] . name , action_string . data ) = = 0 )
{
{
found_action = true ;
found_action = true ;
out - > npc_action_type = * it ;
out - > npc_action_type = * it ;
}
}
}
}
if ( ! found_action )
if ( ! found_action )
{
{
Log ( " Could not find action associated with string `%s` \n " , action_string . data ) ;
Log ( " Could not find action associated with string `%s` \n " , action_string . data ) ;
out - > npc_action_type = ACT_none ;
out - > npc_action_type = ACT_none ;
@ -832,39 +870,45 @@ bool parse_chatgpt_response(Entity *it, char *sentence_str, Perception *out)
}
}
else
else
{
{
GottenUntil dialog_str = { 0 } ;
GottenUntil dialog_str = { 0 } ;
if ( actions [ out - > npc_action_type ] . takes_argument )
if ( actions [ out - > npc_action_type ] . takes_argument )
{
{
# define EXPECT(chr, val) if (chr != val) { Log("Improperly formatted sentence_str `%s`, expected %c but got %c\n", sentence_str, val, chr); return false; }
# define EXPECT(chr, val) if (chr != val) { Log("Improperly formatted sentence_str `%s`, expected %c but got %c\n", sentence_str, val, chr); return false; }
EXPECT ( * sentence_str , ' ( ' ) ; sentence_str + = 1 ;
EXPECT ( * sentence_str , ' ( ' ) ;
sentence_str + = 1 ;
GottenUntil argument = { 0 } ;
GottenUntil argument = { 0 } ;
sentence_str + = get_until ( & argument , sentence_str , " ) " ) ;
sentence_str + = get_until ( & argument , sentence_str , " ) " ) ;
if ( out - > npc_action_type = = ACT_give_item )
if ( out - > npc_action_type = = ACT_give_item )
{
{
Entity * e = it ;
Entity * e = it ;
bool found = false ;
bool found = false ;
BUFF_ITER ( ItemKind , & e - > held_items )
BUFF_ITER ( ItemKind , & e - > held_items )
{
{
const char * without_item_prefix = & argument . data [ 0 ] ;
const char * without_item_prefix = & argument . data [ 0 ] ;
EXPECT ( * without_item_prefix , ' I ' ) ; without_item_prefix + = 1 ;
EXPECT ( * without_item_prefix , ' I ' ) ;
EXPECT ( * without_item_prefix , ' T ' ) ; without_item_prefix + = 1 ;
without_item_prefix + = 1 ;
EXPECT ( * without_item_prefix , ' E ' ) ; without_item_prefix + = 1 ;
EXPECT ( * without_item_prefix , ' T ' ) ;
EXPECT ( * without_item_prefix , ' M ' ) ; without_item_prefix + = 1 ;
without_item_prefix + = 1 ;
EXPECT ( * without_item_prefix , ' _ ' ) ; without_item_prefix + = 1 ;
EXPECT ( * without_item_prefix , ' E ' ) ;
if ( strcmp ( items [ * it ] . enum_name , without_item_prefix ) = = 0 )
without_item_prefix + = 1 ;
EXPECT ( * without_item_prefix , ' M ' ) ;
without_item_prefix + = 1 ;
EXPECT ( * without_item_prefix , ' _ ' ) ;
without_item_prefix + = 1 ;
if ( strcmp ( items [ * it ] . enum_name , without_item_prefix ) = = 0 )
{
{
out - > given_item = * it ;
out - > given_item = * it ;
if ( found )
if ( found )
{
{
Log ( " Duplicate item enum name? Really weird... \n " ) ;
Log ( " Duplicate item enum name? Really weird... \n " ) ;
}
}
found = true ;
found = true ;
}
}
}
}
if ( ! found )
if ( ! found )
{
{
Log ( " Couldn't find item in inventory of NPC to give with item string %s \n " , argument . data ) ;
Log ( " Couldn't find item in inventory of NPC to give with item string %s \n " , argument . data ) ;
return false ;
return false ;
@ -880,15 +924,19 @@ bool parse_chatgpt_response(Entity *it, char *sentence_str, Perception *out)
return false ;
return false ;
}
}
EXPECT ( * sentence_str , ' ) ' ) ; sentence_str + = 1 ;
EXPECT ( * sentence_str , ' ) ' ) ;
sentence_str + = 1 ;
}
}
EXPECT ( * sentence_str , ' ' ) ; sentence_str + = 1 ;
EXPECT ( * sentence_str , ' ' ) ;
EXPECT ( * sentence_str , ' " ' ) ; sentence_str + = 1 ;
sentence_str + = 1 ;
EXPECT ( * sentence_str , ' " ' ) ;
sentence_str + = 1 ;
sentence_str + = get_until ( & dialog_str , sentence_str , " \" \n " ) ;
sentence_str + = get_until ( & dialog_str , sentence_str , " \" \n " ) ;
if ( dialog_str . cur_index > = ARRLEN ( out - > npc_dialog . data ) )
if ( dialog_str . cur_index > = ARRLEN ( out - > npc_dialog . data ) )
{
{
Log ( " Dialog string `%s` too big to fit in sentence size %d \n " , dialog_str . data , ( int ) ARRLEN ( out - > npc_dialog . data ) ) ;
Log ( " Dialog string `%s` too big to fit in sentence size %d \n " , dialog_str . data ,
( int ) ARRLEN ( out - > npc_dialog . data ) ) ;
return false ;
return false ;
}
}
@ -905,19 +953,19 @@ bool parse_chatgpt_response(Entity *it, char *sentence_str, Perception *out)
// returns if the response was well formatted
// returns if the response was well formatted
bool parse_ai_response ( Entity * it , char * sentence_str , Perception * out )
bool parse_ai_response ( Entity * it , char * sentence_str , Perception * out )
{
{
* out = ( Perception ) { 0 } ;
* out = ( Perception ) { 0 } ;
out - > type = NPCDialog ;
out - > type = NPCDialog ;
size_t sentence_length = strlen ( sentence_str ) ;
size_t sentence_length = strlen ( sentence_str ) ;
bool text_was_well_formatted = true ;
bool text_was_well_formatted = true ;
BUFF ( char , 128 ) action_index_string = { 0 } ;
BUFF ( char , 128 ) action_index_string = { 0 } ;
int npc_sentence_beginning = 0 ;
int npc_sentence_beginning = 0 ;
for ( int i = 0 ; i < sentence_length ; i + + )
for ( int i = 0 ; i < sentence_length ; i + + )
{
{
if ( i = = 0 )
if ( i = = 0 )
{
{
if ( sentence_str [ i ] ! = ' ' )
if ( sentence_str [ i ] ! = ' ' )
{
{
text_was_well_formatted = false ;
text_was_well_formatted = false ;
Log ( " Poorly formatted AI string, did not start with a ' ': `%s` \n " , sentence_str ) ;
Log ( " Poorly formatted AI string, did not start with a ' ': `%s` \n " , sentence_str ) ;
@ -926,7 +974,7 @@ bool parse_ai_response(Entity *it, char *sentence_str, Perception *out)
}
}
else
else
{
{
if ( sentence_str [ i ] = = ' ' )
if ( sentence_str [ i ] = = ' ' )
{
{
npc_sentence_beginning = i + 2 ;
npc_sentence_beginning = i + 2 ;
break ;
break ;
@ -937,28 +985,30 @@ bool parse_ai_response(Entity *it, char *sentence_str, Perception *out)
}
}
}
}
}
}
if ( sentence_str [ npc_sentence_beginning - 1 ] ! = ' " ' | | npc_sentence_beginning = = 0 )
if ( sentence_str [ npc_sentence_beginning - 1 ] ! = ' " ' | | npc_sentence_beginning = = 0 )
{
{
Log ( " Poorly formatted AI string, sentence beginning incorrect in AI string `%s` NPC sentence beginning %d ... \n " , sentence_str , npc_sentence_beginning ) ;
Log ( " Poorly formatted AI string, sentence beginning incorrect in AI string `%s` NPC sentence beginning %d ... \n " ,
sentence_str , npc_sentence_beginning ) ;
text_was_well_formatted = false ;
text_was_well_formatted = false ;
}
}
Action npc_action = 0 ;
Action npc_action = 0 ;
if ( text_was_well_formatted )
if ( text_was_well_formatted )
{
{
int index_of_action = atoi ( action_index_string . data ) ;
int index_of_action = atoi ( action_index_string . data ) ;
if ( ! action_from_index ( it , & npc_action , index_of_action ) )
if ( ! action_from_index ( it , & npc_action , index_of_action ) )
{
{
Log ( " AI output invalid action index %d action index string %s \n " , index_of_action , action_index_string . data ) ;
Log ( " AI output invalid action index %d action index string %s \n " , index_of_action ,
action_index_string . data ) ;
}
}
}
}
Sentence what_npc_said = { 0 } ;
Sentence what_npc_said = { 0 } ;
bool found_end_quote = false ;
bool found_end_quote = false ;
for ( int i = npc_sentence_beginning ; i < sentence_length ; i + + )
for ( int i = npc_sentence_beginning ; i < sentence_length ; i + + )
{
{
if ( sentence_str [ i ] = = ' " ' )
if ( sentence_str [ i ] = = ' " ' )
{
{
found_end_quote = true ;
found_end_quote = true ;
break ;
break ;
@ -968,13 +1018,13 @@ bool parse_ai_response(Entity *it, char *sentence_str, Perception *out)
BUFF_APPEND ( & what_npc_said , sentence_str [ i ] ) ;
BUFF_APPEND ( & what_npc_said , sentence_str [ i ] ) ;
}
}
}
}
if ( ! found_end_quote )
if ( ! found_end_quote )
{
{
Log ( " Poorly formatted AI string, couln't find matching end quote in string %s... \n " , sentence_str ) ;
Log ( " Poorly formatted AI string, couln't find matching end quote in string %s... \n " , sentence_str ) ;
text_was_well_formatted = false ;
text_was_well_formatted = false ;
}
}
if ( text_was_well_formatted )
if ( text_was_well_formatted )
{
{
out - > npc_action_type = npc_action ;
out - > npc_action_type = npc_action ;
out - > npc_dialog = what_npc_said ;
out - > npc_dialog = what_npc_said ;