commit 00e123b43299885895dc8321d5bf71c5ee32d425 Author: Keith Solomon Date: Fri Aug 15 06:25:45 2025 -0500 Initial commit diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..97f6166 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "workbench.colorCustomizations": { + "tree.indentGuidesStroke": "#3d92ec", + "activityBar.background": "#3A2A1B", + "titleBar.activeBackground": "#513B25", + "titleBar.activeForeground": "#FCFAF8" + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..802a9e1 --- /dev/null +++ b/README.md @@ -0,0 +1,31 @@ +# Last Days of Rome + +This is a simple web-based game where you play as a citizen in the failing Roman Empire. The emperor is trying to burn the city to the ground, and you must escape before it's too late. + +## How to Play + +The goal of the game is to escape the city by reaching a **Relocation** score of 10. You lose if your **Flames** or **Desolation** score reaches 10. + +Each turn, you roll the dice to see what happens in the city. The events will affect your scores. + +You can also try to assassinate the emperor. This is a risky move, but if you succeed, you win the game (though you die a martyr). + +## Technical Details + +This project is built using: + +* **Frontend:** HTML, CSS, JavaScript +* **Backend:** PHP + +## How to Run Locally + +To run this project locally, you need a PHP server. You can use a local development environment like XAMPP or MAMP, or you can use the built-in PHP server. + +1. Clone this repository. +2. Start a PHP server in the project directory. For example, you can run the following command: + + ```bash + php -S localhost:8000 + ``` + +3. Open your web browser and go to `http://localhost:8000`. diff --git a/game.php b/game.php new file mode 100644 index 0000000..c6033bb --- /dev/null +++ b/game.php @@ -0,0 +1,146 @@ + 0, + 'desolation' => 0, + 'relocation' => 0, + ]; +} + +$action = $_GET['action'] ?? ''; + +if ($action === 'roll_dice') { + $event = roll_dice(); + $gameState = check_game_state(); + echo json_encode([ + 'scores' => $_SESSION['scores'], + 'event' => $event, + 'gameState' => $gameState, + ]); +} elseif ($action === 'assassinate') { + $assassinationResult = assassinate(); + echo json_encode([ + 'event' => $assassinationResult['event'], + 'gameState' => $assassinationResult['gameState'], + ]); +} elseif ($action === 'reset_game') { + $_SESSION['scores'] = [ + 'flames' => 0, + 'desolation' => 0, + 'relocation' => 0, + ]; + echo json_encode([ + 'scores' => $_SESSION['scores'], + ]); +} + +function roll_dice() { + $roll = rand(1, 6); + $event = ''; + + if ($roll <= 3) { + $event = 'In the Imperial Palace: ' . imperial_palace_event(); + } elseif ($roll <= 5) { + $event = 'Relative Unrest: ' . relative_unrest_event(); + } else { + $_SESSION['scores']['flames']++; + $event = 'The fires spread. +1 Flames.'; + } + return $event; +} + +function imperial_palace_event() { + $roll = rand(1, 6); + $event = ''; + switch ($roll) { + case 1: + $_SESSION['scores']['flames']++; + $event = 'The emperor fiddles with manic glee. +1 Flames.'; + break; + case 2: + $_SESSION['scores']['desolation']++; + $_SESSION['scores']['relocation']++; + $event = 'The emperor raises another horse to the position of senator. +1 Desolation, +1 Relocation.'; + break; + case 3: + $_SESSION['scores']['relocation']++; + $event = 'It\'s execution night at the palace. +1 Relocation.'; + break; + case 4: + $_SESSION['scores']['desolation']++; + $event = 'The emperor screams like a baby. +1 Desolation.'; + break; + case 5: + $_SESSION['scores']['flames']++; + $_SESSION['scores']['relocation']++; + $event = 'The emperor sits in front of the flame and commands it to obey. It does not. +1 Flames, +1 Relocation.'; + break; + case 6: + $_SESSION['scores']['flames']++; + $event = 'Work continues on a house made of pure gold. It keeps melting. +1 Flames.'; + break; + } + return $event; +} + +function relative_unrest_event() { + $roll = rand(1, 6); + $event = ''; + switch ($roll) { + case 1: + $_SESSION['scores']['relocation']++; + $event = 'People complain - this is unacceptable. Then they go about their business. +1 Relocation.'; + break; + case 2: + $_SESSION['scores']['desolation']++; + $event = 'There are no goods at market. +1 Desolation.'; + break; + case 3: + $_SESSION['scores']['relocation']++; + $event = 'You receive a letter asking you to reassert your faith in the emperor. In writing. +1 Relocation.'; + break; + case 4: + $_SESSION['scores']['desolation']++; + $_SESSION['scores']['flames']++; + $event = 'Lions are released onto the streets. +1 Desolation, +1 Flames.'; + break; + case 5: + $_SESSION['scores']['desolation']++; + $event = 'Is Rome really over? +1 Desolation.'; + break; + case 6: + $_SESSION['scores']['flames']++; + $event = 'The burnings will continue until morale improves. +1 Flames.'; + break; + } + return $event; +} + +function assassinate() { + $desolation = $_SESSION['scores']['desolation']; + $total = 0; + for ($i = 0; $i < $desolation; $i++) { + $total += rand(1, 6); + } + + if ($total >= 20) { + return ['event' => 'You have assassinated the emperor! You die a martyr.', 'gameState' => 'win_assassination']; + } else { + return ['event' => 'Your attempt to assassinate the emperor has failed. You are executed.', 'gameState' => 'loss_assassination']; + } +} + +function check_game_state() { + if ($_SESSION['scores']['flames'] >= 10) { + return 'loss_flames'; + } + if ($_SESSION['scores']['desolation'] >= 10) { + return 'loss_desolation'; + } + if ($_SESSION['scores']['relocation'] >= 10) { + return 'win'; + } + return 'ongoing'; +} diff --git a/index.php b/index.php new file mode 100644 index 0000000..774ac69 --- /dev/null +++ b/index.php @@ -0,0 +1,46 @@ + + + + + + Last Days of Rome + + + + +
+ +
+ +
+

Last Days of Rome

+ +
+ + + +
+ +
+

You are a citizen in a relatively successful empire. Aalas, the new emperor has arrived, and he's decided that the best thing for everyone would be to burn it all to the ground with you all still inside it. Nothing to be done but carry on as usual, you suppose. He is, after all, the emperor.

+ +

Each turn, roll the dice to see what happens in the imperial palace and in the city. Your goal is to escape the city (Relocation reaches 10) before you're consumed by the flames (Flames reaches 10) or the empire falls and you die in the collapse (Desolation reaches 10).

+ +

You can also attempt to assassinate the emperor. This is risky, but if you succeed, you win the game (you die, but you go out as a martyr, so there's that).

+
+ +
+

Flames: 0

+

Desolation: 0

+

Relocation: 0

+
+ +
+

Event Log

+
    +
    +
    + + + + diff --git a/last-days-of-rome.md b/last-days-of-rome.md new file mode 100644 index 0000000..5e1503d --- /dev/null +++ b/last-days-of-rome.md @@ -0,0 +1,85 @@ +# LAST DAYS OF ROME + +You are a citizen in a relatively successful empire. Aalas, the new emperor has arrived, and he's decided that the best thing for everyone would be to burn it all to the ground with you all still inside it. Nothing to be done but carry on as usual, you suppose. He is, after all, the emperor + +## RULES + +You have three scores, which starts O: + +- **FLAMES** +- **DESOLATION** +- **RELOCATION** + +## WHAT AN ADVANCED SOCIETY WE ARE + +To start the game, you generate a new City Event by rolling a six-sided die (d6). Adjust your scores as directed by the table (or subtable), then roll a new event. Keep rolling new events until one of the following occurs: + +1. If your **FLAMES** score reaches 1O, your house is finally consumed by the spreading wildfires, and you are burned alive in the process. You saw it coming, of course. That knowledge comforts you. + +2. If your **DESOLATION** score reaches 1O, then society breaks down completely and you are killed in the rampage. If it makes any difference to you, then you should know that there is no such thing as an emperor in a wasteland. He shall rule over ashes. + +3. If your **RELOCATION** score reaches 10, you come to your senses and gather your belongings, striking out into the unknown. Maybe you'll find a better life out there,away from fiddling emperors. You'll probably be eaten by wolves,but you should take the chance. + +## WE SHOULD TOTALLY JUST KILL CAESAR + +At any time you can declare that "we should totally just kill Caesar". Roll a number of d6 equal to the current number of your DESOLATION score,and add up the dice.If the total result is 2O or more,then you assassinate the emperor. You die either way, but if you pull it off then it will be immensely satisfying. + +## CITY EVENTS (ROLL A D6) + + + + + + + + + + + +
    1, 2, or 3In the Imperial Palace
    4 or 5Relative Unrest
    6Add 1 to your FLAMES score
    + +## IN THE IMPERIAL PALACE (ROLL A D6) + + + + + + + + + + + + + + + + + + + + +
    1The emperor fiddles with manic glee. It's not tuneful.+1 FLAMES
    2The emperor raises another horse to the position of senator+1 DESOLATION
    +1 RELOCATION
    3It's execution night at the palace. Participants are selected based on performance reviews.+1 RELOCATION
    4The emperor screams like a baby. Why does the world not bend to his whims?+1 DESOLATION
    5The emperor sits in front of the flame and *commands* it to obey. It does not.+1 FLAMES
    +1 RELOCATION
    6Work continues on a house made of pure gold. It keeps melting.+1 FLAMES
    + +## RELATIVE UNREST (ROLL A D6) + + + + + + + + + + + + + + + + + + + + +
    1People complain - this is unacceptable. Then they go about their business.+1 RELOCATION
    2There are no goods at market. "If you have no bread,then eat shit" is the word from the palace+1 DESOLATION
    3You receive a letter asking you to reassert your faith in the emperor. In writing.+1 RELOCATION
    4Lions are released onto the streets in an attempt to calm the population.+1 DESOLATION
    +1 FLAMES
    5Is Rome really over? It's hard to say. Who knew burning a city took this long?+1 DESOLATION
    6The Emperor decrees that the burnings will continue until morale improves.+1 FLAMES
    diff --git a/last-days-of-rome.pdf b/last-days-of-rome.pdf new file mode 100644 index 0000000..b1db782 Binary files /dev/null and b/last-days-of-rome.pdf differ diff --git a/script.js b/script.js new file mode 100644 index 0000000..d11bce6 --- /dev/null +++ b/script.js @@ -0,0 +1,90 @@ +document.addEventListener('DOMContentLoaded', () => { + const rollButton = document.getElementById('roll-button'); + const assassinateButton = document.getElementById('assassinate-button'); + const newGameButton = document.getElementById('new-game-button'); + const themeSwitchButton = document.getElementById('theme-switch'); + const flamesScore = document.getElementById('flames'); + const desolationScore = document.getElementById('desolation'); + const relocationScore = document.getElementById('relocation'); + const logList = document.getElementById('log-list'); + + const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; + const savedTheme = localStorage.getItem('theme'); + + if (savedTheme) { + document.body.setAttribute('data-theme', savedTheme); + } else if (prefersDark) { + document.body.setAttribute('data-theme', 'dark'); + } + + themeSwitchButton.addEventListener('click', () => { + const currentTheme = document.body.getAttribute('data-theme'); + const newTheme = currentTheme === 'dark' ? 'light' : 'dark'; + document.body.setAttribute('data-theme', newTheme); + localStorage.setItem('theme', newTheme); + }); + + rollButton.addEventListener('click', () => { + fetch('game.php?action=roll_dice') + .then(response => response.json()) + .then(data => { + updateScores(data.scores); + logEvent(data.event); + checkGameState(data.gameState); + }); + }); + + assassinateButton.addEventListener('click', () => { + fetch('game.php?action=assassinate') + .then(response => response.json()) + .then(data => { + logEvent(data.event); + checkGameState(data.gameState); + }); + }); + + newGameButton.addEventListener('click', () => { + fetch('game.php?action=reset_game') + .then(response => response.json()) + .then(data => { + updateScores(data.scores); + logList.innerHTML = ''; + rollButton.disabled = false; + assassinateButton.disabled = false; + newGameButton.style.display = 'none'; + }); + }); + + function updateScores(scores) { + flamesScore.textContent = scores.flames; + desolationScore.textContent = scores.desolation; + relocationScore.textContent = scores.relocation; + } + + function logEvent(event) { + const li = document.createElement('li'); + li.textContent = event; + logList.prepend(li); + } + + function checkGameState(gameState) { + if (gameState !== 'ongoing') { + rollButton.disabled = true; + assassinateButton.disabled = true; + newGameButton.style.display = 'block'; + let message = ''; + if (gameState === 'win') { + message = 'You have escaped the city! You win!'; + } else if (gameState === 'loss_flames') { + message = 'You were burned alive. You lose.'; + } else if (gameState === 'loss_desolation') { + message = 'You were killed in the rampage. You lose.'; + } else if (gameState === 'win_assassination') { + message = 'You have assassinated the emperor! You die a martyr, but you win!'; + } else if (gameState === 'loss_assassination') { + message = 'Your attempt to assassinate the emperor has failed. You are executed. You lose.'; + } + setTimeout(() => alert(message), 100); + } + } +}); \ No newline at end of file diff --git a/style.css b/style.css new file mode 100644 index 0000000..46323a2 --- /dev/null +++ b/style.css @@ -0,0 +1,140 @@ +:root { + --background-color-light: #f4f4f4; + --text-color-light: #333; + --h1-color-light: #a00; + --log-background-color-light: #fff; + --log-border-color-light: #a00; + + --background-color-dark: #333; + --text-color-dark: #f4f4f4; + --h1-color-dark: #f4f4f4; + --log-background-color-dark: #444; + --log-border-color-dark: #f4f4f4; + + --button-background-color: #a00; + --button-text-color: #fff; + --button-hover-background-color: #c00; +} + +body { + font-family: sans-serif; + text-align: center; + transition: background-color 0.3s, color 0.3s; +} + +body[data-theme='light'] { + background-color: var(--background-color-light); + color: var(--text-color-light); +} + +body[data-theme='dark'] { + background-color: var(--background-color-dark); + color: var(--text-color-dark); +} + +.theme-switcher { + position: absolute; + right: .75rem; + top: .75rem; +} + +#theme-switch { + background: none; + border: 1px solid; + cursor: pointer; + padding: .25rem .5rem; +} + +body[data-theme='light'] #theme-switch { + border-color: var(--text-color-light); + color: var(--text-color-light); +} + +body[data-theme='dark'] #theme-switch { + border-color: var(--text-color-dark); + color: var(--text-color-dark); +} + +.container { + margin: auto; + overflow: hidden; + padding: 1.25rem; + width: 80%; +} + +h1 { color: var(--h1-color-light); } + +body[data-theme='dark'] h1 { color: var(--h1-color-dark); } + +.scores { + border: 1px solid; + border-left: none; + border-right: none; + display: flex; + justify-content: space-around; + margin-bottom: 1.25rem; + padding: .75rem 0; + + h2 { + font-size: 1.5rem; + margin: 0; + padding: 0; + } +} + +body[data-theme='light'] .scores { + border-color: var(--text-color-light); + color: var(--text-color-light); +} + +body[data-theme='dark'] .scores { + border-color: var(--text-color-dark); + color: var(--text-color-dark); +} + +.actions button { + background-color: var(--button-background-color); + border: none; + color: var(--button-text-color); + cursor: pointer; + font-size: 1em; + margin: 0 .5rem 1.5rem; + padding: .5rem 1.5rem; + transition: background-color 0.3s; + + &:hover { background-color: var(--button-hover-background-color); } +} + +.intro { + font-size: 1.1rem; + line-height: 1.4; + margin: 0 auto 1.5rem; + max-width: 64rem; + text-wrap: balance; +} + +.log { + margin-top: 1.5rem; + text-align: left; +} + +.log ul { + list-style-type: none; + padding: 0; +} + +.log li { + margin-bottom: .25rem; + padding: .5rem; + transition: background-color 0.3s, border-left-color 0.3s; +} + +body[data-theme='light'] .log li { + background: var(--log-background-color-light); + border-left: 5px solid var(--log-border-color-light); +} + +body[data-theme='dark'] .log li { + background: var(--log-background-color-dark); + border-left: 5px solid var(--log-border-color-dark); +}