/* ************************************** * Includes * * **************************************/ #include "Unit.h" /* Units sprite data */ #include "PeasantSpr.i" #include "SoldierSpr.i" /* Buildings sprite data */ #include "BarracksSpr.i" #include "TownCentre.i" /* ************************************** * Defines * * **************************************/ /* ************************************** * Structs and enums * * **************************************/ struct t_coordinates { int8_t x; int8_t y; }; /* ************************************** * Local prototypes * * **************************************/ /* ************************************** * Local variables * * **************************************/ /* Tables */ static uint8_t const UnitHPTable[MAX_UNITS_BUILDINGS] = { [PEASANT] = 25, [SOLDIER] = 80, [BARRACKS] = 100, [TOWN_CENTER] = 200 }; static TYPE_RESOURCES const UnitResourcesTable[MAX_UNITS_BUILDINGS] = { [PEASANT] = {.Wood = 0, .Gold = 0, .Food = 50}, [SOLDIER] = {.Wood = 25, .Gold = 10, .Food = 50}, [BARRACKS] = {.Wood = 100, .Gold = 0, .Food = 0}, [TOWN_CENTER] = {.Wood = 200, .Gold = 0, .Food = 0} }; static uint8_t const UnitSpeedTable[MAX_UNITS_BUILDINGS] = { [PEASANT] = 1, [SOLDIER] = 1, [BARRACKS] = 0, [TOWN_CENTER] = 0 }; static const char* const UnitActionsTable_Level[MAX_ACTIONS] = { [ACTION_BUILD_BARRACKS] = "B.BARR", [ACTION_CREATE_PEASANT] = "C.PEAS.", [ACTION_CREATE_SOLDIER] = "C.SLDR.", [ACTION_BUILD_TOWER_CENTER] = "C.TWNC."}; static uint8_t const UnitActionsTable[MAX_UNITS_BUILDINGS] = { [PEASANT] = ((1 << ACTION_BUILD_BARRACKS) | (1 << ACTION_BUILD_TOWER_CENTER)), [SOLDIER] = 0, [BARRACKS] = (1 << ACTION_CREATE_SOLDIER), [TOWN_CENTER] = (1 << ACTION_CREATE_PEASANT) }; /* **************. */ /* Sprite tables. */ /* **************. */ static TYPE_SPRITE UnitSprTable[MAX_UNITS_BUILDINGS]; static TYPE_SPRITE UnitWalkingShadowSprTable[MAX_UNITS_BUILDINGS]; static const struct t_coordinates UnitShadowOffsetTable[MAX_BUILDING_ID - FIRST_BUILDING_ID] = { [BARRACKS - FIRST_BUILDING_ID] = {.x = -6, .y = 0}, [TOWN_CENTER - FIRST_BUILDING_ID] = {.x = -3, .y = 0} }; void UnitInit(void) { /* *********************************** * Unit sprite data init * ***********************************/ UnitSprTable[PEASANT].Data = Peasant_SprData; UnitSprTable[PEASANT].w = GfxGetWidthFromSpriteData(Peasant_SprData); UnitSprTable[PEASANT].h = GfxGetHeightFromSpriteData(Peasant_SprData); UnitSprTable[PEASANT].flip = 0; UnitSprTable[PEASANT].rotation = 0; UnitSprTable[PEASANT].color = GFX_BLACK; UnitSprTable[SOLDIER].Data = SoldierSprData; UnitSprTable[SOLDIER].w = GfxGetWidthFromSpriteData(SoldierSprData); UnitSprTable[SOLDIER].h = GfxGetHeightFromSpriteData(SoldierSprData); UnitSprTable[SOLDIER].flip = 0; UnitSprTable[SOLDIER].rotation = 0; UnitSprTable[SOLDIER].color = GFX_BLACK; UnitWalkingShadowSprTable[PEASANT].Data = Peasant_Walking_SprData; UnitWalkingShadowSprTable[PEASANT].w = GfxGetWidthFromSpriteData(Peasant_Walking_SprData); UnitWalkingShadowSprTable[PEASANT].h = GfxGetHeightFromSpriteData(Peasant_Walking_SprData); UnitWalkingShadowSprTable[PEASANT].flip = 0; UnitWalkingShadowSprTable[PEASANT].rotation = 0; UnitWalkingShadowSprTable[PEASANT].color = GFX_BLACK; UnitWalkingShadowSprTable[SOLDIER].Data = SoldierSprData_Walking; UnitWalkingShadowSprTable[SOLDIER].w = GfxGetWidthFromSpriteData(SoldierSprData_Walking); UnitWalkingShadowSprTable[SOLDIER].h = GfxGetHeightFromSpriteData(SoldierSprData_Walking); UnitWalkingShadowSprTable[SOLDIER].flip = 0; UnitWalkingShadowSprTable[SOLDIER].rotation = 0; UnitWalkingShadowSprTable[SOLDIER].color = GFX_BLACK; /* *********************************** * Buildings sprite data init * ***********************************/ UnitSprTable[BARRACKS].Data = BarracksSpr_Data; UnitSprTable[BARRACKS].w = GfxGetWidthFromSpriteData(BarracksSpr_Data); UnitSprTable[BARRACKS].h = GfxGetHeightFromSpriteData(BarracksSpr_Data); UnitSprTable[BARRACKS].flip = 0; UnitSprTable[BARRACKS].rotation = 0; UnitSprTable[BARRACKS].color = GFX_BLACK; UnitSprTable[TOWN_CENTER].Data = TownCentreSprData; UnitSprTable[TOWN_CENTER].w = GfxGetWidthFromSpriteData(TownCentreSprData); UnitSprTable[TOWN_CENTER].h = GfxGetHeightFromSpriteData(TownCentreSprData); UnitSprTable[TOWN_CENTER].flip = 0; UnitSprTable[TOWN_CENTER].rotation = 0; UnitSprTable[TOWN_CENTER].color = GFX_BLACK; UnitWalkingShadowSprTable[BARRACKS].Data = BarracksShadowSpr_Data; UnitWalkingShadowSprTable[BARRACKS].w = GfxGetWidthFromSpriteData(BarracksShadowSpr_Data); UnitWalkingShadowSprTable[BARRACKS].h = GfxGetHeightFromSpriteData(BarracksShadowSpr_Data); UnitWalkingShadowSprTable[BARRACKS].flip = 0; UnitWalkingShadowSprTable[BARRACKS].rotation = 0; UnitWalkingShadowSprTable[BARRACKS].color = GFX_GRAY; UnitWalkingShadowSprTable[TOWN_CENTER].Data = TownCentreShadowSprData; UnitWalkingShadowSprTable[TOWN_CENTER].w = GfxGetWidthFromSpriteData(TownCentreShadowSprData); UnitWalkingShadowSprTable[TOWN_CENTER].h = GfxGetHeightFromSpriteData(TownCentreShadowSprData); UnitWalkingShadowSprTable[TOWN_CENTER].flip = 0; UnitWalkingShadowSprTable[TOWN_CENTER].rotation = 0; UnitWalkingShadowSprTable[TOWN_CENTER].color = GFX_GRAY; } void UnitDrawShadow(TYPE_UNIT *ptrUnit, TYPE_CAMERA *ptrCamera) { uint8_t id = ptrUnit->id; CameraApplyCoordinatesToSprite( ptrCamera, &UnitWalkingShadowSprTable[id], ptrUnit->x + UnitShadowOffsetTable[id - FIRST_BUILDING_ID].x, ptrUnit->y + UnitShadowOffsetTable[id - FIRST_BUILDING_ID].y ); GfxDrawSprite(&UnitWalkingShadowSprTable[id]); } void UnitDraw(TYPE_UNIT *ptrUnit, TYPE_CAMERA* ptrCamera, bool bHighlighted) { uint8_t id = ptrUnit->id; TYPE_SPRITE* ptrSpr; if (ptrUnit->alive == false) { return; } if (ptrUnit->building == false) { enum { WALK_FRAMES = 4 }; /* ***************. */ /* Units. */ /* ***************. */ ptrSpr = ptrUnit->walking ? &UnitWalkingShadowSprTable[id] : &UnitSprTable[id]; ptrSpr->rotation = GFX_NOROT; ptrSpr->flip = GFX_NOFLIP; if (ptrUnit->walking != false) { if (++ptrUnit->walk_counter > WALK_FRAMES) { ptrUnit->walk_counter = 0; ptrUnit->mirror = ptrUnit->mirror ? false : true; } } switch (ptrUnit->dir) { case DIRECTION_UP: ptrSpr->flip |= GFX_FLIPV; if (ptrUnit->mirror != false) { ptrSpr->flip |= GFX_FLIPH; } break; case DIRECTION_DOWN: if (ptrUnit->mirror != false) { ptrSpr->flip |= GFX_FLIPH; } break; case DIRECTION_LEFT: ptrSpr->rotation = GFX_ROTCCW; ptrSpr->flip |= GFX_FLIPH; if (ptrUnit->mirror != false) { ptrSpr->flip |= GFX_FLIPV; } break; case DIRECTION_RIGHT: ptrSpr->rotation = GFX_ROTCCW; if (ptrUnit->mirror != false) { ptrSpr->flip |= GFX_FLIPV; } break; } } else { /* *******************. */ /* Buildings. */ /* *******************. */ ptrSpr = &UnitSprTable[id]; } CameraApplyCoordinatesToSprite( ptrCamera, ptrSpr, ptrUnit->x, ptrUnit->y ); GfxDrawSprite(ptrSpr); if ( (bHighlighted != false) || (ptrUnit->selected != false) ) { TYPE_COLLISION_BLOCK cb = CameraApplyCoordinatesToCoordinates(ptrCamera, ptrUnit->x, ptrUnit->y); int8_t colour = ptrUnit->selected? GFX_BLACK : GFX_GRAY; uint8_t w = UnitGetWidthFromID(id); uint8_t h = UnitGetHeightFromID(id); if (ptrUnit->building != false) { GfxDrawRectangle(cb.x - (w >> 3), cb.y - (h >> 3), w + (w >> 2), h + (h >> 2), colour); } else { GfxDrawCircle(cb.x + (w >> 1), cb.y + (h >> 1), w, colour); } } } uint8_t UnitGetWidthFromID(TYPE_UNIT_ID id) { return GfxGetWidthFromSpriteData(UnitSprTable[id].Data); } uint8_t UnitGetHeightFromID(TYPE_UNIT_ID id) { return GfxGetHeightFromSpriteData(UnitSprTable[id].Data); } uint8_t UnitGetHpFromID(TYPE_UNIT_ID id) { return UnitHPTable[id]; } TYPE_RESOURCES UnitNeededResourcesFromID(TYPE_UNIT_ID id) { return UnitResourcesTable[id]; } void UnitMoveTo(TYPE_UNIT *ptrUnit, uint16_t x, uint16_t y) { ptrUnit->target_x = x; ptrUnit->target_y = y; ptrUnit->walking = true; } void UnitAttackAccepted(TYPE_UNIT *ptrUnit) { ptrUnit->selecting_attack = true; } bool UnitCheckCollisionAgainstOtherUnits(TYPE_COLLISION_BLOCK* cb, TYPE_UNIT *ptrUnitArray, TYPE_UNIT* ptrCurrentUnit) { for (uint8_t i = 0; i < PLAYER_MAX_UNITS_BUILDINGS; i++) { TYPE_UNIT *ptrOtherUnit = &ptrUnitArray[i]; TYPE_COLLISION_BLOCK ocb = {.x = ptrOtherUnit->x, .y = ptrOtherUnit->y, .w = UnitGetWidthFromID(ptrOtherUnit->id), .h = UnitGetHeightFromID(ptrOtherUnit->id)}; if (ptrOtherUnit->alive == false) { continue; } if (ptrOtherUnit == ptrCurrentUnit) { /* We are referring to the same TYPE_UNIT instance. Discard. */ continue; } if (SystemCollisionCheck(cb, &ocb) != false) { return true; } } return false; } void UnitHandler(TYPE_UNIT* unitArray, size_t sz) { size_t i; for (i = 0; i < sz; i++) { TYPE_UNIT *ptrUnit = &unitArray[i]; if (ptrUnit->alive == false) { continue; } bool bMoving = true; if (ptrUnit->walking != false) { int16_t x_dist = ptrUnit->target_x - (ptrUnit->x + (UnitGetWidthFromID(ptrUnit->id) >> 1)); int16_t y_dist = ptrUnit->target_y - (ptrUnit->y + (UnitGetHeightFromID(ptrUnit->id) >> 1)); uint8_t unit_speed = UnitSpeedTable[ptrUnit->id]; int8_t x_d = 0; int8_t y_d = 0; if ( (uint16_t)abs(x_dist) > (uint16_t)(abs(y_dist) << 2) ) /* Add some hysteresis so unit does not change constantly its direction */ { if (x_dist >= (int16_t)unit_speed) { x_d = unit_speed; ptrUnit->dir = DIRECTION_RIGHT; } else if (x_dist <= (int16_t)-unit_speed) { x_d = (int8_t)-unit_speed; ptrUnit->dir = DIRECTION_LEFT; } else { bMoving = false; } } else { if (y_dist >= (int16_t)unit_speed) { y_d = unit_speed; ptrUnit->dir = DIRECTION_DOWN; } else if (y_dist <= (int16_t)-unit_speed) { y_d = (int8_t)-unit_speed; ptrUnit->dir = DIRECTION_UP; } else { bMoving = false; } } /* ~ if ( (ptrUnit->x - UnitSpeedTable[ptrUnit->id]) > ptrUnit->target_x). */ /* ~ {. */ /* ~ ptrUnit->dir = DIRECTION_LEFT;. */ /* ~ x_d = (int8_t)-UnitSpeedTable[ptrUnit->id];. */ /* ~ }. */ /* ~ else if ( (ptrUnit->x + UnitSpeedTable[ptrUnit->id]) < ptrUnit->target_x). */ /* ~ {. */ /* ~ ptrUnit->dir = DIRECTION_RIGHT;. */ /* ~ x_d = (int8_t)UnitSpeedTable[ptrUnit->id];. */ /* ~ }. */ /* ~ else if ( (ptrUnit->y - UnitSpeedTable[ptrUnit->id]) > ptrUnit->target_y). */ /* ~ {. */ /* ~ ptrUnit->dir = DIRECTION_UP;. */ /* ~ y_d = (int8_t)-UnitSpeedTable[ptrUnit->id];. */ /* ~ }. */ /* ~ else if ( (ptrUnit->y + UnitSpeedTable[ptrUnit->id]) < ptrUnit->target_y). */ /* ~ {. */ /* ~ ptrUnit->dir = DIRECTION_DOWN;. */ /* ~ y_d = (int8_t)UnitSpeedTable[ptrUnit->id];. */ /* ~ }. */ /* ~ else. */ /* ~ {. */ /* ~ bMoving = false;. */ /* ~ }. */ if (ptrUnit->walking != false) { TYPE_COLLISION_BLOCK cu = { .x = ptrUnit->x + x_d, .y = ptrUnit->y + y_d, .w = UnitGetWidthFromID(ptrUnit->id), .h = UnitGetHeightFromID(ptrUnit->id) }; if (UnitCheckCollisionAgainstOtherUnits(&cu, unitArray, ptrUnit) == true) { switch (ptrUnit->dir) { case DIRECTION_LEFT: /* Fall through. */ case DIRECTION_RIGHT: y_d = -x_d; x_d = 0; ptrUnit->dir = y_d > 0? DIRECTION_DOWN: DIRECTION_UP; break; case DIRECTION_UP: /* Fall through. */ case DIRECTION_DOWN: x_d = y_d; y_d = 0; ptrUnit->dir = x_d > 0? DIRECTION_RIGHT: DIRECTION_LEFT; break; } } } ptrUnit->walking = bMoving; if (ptrUnit->walking != false) { /* If no collision is detected, keep moving to the new position */ ptrUnit->x += x_d; ptrUnit->y += y_d; } } } } uint8_t UnitGetAvailableActions(TYPE_UNIT *ptrUnit) { return UnitActionsTable[ptrUnit->id]; } const char* UnitGetActionString(UNIT_ACTION action) { return UnitActionsTable_Level[action]; }