Delete action elements from game. Rolling, attacking, etc

main
Cameron Murphy Reikes 2 years ago
parent 6feb43e93d
commit 380a422aa0

363
main.c

@ -454,10 +454,6 @@ Vec2 entity_aabb_size(Entity *e)
return (Vec2) { 0 }; return (Vec2) { 0 };
} }
} }
else if (e->is_bullet)
{
return V2(TILE_SIZE*0.25f, TILE_SIZE*0.25f);
}
else if (e->is_prop) else if (e->is_prop)
{ {
return V2(TILE_SIZE*0.5f, TILE_SIZE*0.5f); return V2(TILE_SIZE*0.5f, TILE_SIZE*0.5f);
@ -872,15 +868,6 @@ void end_text_input(char *what_player_said)
Entity *talking = gete(player->talking_to); Entity *talking = gete(player->talking_to);
assert(talking); assert(talking);
ItemKind player_holding = ITEM_none;
if (gete(player->holding_item) != 0)
{
player_holding = gete(player->holding_item)->item_kind;
}
if (talking->last_seen_holding_kind != player_holding)
{
process_perception(talking, (Perception) { .type = PlayerHeldItemChanged, .holding = player_holding, }, player, &gs);
}
process_perception(talking, (Perception) { .type = PlayerDialog, .player_dialog = what_player_said_sentence, }, player, &gs); process_perception(talking, (Perception) { .type = PlayerDialog, .player_dialog = what_player_said_sentence, }, player, &gs);
} }
} }
@ -942,39 +929,6 @@ void audio_stream_callback(float *buffer, int num_frames, int num_channels)
} }
typedef BUFF(Entity*, 16) SwordToDamage;
SwordToDamage entity_sword_to_do_damage(Entity *from, Overlapping o)
{
SwordToDamage to_return = { 0 };
BUFF_ITER(Overlap, &o)
{
if (!it->is_tile && it->e != from)
{
bool done_damage = false;
Entity *looking_for = it->e;
BUFF_ITER(EntityRef, &from->done_damage_to_this_swing)
{
EntityRef ref = *it;
Entity *it = gete(ref);
if (it == looking_for) done_damage = true;
}
if (!done_damage)
{
if (!BUFF_HAS_SPACE(&from->done_damage_to_this_swing))
{
BUFF_REMOVE_FRONT(&from->done_damage_to_this_swing);
Log("Too many things to do damage to...\n");
assert(false);
}
BUFF_APPEND(&to_return, looking_for);
BUFF_APPEND(&from->done_damage_to_this_swing, frome(looking_for));
}
}
}
return to_return;
}
#define WHITE ((Color) { 1.0f, 1.0f, 1.0f, 1.0f }) #define WHITE ((Color) { 1.0f, 1.0f, 1.0f, 1.0f })
#define GREY ((Color) { 0.4f, 0.4f, 0.4f, 1.0f }) #define GREY ((Color) { 0.4f, 0.4f, 0.4f, 1.0f })
#define BLACK ((Color) { 0.0f, 0.0f, 0.0f, 1.0f }) #define BLACK ((Color) { 0.0f, 0.0f, 0.0f, 1.0f })
@ -1741,46 +1695,6 @@ void dbgrect(AABB rect)
#endif #endif
} }
// from_point is for knockback
void request_do_damage(Entity *to, Entity *from, float damage)
{
Vec2 from_point = from->pos;
if (to == NULL) return;
if (to->is_bullet)
{
Vec2 norm = NormV2(SubV2(to->pos, from_point));
dbgvec(from_point, norm);
to->vel = ReflectV2(to->vel, norm);
dbgprint("deflecitng\n");
}
else if (true)
{
// damage processing is done in process perception so in training, has accurate values for
// NPC health
if (to->is_character)
{
to->damage += damage;
}
else
{
if (from->is_character)
{
process_perception(to, (Perception) { .type = PlayerAction, .player_action_type = ACT_hits_npc, .damage_done = damage, }, player, &gs);
}
else
{
process_perception(to, (Perception) { .type = EnemyAction, .enemy_action_type = ACT_hits_npc, .damage_done = damage, }, player, &gs);
}
}
to->vel = MulV2F(NormV2(SubV2(to->pos, from_point)), 15.0f);
}
else
{
Log("Can't do damage to npc...\n");
}
}
typedef struct TextParams typedef struct TextParams
{ {
bool world_space; bool world_space;
@ -2635,8 +2549,6 @@ void frame(void)
uint64_t time_start_frame = stm_now(); uint64_t time_start_frame = stm_now();
Vec2 movement = { 0 }; Vec2 movement = { 0 };
bool attack = false;
bool roll = false;
bool interact = false; bool interact = false;
if (mobile_controls) if (mobile_controls)
{ {
@ -2645,8 +2557,6 @@ void frame(void)
{ {
movement = MulV2F(NormV2(movement), LenV2(movement) / (thumbstick_base_size()*0.5f)); movement = MulV2F(NormV2(movement), LenV2(movement) / (thumbstick_base_size()*0.5f));
} }
attack = mobile_attack_pressed;
roll = mobile_roll_pressed;
interact = pressed.interact; interact = pressed.interact;
} }
else else
@ -2655,11 +2565,6 @@ void frame(void)
(float)keydown[SAPP_KEYCODE_D] - (float)keydown[SAPP_KEYCODE_A], (float)keydown[SAPP_KEYCODE_D] - (float)keydown[SAPP_KEYCODE_A],
(float)keydown[SAPP_KEYCODE_W] - (float)keydown[SAPP_KEYCODE_S] (float)keydown[SAPP_KEYCODE_W] - (float)keydown[SAPP_KEYCODE_S]
); );
attack = keydown[SAPP_KEYCODE_SPACE];
#ifdef DEVTOOLS
attack = attack || keydown[SAPP_KEYCODE_LEFT_CONTROL];
#endif
roll = keydown[ROLL_KEY];
interact = pressed.interact; interact = pressed.interact;
} }
if (LenV2(movement) > 1.0) if (LenV2(movement) > 1.0)
@ -2930,7 +2835,7 @@ void frame(void)
} }
// A* and shotgun movement code // A* code
if(false) if(false)
if (it->standing == STANDING_FIGHTING || it->standing == STANDING_JOINED) if (it->standing == STANDING_FIGHTING || it->standing == STANDING_JOINED)
{ {
@ -3177,110 +3082,6 @@ void frame(void)
dbgcol(BLUE) dbgline(*it, path.data[i-1]); dbgcol(BLUE) dbgline(*it, path.data[i-1]);
} }
} }
{
if (npc_attacks_with_sword(it))
{
if (fabsf(it->vel.x) > 0.01f)
it->facing_left = it->vel.x < 0.0f;
it->pos = move_and_slide((MoveSlideParams) { it, it->pos, MulV2F(it->vel, pixels_per_meter * dt) });
AABB weapon_aabb = entity_sword_aabb(it, 30.0f, 18.0f);
Vec2 target_vel = { 0 };
Overlapping overlapping_weapon = get_overlapping(cur_level, weapon_aabb);
if (it->swing_timer > 0.0)
{
player_in_combat = true;
it->swing_timer += dt;
if (it->swing_timer >= anim_sprite_duration(ANIM_skeleton_swing_sword))
{
it->swing_timer = 0.0;
}
if (it->swing_timer >= 0.4f)
{
SwordToDamage to_damage = entity_sword_to_do_damage(it, overlapping_weapon);
Entity *from = it;
BUFF_ITER(Entity *, &to_damage)
{
request_do_damage(*it, from, DAMAGE_SWORD);
}
}
}
else
{
// in huntin' range
//it->walking = LenV2(SubV2(player->pos, it->pos)) < 250.0f;
it->walking = true;
if (it->walking)
{
player_in_combat = true;
Entity *skele = it;
BUFF_ITER(Overlap, &overlapping_weapon)
{
if (it->e && it->e->is_character)
{
skele->swing_timer += dt;
BUFF_CLEAR(&skele->done_damage_to_this_swing);
}
}
target_vel = MulV2F(NormV2(SubV2(next_point_on_path, it->pos)), PLAYER_ROLL_SPEED);
}
else
{
}
}
it->vel = LerpV2(it->vel, dt*8.0f, target_vel);
}
if (npc_attacks_with_shotgun(it))
if (succeeded)
{
Vec2 to_player = NormV2(SubV2(targeting->pos, it->pos));
Vec2 rotate_direction;
if (it->direction_of_spiral_pattern)
{
rotate_direction = rotate_counter_clockwise(to_player);
}
else
{
rotate_direction = rotate_clockwise(to_player);
}
Vec2 target_vel = NormV2(SubV2(next_point_on_path, it->pos));
target_vel = MulV2F(target_vel, 3.0f);
it->vel = LerpV2(it->vel, 15.0f * dt, target_vel);
CollisionInfo col = { 0 };
it->pos = move_and_slide((MoveSlideParams) { it, it->pos, MulV2F(it->vel, pixels_per_meter * dt), .col_info_out = &col });
if (col.happened)
{
it->direction_of_spiral_pattern = !it->direction_of_spiral_pattern;
}
if (it->standing == STANDING_FIGHTING)
{
it->shotgun_timer += dt;
Vec2 to_player = NormV2(SubV2(targeting->pos, it->pos));
if (it->shotgun_timer >= 1.0f)
{
it->shotgun_timer = 0.0f;
const float spread = (float)PI / 4.0f;
// shoot shotgun
int num_bullets = 5;
for (int i = 0; i < num_bullets; i++)
{
Vec2 dir = to_player;
float theta = Lerp(-spread / 2.0f, ((float)i / (float)(num_bullets - 1)), spread / 2.0f);
dir = RotateV2(dir, theta);
Entity *new_bullet = new_entity();
new_bullet->is_bullet = true;
new_bullet->pos = AddV2(it->pos, MulV2F(dir, 20.0f));
new_bullet->vel = MulV2F(dir, 15.0f);
it->vel = AddV2(it->vel, MulV2F(dir, -3.0f));
}
}
}
}
}
} }
if (it->npc_kind == NPC_OldMan) if (it->npc_kind == NPC_OldMan)
{ {
@ -3407,25 +3208,6 @@ void frame(void)
//draw_quad((DrawParams){true, it->pos, IMG(image_white_square) //draw_quad((DrawParams){true, it->pos, IMG(image_white_square)
} }
else if (it->is_bullet)
{
it->pos = AddV2(it->pos, MulV2F(it->vel, pixels_per_meter * dt));
dbgvec(it->pos, it->vel);
Overlapping over = get_overlapping(cur_level, entity_aabb(it));
Entity *from_bullet = it;
bool destroy_bullet = false;
BUFF_ITER(Overlap, &over) if (it->e != from_bullet)
{
if (!it->is_tile && !(it->e->is_bullet))
{
// knockback and damage
request_do_damage(it->e, from_bullet, DAMAGE_BULLET);
destroy_bullet = true;
}
}
if (destroy_bullet) *from_bullet = (Entity) { 0 };
if (!has_point(level_aabb, it->pos)) *it = (Entity) { 0 };
}
else if (it->is_character) else if (it->is_character)
{ {
} }
@ -3539,9 +3321,7 @@ void frame(void)
if (entity_talkable) entity_talkable = entity_talkable && it->e->gen_request_id == 0; if (entity_talkable) entity_talkable = entity_talkable && it->e->gen_request_id == 0;
#endif #endif
bool entity_pickupable = !it->is_tile && !gete(player->holding_item) && it->e->is_item; if (entity_talkable)
if (entity_talkable || entity_pickupable)
{ {
float dist = LenV2(SubV2(it->e->pos, player->pos)); float dist = LenV2(SubV2(it->e->pos, player->pos));
if (dist < closest_interact_with_dist) if (dist < closest_interact_with_dist)
@ -3592,87 +3372,22 @@ void frame(void)
player->state = CHARACTER_TALKING; player->state = CHARACTER_TALKING;
player->talking_to = frome(closest_interact_with); player->talking_to = frome(closest_interact_with);
} }
else if (closest_interact_with->is_item)
{
// pick up item
closest_interact_with->held_by_player = true;
player->holding_item = frome(closest_interact_with);
}
else else
{ {
assert(false); assert(false);
} }
} }
else
{
if (gete(player->holding_item))
{
// throw item if not talking to somebody with item
Entity *thrown = gete(player->holding_item);
assert(thrown);
thrown->vel = MulV2F(player->to_throw_direction, 20.0f);
thrown->held_by_player = false;
player->holding_item = (EntityRef) { 0 };
}
}
} }
float speed = 0.0f; float speed = 0.0f;
{ {
if (roll && !player->is_rolling && player->time_not_rolling > 0.3f && (player->state == CHARACTER_IDLE || player->state == CHARACTER_WALKING))
{
player->is_rolling = true;
player->roll_progress = 0.0;
}
if (attack && (player->state == CHARACTER_IDLE || player->state == CHARACTER_WALKING))
{
player->state = CHARACTER_ATTACK;
BUFF_CLEAR(&player->done_damage_to_this_swing);
player->swing_progress = 0.0;
}
// after images
BUFF_ITER(PlayerAfterImage, &player->after_images)
{
it->alive_for += dt;
}
if (player->after_images.data[0].alive_for >= AFTERIMAGE_LIFETIME)
{
BUFF_REMOVE_FRONT(&player->after_images);
}
// roll processing
{
if (player->state != CHARACTER_IDLE && player->state != CHARACTER_WALKING)
{
player->roll_progress = 0.0;
player->is_rolling = false;
}
if (player->is_rolling)
{
player->after_image_timer += dt;
player->time_not_rolling = 0.0f;
player->roll_progress += dt;
if (player->roll_progress > anim_sprite_duration(ANIM_knight_rolling))
{
player->is_rolling = false;
}
}
if (!player->is_rolling) player->time_not_rolling += dt;
}
Vec2 target_vel = { 0 }; Vec2 target_vel = { 0 };
if (LenV2(movement) > 0.01f) player->to_throw_direction = NormV2(movement);
if (player->state == CHARACTER_WALKING) if (player->state == CHARACTER_WALKING)
{ {
speed = PLAYER_SPEED; speed = PLAYER_SPEED;
if (player->is_rolling) speed = PLAYER_ROLL_SPEED; if (player->is_rolling) speed = PLAYER_ROLL_SPEED;
if (gete(player->holding_item) && gete(player->holding_item)->item_kind == ITEM_Boots)
{
speed *= 2.0f;
}
if (LenV2(movement) == 0.0) if (LenV2(movement) == 0.0)
{ {
player->state = CHARACTER_IDLE; player->state = CHARACTER_IDLE;
@ -3685,21 +3400,6 @@ void frame(void)
{ {
if (LenV2(movement) > 0.01) player->state = CHARACTER_WALKING; if (LenV2(movement) > 0.01) player->state = CHARACTER_WALKING;
} }
else if (player->state == CHARACTER_ATTACK)
{
AABB weapon_aabb = entity_sword_aabb(player, 40.0f, 25.0f);
dbgrect(weapon_aabb);
SwordToDamage to_damage = entity_sword_to_do_damage(player, get_overlapping(cur_level, weapon_aabb));
BUFF_ITER(Entity*, &to_damage)
{
request_do_damage(*it, player, DAMAGE_SWORD);
}
player->swing_progress += dt;
if (player->swing_progress > anim_sprite_duration(ANIM_knight_attack))
{
player->state = CHARACTER_IDLE;
}
}
else if (player->state == CHARACTER_TALKING) else if (player->state == CHARACTER_TALKING)
{ {
} }
@ -3790,31 +3490,13 @@ void frame(void)
} }
if (player->state == CHARACTER_WALKING) if (player->state == CHARACTER_WALKING)
{
if (player->is_rolling)
{
to_draw = (DrawnAnimatedSprite) { ANIM_knight_running, player->roll_progress, player->facing_left, character_sprite_pos, WHITE };
}
else
{ {
to_draw = (DrawnAnimatedSprite) { ANIM_knight_running, elapsed_time, player->facing_left, character_sprite_pos, WHITE }; to_draw = (DrawnAnimatedSprite) { ANIM_knight_running, elapsed_time, player->facing_left, character_sprite_pos, WHITE };
} }
}
else if (player->state == CHARACTER_IDLE) else if (player->state == CHARACTER_IDLE)
{
if (player->is_rolling)
{
to_draw = (DrawnAnimatedSprite) { ANIM_knight_running, player->roll_progress, player->facing_left, character_sprite_pos, WHITE };
}
else
{ {
to_draw = (DrawnAnimatedSprite) { ANIM_knight_idle, elapsed_time, player->facing_left, character_sprite_pos, WHITE }; to_draw = (DrawnAnimatedSprite) { ANIM_knight_idle, elapsed_time, player->facing_left, character_sprite_pos, WHITE };
} }
}
else if (player->state == CHARACTER_ATTACK)
{
to_draw = (DrawnAnimatedSprite) { ANIM_knight_attack, player->swing_progress, player->facing_left, character_sprite_pos, WHITE };
}
else if (player->state == CHARACTER_TALKING) else if (player->state == CHARACTER_TALKING)
{ {
to_draw = (DrawnAnimatedSprite) { ANIM_knight_idle, elapsed_time, player->facing_left, character_sprite_pos, WHITE }; to_draw = (DrawnAnimatedSprite) { ANIM_knight_idle, elapsed_time, player->facing_left, character_sprite_pos, WHITE };
@ -3838,42 +3520,9 @@ void frame(void)
} }
to_draw.anim = player->cur_animation; to_draw.anim = player->cur_animation;
Vec2 target_sprite_pos = to_draw.pos;
BUFF_ITER_I(PlayerAfterImage, &player->after_images, i)
{
{
DrawnAnimatedSprite to_draw = it->drawn;
to_draw.tint.a = 0.5f;
float progress_through_life = it->alive_for / AFTERIMAGE_LIFETIME;
if (progress_through_life > 0.5f)
{
float fade_amount = (progress_through_life - 0.5f) / 0.5f;
to_draw.tint.a = Lerp(0.8f, fade_amount, 0.0f);
Vec2 target;
if (i != player->after_images.cur_index-1) target = player->after_images.data[i + 1].drawn.pos;
else target = target_sprite_pos;
to_draw.pos = LerpV2(to_draw.pos, fade_amount, target);
}
to_draw.no_shadow = true;
draw_animated_sprite(to_draw);
}
}
//if(player->is_rolling^) to_draw.tint.a = 0.5f;
if (to_draw.anim) if (to_draw.anim)
{ {
draw_animated_sprite(to_draw); draw_animated_sprite(to_draw);
if (player->after_image_timer >= TIME_TO_GEN_AFTERIMAGE)
{
player->after_image_timer = 0.0;
if (BUFF_HAS_SPACE(&player->after_images))
BUFF_APPEND(&player->after_images, (PlayerAfterImage) { .drawn = to_draw });
}
} }
} }
@ -4005,12 +3654,6 @@ void frame(void)
{ {
draw_item(true, it->item_kind, centered_aabb(it->pos, V2(15.0f, 15.0f)), 1.0f); draw_item(true, it->item_kind, centered_aabb(it->pos, V2(15.0f, 15.0f)), 1.0f);
} }
else if (it->is_bullet)
{
AABB normal_aabb = entity_aabb(it);
Quad drawn = quad_centered(aabb_center(normal_aabb), MulV2F(aabb_size(normal_aabb), 1.5f));
draw_quad((DrawParams) { true, drawn, IMG(image_bullet), WHITE });
}
else if (it->is_character) else if (it->is_character)
{ {
} }
@ -4321,7 +3964,7 @@ void frame(void)
draw_quad((DrawParams) { false, quad_centered(thumbstick_base_pos, V2(thumbstick_base_size(), thumbstick_base_size())), IMG(image_mobile_thumbstick_base), WHITE, .layer = LAYER_UI_FG }); draw_quad((DrawParams) { false, quad_centered(thumbstick_base_pos, V2(thumbstick_base_size(), thumbstick_base_size())), IMG(image_mobile_thumbstick_base), WHITE, .layer = LAYER_UI_FG });
draw_quad((DrawParams) { false, quad_centered(thumbstick_nub_pos, V2(thumbstick_nub_size, thumbstick_nub_size)), IMG(image_mobile_thumbstick_nub), WHITE, .layer = LAYER_UI_FG }); draw_quad((DrawParams) { false, quad_centered(thumbstick_nub_pos, V2(thumbstick_nub_size, thumbstick_nub_size)), IMG(image_mobile_thumbstick_nub), WHITE, .layer = LAYER_UI_FG });
if (interacting_with || gete(player->holding_item)) if (interacting_with)
{ {
draw_quad((DrawParams) { false, quad_centered(interact_button_pos(), V2(mobile_button_size(), mobile_button_size())), IMG(image_mobile_button), WHITE, .layer = LAYER_UI_FG }); draw_quad((DrawParams) { false, quad_centered(interact_button_pos(), V2(mobile_button_size(), mobile_button_size())), IMG(image_mobile_button), WHITE, .layer = LAYER_UI_FG });
} }

@ -105,8 +105,6 @@ typedef enum PerceptionType
PlayerAction, PlayerAction,
PlayerDialog, PlayerDialog,
NPCDialog, // includes an npc action in every npc dialog. So it's often ACT_none NPCDialog, // includes an npc action in every npc dialog. So it's often ACT_none
EnemyAction, // An enemy performed an action against the NPC
PlayerHeldItemChanged,
} PerceptionType; } PerceptionType;
typedef struct Perception typedef struct Perception
@ -139,12 +137,6 @@ typedef struct Perception
Action npc_action_type; Action npc_action_type;
Sentence npc_dialog; Sentence npc_dialog;
}; };
// enemy action
Action enemy_action_type;
// player holding item. MUST precede any perceptions which come after the player is holding the item
ItemKind holding;
}; };
} Perception; } Perception;
@ -166,7 +158,6 @@ typedef enum CharacterState
{ {
CHARACTER_WALKING, CHARACTER_WALKING,
CHARACTER_IDLE, CHARACTER_IDLE,
CHARACTER_ATTACK,
CHARACTER_TALKING, CHARACTER_TALKING,
} CharacterState; } CharacterState;
@ -233,14 +224,10 @@ typedef struct Entity
bool facing_left; bool facing_left;
double dead_time; double dead_time;
bool dead; bool dead;
// multiple gs.entities have a sword swing
BUFF(EntityRef, 8) done_damage_to_this_swing; // only do damage once, but hitbox stays around
// npcs and player // npcs and player
BUFF(ItemKind, 32) held_items; BUFF(ItemKind, 32) held_items;
bool is_bullet;
// props // props
bool is_prop; bool is_prop;
PropKind prop_kind; PropKind prop_kind;
@ -266,7 +253,6 @@ typedef struct Entity
NPCPlayerStanding standing; NPCPlayerStanding standing;
NpcKind npc_kind; NpcKind npc_kind;
PathCacheHandle cached_path; PathCacheHandle cached_path;
ItemKind last_seen_holding_kind;
#ifdef WEB #ifdef WEB
int gen_request_id; int gen_request_id;
#endif #endif
@ -280,7 +266,6 @@ typedef struct Entity
// character // character
bool is_character; bool is_character;
bool knighted; bool knighted;
EntityRef holding_item;
bool in_conversation_mode; bool in_conversation_mode;
Vec2 to_throw_direction; Vec2 to_throw_direction;
@ -294,11 +279,6 @@ typedef struct Entity
// so doesn't change animations while time is stopped // so doesn't change animations while time is stopped
AnimKind cur_animation; AnimKind cur_animation;
float anim_change_timer; float anim_change_timer;
BUFF(PlayerAfterImage, MAX_AFTERIMAGES) after_images;
double after_image_timer;
double roll_progress;
double swing_progress;
} Entity; } Entity;
bool npc_is_knight_sprite(Entity *it) bool npc_is_knight_sprite(Entity *it)
@ -485,10 +465,6 @@ void process_perception(Entity *happened_to_npc, Perception p, Entity *player, G
else if (p.type == PlayerDialog) else if (p.type == PlayerDialog)
{ {
}
else if (p.type == PlayerHeldItemChanged)
{
happened_to_npc->last_seen_holding_kind = p.holding;
} }
else if (p.type == NPCDialog) else if (p.type == NPCDialog)
{ {
@ -750,10 +726,6 @@ MD_String8 generate_chatgpt_prompt(MD_Arena *arena, Entity *it)
sent_type = MSG_ASSISTANT; sent_type = MSG_ASSISTANT;
} }
else if (it->type == PlayerHeldItemChanged)
{
// @TODO delete this branch and delete held item changed
}
else else
{ {
assert(false); assert(false);
@ -806,7 +778,7 @@ MD_String8 generate_chatgpt_prompt(MD_Arena *arena, Entity *it)
MD_String8List latest_state = {0}; MD_String8List latest_state = {0};
MD_S8ListPushFmt(scratch.arena, &latest_state, "NPC health status: Right now, %s\n%s\n", health_string, items[it->last_seen_holding_kind].global_prompt); MD_S8ListPushFmt(scratch.arena, &latest_state, "NPC health status: Right now, %s\n", health_string);
if(e->held_items.cur_index > 0) if(e->held_items.cur_index > 0)
{ {

Loading…
Cancel
Save