/* ************************************** * Includes * * **************************************/ #include "Player.h" #include "Pad.h" #include "Unit.h" #include "Gameplay.h" /* ************************************** * Defines * * **************************************/ #define ACCEPT_UNIT_BUILDING_OPTIONS_FRAMES 5 #define MAX_SELECTION_DIST 400 #define NO_SELECTION -1 /* ************************************** * Local variables * * **************************************/ Player::Player(void) { } Player::~Player(void) { } void Player::Init(void) { enum { DEFAULT_WOOD = 1000, DEFAULT_GOLD = 1000, DEFAULT_FOOD = 1000 }; uint8_t i; unit_i = 0; selectedUnitCandidate = NO_SELECTION; CameraInit(&Camera); UnitInit(); for (i = 0; i < PLAYER_MAX_UNITS_BUILDINGS; i++) { memset(&units[i], 0, sizeof(TYPE_UNIT)); } TYPE_COLLISION_BLOCK cb; Resources.Wood = DEFAULT_WOOD; Resources.Gold = DEFAULT_GOLD; Resources.Food = DEFAULT_FOOD; cb.x = SystemRand(0, 24); cb.y = SystemRand(0, 24); createUnit(TOWN_CENTER, cb); cb.x = SystemRand(48, 56); cb.y = SystemRand(48, 56); cb.w = UnitGetWidthFromID(PEASANT); cb.h = UnitGetHeightFromID(PEASANT); showActionsMenu_counter = 0; showActionsMenu_index = 0; showActionsMenu = false; anyUnitSelected = false; createUnit(PEASANT, cb); } void Player::showHealth(uint8_t hp) { enum { HP_TEXT_X = 4, HP_TEXT_Y = Y_SCREEN_RESOLUTION - 4, }; char str[8]; str[0] = 'H'; str[1] = 'P'; str[2] = '='; GfxFillRectangle(0, Y_SCREEN_RESOLUTION - 5, X_SCREEN_RESOLUTION, 8, GFX_WHITE); Systemitoa(&str[3], sizeof(str) - 3, hp); GfxPrintTextFont(str, font3x3, HP_TEXT_X, HP_TEXT_Y); } void Player::DrawHandler(void) { enum { PROGRESS_BAR_X = X_SCREEN_RESOLUTION - 16, PROGRESS_BAR_Y = Y_SCREEN_RESOLUTION - 4, PROGRESS_BAR_W = ACCEPT_UNIT_BUILDING_OPTIONS_FRAMES << 1, PROGRESS_BAR_H = 3, }; uint8_t i; bool bAnyoneSelected = false; for (i = 0; i < PLAYER_MAX_UNITS_BUILDINGS; i++) { TYPE_UNIT* u = &units[i]; if (u->alive == false) { continue; } bool selected = false; if (selectedUnitCandidate != NO_SELECTION) { selected = (i == selectedUnitCandidate); } UnitDraw(u, &Camera, selected); if ( (u->selected != false) && (bAnyoneSelected == false) ) { bAnyoneSelected = true; showHealth(u->hp); } } if (human != false) { if (bAnyoneSelected != false) { GfxDrawRectangle(PROGRESS_BAR_X, PROGRESS_BAR_Y, PROGRESS_BAR_W, PROGRESS_BAR_H, GFX_BLACK); if (showActionsMenu_counter != 0) { GfxFillRectangle(PROGRESS_BAR_X, PROGRESS_BAR_Y, showActionsMenu_counter << 1, PROGRESS_BAR_H, GFX_BLACK); } } ActionsMenu(); ShowResources(); } } void Player::ShowResources(void) { char str[8]; uint8_t i; str[1] = '='; gb.display.setColor(GFX_GRAY); gb.display.fillRect(0, 0, X_SCREEN_RESOLUTION, 8); str[0] = 'W'; Systemitoa(&str[2], 6, Resources.Wood); GfxPrintTextFont(str, font3x3, 8, 1); str[0] = 'G'; Systemitoa(&str[2], 6, Resources.Gold); GfxPrintTextFont(str, font3x3, 2, 5); str[0] = 'F'; Systemitoa(&str[2], 6, Resources.Food); GfxPrintTextFont(str, font3x3, 42, 1); Systemitoa(str, 3, unit_i); for (i = 0; i < 3; i++) { if (str[i] == '\0') { break; } } str[i++] = '/'; Systemitoa(&str[i], sizeof(str) - i, PLAYER_MAX_UNITS_BUILDINGS); //~ snprintf(str, sizeof(str), "%d/%d", unit_i, PLAYER_MAX_UNITS_BUILDINGS); GfxPrintTextFont(str, font3x3, 42, 5); } bool Player::checkNewBuildingPosition(TYPE_COLLISION_BLOCK * cb) { uint8_t i; TYPE_COLLISION_BLOCK bldgCB; bool success; static uint8_t max_tries = 0; for (i = 0; i < PLAYER_MAX_UNITS_BUILDINGS; i++) { TYPE_UNIT* ptrUnit = &units[i]; success = false; if (ptrUnit->building == false) { continue; } bldgCB.x = ptrUnit->x; bldgCB.y = ptrUnit->y; bldgCB.w = UnitGetWidthFromID(ptrUnit->id); bldgCB.h = UnitGetHeightFromID(ptrUnit->id); if (SystemCollisionCheck(cb, &bldgCB) != false) { success = false; } else { success = true; } if (success == false) { cb->x = SystemRand(0, 128); cb->y = SystemRand(0, 128); if (++max_tries < 16) { if (checkNewBuildingPosition(cb) == false) { return false; } else { return true; } } else { return false; } } } max_tries = 0; return true; } void Player::createUnit(TYPE_UNIT_ID id, TYPE_COLLISION_BLOCK cb) { TYPE_RESOURCES res = UnitNeededResourcesFromID(id); if ( (Resources.Food < res.Food) || (Resources.Wood < res.Wood) || (Resources.Gold < res.Gold) ) { GfxPrintText_Flash(F("Insuff. resources")); return; } if (unit_i < PLAYER_MAX_UNITS_BUILDINGS) { TYPE_UNIT* ptrNewUnit = &units[unit_i++]; ptrNewUnit->id = id; ptrNewUnit->x = cb.x; ptrNewUnit->y = cb.y; ptrNewUnit->hp = UnitGetHpFromID(id); ptrNewUnit->alive = true; ptrNewUnit->building = (id > MAX_UNIT_ID); /* Substract resources from player */ Resources.Wood -= res.Wood; Resources.Gold -= res.Gold; Resources.Food -= res.Food; } else { GfxPrintText_Flash(F("Popul. limit")); } } TYPE_COLLISION_BLOCK Player::GetCursorPos(void) { TYPE_COLLISION_BLOCK cb; cb.x = (X_SCREEN_RESOLUTION >> 1) - 4 - Camera.X_Offset; cb.y = (Y_SCREEN_RESOLUTION >> 1) - 4 - Camera.Y_Offset; cb.w = 8; cb.h = 8; return cb; } void Player::UnitBuildingSelection(void) { uint16_t i; uint32_t nearest_unit_dist = 0xFFFFFFFF; // Set maximum value uint32_t dist; int8_t nearest_unit = NO_SELECTION; for (i = 0; i < PLAYER_MAX_UNITS_BUILDINGS; i++) { TYPE_UNIT* u = &units[i]; if ( (u->alive == false) || (u->selected != false) ) { continue; } TYPE_COLLISION_BLOCK cursor_cb = GetCursorPos(); TYPE_COLLISION_BLOCK u_cb = {u->x, u->y, UnitGetWidthFromID(u->id), UnitGetHeightFromID(u->id) }; uint16_t dist_x = (u_cb.x + (u_cb.w >> 1) - cursor_cb.x); uint16_t dist_y = (u_cb.y + (u_cb.h >> 1) - cursor_cb.y); dist = (dist_x * dist_x) + (dist_y * dist_y); if (dist < nearest_unit_dist) { nearest_unit_dist = dist; nearest_unit = (int8_t)i; } } selectedUnitCandidate = nearest_unit; } void Player::ActionsMenu(void) { if (showActionsMenu != false) { for (uint8_t i = 0; i < PLAYER_MAX_UNITS_BUILDINGS; i++) { TYPE_UNIT* ptrUnit = &units[i]; if (ptrUnit->selected != false) { uint8_t availableActions = UnitGetAvailableActions(ptrUnit); if (!(availableActions & (1 << showActionsMenu_index) ) ) { IncreaseShowActionsMenuIndex(); } UNIT_ACTION action = (UNIT_ACTION)(showActionsMenu_index); const char* str = UnitGetActionString(action); GfxPrintTextFont(str, font3x3, 40, Y_SCREEN_RESOLUTION - 4); break; } } } } void Player::Handler(void) { CameraSetLock(&Camera, showActionsMenu); CameraHandler(&Camera); UnitBuildingSelection(); UnitHandler(units, sizeof(units) / sizeof(units[0])); ButtonHandler(); } void Player::ButtonHandler(void) { if (PadButtonPressed(PAD_A) != false) { ButtonAPressed(); } else if (PadButtonReleased(PAD_A) != false) { ButtonAReleased(); } else if (PadButtonPressed(PAD_B) != false) { ButtonBPressed(); } else if (PadButtonReleased(PAD_B) != false) { ButtonBReleased(); } else if (PadButtonReleased(PAD_LEFT) != false) { ButtonLeftReleased(); } else if (PadButtonReleased(PAD_RIGHT) != false) { ButtonRightReleased(); } } void Player::ButtonAPressed(void) { // Only increase progress bar when any unit has been previously selected if (anyUnitSelected != false) { if (showActionsMenu == false) { if (showActionsMenu_counter < ACCEPT_UNIT_BUILDING_OPTIONS_FRAMES) { showActionsMenu_counter++; } } } } void Player::ButtonAReleased(void) { if (showActionsMenu_counter < ACCEPT_UNIT_BUILDING_OPTIONS_FRAMES) { if (selectedUnitCandidate != NO_SELECTION) { // When actions menu is not active, select unit if // a candidate is present TYPE_UNIT* ptrUnit = &units[selectedUnitCandidate]; ptrUnit->selected = true; anyUnitSelected = true; showActionsMenu_index = 0; } showActionsMenu_counter = 0; } else if (showActionsMenu != false) { uint8_t i = 0; for (i = 0; i < PLAYER_MAX_UNITS_BUILDINGS; i++) { TYPE_UNIT* ptrUnit = &units[i]; if (ptrUnit->selected != false) { showActionsMenu_counterLevel1 = 0; switch (showActionsMenu_index) { case ACTION_CREATE_PEASANT: ActionCreateUnit(ptrUnit, PEASANT); break; case ACTION_CREATE_SOLDIER: ActionCreateUnit(ptrUnit, SOLDIER); break; case ACTION_BUILD_BARRACKS: ActionCreateBuilding(ptrUnit, BARRACKS); break; case ACTION_BUILD_TOWER_CENTER: ActionCreateBuilding(ptrUnit, TOWN_CENTER); break; default: break; } break; } } } showActionsMenu = (showActionsMenu_counter < ACCEPT_UNIT_BUILDING_OPTIONS_FRAMES)? false: true; } void Player::ActionCreateUnit(TYPE_UNIT* ptrUnit, TYPE_UNIT_ID unit) { uint8_t w = UnitGetWidthFromID(ptrUnit->id); uint8_t h = UnitGetHeightFromID(ptrUnit->id); uint8_t new_pos_x = ptrUnit->x + SystemRand(0, w + (w >> 1)); uint8_t new_pos_y = ptrUnit->y + SystemRand(h, h + (h >> 1)); TYPE_COLLISION_BLOCK cb = {.x = new_pos_x, .y = new_pos_y}; createUnit(unit, cb); } void Player::ActionCreateBuilding(TYPE_UNIT* ptrUnit, TYPE_UNIT_ID bldg) { TYPE_COLLISION_BLOCK cb = GetCursorPos(); for (uint8_t i = 0; i < unit_i; i++) { TYPE_UNIT* ptrOtherUnit = &units[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 (SystemCollisionCheck(&cb, &ocb) != false) { GfxPrintText_Flash(F("Cannot build here")); return; } } createUnit(bldg, cb); } void Player::ButtonBPressed(void) { enum { CANCEL_SELECTION_FRAMES = 5 }; if (anyUnitSelected != false) { if (unselectUnits_counter < CANCEL_SELECTION_FRAMES) { unselectUnits_counter++; } else { if (anyUnitSelected != false) { uint8_t i; /* Cancel selection of all units */ for (i = 0; i < PLAYER_MAX_UNITS_BUILDINGS; i++) { TYPE_UNIT* ptrUnit = &units[i]; if (ptrUnit->selected != false) { ptrUnit->selected = false; } } /* Reset accumulated counter and flags */ unselectUnits_counter = 0; anyUnitSelected = false; showActionsMenu = false; showActionsMenu_counter = 0; } } } } void Player::ButtonBReleased(void) { if (anyUnitSelected != false) { TYPE_COLLISION_BLOCK cursor = GetCursorPos(); uint8_t i; for (i = 0; i < PLAYER_MAX_UNITS_BUILDINGS; i++) { TYPE_UNIT* ptrUnit = &units[i]; if (ptrUnit->selected != false) { if (showActionsMenu == false) { UnitMoveTo(ptrUnit, cursor.x, cursor.y); } else { showActionsMenu = false; showActionsMenu_counter = 0; } } } } /* Reset accumulated counter */ unselectUnits_counter = 0; } void Player::ButtonLeftReleased(void) { if (showActionsMenu != false) { uint8_t i; for (i = 0; i < PLAYER_MAX_UNITS_BUILDINGS; i++) { TYPE_UNIT* ptrUnit = &units[i]; if (ptrUnit->selected != false) { /* We need to iterate over all available actions * for current unit. */ uint8_t availableActions = UnitGetAvailableActions(ptrUnit); for (uint8_t j = showActionsMenu_index - 1; j != showActionsMenu_index ; j--) { if (j > (sizeof(uint8_t) << 3)) { // Maximum index: 7 j = (sizeof(uint8_t) << 3) - 1; } if (availableActions & (1 << j)) { showActionsMenu_index = j; break; } } } } } } void Player::ButtonRightReleased(void) { IncreaseShowActionsMenuIndex(); } void Player::IncreaseShowActionsMenuIndex(void) { if (showActionsMenu != false) { uint8_t i; for (i = 0; i < PLAYER_MAX_UNITS_BUILDINGS; i++) { TYPE_UNIT* ptrUnit = &units[i]; if (ptrUnit->selected != false) { /* We need to iterate over all available actions * for current unit. */ uint8_t availableActions = UnitGetAvailableActions(ptrUnit); for (uint8_t j = showActionsMenu_index + 1; j != showActionsMenu_index ; j++) { if (j >= (sizeof(uint8_t) << 3) ) { j = 0; } if (availableActions & (1 << j)) { showActionsMenu_index = j; break; } } } } } }