/* Slangin' Author: Rudis Muiznieks License: WTFPL */ #include #include "src/fonts/Font3x5.h" #include "src/fonts/Font4x6.h" Arduboy2 arduboy; Font4x6 font4x6 = Font4x6(); Font3x5 font3x5 = Font3x5(); enum GameState { STATE_TITLE = 0, STATE_TURN_MENU, STATE_JET_MENU, STATE_BUY_MENU, STATE_SELL_MENU, STATE_SHARK_MENU, STATE_BANK_MENU, STATE_FIGHT_MENU, STATE_INVENTORY, STATE_INFO_DIALOG, STATE_BUY_GUN_DIALOG, STATE_BUY_COAT_DIALOG, STATE_DO_WEED_DIALOG, STATE_HEAL_DIALOG, STATE_BUY_QTY_INPUT, STATE_SELL_QTY_INPUT, STATE_PAY_LOAN_INPUT, STATE_BORROW_INPUT, STATE_DEPOSIT_INPUT, STATE_WITHDRAW_INPUT }; enum GameLocation { LOC_BRONX = 0, LOC_GHETTO, LOC_CENTRAL_PARK, LOC_MANHATTEN, LOC_CONEY_ISLAND, LOC_BROOKLYN }; enum Drug { DRUG_COCAINE = 0, DRUG_HEROINE, DRUG_ACID, DRUG_WEED, DRUG_SPEED, DRUG_LUDES }; enum DialogType { DIALOG_INFO = 0, DIALOG_YESNO, DIALOG_AMOUNT }; const uint8_t PROGMEM spriteDollar[] = { 5, 5, 0x12, 0x15, 0x1f, 0x15, 0x09, }; const uint8_t PROGMEM spriteAdjust[] = { 5, 8, 0x24, 0x66, 0xe7, 0x66, 0x24, }; /**************/ /* game state */ /**************/ bool screenInitialized; GameState sGameState; GameState sPreviousGameState; int sCurrentDay; int sDrugPrices[6]; int sRandomEvent; int sCurrentDrug; long sCurrentQty; long sQtyMax; /****************/ /* player state */ /****************/ long pMoney; long pLoanAmount; long pSavingsAmount; GameLocation pLocation; int pGuns; int pCapacity; int pHealth; int pInventory[6]; String menu[8]; int menuLength = 0; bool menuCols = false; bool menuSmall = false; int menuSelected = 0; String dialog[5]; int dialogLength; bool dialogSmall = false; /***********************/ /* game setup and loop */ /***********************/ void setup() { sGameState = STATE_TITLE; screenInitialized = false; arduboy.begin(); arduboy.setFrameRate(15); arduboy.initRandomSeed(); } void loop() { if (!(arduboy.nextFrame())) return; arduboy.pollButtons(); // draw screen if needed if (!screenInitialized) { arduboy.clear(); menuSelected = 0; switch (sGameState) { case STATE_TITLE: arduboy.setCursor(25, 10); arduboy.print(F("Slangin' v0.9")); menu[0] = F("New Game"); menuLength = 1; menuSmall = false; menuCols = true; drawMenu(menuSmall, menuCols, menuLength, menu); drawMenuIndicator(menuSelected, menuSmall, menuCols, menuLength, menu, false); break; case STATE_TURN_MENU: drawStatusBar(); drawTitle(lookupLocation(pLocation)); menu[0] = F("Trenchcoat"); menu[1] = F("Buy Drugs"); menu[2] = F("Sell Drugs"); menu[3] = F("Jet"); if (pLocation == LOC_BRONX) { menu[4] = F("Loan Shark"); menu[5] = F("Bank"); menuLength = 6; } else { menuLength = 4; } menuSmall = false; menuCols = true; drawMenu(menuSmall, menuCols, menuLength, menu); drawMenuIndicator(menuSelected, menuSmall, menuCols, menuLength, menu, false); break; case STATE_INVENTORY: drawStatusBar(); drawTitle("Trenchcoat"); buildDrugMenu(pInventory); drawMenu(menuSmall, menuCols, menuLength, menu); break; case STATE_BUY_MENU: case STATE_SELL_MENU: drawStatusBar(); if (sGameState == STATE_BUY_MENU) drawTitle("Buy Drugs"); else drawTitle("Sell Drugs"); buildDrugMenu(sDrugPrices); drawMenu(menuSmall, menuCols, menuLength, menu); drawMenuIndicator(menuSelected, menuSmall, menuCols, menuLength, menu, false); break; case STATE_JET_MENU: { String title = F("Jet from "); title = title + lookupLocation(pLocation); drawStatusBar(); drawTitle(title); for (int i = 0; i < 5; i++) { int loc = i >= pLocation ? i + 1 : i; menu[i] = lookupLocation(loc); } menuLength = 5; menuSmall = true; menuCols = true; drawMenu(menuSmall, menuCols, menuLength, menu); drawMenuIndicator(menuSelected, menuSmall, menuCols, menuLength, menu, false); } break; case STATE_SHARK_MENU: drawStatusBar(); drawTitle("Loan Shark"); menu[0] = F("Repay Loan"); menu[1] = F("Borrow Money"); menuLength = 2; menuSmall = false; menuCols = false; drawMenu(menuSmall, menuCols, menuLength, menu); drawMenuIndicator(menuSelected, menuSmall, menuCols, menuLength, menu, false); break; case STATE_BANK_MENU: drawStatusBar(); drawTitle("Bank"); menu[0] = F("Deposit Money"); menu[1] = F("Withdraw Money"); menuLength = 2; menuSmall = false; menuCols = false; drawMenu(menuSmall, menuCols, menuLength, menu); drawMenuIndicator(menuSelected, menuSmall, menuCols, menuLength, menu, false); break; case STATE_INFO_DIALOG: drawDialog(); break; case STATE_BUY_QTY_INPUT: case STATE_SELL_QTY_INPUT: dialog[0] = sGameState == STATE_BUY_QTY_INPUT ? F("Buy ") : F("Sell "); dialog[0] += lookupDrug(sCurrentDrug); dialog[1] = F(""); dialog[2] = F(""); dialog[3] = F("Cost"); dialog[4] = F("You have"); dialogLength = 5; dialogSmall = true; drawDialog(); drawMoney(dialog[0].length() * 5 + 15, 10, sDrugPrices[sCurrentDrug]); drawMoney(35, 37, sCurrentQty * sDrugPrices[sCurrentDrug]); drawMoney(55, 46, pMoney); drawQtySelector(F("Qty")); break; } screenInitialized = true; } // handle user input // menu screens switch (sGameState) { case STATE_TITLE: case STATE_TURN_MENU: case STATE_JET_MENU: case STATE_BUY_MENU: case STATE_SELL_MENU: case STATE_FIGHT_MENU: case STATE_SHARK_MENU: case STATE_BANK_MENU: { if (arduboy.justPressed(UP_BUTTON) || arduboy.justPressed(DOWN_BUTTON) || arduboy.justPressed(LEFT_BUTTON) || arduboy.justPressed(RIGHT_BUTTON) || arduboy.justPressed(A_BUTTON) || arduboy.justPressed(B_BUTTON)) { drawMenuIndicator(menuSelected, menuSmall, menuCols, menuLength, menu, true); int col1Count = round(menuLength / 2.0); int inCol = menuSelected < col1Count ? 1 : 2; if (arduboy.justPressed(DOWN_BUTTON)) { menuSelected++; if (menuSelected >= menuLength) menuSelected = menuLength - 1; } else if (arduboy.justPressed(UP_BUTTON)) { menuSelected--; if (menuSelected < 0) menuSelected = 0; } else if (arduboy.justPressed(RIGHT_BUTTON)) { if (menuCols && inCol == 1) { menuSelected += col1Count; } else { menuSelected++; } if (menuSelected >= menuLength) menuSelected = menuLength - 1; } else if (arduboy.justPressed(LEFT_BUTTON)) { if (menuCols && inCol == 2) { menuSelected -= col1Count; } else { menuSelected--; } if (menuSelected < 0) menuSelected = 0; } else if (arduboy.justPressed(A_BUTTON)) { // advance to next state handleMenuAction(); } else if (arduboy.justPressed(B_BUTTON)) { // return to turn menu from the other menus if (sGameState == STATE_JET_MENU || sGameState == STATE_BUY_MENU || sGameState == STATE_SELL_MENU || sGameState == STATE_SHARK_MENU || sGameState == STATE_BANK_MENU) { screenInitialized = false; sGameState = STATE_TURN_MENU; } else if (sGameState == STATE_BUY_QTY_INPUT) { screenInitialized = false; sGameState = STATE_BUY_MENU; } else if (sGameState == STATE_SELL_QTY_INPUT) { screenInitialized = false; sGameState = STATE_SELL_MENU; } } drawMenuIndicator(menuSelected, menuSmall, menuCols, menuLength, menu, false); } break; } case STATE_INVENTORY: case STATE_INFO_DIALOG: checkBackedOut(); break; case STATE_SELL_QTY_INPUT: case STATE_BUY_QTY_INPUT: checkBackedOut(); if (arduboy.pressed(UP_BUTTON)) { screenInitialized = false; sCurrentQty++; } else if (arduboy.pressed(RIGHT_BUTTON)) { screenInitialized = false; sCurrentQty += 10; } else if (arduboy.pressed(DOWN_BUTTON)) { screenInitialized = false; sCurrentQty--; } else if (arduboy.pressed(LEFT_BUTTON)) { screenInitialized = false; sCurrentQty -= 10; } if (sCurrentQty > sQtyMax) sCurrentQty = sQtyMax; if (sCurrentQty < 0) sCurrentQty = 0; break; } arduboy.display(); } /***********************/ /* screen draw heplers */ /***********************/ void drawStatusBar() { if (sGameState == STATE_INVENTORY) { const int chars = numberChars(pCapacity); const int x = 128 - ((chars + 9) * 4); font3x5.setCursor(1, 0); font3x5.print("Guns "); font3x5.print(pGuns); font3x5.setCursor(x, 0); font3x5.print("Capacity "); font3x5.print(pCapacity); } else { const int chars = numberChars(pMoney); const int x = 128 - (chars * 4); if (sGameState == STATE_SHARK_MENU || sGameState == STATE_BANK_MENU) { font3x5.setCursor(1, 0); font3x5.print(sGameState == STATE_SHARK_MENU ? "Loan" : "Acct"); Sprites::drawOverwrite(19, 1, spriteDollar, 0); font3x5.setCursor(25, 0); font3x5.print(sGameState == STATE_SHARK_MENU ? pLoanAmount : pSavingsAmount); } else { font3x5.setCursor(1, 0); font3x5.print("Day"); font3x5.setCursor(14, 0); font3x5.print(sCurrentDay); } Sprites::drawOverwrite(x - 6, 1, spriteDollar, 0); font3x5.setCursor(x, 0); font3x5.print(pMoney); } arduboy.fillRect(0, 8, 128, 1, WHITE); } void drawTitle(const String title) { font4x6.setCursor(6, 12); font4x6.print(title); } void buildDrugMenu(int extra[6]) { int lengths[6]; int col1Max = 0; int col2Max = 0; for (int i = 0; i < 6; i++) { int extraLen = numberChars(extra[i]); lengths[i] = lookupDrug(i).length() + 1 + extraLen; if (i < 3 && lengths[i] > col1Max) col1Max = lengths[i]; if (i >= 3 && lengths[i] > col2Max) col2Max = lengths[i]; } for (int i = 0; i < 6; i++) { int colMax = i < 3 ? col1Max : col2Max; menu[i] = lookupDrug(i) + F(" "); for (int s = 0; s < colMax - lengths[i]; s++) { menu[i] = menu[i] + F(" "); } menu[i] = menu[i] + extra[i]; } menuLength = 6; menuCols = true; menuSmall = col1Max + col2Max > 22; } int numberChars(long num) { int length = 1; while (num /= 10) length++; return length; } void drawDialog() { arduboy.drawRoundRect(0, 0, 128, 64, 5, WHITE); if (dialogSmall) { for (int i = 0; i < dialogLength; i++) { font4x6.setCursor(10, i * 9 + 10); font4x6.print(dialog[i]); } } else { for (int i = 0; i < dialogLength; i++) { arduboy.setCursor(10, i * 10 + 10); arduboy.print(dialog[i]); } } } void drawMoney(const int x, const int y, const long amount) { Sprites::drawOverwrite(x, y + 2, spriteDollar, 0); font4x6.setCursor(x + 6, y); font4x6.print(amount); } void drawQtySelector(String label) { font4x6.setCursor(10, 24); font4x6.print(label); const int labelLength = label.length() * 5; arduboy.drawRect(labelLength + 15, 22, 103 - labelLength, 12, WHITE); font4x6.setCursor(labelLength + 19, 24); font4x6.print(sCurrentQty); Sprites::drawOverwrite(111, 24, spriteAdjust, 0); }