From b652e2e64021a79db676a01813e0de1b9e04c34e Mon Sep 17 00:00:00 2001 From: Cameron Reikes Date: Tue, 26 Sep 2023 18:26:13 -0700 Subject: [PATCH] Game is finally running without crashing again --- main.c | 530 +++++++-------------------------------------------- makeprompt.h | 28 +-- tuning.h | 1 - 3 files changed, 73 insertions(+), 486 deletions(-) diff --git a/main.c b/main.c index 0dcdd25..14d2bfe 100644 --- a/main.c +++ b/main.c @@ -1350,7 +1350,7 @@ Room *get_cur_room(GameState *gs, ThreeDeeLevel *level) Room *in_room = 0; for(Room *cur = level->room_list; cur; cur = cur->next) { - if(S8Match(cur->name, gs->player->current_room_name, 0)) + if(S8Match(cur->name, gs->current_room_name, 0)) { in_room = cur; break; @@ -1722,7 +1722,7 @@ CanTalkTo get_can_talk_to(Entity *e) CanTalkTo to_return = {0}; ENTITIES_ITER(gs.entities) { - if(it != e && (it->is_npc) && S8Match(it->current_room_name, from->current_room_name, 0)) + if(it != e && (it->is_npc) && S8Match(it->current_room_name, e->current_room_name, 0)) { BUFF_APPEND(&to_return, it); } @@ -1769,11 +1769,7 @@ void remember_action(GameState *gs, Entity *to_modify, Action a, MemoryContext c to_modify->cur_page_index = 0; } } -FUNCTION u8 -CharToUpper(u8 c) -{ - return (c >= 'a' && c <= 'z') ? ('A' + (c - 'a')) : c; -} +u8 CharToUpper(u8 c); String8 npc_identifier(String8 name) { String8 ret; @@ -1784,6 +1780,10 @@ String8 npc_identifier(String8 name) { } return ret; } +// bad helper for now. +String8 npc_identifier_chunk(TextChunk chunk) { + return npc_identifier(TextChunkString8(chunk)); +} // returns reason why allocated on arena if invalid // to might be null here, from can't be null @@ -1818,7 +1818,7 @@ String8 is_action_valid(Arena *arena, Entity *from, Action a) if(!found) { - error_message = FmtWithLint(arena, "Character you're talking to, %.*s, isn't in the same room and so can't be talked to", TextChunkVArg(npc_data(gs, a.talking_to_kind).enum_name)); + error_message = FmtWithLint(arena, "Character you're talking to, %.*s, isn't in the same room and so can't be talked to", S8VArg(npc_identifier_chunk(npc_data(&gs, a.talking_to_kind)->name))); } } @@ -1828,7 +1828,7 @@ String8 is_action_valid(Arena *arena, Entity *from, Action a) } if(error_message.size == 0 && a.kind == ACT_join && gete(from->joined) != 0) { - error_message = FmtWithLint(arena, "You can't join somebody, you're already in %.*s's party", TextChunkVArg(npc_data(gs, gete(from->joined)->npc_kind)->name)); + error_message = FmtWithLint(arena, "You can't join somebody, you're already in %.*s's party", TextChunkVArg(npc_data(&gs, gete(from->joined)->npc_kind)->name)); } if(error_message.size == 0 && a.kind == ACT_fire_shotgun && gete(from->aiming_shotgun_at) == 0) { @@ -1850,7 +1850,7 @@ String8 is_action_valid(Arena *arena, Entity *from, Action a) } if(arg_valid == false) { - error_message = FmtWithLint(arena, "Your action_argument for who the action `%.*s` be directed at, %s, is either invalid (you can't operate on nobody) or it's not an NPC that's near you right now.", actions[a.kind].name, TextChunkVArg(npc_data(gs, a.argument.targeting)->name)); + error_message = FmtWithLint(arena, "Your action_argument for who the action `%s` be directed at, %.*s, is either invalid (you can't operate on nobody) or it's not an NPC that's near you right now.", actions[a.kind].name, TextChunkVArg(npc_data(&gs, a.argument.targeting)->name)); } } @@ -1928,58 +1928,6 @@ void cause_action_side_effects(Entity *from, Action a) ReleaseScratch(scratch); } -typedef struct PropagatingAction -{ - struct PropagatingAction *next; - - Action a; - MemoryContext context; - - TextChunk in_room_name; - Vec2 from; - bool already_propagated_to[MAX_ENTITIES]; // tracks by index of entity - float progress; // if greater than or equal to 1.0, is freed -} PropagatingAction; - -PropagatingAction *propagating = 0; - -PropagatingAction ignore_entity(Entity *to_ignore, PropagatingAction p) -{ - PropagatingAction to_return = p; - to_return.already_propagated_to[frome(to_ignore).index] = true; - return to_return; -} - -void push_propagating(PropagatingAction to_push) -{ - to_push.context.heard_physically = true; - bool found = false; - for(PropagatingAction *cur = propagating; cur; cur = cur->next) - { - if(cur->progress >= 1.0f) - { - PropagatingAction *prev_next = cur->next; - *cur = to_push; - cur->next = prev_next; - found = true; - break; - } - } - - if(!found) - { - PropagatingAction *cur = PushArray(persistent_arena, PropagatingAction, 1); - *cur = to_push; - StackPush(propagating, cur); - } -} - -float propagating_radius(PropagatingAction *p) -{ - float t = powf(p->progress, 0.65f); - return Lerp(0.0f, t, PROPAGATE_ACTIONS_RADIUS); -} - // only called when the action is instantiated, correctly propagates the information // of the action physically and through the party // If the action is invalid, remembers the error if it's an NPC, and does nothing else @@ -2001,15 +1949,13 @@ bool perform_action(GameState *gs, Entity *from, Action a) bool proceed_propagating = true; if(is_valid.size > 0) { - assert(from->npc_kind != NPC_Player); + assert(!from->is_player); append_to_errors(from, make_memory(a, context), is_valid); proceed_propagating = false; } - bool angel_heard_action = false; - Entity *targeted = 0; if(proceed_propagating) { @@ -2022,11 +1968,10 @@ bool perform_action(GameState *gs, Entity *from, Action a) cause_action_side_effects(from, a); // self memory - if(from->npc_kind != NPC_Player) + if(!from->is_player) { MemoryContext my_context = context; my_context.i_said_this = true; - angel_heard_action = angel_heard_action || from->npc_kind == NPC_Angel; remember_action(gs, from, a, my_context); } @@ -2041,22 +1986,21 @@ bool perform_action(GameState *gs, Entity *from, Action a) // memory of target if(targeted) { - angel_heard_action = angel_heard_action || targeted->npc_kind == NPC_Angel; remember_action(gs, targeted, a, context); } - // propagate physically - PropagatingAction to_propagate = {0}; - chunk_from_s8(&to_propagate.in_room_name, from->current_room_name); - to_propagate.a = a; - to_propagate.context = context; - to_propagate.from = from->pos; - to_propagate = ignore_entity(from, to_propagate); - if(targeted) + // propagate to other npcs in the room + ENTITIES_ITER(gs->entities) { - to_propagate = ignore_entity(targeted, to_propagate); + if( + !it->is_player + && S8Match(it->current_room_name, from->current_room_name, 0) + && it != from + && it != targeted + ) { + remember_action(gs, it, a, context); + } } - push_propagating(to_propagate); } ReleaseScratch(scratch); @@ -2225,7 +2169,7 @@ void transition_to_room(GameState *gs, ThreeDeeLevel *level, String8 new_room_na assert(gs); (void)level; - gs->player->current_room_name = new_room_name; + gs->current_room_name = new_room_name; } @@ -2244,216 +2188,18 @@ void initialize_gamestate_from_threedee_level(GameState *gs, ThreeDeeLevel *leve { for (PlacedEntity *cur = cur_room->placed_entity_list; cur; cur = cur->next) { + assert(false); // need to decide on a way entitie kind is decided on Entity *cur_entity = new_entity(gs); cur_entity->npc_kind = cur->npc_kind; cur_entity->pos = point_plane(cur->t.offset); - cur_entity->is_npc = true; cur_entity->current_room_name = cur_room->name; - if (cur_entity->npc_kind == NPC_Player) - { - assert(!gs->player); - gs->player = cur_entity; - } } } gs->world_entity = new_entity(gs); gs->world_entity->is_world = true; - // @Place(parse and enact the drama document parse drama) - if(1) - { - String8List drama_errors = {0}; - - ArenaTemp scratch = GetScratch(0, 0); - String8 filename = S8Lit("assets/drama.mdesk"); - String8 drama_document = LoadEntireFile(scratch.arena, filename); - assert(drama_document.size != 0); - ParseResult parse = ParseWholeString(scratch.arena, filename, drama_document); - if(parse.errors.first) - { - for(Message *cur = parse.errors.first; cur; cur = cur->next) - { - String8 to_print = MD_FormatMessage(scratch.arena, CodeLocFromNode(cur->node), cur->kind, cur->string); - PushWithLint(scratch.arena, &drama_errors, "Failed to parse: `%.*s`\n", S8VArg(to_print)); - } - } - - BUFF(NpcKind, 128) not_on_map = {0}; - if(drama_errors.node_count == 0) - { - // used - - Node *can_hear = NilNode(); - for(Node *cur = parse.node->first_child->first_child; !NodeIsNil(cur) && drama_errors.node_count == 0; cur = cur->next) - { - Node *cur_can_hear = MD_ChildFromString(cur, S8Lit("can_hear"), 0); - if(!NodeIsNil(cur_can_hear)) - { - if(NodeIsNil(cur_can_hear->first_child)) - { - PushWithLint(scratch.arena, &drama_errors, "`can_hear` must be followed by a valid array of NPC kinds who can hear the following conversation"); - } - else - { - can_hear = cur_can_hear->first_child; - } - } - else - { - if(NodeIsNil(can_hear)) - { - PushWithLint(scratch.arena, &drama_errors, "Expected a statement with `can_hear` before any speech that says who can hear the current speech"); - } - - Action current_action = {0}; - MemoryContext current_context = {0}; - current_context.drama_memory = true; - if(drama_errors.node_count == 0) - { - String8 enum_str = expect_childnode(scratch.arena, cur, S8Lit("enum"), &drama_errors)->first_child->string; - String8 dialog = expect_childnode(scratch.arena, cur, S8Lit("dialog"), &drama_errors)->first_child->string; - String8 action_str = MD_ChildFromString(cur, S8Lit("action"), 0)->first_child->string; - String8 action_argument_str = MD_ChildFromString(cur, S8Lit("action_argument"), 0)->first_child->string; - String8 to_str = MD_ChildFromString(cur, S8Lit("to"), 0)->first_child->string; - - if(to_str.size > 0) - { - NpcKind talking_to = parse_enumstr(scratch.arena, to_str, &drama_errors, NpcKind_enum_names, "NpcKind", ""); - if (talking_to == NPC_nobody) - { - 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'", S8VArg(to_str)); - } - else - { - current_context.talking_to_kind = talking_to; - current_action.talking_to_kind = talking_to; - } - } - - current_context.author_npc_kind = parse_enumstr(scratch.arena, enum_str, &drama_errors, NpcKind_enum_names, "NpcKind", ""); - if(action_str.size > 0) - { - current_action.kind = parse_enumstr(scratch.arena, action_str, &drama_errors, ActionKind_names, "ActionKind", "ACT_"); - } - if(action_argument_str.size > 0) - { - String8 error = {0}; - parse_action_argument(scratch.arena, &error, current_action.kind, action_argument_str, ¤t_action.argument); - if(error.size > 0) - { - PushWithLint(scratch.arena, &drama_errors, "Error parsing argument: '%.*s'", S8VArg(error)); - } - } - - if(dialog.size >= ARRLEN(current_action.speech.text)) - { - PushWithLint(scratch.arena, &drama_errors, "Current action_str's speech is of size %d, bigger than allowed size %d", (int)dialog.size, (int)ARRLEN(current_action.speech.text)); - } - - if(drama_errors.node_count == 0) - { - chunk_from_s8(¤t_action.speech, dialog); - } - } - - - if(drama_errors.node_count == 0) - { - for(Node *cur_kind_node = can_hear; !NodeIsNil(cur_kind_node); cur_kind_node = cur_kind_node->next) - { - NpcKind want = parse_enumstr(scratch.arena, cur_kind_node->string, &drama_errors, NpcKind_enum_names, "NpcKind", ""); - if(drama_errors.node_count == 0) - { - bool found = false; - ENTITIES_ITER(gs->entities) - { - if(it->is_npc && it->npc_kind == want) - { - MemoryContext this_context = current_context; - if(it->npc_kind == current_context.author_npc_kind) - { - this_context.i_said_this = true; - } - remember_action(gs, it, current_action, this_context); - if(it->npc_kind != current_context.author_npc_kind && it->npc_kind != current_context.talking_to_kind) - { - // it's good for NPC health that they have examples of not saying anything in response to others speaking, - // so that they do the same when it's unlikely for them to talk. - - if(g_randf(gs) < characters[it->npc_kind].silence_factor) - { - Action no_speak = {0}; - MemoryContext no_speak_context = {.i_said_this = true, .author_npc_kind = it->npc_kind}; - remember_action(gs, it, no_speak, no_speak_context); - } - } - - it->undismissed_action = false; // prevent the animating in sound effects of words said in drama document - - found = true; - break; - } - } - - if(!found) - { - bool already_warned = false; - BUFF_ITER(NpcKind, ¬_on_map) - { - if(*it == want) already_warned = true; - } - if(!already_warned) - { - //Log("Warning: NPC of kind %s isn't on the map, but has entries in the drama document\n", characters[want].enum_name); - BUFF_APPEND(¬_on_map, want); - } - } - } - } - } - - // if ended conversation, make the target of the end of conversation action output silence - if(drama_errors.node_count == 0) - { - // I use FmtWithLint and the actions[] array here so it's a compile error if we ever remove this action or rename it, instead of just directly putting the string - if(current_action.kind == ACT_end_conversation) - { - ENTITIES_ITER(gs->entities) - { - if(it->is_npc && it->npc_kind == current_action.argument.targeting) - { - Action no_speak = {0}; - MemoryContext no_speak_context = {.i_said_this = true, .author_npc_kind = it->npc_kind}; - remember_action(gs, it, no_speak, no_speak_context); - } - } - } - } - } - } - } - - if(drama_errors.node_count > 0) - { - for(String8Node *cur = drama_errors.first; cur; cur = cur->next) - { - fprintf(stderr, "Error: %.*s\n", S8VArg(cur->string)); - } - assert(false); - } - - - ENTITIES_ITER(gs->entities) - { - it->perceptions_dirty = false; // nobody should say anything about jester memories - } - } - - - - - transition_to_room(gs, &level_threedee, gs->player->current_room_name); + transition_to_room(gs, &level_threedee, level->room_list->name); } @@ -2603,29 +2349,18 @@ void ser_GameState(SerState *ser, GameState *gs) ser_entity(ser, &(gs->entities[i])); } } - - gs->player = 0; gs->world_entity = 0; - if(!ser->cur_error.failed) { ARR_ITER(Entity, gs->entities) { - if(it->npc_kind == NPC_Player) - { - gs->player = it; - } if(it->is_world) { gs->world_entity = it; } } - if(gs->player == 0) - { - ser->cur_error = (SerError){.failed = true, .why = S8Lit("No player entity found in deserialized entities")}; - } if(gs->world_entity == 0) { ser->cur_error = (SerError){.failed = true, .why = S8Lit("No world entity found in deserialized entities")}; @@ -2776,6 +2511,7 @@ void end_text_input(char *what_player_said_cstr) chunk_from_s8(&to_perform.speech, what_player_said); + /* if (gete(gs.player->talking_to)) { assert(gete(gs.player->talking_to)->is_npc); @@ -2783,6 +2519,8 @@ void end_text_input(char *what_player_said_cstr) } perform_action(&gs, gs.player, to_perform); +*/ + Log("UNIMPLEMENTED!!\n"); } ReleaseScratch(scratch); } @@ -3217,8 +2955,9 @@ void do_parsing_tests() // these tests rely on the base level having been loaded void do_serialization_tests() { - Log("Testing serialization...\n"); + Log("(UNIMPLEMENTED) Testing serialization...\n"); + /* ArenaTemp scratch = GetScratch(0, 0); GameState gs = {0}; @@ -3241,6 +2980,7 @@ void do_serialization_tests() Log("Default save data size is %lld bytes\n", saved.size); ReleaseScratch(scratch); +*/ } void do_float_encoding_tests() @@ -5711,7 +5451,7 @@ void frame(void) text_input_fade = Lerp(text_input_fade, unwarped_dt * 8.0f, receiving_text_input ? 1.0f : 0.0f); - Vec3 player_pos = V3(gs.player->pos.x, 0.0, gs.player->pos.y); + Vec3 cam_target_pos = V3(0,0,0); //dbgline(V2(0,0), V2(500, 500)); const float vertical_to_horizontal_ratio = CAM_VERTICAL_TO_HORIZONTAL_RATIO; const float cam_distance = CAM_DISTANCE; @@ -5727,7 +5467,7 @@ void frame(void) { away_from_player = get_cur_room(&gs, &level_threedee)->camera_offset; } - Vec3 cam_pos = AddV3(player_pos, away_from_player); + Vec3 cam_pos = AddV3(cam_target_pos, away_from_player); Vec2 movement = { 0 }; if (mobile_controls) @@ -5753,13 +5493,13 @@ void frame(void) Vec3 light_dir; { - float t = clamp01((float)(gs.time / LENGTH_OF_DAY)); + float t = 0.0f; Vec3 sun_vector = V3(2.0f*t - 1.0f, sinf(t*PI32)*0.8f + 0.2f, 0.8f); // where the sun is pointing from light_dir = NormV3(MulV3F(sun_vector, -1.0f)); } // make movement relative to camera forward - Vec3 facing = NormV3(SubV3(player_pos, cam_pos)); + Vec3 facing = NormV3(SubV3(cam_target_pos, cam_pos)); Vec3 right = Cross(facing, V3(0,1,0)); Vec2 forward_2d = NormV2(V2(facing.x, facing.z)); Vec2 right_2d = NormV2(V2(right.x, right.z)); @@ -5769,7 +5509,7 @@ void frame(void) movement = V2(0,0); view = Translate(V3(0.0, 1.0, -5.0f)); - Mat4 normal_cam_view = LookAt_RH(cam_pos, player_pos, V3(0, 1, 0)); + Mat4 normal_cam_view = LookAt_RH(cam_pos, cam_target_pos, V3(0, 1, 0)); if(flycam) { Basis basis = flycam_basis(); @@ -5853,46 +5593,15 @@ void frame(void) ENTITIES_ITER(gs.entities) { - if(it->is_npc && S8Match(it->current_room_name, gs.player->current_room_name, 0)) + if(it->is_npc && S8Match(it->current_room_name, gs.current_room_name, 0)) { assert(it->is_npc); Transform draw_with = entity_transform(it); - if(it->npc_kind == NPC_AngelTotem) - { - draw_thing((DrawnThing){.mesh = &mesh_angel_totem, .t = draw_with, .outline = gete(gs.player->interacting_with) == it}); - } - else if(it->npc_kind == NPC_Tombstone) - { - draw_thing((DrawnThing){.mesh = &mesh_tombstone, .t = draw_with, .outline = gete(gs.player->interacting_with) == it}); - } - else + // draw_thing((DrawnThing){.mesh = &mesh_tombstone, .t = draw_with, .outline = gete(gs.player->interacting_with) == it}); { - Armature *to_use = 0; - switch (it->npc_kind) - { - case NPC_Daniel: - to_use = &farmer_armature; - break; - case NPC_Raphael: - to_use = &man_in_black_armature; - break; - case NPC_Angel: - to_use = &angel_armature; - break; - case NPC_Player: - to_use = &player_armature; - break; - case NPC_nobody: - case NPC_AngelTotem: - case NPC_Tombstone: - case NPC_Devil: - case NPC_PreviousPlayer1: - case NPC_PreviousPlayer2: - case NPC_PreviousPlayer3: - assert(false); - break; - } + Armature *to_use = &player_armature; + if (it->killed) { to_use->go_to_animation = S8Lit("Die Backwards"); @@ -5907,7 +5616,7 @@ void frame(void) to_use->go_to_animation = S8Lit("Idle"); } - draw_thing((DrawnThing){.armature = to_use, .t = draw_with, .outline = gete(gs.player->interacting_with) == it}); + draw_thing((DrawnThing){.armature = to_use, .t = draw_with, .outline = gs.player && gete(gs.player->interacting_with) == it}); if (gete(it->aiming_shotgun_at)) { @@ -6025,46 +5734,12 @@ void frame(void) } } - if(gs.judgement_gen_request != 0) - { - GenRequestStatus stat = gen_request_status(gs.judgement_gen_request); - switch(stat) - { - case GEN_NotDoneYet: - break; - case GEN_Success: - { - TextChunk generated = gen_request_content(gs.judgement_gen_request); - if (generated.text_length > 0 && S8FindSubstring(TextChunkString8(generated), S8Lit("yes"), 0, StringMatchFlag_CaseInsensitive) == 0) - { - Log("Starts with yes, success!\n"); - gs.won = true; - } - else if (S8FindSubstring(TextChunkString8(generated), S8Lit("no"), 0, StringMatchFlag_CaseInsensitive) == generated.text_length) - { - Log("WARNING: generated judgement string '%.*s', doesn't match yes or no, and so is nonsensical! AI acting up!\n", TextChunkVArg(generated)); - } - } - break; - case GEN_Failed: - having_errors = true; - break; - case GEN_Deleted: - break; - } - if(stat != GEN_NotDoneYet) - { - done_with_request(gs.judgement_gen_request); - gs.judgement_gen_request = 0; - } - } - // @Place(UI rendering that happens before gameplay processing so can consume events before the gameplay needs them) PROFILE_SCOPE("Entity UI Rendering") { ENTITIES_ITER(gs.entities) { - if (it->is_npc && it->npc_kind != NPC_Player && S8Match(it->current_room_name, gs.player->current_room_name, 0)) + if (it->is_npc && !it->is_player && S8Match(it->current_room_name, gs.current_room_name, 0)) { if(it->undismissed_action) { @@ -6073,7 +5748,8 @@ void frame(void) // dialog bubble rendering const float text_scale = speech_bubble.text_scale; - float dist = LenV2(SubV2(it->pos, gs.player->pos)); + float dist = 0.0f; + if(gs.player) dist = LenV2(SubV2(it->pos, gs.player->pos)); float bubble_factor = 1.0f - clamp01(dist / 6.0f); Vec3 bubble_pos = AddV3(plane_point(it->pos), V3(0, 1.7f, 0)); // 1.7 meters is about 5'8", average person height Vec2 head_pos = threedee_to_screenspace(bubble_pos); @@ -6157,8 +5833,6 @@ void frame(void) } } - assert(gs.player != NULL); - // gameplay processing loop, do multiple if lagging // these are static so that, on frames where no gameplay processing is necessary and just rendering, the rendering uses values from last frame // @Place(gameplay processing loops) @@ -6195,36 +5869,6 @@ void frame(void) gs.tick += 1; - PROFILE_SCOPE("propagate actions") - { - for(PropagatingAction *cur = propagating; cur; cur = cur->next) - { - assert(cur->in_room_name.text_length > 0); - if(cur->progress < 1.0f) - { - cur->progress += dt; - float effective_radius = propagating_radius(cur); - ENTITIES_ITER(gs.entities) - { - bool should_propagate = true - && it->is_npc - && LenV2(SubV2(it->pos, cur->from)) < effective_radius - && S8Match(TextChunkString8(cur->in_room_name), it->current_room_name, 0) - && it->npc_kind != NPC_Angel // angels already hear everything, this would duplicate the hearing of the action - ; - if(should_propagate) - { - if(!cur->already_propagated_to[frome(it).index]) - { - cur->already_propagated_to[frome(it).index] = true; - remember_action(&gs, it, cur->a, cur->context); - } - } - } - } - } - } - // process gs.entities process entities PROFILE_SCOPE("entity processing") { @@ -6253,7 +5897,7 @@ void frame(void) if(toface) it->target_rotation = AngleOfV2(SubV2(toface->pos, it->pos)); - if(it->npc_kind != NPC_Player) + if(!it->is_player) it->rotation = lerp_angle(it->rotation, unwarped_dt*8.0f, it->target_rotation); if (it->gen_request_id != 0 && !gs.stopped_time) @@ -6279,7 +5923,7 @@ void frame(void) Action out = {0}; Log("Parsing `%.*s`...\n", S8VArg(sentence_str)); - String8 parse_response = parse_chatgpt_response(frame_arena, it, sentence_str, &out); + String8 parse_response = parse_chatgpt_response(frame_arena, &gs, it, sentence_str, &out); // check that it wraps in below two lines TextPlacementSettings *to_wrap_to = &speech_bubble; @@ -6334,7 +5978,6 @@ void frame(void) if (it->is_npc) { // character speech animation text input - if (it->npc_kind != NPC_Angel) { ArenaTemp scratch = GetScratch(0, 0); @@ -6381,7 +6024,7 @@ void frame(void) } } - Vec2 target = get_point_along_trail(BUFF_MAKEREF(&gs.player->position_history), (float)place_in_line * 1.0f); + Vec2 target = get_point_along_trail(BUFF_MAKEREF(&it->position_history), (float)place_in_line * 1.0f); Vec2 last_pos = it->pos; it->pos = LerpV2(it->pos, dt*5.0f, target); @@ -6439,7 +6082,7 @@ void frame(void) const float jump_size = TILE_SIZE / 2.0f; BUFF_APPEND(&nodes, ((AStarNode) { .in_open_set = true, .pos = from })); Vec2 from_hash = V2_HASH(from); - float got_there_tolerance = max_coord(entity_aabb_size(gs.player))*1.5f; + float got_there_tolerance = max_coord(entity_aabb_size(targeting))*1.5f; hmput(node_cache, from_hash, &nodes.data[0]); bool should_quit = false; @@ -6725,10 +6368,7 @@ void frame(void) if(it->is_npc) { bool doesnt_prompt_on_dirty_perceptions = false - || it->npc_kind == NPC_Player - || !npc_does_dialog(it) // not sure what's up with this actually, potentially remove - || !S8Match(it->current_room_name, gs.player->current_room_name, 0) - || it->npc_kind == NPC_AngelTotem + || !S8Match(it->current_room_name, gs.current_room_name, 0) ; if (it->perceptions_dirty && doesnt_prompt_on_dirty_perceptions) { @@ -6758,28 +6398,20 @@ void frame(void) { const char *action = 0; const char *action_argument = "Raphael"; - if(it->npc_kind == NPC_Daniel) + if (gete(it->aiming_shotgun_at)) { - if (gete(it->aiming_shotgun_at)) - { - action = "fire_shotgun"; - } - else - { - action = "aim_shotgun"; - } + action = "fire_shotgun"; } - else if(it->npc_kind == NPC_Angel) + else { - action = "assign_gameplay_objective"; - action_argument = "KILL Raphael"; + action = "aim_shotgun"; } char *rigged_dialog[] = { "Repeated amounts of testing dialog overwhelmingly in support of the mulaney brothers", }; char *next_dialog = rigged_dialog[it->times_talked_to % ARRLEN(rigged_dialog)]; - String8 target = TextChunkString8(npc_data(gs, it->memories_last->context.author_npc_kind)->name); - ai_response = FmtWithLint(frame_arena, "{\"target\": \"%s\", \"action\": \"%s\", \"action_argument\": \"%s\", \"speech\": \"%s\"}", target, action, action_argument, next_dialog); + String8 target = TextChunkString8(npc_data(&gs, it->memories_last->context.author_npc_kind)->name); + ai_response = FmtWithLint(frame_arena, "{\"target\": \"%.*s\", \"action\": \"%s\", \"action_argument\": \"%s\", \"speech\": \"%s\"}", S8VArg(target), action, action_argument, next_dialog); it->times_talked_to += 1; } else @@ -6794,7 +6426,7 @@ void frame(void) String8 error_message = S8Lit("Something really bad happened bro. File " STRINGIZE(__FILE__) " Line " STRINGIZE(__LINE__)); if (succeeded) { - error_message = parse_chatgpt_response(frame_arena, it, ai_response, &a); + error_message = parse_chatgpt_response(frame_arena, &gs, it, ai_response, &a); } assert(succeeded); @@ -6815,6 +6447,7 @@ void frame(void) } // @Place(process player) + if(gs.player) PROFILE_SCOPE("process player") { // do dialog @@ -6830,7 +6463,7 @@ void frame(void) { bool entity_talkable = true; if (entity_talkable) entity_talkable = entity_talkable && (*it)->is_npc; - if (entity_talkable) entity_talkable = entity_talkable && (*it)->npc_kind != NPC_Player; + if (entity_talkable) entity_talkable = entity_talkable && !(*it)->is_player; if (entity_talkable) entity_talkable = entity_talkable && !(*it)->killed; #ifdef WEB if (entity_talkable) entity_talkable = entity_talkable && (*it)->gen_request_id == 0; @@ -6860,10 +6493,6 @@ void frame(void) { if (closest_interact_with->is_npc) { - if(closest_interact_with->npc_kind == NPC_AngelTotem) - { - transition_to_room(&gs, &level_threedee, S8Lit("StartingRoom")); - } // begin dialog with closest npc gs.player->talking_to = frome(closest_interact_with); begin_text_input(); @@ -6938,16 +6567,6 @@ void frame(void) #ifdef DEVTOOLS - if(keypressed[SAPP_KEYCODE_P]) - { - ENTITIES_ITER(gs.entities) - { - if(it->npc_kind == NPC_Raphael) - { - it->killed = true; - } - } - } if(flycam) { Basis basis = flycam_basis(); @@ -7007,19 +6626,6 @@ void frame(void) } } - // @Place(UI rendering) - PROFILE_SCOPE("propagating") - { - for(PropagatingAction *cur = propagating; cur; cur = cur->next) - { - if(cur->progress < 1.0f) - { - float radius = propagating_radius(cur); - Quad to_draw = quad_centered(cur->from, V2(radius, radius)); - draw_quad((DrawParams){ to_draw, IMG(image_hovering_circle), blendalpha(WHITE, 1.0f - cur->progress)}); - } - } - } if (having_errors) { @@ -7051,12 +6657,8 @@ void frame(void) } } - if(gs.time > LENGTH_OF_DAY) - { - gs.player->killed = true; - } - // killed screen + if(gs.player) { static float visible = 0.0f; float target = 0.0f; @@ -7154,29 +6756,29 @@ void frame(void) Vec2 start_at = V2(0,300); Vec2 cur_pos = start_at; - AABB bounds = draw_text((TextParams){false, S8Fmt(frame_arena, "--Memories for %.*s--", TextChunkVArg(npc_data(gs, to_view->npc_kind)->name)), cur_pos, WHITE, 1.0}); + AABB bounds = draw_text((TextParams){false, S8Fmt(frame_arena, "--Memories for %.*s--", TextChunkVArg(npc_data(&gs, to_view->npc_kind)->name)), cur_pos, WHITE, 1.0}); cur_pos.y -= aabb_size(bounds).y; for(Memory *cur = to_view->memories_first; cur; cur = cur->next) if(cur->speech.text_length > 0) { - String8 to_text = cur->context.talking_to_kind != NPC_nobody ? S8Fmt(frame_arena, " to %.*s ", TextChunkVArg(npc_data(gs, cur->context.talking_to_kind)->name)) : S8Lit(""); - String8 text = S8Fmt(frame_arena, "%s%.*s%.*s: %.*s", to_view->npc_kind == cur->context.author_npc_kind ? "(Me) " : "", TextChunkVArg(npc_data(gs, cur->context.author_npc_kind)->name), S8VArg(to_text), cur->speech.text_length, cur->speech); + String8 to_text = cur->context.talking_to_kind != NPC_nobody ? S8Fmt(frame_arena, " to %.*s ", TextChunkVArg(npc_data(&gs, cur->context.talking_to_kind)->name)) : S8Lit(""); + String8 text = S8Fmt(frame_arena, "%s%.*s%.*s: %.*s", to_view->npc_kind == cur->context.author_npc_kind ? "(Me) " : "", TextChunkVArg(npc_data(&gs, cur->context.author_npc_kind)->name), S8VArg(to_text), cur->speech.text_length, cur->speech); AABB bounds = draw_text((TextParams){false, text, cur_pos, WHITE, 1.0}); cur_pos.y -= aabb_size(bounds).y; } if(keypressed[SAPP_KEYCODE_Q] && !receiving_text_input) { - Log("\n\n==========------- Printing debugging information for %.*s -------==========\n", TextChunkVArg(npc_data(gs, to_view->npc_kind)->name)); + Log("\n\n==========------- Printing debugging information for %.*s -------==========\n", TextChunkVArg(npc_data(&gs, to_view->npc_kind)->name)); Log("\nMemories-----------------------------\n"); int mem_idx = 0; for(Memory *cur = to_view->memories_first; cur; cur = cur->next) { - String8 to_text = cur->context.talking_to_kind != NPC_nobody ? S8Fmt(frame_arena, " to %.*s ", TextChunkVArg(npc_data(gs, cur->context.talking_to_kind)->name)) : S8Lit(""); + String8 to_text = cur->context.talking_to_kind != NPC_nobody ? S8Fmt(frame_arena, " to %.*s ", TextChunkVArg(npc_data(&gs, cur->context.talking_to_kind)->name)) : S8Lit(""); String8 speech = TextChunkString8(cur->speech); if(speech.size == 0) speech = S8Lit(""); - String8 text = S8Fmt(frame_arena, "%s%.*s%.*s: %.*s", to_view->npc_kind == cur->context.author_npc_kind ? "(Me) " : "", TextChunkVArg(npc_data(gs, cur->context.author_npc_kind)->name), S8VArg(to_text), S8VArg(speech)); + String8 text = S8Fmt(frame_arena, "%s%.*s%.*s: %.*s", to_view->npc_kind == cur->context.author_npc_kind ? "(Me) " : "", TextChunkVArg(npc_data(&gs, cur->context.author_npc_kind)->name), S8VArg(to_text), S8VArg(speech)); printf("Memory %d: %.*s\n", mem_idx, S8VArg(text)); mem_idx++; } @@ -7211,7 +6813,7 @@ void frame(void) profiling ? "yes" : "no", num_timestep_loops, flycam ? "yes" : "no", - v2varg(gs.player->pos)); + v2varg((gs.player ? gs.player->pos : V2(0,0)))); AABB bounds = draw_text((TextParams) { true, stats, pos, BLACK, 1.0f }); pos.Y -= bounds.upper_left.Y - screen_size().Y; bounds = draw_text((TextParams) { true, stats, pos, BLACK, 1.0f }); diff --git a/makeprompt.h b/makeprompt.h index 27b2d1b..c6ff8c1 100644 --- a/makeprompt.h +++ b/makeprompt.h @@ -235,6 +235,7 @@ typedef struct Entity String8 current_room_name; // npcs + bool is_player; EntityRef joined; EntityRef aiming_shotgun_at; EntityRef looking_at; // aiming shotgun at takes facing priority over this @@ -298,6 +299,7 @@ typedef struct GameState { BUFF(Npc, 10) characters; // these must point entities in its own array. + String8 current_room_name; Entity *player; Entity *world_entity; Entity entities[MAX_ENTITIES]; @@ -442,7 +444,7 @@ String8List dump_memory_as_json(Arena *arena, GameState *gs, Memory *it) AddFmt("{"); AddFmt("\"speech\":\"%.*s\",", TextChunkVArg(it->speech)); AddFmt("\"action\":\"%s\",", actions[it->action_taken].name); - String8 arg_str = action_argument_string(scratch.arena, it->action_argument); + String8 arg_str = action_argument_string(scratch.arena, gs, it->action_argument); AddFmt("\"action_argument\":\"%.*s\",", S8VArg(arg_str)); AddFmt("\"target\":\"%.*s\"}", TextChunkVArg(npc_data(gs, it->context.talking_to_kind)->name)); @@ -592,8 +594,6 @@ String8 generate_chatgpt_prompt(Arena *arena, GameState *gs, Entity *e, CanTalkT } String8List current_list = {0}; - bool in_drama_memories = true; - assert(e->memories_first->context.drama_memory); for(Memory *it = e->memories_first; it; it = it->next) { // going through memories, I'm going to accumulate human understandable sentences for what happened in current_list. @@ -601,11 +601,6 @@ String8 generate_chatgpt_prompt(Arena *arena, GameState *gs, Entity *e, CanTalkT // write a new human understandable sentence or two to current_list if (!it->context.i_said_this) { - if(in_drama_memories && !it->context.drama_memory) - { - in_drama_memories = false; - AddFmt("Some time passed...\n"); - } String8List desc_list = memory_description(scratch.arena, gs, e, it); S8ListConcat(¤t_list, &desc_list); } @@ -686,7 +681,7 @@ void parse_action_argument(Arena *error_arena, GameState *gs, String8 *cur_error // if returned string has size greater than 0, it's the error message. Allocated // on arena passed into it or in constant memory -String8 parse_chatgpt_response(Arena *arena, Entity *e, String8 action_in_json, Action *out) +String8 parse_chatgpt_response(Arena *arena, GameState *gs, Entity *e, String8 action_in_json, Action *out) { ArenaTemp scratch = GetScratch(&arena, 1); @@ -726,7 +721,7 @@ String8 parse_chatgpt_response(Arena *arena, Entity *e, String8 action_in_json, { error_message = FmtWithLint(arena, "Speech string provided is too big, maximum bytes is %d", MAX_SENTENCE_LENGTH); } - assert(e->npc_kind != NPC_Player); // player can't perform AI actions? + assert(!e->is_player); // player can't perform AI actions? if(error_message.size == 0) { @@ -736,16 +731,7 @@ String8 parse_chatgpt_response(Arena *arena, Entity *e, String8 action_in_json, } else { -Npc *npc_data_by_name(GameState *gs, String8 name) { - BUFF_ITER(Npc, &gs->characters) { - if(S8Match(TextChunkString8(it->name), name, 0)) { - return it; - } - } - return 0; -} - - Npc * npc = ncp_data_by_name(gs, target_str); + Npc * npc = npc_data_by_name(gs, target_str); bool found = false; if(npc) { found = true; @@ -785,7 +771,7 @@ Npc *npc_data_by_name(GameState *gs, String8 name) { { if(actions[out->kind].takes_argument) { - parse_action_argument(arena,&error_message, out->kind, action_argument_str, &out->argument); + parse_action_argument(arena, gs, &error_message, out->kind, action_argument_str, &out->argument); } } } diff --git a/tuning.h b/tuning.h index d92eec7..c36dd61 100644 --- a/tuning.h +++ b/tuning.h @@ -3,7 +3,6 @@ #define RANDOM_SEED 42 #define LEVEL_TILES 150 // width and height of level tiles array -#define LENGTH_OF_DAY (60.0 * 5.0) // in seconds #define LAYERS 3 #define TILE_SIZE 0.5f // in pixels #define PLAYER_SPEED 0.15f // in meters per second