#include "entity.h" #include "game.h" #include "entitiesInclude.h" const EntityEntry entityEntries[ENTITY_COUNT] = { (EntityEntry){ .name = "Old Mint", .initCallback = initOldMint, .updateCallback = updateOldMint, .closeCallback = NULL, .interactionCallback = NULL, .isPlace = false, .isBuilding = false, .canBeSelected = true }, (EntityEntry){ .name = "Sticky Nickel", .initCallback = initStickyNickel, .updateCallback = updateStickyNickel, .closeCallback = NULL, .interactionCallback = NULL, .isPlace = false, .isBuilding = false, .canBeSelected = true }, (EntityEntry){ .name = "Tree", .initCallback = initTree, .updateCallback = updateTree, .closeCallback = NULL, .interactionCallback = NULL, .isPlace = false, .isBuilding = false, .canBeSelected = true }, (EntityEntry){ .name = "Bush", .initCallback = initBush, .updateCallback = updateBush, .closeCallback = NULL, .interactionCallback = NULL, .isPlace = false, .isBuilding = false, .canBeSelected = true }, (EntityEntry){ .name = "Flower", .initCallback = initFlower, .updateCallback = updateFlower, .closeCallback = NULL, .interactionCallback = NULL, .isPlace = false, .isBuilding = false, .canBeSelected = true }, (EntityEntry){ .name = "Pond", .initCallback = initPond, .updateCallback = updatePond, .closeCallback = NULL, .interactionCallback = NULL, .isPlace = true, .isBuilding = false, .canBeSelected = true }, (EntityEntry){ .name = "Utility Pole", .initCallback = initUtilityPole, .updateCallback = NULL, .closeCallback = NULL, .interactionCallback = NULL, .isPlace = false, .isBuilding = false, .canBeSelected = false }, (EntityEntry){ .name = "Samantha", .initCallback = initSamantha, .updateCallback = updateSamantha, .closeCallback = closeSamantha, .interactionCallback = interactWithSamantha, .isPlace = false, .isBuilding = false, .canBeSelected = true }, (EntityEntry){ .name = "Samantha's Spot", .initCallback = initSamanthasSpot, .updateCallback = updateSamanthasSpot, .closeCallback = NULL, .interactionCallback = NULL, .isPlace = true, .isBuilding = false, .canBeSelected = false }, (EntityEntry){ .name = "Trashcan", .initCallback = initTrashcan, .updateCallback = updateTrashcan, .closeCallback = NULL, .interactionCallback = NULL, .isPlace = false, .isBuilding = false, .canBeSelected = true }, (EntityEntry){ .name = "Trash", .initCallback = initTrash, .updateCallback = updateTrash, .closeCallback = NULL, .interactionCallback = NULL, .isPlace = false, .isBuilding = false, .canBeSelected = true }, (EntityEntry){ .name = "Medical Trash", .initCallback = initMedicalTrash, .updateCallback = updateMedicalTrash, .closeCallback = NULL, .interactionCallback = NULL, .isPlace = false, .isBuilding = false, .canBeSelected = true }, (EntityEntry){ .name = "John", .initCallback = initJohn, .updateCallback = updateJohn, .closeCallback = NULL, .interactionCallback = NULL, .isPlace = false, .isBuilding = false, .canBeSelected = true }, (EntityEntry){ .name = "John's store", .initCallback = initJohnsStore, .updateCallback = updateJohnsStore, .closeCallback = closeJohnsStore, .interactionCallback = NULL, .isPlace = true, .isBuilding = true, .canBeSelected = false }, (EntityEntry){ .name = "Ron", .initCallback = initRon, .updateCallback = updateRon, .closeCallback = NULL, .interactionCallback = interactWithRon, .isPlace = false, .isBuilding = false, .canBeSelected = true } }; Entity createEntity(EntityId id, Vector3 position) { Entity entity; entity.id = id; entity.data = NULL; // Run init callback. InitEntityCallback initCallback = entityEntries[id].initCallback; if (initCallback != NULL) { initCallback(&entity); } entity.position = Vector3Zero(); setEntityPosition(&entity, position); return entity; } void updateEntity(Entity* entity, Game* game) { if (entity->id == ENTITY_NONE) { return; } UpdateEntityCallback updateCallback = entityEntries[entity->id].updateCallback; if (updateCallback != NULL) { updateCallback(entity, game); } } void closeEntity(Entity* entity) { if (entity->id == ENTITY_NONE) { return; } CloseEntityCallback closeCallback = entityEntries[entity->id].closeCallback; if (closeCallback != NULL) { closeCallback(entity); } entity->id = ENTITY_NONE; } const char* getEntityName(EntityId id) { if (id == ENTITY_NONE) { return NULL; } return entityEntries[id].name; } void setEntityPosition(Entity* entity, Vector3 position) { Vector3 movedBy = Vector3Subtract(position, entity->position); entity->position = position; entity->box.min = Vector3Add(entity->box.min, movedBy); entity->box.max = Vector3Add(entity->box.max, movedBy); } void placeEntityOnGround(Entity* entity, const World* world) { Vector3 position = entity->position; if (entity->id == ENTITY_NONE) { return; } position.y = getWorldHeightAtLocation( world, entity->position.x, entity->position.z); if (entityIsBuilding(entity->id)) { position.y += TOUCHING_OFFSET; } else { position.y += (entity->box.max.y - entity->box.min.y) / 2.0; } setEntityPosition(entity, position); } bool entityIsPlace(EntityId id) { if (id == ENTITY_NONE) { return false; } return entityEntries[id].isPlace; } bool entityIsBuilding(EntityId id) { if (id == ENTITY_NONE) { return false; } return entityEntries[id].isBuilding; } bool entityCanBeSelected(EntityId id) { if (id == ENTITY_NONE) { return false; } return entityEntries[id].canBeSelected; } float getEntityDistance(Entity entity, Vector3 position) { return Vector3Distance(entity.position, position) - (Vector3Distance(entity.box.min, entity.box.max) / 2.0); } InteractionCommand interactWithEntity(Entity* entity, Game* game, Selection selection) { if (entity->id == ENTITY_NONE) { return INTERACTION_END; } InteractionCallback callback = entityEntries[entity->id].interactionCallback; if (callback != NULL) { return callback(entity, game, selection); } return INTERACTION_END; } int getInteractionMenuIndex(Selection selection) { if (selection < SELECTION_MENU_ITEM || selection >= SELECTION_LEAVE) { return SELECTION_NONE; } return selection - SELECTION_MENU_ITEM; } BoundingBox entityBoxFromScale(float scale, float width, float height) { Vector2 size = (Vector2){width / height * scale, scale}; size = Vector2Scale(size, 0.5); return (BoundingBox){ .min = (Vector3){-size.x, -size.y, -size.x}, .max = (Vector3){size.x, size.y, size.x} }; } EntityBuilding* createEntityBuilding(Image heightmap) { EntityBuilding* building = (EntityBuilding*)FT_MALLOC(sizeof(EntityBuilding)); if (building == NULL) { ALLOCATION_ERROR; return NULL; } float widthInWorld = heightmap.width * ENTITY_BUILDING_CUBE_SIZE.x; float heightInWorld = heightmap.height * ENTITY_BUILDING_CUBE_SIZE.z; *building = (EntityBuilding){ .model = LoadModelFromMesh(GenMeshCubicmap(heightmap, ENTITY_BUILDING_CUBE_SIZE)), .pixelMap = LoadImageColors(heightmap), .width = heightmap.width, .height = heightmap.height, .roofSize = (hypotf(widthInWorld, heightInWorld) + ENTITY_BUILDING_CUBE_SIZE.x) / 2.0, .roofHeight = BUILDING_DEFAULT_ROOF_HEIGHT, .roofOffset = (Vector3){ widthInWorld / 2.0 - ENTITY_BUILDING_CUBE_SIZE.x / 2.0, ENTITY_BUILDING_CUBE_SIZE.y + TOUCHING_OFFSET, heightInWorld / 2.0 - ENTITY_BUILDING_CUBE_SIZE.z / 2.0 } }; return building; } void freeEntityBuilding(EntityBuilding* building) { if (building != NULL) { UnloadModel(building->model); UnloadImageColors(building->pixelMap); FT_FREE(building); } } bool isBuildingBlockWall(const EntityBuilding* building, int x, int y) { if (x < 0 || x >= building->width || y < 0 || y >= building->height) { return false; } return building->pixelMap[y * building->width + x].r == 255; } void drawBuildingRoof(const EntityBuilding* building, Vector3 position, Color color) { DrawCylinder(Vector3Add(position, building->roofOffset), 1.0, building->roofSize, building->roofHeight, BUILDING_ROOF_SLICES, color); }