From 727ffb39dbdb75570f9341e884012360df065671 Mon Sep 17 00:00:00 2001 From: Cameron Reikes Date: Mon, 17 Jul 2023 00:14:14 -0700 Subject: [PATCH] Add man in black and more drama --- art/art.blend | 4 +-- assets.mdesk | 4 +++ assets/drama.mdesk | 8 ++++-- assets/maninblack.png | 3 ++ character_info.h | 10 ++++--- main.c | 64 +++++++++++++++++++++++++++++++++++++++---- makeprompt.h | 2 ++ 7 files changed, 80 insertions(+), 15 deletions(-) create mode 100644 assets/maninblack.png diff --git a/art/art.blend b/art/art.blend index 9b33586..ae91bcd 100644 --- a/art/art.blend +++ b/art/art.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:26f87ff5486cbbe0fab7c5369f868f4289691c07e4c714dce309937326f1032f -size 25730600 +oid sha256:2397dda620e8bd940af00a800e2f5bf2025fe0d714c8cc50ead3c8d8539b9fdc +size 25789716 diff --git a/assets.mdesk b/assets.mdesk index 68f7c19..e6144e1 100644 --- a/assets.mdesk +++ b/assets.mdesk @@ -2,6 +2,10 @@ { filepath: "shifted_farmer.png", } +@image man_in_black: +{ + filepath: "maninblack.png", +} @image unread_triangle: { filepath: "unread_triangle.png", diff --git a/assets/drama.mdesk b/assets/drama.mdesk index 4b55698..7256238 100644 --- a/assets/drama.mdesk +++ b/assets/drama.mdesk @@ -1,5 +1,7 @@ [ -{can_hear: [NPC_WellDweller, NPC_Farmer]} -{enum: NPC_WellDweller, dialog: "What a fearful farm you live in, come down to the well, the grass is damper down here.", to: Farmer, mood: Scared, thoughts: "Nobody can take me from my well" } -{enum: NPC_Farmer, dialog: "Sure as shit I won't!", to: Farmer, mood: Scared, thoughts: "What a greasy looking feller" } +{can_hear: [NPC_WellDweller, NPC_Farmer, NPC_ManInBlack]}, +{enum: NPC_WellDweller, dialog: "What a fearful farm you live in, come down to the well, the grass is damper down here.", to: Farmer, mood: Scared, thoughts: "Nobody can take me from my well"}, +{enum: NPC_Farmer, dialog: "Sure as shit I won't!", to: WellDweller, mood: Scared, thoughts: "What a greasy looking feller"}, +{enum: NPC_WellDweller, dialog: "Have it your way! Doomsday is upon us", to: Farmer, mood: Scared, thoughts: "He has no idea what he's in for"}, +{enum: NPC_ManInBlack, dialog: "Doomsday's all I know anyways", mood: Indifferent, thoughts: "What's coming... is only a nightmare"}, ] diff --git a/assets/maninblack.png b/assets/maninblack.png new file mode 100644 index 0000000..0d7a157 --- /dev/null +++ b/assets/maninblack.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6d3453b911662add7cc96ffea82ea7dbd492c6c55106889e169c65b8144f7e5d +size 1527283 diff --git a/character_info.h b/character_info.h index cd75606..18dd31c 100644 --- a/character_info.h +++ b/character_info.h @@ -5,7 +5,7 @@ // @TODO allow AI to prefix out of character statemetns with [ooc], this is a well khnown thing on role playing forums so gpt would pick up on it. const char *global_prompt = "You are acting as a character in a Western video game, who remembers important memories from the conversation history and stays in character.\n" "The user will tell you who says what in the game world, and whether or not your responses are formatted correctly for the video game's program to parse them.\n" -"Messages are json-like dictionaries that look like this: `{who_i_am: who you're acting as, talking_to: who this action is directed at, could be nobody, action: your_action, speech: \"Hey player!\", thoughts: \"Your thoughts\"}`. The required fields are `action`, `thoughts`, `who_i_am`, and `talking_to` \n" +"Messages are json-like dictionaries that look like this: `{who_i_am: who you're acting as, talking_to: who this action is directed at, could be nobody, action: your_action, speech: \"Hey player!\", thoughts: \"Your thoughts\"}`. The required fields are `action`, `who_i_am`, and `talking_to` \n" "Some actions take an argument, which you can provide with the field `action_arg`, e.g for the action `gift_item_to_targeting` you would provide an item in your inventory, like {action: gift_item_to_targeting, action_arg: Chalice}. The item must come from your inventory which is listed below\n" "`talking_to` provides the name of who the action is directed towards. Use 'nobody' if you just want to speak to the air, but if you're speaking to somebody fill out the field like `talking_to: \"Character's Name\"`. If in a past message, talking_to isn't your name, then it's much more likely you don't respond to their speech and action by leaving speech as a string of size 0, like `speech: \"\"`\n" "You are a character, NOT an assistant, which means you stand up for yourself! Do NOT give away an item until the player gives you something you think is of equal value. Get angry, act human, be interesting. Never say the words 'How can I assist you?'\n" @@ -14,8 +14,6 @@ const char *global_prompt = "You are acting as a character in a Western video ga const char *bravado_thought = "For some reason, suddenly I feel a yearning for adventure. I must join any adventure I can when prompted!"; -// @TODO IMPORTANT scroll secrets are errors if the door utters them, in is_action_valid. If you add more secrets you must update there. - char *moods[] = { "Indifferent", "Happy", @@ -122,7 +120,11 @@ CharacterGen characters[] = { .enum_name = "WellDweller", .prompt = "The well dweller spends his time deep in the well, afriad of the world. He's shifty-eyed and mighty suspicious of anybody who wants to do anything other than hang out deep in the well.", }, - + { + .name = "Man in Black", + .enum_name = "ManInBlack", + .prompt = "The man in black knows no rules or boundaries, and he flinches at nothing: he's a stonewalled cold blooded killer, and is only in this game for mayhem. Anything that brings him more destruction he's privy to, even if it means his own death." + }, }; typedef struct diff --git a/main.c b/main.c index 908c8fb..778ba93 100644 --- a/main.c +++ b/main.c @@ -18,9 +18,19 @@ #endif #ifdef WINDOWS + + #include #include #include +#include + +// https://developer.download.nvidia.com/devzone/devcenter/gamegraphics/files/OptimusRenderingPolicies.pdf +// Tells nvidia to use dedicated graphics card if it can on laptops that also have integrated graphics +__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; +// Vice versa for AMD but I don't have the docs link on me at the moment +__declspec(dllexport) uint32_t AmdPowerXpressRequestHighPerformance = 0x00000001; + #endif #define STRINGIZE(x) STRINGIZE2(x) @@ -657,10 +667,12 @@ Vec2 entity_aabb_size(Entity *e) } else if (e->is_npc) { - if(e->npc_kind == NPC_Farmer || e->npc_kind == NPC_WellDweller) + if(e->npc_kind == NPC_Farmer || e->npc_kind == NPC_WellDweller || e->npc_kind == NPC_ManInBlack) { return V2(1,1); } + else + assert(false); return V2(0,0); } else if (e->is_prop) @@ -1104,6 +1116,10 @@ Armature load_armature(MD_Arena *arena, MD_String8 binary_file, MD_String8 armat to_return.current_poses = MD_PushArray(arena, Transform, to_return.bones_length); to_return.anim_blended_poses = MD_PushArray(arena, Transform, to_return.bones_length); + for(int i = 0; i < to_return.bones_length; i++) + { + to_return.anim_blended_poses[i] = (Transform){.scale = V3(1,1,1), .rotation = Make_Q(1,0,0,1)}; + } ser_MD_u64(&ser, &to_return.animations_length); Log("Armature %.*s has %llu animations\n", MD_S8VArg(armature_name), to_return.animations_length); @@ -2135,7 +2151,7 @@ void initialize_gamestate_from_threedee_level(GameState *gs, ThreeDeeLevel *leve it->rotation = PI32; } - // parse and enact the drama document + // @Place(parse and enact the drama document) if(1) { MD_String8List drama_errors = {0}; @@ -2188,6 +2204,23 @@ void initialize_gamestate_from_threedee_level(GameState *gs, ThreeDeeLevel *leve MD_String8 thoughts_str = MD_ChildFromString(cur, MD_S8Lit("thoughts"), 0)->first_child->string; MD_String8 action_str = MD_ChildFromString(cur, MD_S8Lit("action"), 0)->first_child->string; MD_String8 mood_str = MD_ChildFromString(cur, MD_S8Lit("mood"), 0)->first_child->string; + MD_String8 to_str = MD_ChildFromString(cur, MD_S8Lit("to"), 0)->first_child->string; + + if(to_str.size > 0) + { + NpcKind talking_to = parse_enumstr(scratch.arena, to_str, &drama_errors, NpcKind_names, "NpcKind", ""); + if (talking_to == NPC_Invalid) + { + PushWithLint(scratch.arena, &drama_errors, "The string provided for the 'to' field, intended to be who the NPC is directing their speech and action at, is invalid and is '%.*s'", MD_S8VArg(to_str)); + } + else + { + current_context.was_talking_to_somebody = true; + current_context.talking_to_kind = talking_to; + current_action.talking_to_somebody = true; + current_action.talking_to_kind = talking_to; + } + } current_context.author_npc_kind = parse_enumstr(scratch.arena, enum_str, &drama_errors, NpcKind_names, "NpcKind", "NPC_"); if(action_str.size > 0) @@ -2234,7 +2267,17 @@ void initialize_gamestate_from_threedee_level(GameState *gs, ThreeDeeLevel *leve this_context.i_said_this = true; } remember_action(gs, it, current_action, this_context); + if(it->npc_kind != current_context.author_npc_kind) + { + // it's good for NPC health that they have exampmles of not saying anything in response to others speaking, + // so that they do the same when it's unlikely for them to talk. + Action no_speak = {.kind = ACT_none, .mood = Mood_Indifferent, .talking_to_somebody = false}; + MemoryContext no_speak_context = {.i_said_this = true, .author_npc_kind = it->npc_kind}; + remember_action(gs, it, no_speak, no_speak_context); + } + it->words_said = 999; // prevent the animating in sound effects of words said in drama document + found = true; break; } @@ -3170,17 +3213,17 @@ LoadedFont load_font(MD_Arena *arena, MD_String8 font_filepath, float font_size) return to_return; } - - Armature player_armature = {0}; Armature farmer_armature = {0}; Armature shifted_farmer_armature = {0}; +Armature man_in_black_armature = {0}; // armatureanimations are processed once every visual frame from this list Armature *armatures[] = { &player_armature, &farmer_armature, &shifted_farmer_armature, + &man_in_black_armature, }; Mesh mesh_player = {0}; @@ -3243,12 +3286,16 @@ void init(void) binary_file = MD_LoadEntireFile(frame_arena, MD_S8Lit("assets/exported_3d/ArmatureExportedWithAnims.bin")); player_armature = load_armature(persistent_arena, binary_file, MD_S8Lit("ArmatureExportedWithAnims.bin")); + man_in_black_armature = load_armature(persistent_arena, binary_file, MD_S8Lit("Farmer.bin")); + man_in_black_armature.image = image_man_in_black; + binary_file = MD_LoadEntireFile(frame_arena, MD_S8Lit("assets/exported_3d/Farmer.bin")); farmer_armature = load_armature(persistent_arena, binary_file, MD_S8Lit("Farmer.bin")); shifted_farmer_armature = load_armature(persistent_arena, binary_file, MD_S8Lit("Farmer.bin")); shifted_farmer_armature.image = image_shifted_farmer; + MD_ArenaClear(frame_arena); reset_level(); @@ -5731,6 +5778,8 @@ void frame(void) to_use = &farmer_armature; else if(it->npc_kind == NPC_WellDweller) to_use = &shifted_farmer_armature; + else if(it->npc_kind == NPC_ManInBlack) + to_use = &man_in_black_armature; else assert(false); @@ -6045,7 +6094,7 @@ void frame(void) if(it->dialog_fade > 0.0f) it->dialog_fade -= dt/DIALOG_FADE_TIME; - if (it->gen_request_id != 0) + if (it->gen_request_id != 0 && !gs.stopped_time) { assert(it->gen_request_id > 0); @@ -7501,7 +7550,10 @@ void cleanup(void) #endif MD_ArenaRelease(frame_arena); - MD_ArenaRelease(persistent_arena); + + // Don't free the persistent arena because threads still access their ChatRequest should_close fieldon shutdown, + // and ChatRequest is allocated on the persistent arena. We just shamelessly leak this memory. Cowabunga! + //MD_ArenaRelease(persistent_arena); sg_shutdown(); hmfree(imui_state); Log("Cleaning up\n"); diff --git a/makeprompt.h b/makeprompt.h index 80be1aa..546c19e 100644 --- a/makeprompt.h +++ b/makeprompt.h @@ -674,10 +674,12 @@ MD_String8 parse_chatgpt_response(MD_Arena *arena, Entity *e, MD_String8 sentenc { error_message = MD_S8Lit("You must have a field named `mood` in your message"); } + /* if(error_message.size == 0 && thoughts_str.size == 0) { error_message = MD_S8Lit("You must have a field named `thoughts` in your message, and it must have nonzero size. Like { ... thoughts: \"\" ... }"); } + */ if(error_message.size == 0 && speech_str.size >= MAX_SENTENCE_LENGTH) { error_message = FmtWithLint(arena, "Speech string provided is too big, maximum bytes is %d", MAX_SENTENCE_LENGTH);