slangin/slangin.ino

531 lines
15 KiB
Arduino
Raw Normal View History

2022-06-15 12:47:42 -05:00
/*
2022-06-15 12:51:24 -05:00
Slangin'
Author: Rudis Muiznieks
License: WTFPL
2022-06-15 12:47:42 -05:00
*/
#include <Arduboy2.h>
2022-06-17 18:55:36 -05:00
#include "src/fonts/Font3x5.h"
#include "src/fonts/Font4x6.h"
2022-06-15 12:47:42 -05:00
Arduboy2 arduboy;
2022-06-16 23:25:49 -05:00
Font4x6 font4x6 = Font4x6();
Font3x5 font3x5 = Font3x5();
2022-06-15 12:47:42 -05:00
2022-06-15 18:36:12 -05:00
enum GameState {
STATE_TITLE = 0,
STATE_TURN_MENU,
STATE_JET_MENU,
STATE_BUY_MENU,
STATE_SELL_MENU,
STATE_SHARK_MENU,
STATE_BANK_MENU,
2022-06-18 13:20:04 -05:00
STATE_FIGHT_MENU,
2022-06-18 12:24:23 -05:00
STATE_INVENTORY,
2022-06-15 18:36:12 -05:00
STATE_INFO_DIALOG,
2022-06-18 16:54:40 -05:00
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
2022-06-15 18:36:12 -05:00
};
enum GameLocation {
LOC_BRONX = 0,
2022-06-18 22:55:00 -05:00
LOC_GHETTO = 1,
LOC_CENTRAL_PARK = 2,
LOC_MANHATTEN = 3,
LOC_CONEY_ISLAND = 4,
LOC_BROOKLYN = 5
2022-06-15 18:36:12 -05:00
};
enum Drug {
DRUG_COCAINE = 0,
2022-06-18 22:55:00 -05:00
DRUG_HEROINE = 1,
DRUG_ACID = 2,
DRUG_WEED = 3,
DRUG_SPEED = 4,
DRUG_LUDES = 5
2022-06-15 18:36:12 -05:00
};
2022-06-18 00:05:30 -05:00
const uint8_t PROGMEM spriteDollar[] = {
5, 5,
0x12, 0x15, 0x1f, 0x15, 0x09,
};
2022-06-18 16:54:40 -05:00
const uint8_t PROGMEM spriteAdjust[] = {
5, 8,
0x24, 0x66, 0xe7, 0x66, 0x24,
};
2022-06-16 23:25:49 -05:00
/**************/
/* game state */
/**************/
2022-06-15 18:36:12 -05:00
bool screenInitialized;
2022-06-16 23:25:49 -05:00
GameState sGameState;
2022-06-18 00:05:30 -05:00
GameState sPreviousGameState;
2022-06-15 18:36:12 -05:00
int sCurrentDay;
2022-06-18 00:05:30 -05:00
int sDrugPrices[6];
2022-06-18 16:54:40 -05:00
int sCurrentDrug;
2022-06-18 21:34:58 -05:00
long sCurrentQty;
long sQtyMax;
long sLastDebounce = 0;
2022-06-18 18:17:18 -05:00
short sDebounceCount = 0;
2022-06-18 21:34:58 -05:00
bool sAlreadyBorrowed;
2022-06-16 23:25:49 -05:00
/****************/
/* player state */
/****************/
2022-06-18 21:34:58 -05:00
long pMoney;
long pLoanAmount;
long pSavingsAmount;
2022-06-15 18:36:12 -05:00
GameLocation pLocation;
int pGuns;
int pCapacity;
int pHealth;
2022-06-18 00:05:30 -05:00
int pInventory[6];
2022-06-15 18:36:12 -05:00
2022-06-17 18:55:36 -05:00
String menu[8];
int menuLength = 0;
bool menuCols = false;
bool menuSmall = false;
int menuSelected = 0;
2022-06-18 16:54:40 -05:00
String dialog[5];
int dialogLength;
bool dialogSmall = false;
2022-06-16 23:25:49 -05:00
/***********************/
/* game setup and loop */
/***********************/
2022-06-15 12:47:42 -05:00
void setup() {
2022-06-16 23:25:49 -05:00
sGameState = STATE_TITLE;
2022-06-15 18:36:12 -05:00
screenInitialized = false;
2022-06-15 12:47:42 -05:00
arduboy.begin();
arduboy.setFrameRate(15);
2022-06-18 13:20:04 -05:00
arduboy.initRandomSeed();
2022-06-15 12:47:42 -05:00
}
void loop() {
if (!(arduboy.nextFrame()))
return;
2022-06-18 12:24:23 -05:00
arduboy.pollButtons();
2022-06-18 18:17:18 -05:00
/******************/
/* SCREEN DRAWING */
/******************/
2022-06-15 18:36:12 -05:00
if (!screenInitialized) {
2022-06-16 23:25:49 -05:00
arduboy.clear();
2022-06-18 00:05:30 -05:00
menuSelected = 0;
2022-06-16 23:25:49 -05:00
switch (sGameState) {
2022-06-15 18:36:12 -05:00
case STATE_TITLE:
arduboy.setCursor(25, 10);
arduboy.print(F("Slangin' v0.9"));
2022-06-18 00:05:30 -05:00
menu[0] = F("New Game");
menuLength = 1;
menuSmall = false;
2022-06-18 18:17:18 -05:00
menuCols = false;
2022-06-18 00:05:30 -05:00
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;
2022-06-17 18:55:36 -05:00
menuCols = true;
drawMenu(menuSmall, menuCols, menuLength, menu);
2022-06-17 18:55:36 -05:00
drawMenuIndicator(menuSelected, menuSmall, menuCols, menuLength, menu, false);
2022-06-15 18:36:12 -05:00
break;
2022-06-18 00:05:30 -05:00
case STATE_INVENTORY:
drawStatusBar();
2022-06-18 13:20:04 -05:00
drawTitle("Trenchcoat");
2022-06-18 12:24:23 -05:00
buildDrugMenu(pInventory);
drawMenu(menuSmall, menuCols, menuLength, menu);
2022-06-18 00:05:30 -05:00
break;
2022-06-18 13:20:04 -05:00
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: {
drawStatusBar();
2022-06-18 22:55:00 -05:00
String title = F("Jet from ");
title += lookupLocation(pLocation);
2022-06-18 13:20:04 -05:00
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;
2022-06-18 16:54:40 -05:00
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("");
2022-06-18 18:17:18 -05:00
dialog[3] = sGameState == STATE_BUY_QTY_INPUT ? F("Costs") : F("Price");
2022-06-18 16:54:40 -05:00
dialog[4] = F("You have");
dialogLength = 5;
dialogSmall = true;
drawDialog();
drawMoney(dialog[0].length() * 5 + 15, 10, sDrugPrices[sCurrentDrug]);
2022-06-18 18:17:18 -05:00
if (sGameState == STATE_BUY_QTY_INPUT) drawMoney(55, 46, pMoney);
else {
font4x6.setCursor(55, 46);
font4x6.print(pInventory[sCurrentDrug]);
}
drawQtyInputTotal();
2022-06-18 16:54:40 -05:00
drawQtySelector(F("Qty"));
break;
2022-06-18 21:34:58 -05:00
case STATE_PAY_LOAN_INPUT:
case STATE_BORROW_INPUT:
case STATE_DEPOSIT_INPUT:
case STATE_WITHDRAW_INPUT:
dialog[0] = sGameState == STATE_PAY_LOAN_INPUT
? F("Pay Loan")
: sGameState == STATE_BORROW_INPUT
? F("Borrow Money")
: sGameState == STATE_DEPOSIT_INPUT
? F("Deposit Money")
: F("Withdraw Money");
dialog[1] = F("");
dialog[2] = F("");
dialog[3] = sPreviousGameState == STATE_SHARK_MENU
? F("You owe") : F("Savings");
dialog[4] = F("You have");
dialogLength = 5;
dialogSmall = true;
drawDialog();
drawMoney(50, 37, sPreviousGameState == STATE_SHARK_MENU
? pLoanAmount : pSavingsAmount);
drawMoney(55, 46, pMoney);
drawQtySelector(F("Amt"));
break;
2022-06-15 18:36:12 -05:00
}
2022-06-17 18:55:36 -05:00
screenInitialized = true;
}
2022-06-18 18:17:18 -05:00
/******************/
/* INPUT HANDLING */
/******************/
2022-06-18 12:24:23 -05:00
// menu screens
2022-06-17 18:55:36 -05:00
switch (sGameState) {
case STATE_TITLE:
2022-06-18 00:05:30 -05:00
case STATE_TURN_MENU:
case STATE_JET_MENU:
case STATE_BUY_MENU:
case STATE_SELL_MENU:
case STATE_FIGHT_MENU:
case STATE_SHARK_MENU:
2022-06-18 12:24:23 -05:00
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)) {
2022-06-17 18:55:36 -05:00
menuSelected++;
2022-06-18 12:24:23 -05:00
if (menuSelected >= menuLength) menuSelected = menuLength - 1;
2022-06-18 00:05:30 -05:00
2022-06-18 12:24:23 -05:00
} else if (arduboy.justPressed(UP_BUTTON)) {
2022-06-17 18:55:36 -05:00
menuSelected--;
2022-06-18 12:24:23 -05:00
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;
2022-06-18 00:05:30 -05:00
2022-06-18 12:24:23 -05:00
} else if (arduboy.justPressed(LEFT_BUTTON)) {
if (menuCols && inCol == 2) {
menuSelected -= col1Count;
} else {
menuSelected--;
}
if (menuSelected < 0) menuSelected = 0;
2022-06-18 00:05:30 -05:00
2022-06-18 12:24:23 -05:00
} else if (arduboy.justPressed(A_BUTTON)) {
2022-06-18 00:05:30 -05:00
2022-06-18 12:24:23 -05:00
// 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) {
2022-06-18 13:20:04 -05:00
screenInitialized = false;
2022-06-18 12:24:23 -05:00
sGameState = STATE_TURN_MENU;
}
2022-06-18 00:05:30 -05:00
}
2022-06-18 12:24:23 -05:00
drawMenuIndicator(menuSelected, menuSmall, menuCols, menuLength, menu, false);
2022-06-17 18:55:36 -05:00
}
break;
2022-06-18 12:24:23 -05:00
}
2022-06-18 16:54:40 -05:00
case STATE_INVENTORY:
case STATE_INFO_DIALOG:
2022-06-18 18:17:18 -05:00
checkBackedOut(true);
2022-06-18 16:54:40 -05:00
break;
case STATE_SELL_QTY_INPUT:
case STATE_BUY_QTY_INPUT:
2022-06-18 21:34:58 -05:00
case STATE_PAY_LOAN_INPUT:
case STATE_BORROW_INPUT:
case STATE_WITHDRAW_INPUT:
case STATE_DEPOSIT_INPUT:
2022-06-18 18:17:18 -05:00
if (!checkBackedOut(false)) {
if (arduboy.justPressed(A_BUTTON)) {
2022-06-18 21:34:58 -05:00
switch (sGameState) {
case STATE_SELL_QTY_INPUT:
pInventory[sCurrentDrug] -= sCurrentQty;
pMoney += sCurrentQty * sDrugPrices[sCurrentDrug];
break;
case STATE_BUY_QTY_INPUT:
pInventory[sCurrentDrug] += sCurrentQty;
pMoney -= sCurrentQty * sDrugPrices[sCurrentDrug];
break;
case STATE_PAY_LOAN_INPUT:
pLoanAmount -= sCurrentQty;
pMoney -= sCurrentQty;
break;
case STATE_BORROW_INPUT:
pLoanAmount += sCurrentQty;
pMoney += sCurrentQty;
sAlreadyBorrowed = true;
break;
case STATE_WITHDRAW_INPUT:
pSavingsAmount -= sCurrentQty;
pMoney += sCurrentQty;
break;
case STATE_DEPOSIT_INPUT:
pSavingsAmount += sCurrentQty;
pMoney -= sCurrentQty;
break;
2022-06-18 18:17:18 -05:00
}
screenInitialized = false;
sGameState = sPreviousGameState;
} else {
2022-06-18 21:34:58 -05:00
const unsigned long currentMillis = millis();
const int incAmount = sGameState == STATE_BUY_QTY_INPUT ||
sGameState == STATE_SELL_QTY_INPUT ? 1 : 10;
if (currentMillis - sLastDebounce > (sDebounceCount < 2 ? 500 : 100)) {
2022-06-18 18:17:18 -05:00
if (sDebounceCount < 2) sDebounceCount++;
if (arduboy.pressed(UP_BUTTON)) {
sLastDebounce = currentMillis;
2022-06-18 21:34:58 -05:00
if (sCurrentQty <= sQtyMax - incAmount) sCurrentQty += incAmount;
else sCurrentQty = sQtyMax;
2022-06-18 18:17:18 -05:00
drawQtyInputTotal();
} else if (arduboy.pressed(RIGHT_BUTTON)) {
sLastDebounce = currentMillis;
2022-06-18 21:34:58 -05:00
if (sCurrentQty <= sQtyMax - incAmount * 10) sCurrentQty += incAmount * 10;
2022-06-18 18:17:18 -05:00
else sCurrentQty = sQtyMax;
drawQtyInputTotal();
} else if (arduboy.pressed(DOWN_BUTTON)) {
sLastDebounce = currentMillis;
2022-06-18 21:34:58 -05:00
if (sCurrentQty >= incAmount) sCurrentQty -= incAmount;
else sCurrentQty = 0;
2022-06-18 18:17:18 -05:00
drawQtyInputTotal();
} else if (arduboy.pressed(LEFT_BUTTON)) {
sLastDebounce = currentMillis;
2022-06-18 21:34:58 -05:00
if (sCurrentQty >= incAmount * 10) sCurrentQty -= incAmount * 10;
2022-06-18 18:17:18 -05:00
else sCurrentQty = 0;
drawQtyInputTotal();
} else {
sLastDebounce = 0;
sDebounceCount = 0;
}
}
}
2022-06-18 00:05:30 -05:00
}
break;
2022-06-16 23:25:49 -05:00
}
2022-06-17 18:55:36 -05:00
arduboy.display();
2022-06-16 23:25:49 -05:00
}
/***********************/
/* screen draw heplers */
/***********************/
2022-06-18 00:05:30 -05:00
void drawStatusBar() {
2022-06-18 12:24:23 -05:00
if (sGameState == STATE_INVENTORY) {
2022-06-18 18:17:18 -05:00
const int remaining = playerCapacityRemaining();
const int chars = numberChars(remaining);
2022-06-18 12:24:23 -05:00
const int x = 128 - ((chars + 9) * 4);
font3x5.setCursor(1, 0);
font3x5.print("Guns ");
font3x5.print(pGuns);
font3x5.setCursor(x, 0);
font3x5.print("Capacity ");
2022-06-18 18:17:18 -05:00
font3x5.print(remaining);
2022-06-18 12:24:23 -05:00
} else {
const int chars = numberChars(pMoney);
const int x = 128 - (chars * 4);
2022-06-18 13:20:04 -05:00
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);
}
2022-06-18 12:24:23 -05:00
Sprites::drawOverwrite(x - 6, 1, spriteDollar, 0);
font3x5.setCursor(x, 0);
font3x5.print(pMoney);
}
2022-06-18 00:05:30 -05:00
arduboy.fillRect(0, 8, 128, 1, WHITE);
}
2022-06-17 08:58:56 -05:00
void drawTitle(const String title) {
2022-06-18 12:24:23 -05:00
font4x6.setCursor(6, 12);
font4x6.print(title);
2022-06-15 12:47:42 -05:00
}
2022-06-18 12:24:23 -05:00
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++) {
2022-06-18 22:55:00 -05:00
menu[i] += F(" ");
2022-06-18 12:24:23 -05:00
}
2022-06-18 22:55:00 -05:00
menu[i] += extra[i];
2022-06-18 12:24:23 -05:00
}
menuLength = 6;
menuCols = true;
menuSmall = col1Max + col2Max > 22;
}
2022-06-18 21:34:58 -05:00
int numberChars(long num) {
2022-06-18 12:24:23 -05:00
int length = 1;
while (num /= 10) length++;
return length;
}
2022-06-18 16:54:40 -05:00
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]);
}
}
}
2022-06-18 21:34:58 -05:00
void drawMoney(const int x, const int y, const long amount) {
2022-06-18 16:54:40 -05:00
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);
2022-06-18 21:34:58 -05:00
arduboy.drawRect(30, 22, 88, 12, WHITE);
drawQtySelectorAmount();
2022-06-18 18:17:18 -05:00
Sprites::drawOverwrite(111, 24, spriteAdjust, 0);
}
2022-06-18 21:34:58 -05:00
void drawQtySelectorAmount() {
arduboy.fillRect(31, 23, 80, 10, BLACK);
font4x6.setCursor(35, 24);
2022-06-18 16:54:40 -05:00
font4x6.print(sCurrentQty);
2022-06-18 18:17:18 -05:00
}
void drawQtyInputTotal() {
2022-06-18 21:34:58 -05:00
if (sGameState == STATE_SELL_QTY_INPUT ||
sGameState == STATE_BUY_QTY_INPUT) {
arduboy.fillRect(40, 37, 87, 7, BLACK);
drawMoney(40, 37, sCurrentQty * sDrugPrices[sCurrentDrug]);
}
drawQtySelectorAmount();
2022-06-18 16:54:40 -05:00
}