#include "components.h" #include "ai_actions.h" ECS_TAG_DECLARE(GameEntity); ECS_COMPONENT_DECLARE(Resource); ECS_COMPONENT_DECLARE(Owner); ECS_COMPONENT_DECLARE(EmitterAttachment); ECS_COMPONENT_DECLARE(ParticleEmitter); ECS_COMPONENT_DECLARE(ParticleLayer0); ECS_COMPONENT_DECLARE(ParticleLayer1); ECS_COMPONENT_DECLARE(SpatialGridID); ECS_COMPONENT_DECLARE(Position); ECS_COMPONENT_DECLARE(Size); ECS_COMPONENT_DECLARE(Velocity); ECS_COMPONENT_DECLARE(Rotation); ECS_COMPONENT_DECLARE(Orientation); ECS_COMPONENT_DECLARE(Steering); ECS_COMPONENT_DECLARE(TargetPosition); ECS_COMPONENT_DECLARE(Path); ECS_COMPONENT_DECLARE(TextureRegion); ECS_COMPONENT_DECLARE(Animation); ECS_COMPONENT_DECLARE(Easing); ECS_COMPONENT_DECLARE(HitBox); ECS_COMPONENT_DECLARE(Arms); ECS_COMPONENT_DECLARE(Arm); ECS_COMPONENT_DECLARE(BzBTState); ECS_COMPONENT_DECLARE(AIBlackboard); ECS_TAG_DECLARE(Selectable); ECS_TAG_DECLARE(Selected); ECS_COMPONENT_DECLARE(AddPopCapacity); ECS_COMPONENT_DECLARE(ConsumePopCapacity); ECS_COMPONENT_DECLARE(Worker); ECS_COMPONENT_DECLARE(Building); ECS_COMPONENT_DECLARE(Unit); ECS_TAG_DECLARE(Storage); ECS_TAG_DECLARE(Harvestable); ECS_TAG_DECLARE(Buildable); ECS_TAG_DECLARE(Workable); ECS_TAG_DECLARE(Attackable); void initComponentIDs(ecs_world_t *ecs) { ECS_TAG_DEFINE(ecs, GameEntity); ECS_COMPONENT_DEFINE(ecs, Resource); ECS_COMPONENT_DEFINE(ecs, Owner); ECS_COMPONENT_DEFINE(ecs, EmitterAttachment); ECS_COMPONENT_DEFINE(ecs, ParticleEmitter); ECS_COMPONENT_DEFINE(ecs, ParticleLayer0); ECS_COMPONENT_DEFINE(ecs, ParticleLayer1); ECS_COMPONENT_DEFINE(ecs, SpatialGridID); ECS_COMPONENT_DEFINE(ecs, Position); ECS_COMPONENT_DEFINE(ecs, Size); ECS_COMPONENT_DEFINE(ecs, Velocity); ECS_COMPONENT_DEFINE(ecs, Rotation); ECS_COMPONENT_DEFINE(ecs, Orientation); ECS_COMPONENT_DEFINE(ecs, Steering); ECS_COMPONENT_DEFINE(ecs, TargetPosition); ECS_COMPONENT_DEFINE(ecs, Path); ECS_COMPONENT_DEFINE(ecs, TextureRegion); ECS_COMPONENT_DEFINE(ecs, Animation); ECS_COMPONENT_DEFINE(ecs, Easing); ECS_COMPONENT_DEFINE(ecs, HitBox); ECS_COMPONENT_DEFINE(ecs, Arms); ECS_COMPONENT_DEFINE(ecs, Arm); ECS_COMPONENT_DEFINE(ecs, BzBTState); ECS_COMPONENT_DEFINE(ecs, AIBlackboard); ECS_TAG_DEFINE(ecs, Selectable); ECS_TAG_DEFINE(ecs, Selected); ECS_COMPONENT_DEFINE(ecs, AddPopCapacity); ECS_COMPONENT_DEFINE(ecs, ConsumePopCapacity); ECS_COMPONENT_DEFINE(ecs, Worker); ECS_COMPONENT_DEFINE(ecs, Building); ECS_COMPONENT_DEFINE(ecs, Unit); ECS_TAG_DEFINE(ecs, Storage); ECS_TAG_DEFINE(ecs, Harvestable); ECS_TAG_DEFINE(ecs, Buildable); ECS_TAG_DEFINE(ecs, Workable); ECS_TAG_DEFINE(ecs, Attackable); } #include void igTagCheckbox(const char *label, ecs_world_t *ecs, ecs_entity_t entity, ecs_entity_t tag) { bool hasTag = ecs_has_id(ecs, entity, tag); igCheckbox(label, &hasTag); if (hasTag) ecs_add_id(ecs, entity, tag); else ecs_remove_id(ecs, entity, tag); } void igResource(ecs_world_t *ecs, ecs_entity_t entity, ecs_entity_t comp) { Resource *res = ecs_get_mut_id(ecs, entity, comp); const char *resStrings[RES_COUNT]; for (i32 i = 0; i < RES_COUNT; i++) { resStrings[i] = getResourceTypePrettyName(i); } int curType = res->type; igCombo_Str_arr("Type", &curType, resStrings, RES_COUNT, -1); res->type = curType; igInputInt("Amount", &res->amount, 1, 10, 0); } void igOwner(ecs_world_t *ecs, ecs_entity_t entity, ecs_entity_t comp) { Owner *owner = ecs_get_mut_id(ecs, entity, comp); igText("PlayerID: %d", owner->player); } void igSpatialGridID(ecs_world_t *ecs, ecs_entity_t entity, ecs_entity_t comp) { SpatialGridID *id = ecs_get_mut_id(ecs, entity, comp); igText("SpatialID", *id); } void igVec2(Vector2 *vec) { igInputFloat("X", &vec->x, 1.0f, 10.0f, "%.2f", 0); igInputFloat("Y", &vec->y, 1.0f, 10.0f, "%.2f", 0); } void igVec2Comp(ecs_world_t *ecs, ecs_entity_t entity, ecs_entity_t comp) { Vector2 *vec = ecs_get_mut_id(ecs, entity, comp); igVec2(vec); } void igFloat(ecs_world_t *ecs, ecs_entity_t entity, ecs_entity_t comp) { f32 *f = ecs_get_mut_id(ecs, entity, comp); igInputFloat("", f, 0.1f, 1.0f, "%.2f", 0); } void igPath(ecs_world_t *ecs, ecs_entity_t entity, ecs_entity_t comp) { Path path = *(Path *) ecs_get_mut_id(ecs, entity, comp); i32 idx = 0; while (path.paths) { for (int32_t i = path.curWaypoint; i < path.paths->numWaypoints; i++) { igPushID_Int(idx); igText("Waypoint %d:", idx); igInputFloat("X", &path.paths->waypoints[i].x, 1, 16, "%.2f", 0); igInputFloat("Y", &path.paths->waypoints[i].y, 1, 16, "%.2f", 0); igPopID(); idx++; } path.paths = path.paths->next; path.curWaypoint = 0; } } void igRect(Rectangle *rect) { igInputFloat("X", &rect->x, 1, 16, "%.2f", 0); igInputFloat("Y", &rect->y, 1, 16, "%.2f", 0); igInputFloat("Width", &rect->width, 1, 16, "%.2f", 0); igInputFloat("Height", &rect->height, 1, 16, "%.2f", 0); } void igTextureRegion(ecs_world_t *ecs, ecs_entity_t entity, ecs_entity_t comp) { TextureRegion *tex = ecs_get_mut_id(ecs, entity, comp); igText("Texture: %d", tex->texture.id); igRect(&tex->rec); bool flipX = tex->flipX; bool flipY = tex->flipY; igCheckbox("flipX", &flipX); igCheckbox("flipY", &flipY); tex->flipX = flipX; tex->flipY = flipY; } static void igEntityType(EntityType *type) { const char *prettyStrings[ENTITY_COUNT]; for (int i = 0; i < ENTITY_COUNT; i++) { prettyStrings[i] = getEntityStr(i); } igCombo_Str_arr("EntityType", type, prettyStrings, ENTITY_COUNT, -1); } static void igAnimType(AnimType *type) { const char *prettyStrings[ANIM_COUNT]; for (int i = 0; i < ANIM_COUNT; i++) { prettyStrings[i] = getEntityAnimationStr(i); } igCombo_Str_arr("AnimType", type, prettyStrings, ANIM_COUNT, -1); } void igAnimation(ecs_world_t *ecs, ecs_entity_t entity, ecs_entity_t comp) { Animation *anim = ecs_get_mut_id(ecs, entity, comp); EntityType curEntityType = anim->entityType; igEntityType(&curEntityType); anim->entityType = curEntityType; AnimType curAnimType = anim->animType; igAnimType(&curAnimType); anim->animType = curAnimType; igText("tileset: %p", anim->tileset); igText("curFrame: %d", anim->curFrame); igText("elapsed: %.2f < %.2F", anim->elapsed, anim->frame.duration); } void igEasing(ecs_world_t *ecs, ecs_entity_t entity, ecs_entity_t comp) { Easing *easing = ecs_get_mut_id(ecs, entity, comp); igText("Component: %d, offset: %d", easing->compID, easing->offset); igText("x = %.2f + %.2f * (%.2f + (%.2f * %.2f) + %.2f) + %.2f", easing->start, easing->target, easing->easeStart, easing->easeTarget, easing->x, easing->easeOffset, easing->offset); } void igArms(ecs_world_t *ecs, ecs_entity_t entity, ecs_entity_t comp) { } void igArm(ecs_world_t *ecs, ecs_entity_t entity, ecs_entity_t comp) { } static const BzBTNodeState *findNodeState(const BzBTNode *node, const BzBTNodeState *state) { const BzBTNodeState *pState = state; // Although it's painfully slow, it serves as a debug tool, // so speed is not a critical concern. while (pState) { const BzBTNodeState *next = bzBTNodeStateNext(pState); if (bzBTNodeMatchesState(node, pState)) return pState; pState = next; } return NULL; } static void visualizeBTState(const BzBTNode *node, const BzBTNodeState *state, bool sameLine, bool isActive, i32 depth) { const BzBTNode *child = bzBTNodeChild(node); BzBTNodeType type = bzBTGetNodeType(node); char extraInfo[128]; extraInfo[0] = '\0'; const BzBTNodeState *nodeState = findNodeState(node, state); bool hasState = nodeState != NULL; isActive |= nodeState != NULL; switch (type) { case BZ_BT_DECOR_REPEAT: if (hasState) { snprintf(extraInfo, sizeof(extraInfo), " (%d < %d)", bzBTRepeatStateGetIter(nodeState), bzBTDecorGetRepeat(node)); } else { snprintf(extraInfo, sizeof(extraInfo), " (%d)", bzBTDecorGetRepeat(node)); } break; case BZ_BT_DECOR_DELAY: if (isActive) { snprintf(extraInfo, sizeof(extraInfo), " (%.2f < %.2fms)", bzBTDelayStateGetElapsed(nodeState), bzBTDecorGetDelay(node)); } else { snprintf(extraInfo, sizeof(extraInfo), " (%.2fms)", bzBTDecorGetDelay(node)); } break; case BZ_BT_ACTION: snprintf(extraInfo, sizeof(extraInfo), " (%s:%p)", bzBTNodeGetName(node) ? bzBTNodeGetName(node) : "?", bzBTActionGetFn(node)); break; default: break; } ImVec4 color = {1.0f, 1.0f, 1.0f, 1.0f}; if (isActive) color = (ImVec4) {1.0f, 1.0f, 0.5f, 1.0f}; bool hasSingleChild = child && bzBTNodeNext(child) == NULL; i32 offset = depth * 2; if (sameLine) offset = 0; const char *prefix = sameLine ? " > " : ""; const char *postfix = child != NULL && !hasSingleChild ? ":" : ""; igTextColored(color, "%s%*s%s%s%s", prefix, offset, "", bzBTNodeTypeToStr(type), extraInfo, postfix); bool isComposite = type == BZ_BT_COMP_SELECTOR || type == BZ_BT_COMP_PSELECTOR || type == BZ_BT_COMP_SEQUENCE || type == BZ_BT_COMP_PSEQUENCE; depth++; while (child) { if (hasSingleChild) igSameLine(0, 0); bool childHasState = hasState && isComposite && bzBTCompStateGetRunningChild(nodeState) == child; visualizeBTState(child, state, hasSingleChild, childHasState, depth); child = bzBTNodeNext(child); } } void igVisualizeBTState(const BzBTNode *node, const BzBTNodeState *state) { igSeparatorText("Tree State"); visualizeBTState(node, state, false, true, 0); igSeparatorText("State stack"); size_t stateSize = 0; const BzBTNodeState *pState = state; while (pState) { const BzBTNode *pNode = bzBTNodeStateGetNode(pState); stateSize += bzBTGetNodeStateSize(); BzBTNodeType type = bzBTGetNodeType(pNode); igText("%s", bzBTNodeTypeToStr(type)); pState = bzBTNodeStateNext(pState); } igNewLine(); igText("Total stack state size: %ld bytes.", stateSize); } void igBzBTState(ecs_world_t *ecs, ecs_entity_t entity, ecs_entity_t comp) { } void igAIBlackboard(ecs_world_t *ecs, ecs_entity_t entity, ecs_entity_t comp) { } void igWorker(ecs_world_t *ecs, ecs_entity_t entity, ecs_entity_t comp) { } void igUnit(ecs_world_t *ecs, ecs_entity_t entity, ecs_entity_t comp) { }