Compare commits

...

10 Commits
v1.1.0 ... main

5 changed files with 94 additions and 21 deletions

View File

@ -2,6 +2,10 @@
DrugWars (DopeWars) clone for the [Arduboy](https://www.arduboy.com).
You hit the streets of New York with $2000 in your pocket and a $5000 debt to the loan shark. Buy low and sell high to pay off your debt as quickly as possible, then spend the rest of the time getting rich to earn a high score. Each day has the possibility for a random event that could help or hinder your progress, and if you hold on to too much product Officer Hardass and his deputies might notice.
You can play either the classic 30 day mode, or an extended mode where you go for 60 days, and high scores for each mode are persisted to the EEPROM so they'll save between reboots.
[Reference implementation.](https://gist.github.com/mattmanning/1002653/b7a1e88479a10eaae3bd5298b8b2c86e16fb4404)
## Building
@ -23,7 +27,7 @@ Your port path (`-p` argument) may differ depending on your system.
## CandyWar
Run `res/candywar.sh` to generate an alternate version of the game if you have delicate sensibilities.
Run `res/candywar.sh` to generate an alternate version of the game with replaced title images and strings if you have delicate sensibilities.
## Resources
@ -32,3 +36,11 @@ Font libraries used:
- [Font4x6](https://github.com/filmote/Font4x6)
Slightly modified to add missing comma and apostrophe characters.
## Screenshots
![slangin-title.png](https://f000.backblazeb2.com/file/img-rdsm-ca/2022/06/22/slangin-title.png)
![slangin-menu.png](https://f000.backblazeb2.com/file/img-rdsm-ca/2022/06/22/slangin-menu.png)
![slangin-drugs.png](https://f000.backblazeb2.com/file/img-rdsm-ca/2022/06/22/slangin-drugs.png)
![slangin-buy.png](https://f000.backblazeb2.com/file/img-rdsm-ca/2022/06/22/slangin-buy.png)
![slangin-hardass.png](https://f000.backblazeb2.com/file/img-rdsm-ca/2022/06/22/slangin-hardass.png)

View File

@ -170,6 +170,7 @@ int getMenuBackOut() {
case STATE_WITHDRAW_INPUT:
return 1;
case STATE_INVENTORY:
case STATE_HIGH_SCORES:
return 2;
case STATE_JET_MENU:
return 3;

View File

@ -10,6 +10,7 @@ mv candywar/slangin.ino candywar/candywar.ino
for file in candywar/*.ino; do
sed -i "s/Slangin'/CandyWar/g" "$file"
sed -i "s/Hit the Streets/Hit the Sweets/" "$file"
sed -i "s/Drugs/Candy/g" "$file"
sed -i "s/drugs/candy/g" "$file"
sed -i "s/Loan Shark/Rich Buddy/g" "$file"

View File

@ -5,6 +5,7 @@ License: WTFPL
*/
#include <Arduboy2.h>
#include <EEPROM.h>
#include "src/Font3x5.h"
#include "src/Font4x6.h"
@ -14,6 +15,7 @@ Font3x5 font3x5 = Font3x5();
enum GameState {
STATE_TITLE = 0,
STATE_HIGH_SCORES,
STATE_TURN_MENU,
STATE_JET_MENU,
STATE_BUY_MENU,
@ -75,21 +77,27 @@ const uint8_t PROGMEM spriteWeed[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x22, 0x32, 0x2a, 0x24, 0x22, 0x10, 0x0a, 0x08, 0x0c, 0x08, 0x12, 0x21, 0x24, 0x2a, 0x32, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
const uint8_t eepStart = EEPROM_STORAGE_SPACE_START + 1;
/**************/
/* game state */
/**************/
bool screenInitialized;
GameState sGameState;
GameState sPreviousGameState;
int sCurrentDay;
short sMaxDays;
short sCurrentDay;
long sDrugPrices[6];
int sCurrentDrug;
short sCurrentDrug;
long sCurrentQty;
long sQtyMax;
long sLastDebounce = 0;
int sDebounceCount = 0;
bool sAlreadyBorrowed;
int sPigs = 0;
short sPigs = 0;
// default high scores
long s30High = 100000;
long s60High = 200000;
/****************/
/* player state */
@ -119,11 +127,23 @@ bool dialogSmall = false;
/***********************/
void setup() {
arduboy.boot();
arduboy.bootLogoSpritesOverwrite();
arduboy.setFrameRate(15);
arduboy.initRandomSeed();
sGameState = STATE_TITLE;
screenInitialized = false;
arduboy.begin();
// read high scores from eeprom
char check[5];
EEPROM.get(eepStart, check);
if (strcmp(check, "slng") == 0) {
EEPROM.get(eepStart + 5, s30High);
EEPROM.get(eepStart + 9, s60High);
} else {
EEPROM.put(eepStart, "slng");
EEPROM.put(eepStart + 5, s30High);
EEPROM.put(eepStart + 9, s60High);
}
}
void loop() {
@ -143,7 +163,7 @@ void loop() {
// if we're entering the turn menu on day 31 then game is over instead
if (sGameState == STATE_TURN_MENU
&& sCurrentDay >= 31) {
&& sCurrentDay >= sMaxDays) {
sGameState = STATE_GAME_OVER;
}
@ -151,18 +171,27 @@ void loop() {
case STATE_TITLE:
Sprites::drawOverwrite(0, 0, spriteWeed, 0);
Sprites::drawOverwrite(98, 0, spriteWeed, 0);
arduboy.setCursor(42, 15);
arduboy.setCursor(42, 10);
arduboy.print(F("Slangin'"));
font3x5.setCursor(103, 58);
font3x5.print(F("v1.1.0"));
menu[0] = F("Hit the Streets!!");
menuLength = 1;
menuSmall = false;
font3x5.print(F("v1.2.0"));
menu[0] = F("Hit the Streets");
menu[1] = F("Extended Game");
menu[2] = F("High Scores");
menuLength = 3;
menuSmall = true;
menuCols = false;
drawMenu(menuSmall, menuCols, menuLength, menu);
drawMenuIndicator(menuSelected, menuSmall, menuCols, menuLength, menu, false);
break;
case STATE_HIGH_SCORES:
drawDialog();
drawMoney(10, 19, s30High);
drawMoney(10, 39, s60High);
sPreviousGameState = STATE_TITLE;
break;
case STATE_TURN_MENU:
drawStatusBar();
drawTitle(lookupLocation(pLocation));
@ -350,17 +379,33 @@ void loop() {
break;
case STATE_GAME_OVER: {
int score = floor(sqrt((pSavingsAmount + pMoney - pLoanAmount) / 31.5));
if (score > 100) score = 100;
dialog[0] = "";
dialog[1] = " Game over!";
dialog[2] = " Your score: ";
dialog[2] += String(score);
dialog[3] = " out of 100.";
dialogSmall = false;
dialogLength = 4;
dialogLength = 3;
sPreviousGameState = STATE_TITLE;
pMoney += pSavingsAmount - pLoanAmount;
for (int i = 0; i < 6; i++) {
pMoney += pInventory[i] * sDrugPrices[i];
}
if (pMoney < 0) {
pMoney = 0;
}
bool newHigh = sMaxDays == 31 ? pMoney > s30High : pMoney > s60High;
dialog[0] = F("Game over! Score:");
dialog[1] = F("");
dialog[2] = newHigh ? F("Old ") : F("");
dialog[2] += F("High Score:");
drawDialog();
drawMoney(10, 19, pMoney);
drawMoney(10, 39, sMaxDays == 31 ? s30High : s60High);
if (newHigh) {
if (sMaxDays == 31) {
s30High = pMoney;
EEPROM.put(eepStart + 5, s30High);
} else {
s60High = pMoney;
EEPROM.put(eepStart + 9, s60High);
}
}
break;
}
@ -445,6 +490,7 @@ void loop() {
break;
}
case STATE_INVENTORY:
case STATE_HIGH_SCORES:
case STATE_INFO_DIALOG:
case STATE_DID_WEED_DIALOG_1:
case STATE_DID_WEED_DIALOG_2:
@ -528,6 +574,7 @@ void loop() {
break;
}
screenInitialized = false;
menuBackOut = getMenuBackOut();
sGameState = sPreviousGameState;
} else {
const unsigned long currentMillis = millis();

View File

@ -5,7 +5,7 @@ Author: Rudis Muiznieks
License: WTFPL
*/
void initializeNewGame() {
void initializeNewGame(int days) {
sGameState = STATE_TURN_MENU;
pMoney = 2000;
pLoanAmount = 5000;
@ -19,6 +19,7 @@ void initializeNewGame() {
pInventory[i] = 0;
}
sMaxDays = days + 1;
sCurrentDay = 0;
incrementDay(false); // start on day 1
}
@ -198,7 +199,18 @@ void handleMenuAction() {
case STATE_TITLE:
switch (menuSelected) {
case 0: // new game
initializeNewGame();
initializeNewGame(30);
break;
case 1: // extended game
initializeNewGame(60);
break;
case 2: // high scores
dialog[0] = F("Normal Game:");
dialog[1] = F("");
dialog[2] = F("Extended Game:");
dialogSmall = false;
dialogLength = 3;
sGameState = STATE_HIGH_SCORES;
break;
}
break;