You have arrived at the camp.
<<nobr>>
<<script>>
UIBar.unstow().show();
<</script>>
<<if Cycle.get('time').check('morning')>>
The sun is rising, and the birds are singing.
<<set $locationImage to "media/locations/camp_morning_unlit.webp">>
<<elseif Cycle.get('time').check('noon')>>
The sun is high in the sky, and the camp is bustling with activity.
<<set $locationImage to "media/locations/camp_day_unlit.webp">>
<<elseif Cycle.get('time').check('evening')>>
The sun is setting, casting a warm glow over the camp.
<<set $locationImage to "media/locations/camp_evening_unlit.webp">>
<<elseif $timeIndex is 3>>
The fire is warm, and the stars are bright.
<<set $locationImage to "media/locations/camp_night_unlit.webp">>
<</if>>
<</nobr>>
Hunger: <<showcycle 'hunger'>>
Thirst: <<showcycle 'thirst'>>
Sleep: <<showcycle 'sleep'>>
[[Sleep]] [[TestDialog]] [[TestGeneration]] [[TestMap]] [[TestFight]]
<<button "Refresh Page" "DebugCamp">><</button>>
<<customButton "Refresh Page" "This is the move button" "DebugCamp" "move-btn" "media/icons/farmer.svg" "normal" "move button pressed">><</customButton>>
<<customButton "Refresh Page" "This is the interact button" "DebugCamp" "interact-btn pulse-animation" "media/icons/farmer.svg" "large" "interact button pressed">><</customButton>>
<<customButton "Refresh Page" "This is the action button" "DebugCamp" "action-btn" "media/icons/farmer.svg" "large" "action button pressed">><</customButton>>
<<if $placesArray.last()>>[[Go Explore|either($placesArray)]]<<else>> You have no unexplored areas. <</if>>
<<print $placesArray>>
<<nobr>>
<<for _i to 0; _i lt $exploredPlaces.length; _i++>>
<<capture _i>>
[[$exploredPlaces[_i]]]
<</capture>>
<</for>>
<</nobr>>
<<set $playerX = 57>>
<<set $playerY = 53>>
<<set $location to "DebugCamp">><<set _faceSvg = loadAndColorSvg('media/faces/face_1.svg', '#FFFFFF', '#034DF2', '#FFFFFF')>>
<<set _overlaySvg = loadAndColorSvg('media/faces/eyes_1.svg', '#000000', '#CCCCCC', '#00AA00')>>
<div class="svg-layer-container">
<div class="svg-layer base-layer">
<<print _faceSvg>>
</div>
<div class="svg-layer overlay-layer">
<<print _overlaySvg>>
</div>
</div><<conversation
left1:$profileImage $first_name $last_name
left2:$units[1].image Archer
right1:$enemy.image Goblin Leader
right2:$units[0].image Goblin
right4:$units[0].image Goblin
right3:$units[0].image Goblin
>>
<<fadeConversationImage in "left-1" "0s">>
<<fadeConversationImage in "right-1" "0s">>
<<dialogText center>>
<i>You see a goblin!</i>
<</dialogText>>
<<timed 50ms>>
<<highlightConversationImage left-1>>
<<setConversationImageOpacity right-1 0.8>>
<</timed>>
<<dlg "dlg" 0 "➤">>
<<level 0>>
<<line "Hello">>
<<dialogText left left-1>>
Hello.
<</dialogText>>
<<highlightConversationImage right-1>>
<<dialogText right right-1>>
Grr I am a goblin!
<</dialogText>>
<<timed 2s>>
<<fadeConversationImage in "right-2" "1s">>
<<highlightConversationImage right-2>>
<<dialogText center>>
<i>Another goblin appears</i>
<</dialogText>>
<<next>>
<<highlightConversationImage left-2>>
<<fadeConversationImage in "left-2" "1s">>
<<dialogText center>>
<i>Your reinforcements arrive...</i>
<</dialogText>>
<<next>>
<<dialogText left left-2>>
Let me at them!
<</dialogText>>
<<highlightConversationImage left-1>>
<</timed>>
<</line>>
<</level>>
<<level 1>>
<<line "Come on then!">>
<<highlightConversationImage right-3>>
<<dialogText left left-1>>
Come on then!
<</dialogText>>
<<timed 1s>>
<<fadeConversationImage in "right-3" "1s">>
<<dialogText center>>
<i>Yet another goblin appears!</i>
<</dialogText>>
<</timed>>
<</line>>
<br>
<<line "Gotta go!">>
<<highlightConversationImage left-1 remove>>
<<dialogText left left-1>>
I need to leave!
<</dialogText>>
<<timed 1s>>
<<fadeConversationImage out "left-1" "1s">>
<</timed>>
<</line>>
<</level>>
<</dlg>>
<</conversation>>
<<link "Back to Camp" "DebugCamp">>
<</link>><<set $combatIntro to "You found a $enemy.name!">>
<<done>>
<<script>>window.startCombat('TestFight', 'TestFight', 'TestFight', false, false, false);<</script>>
<</done>>
<<include "CombatUI">>
<br>
[[Back|TestFight]]<<link "FIGHT" "TestFightUI">>
<<script>>
generateEnemies([
{ type: 'goblin', armor: "none", weapon: "stone", name: "Stone Goblin", peonData: {
race: "goblin",
skintone: "golden",
ethnicity: "nordic",
gender: "Male",
age: "Adult",
recruitmentChance: 1
},
weights: {attack: 1.0, defend: 0.0, focus: 0.0}},
{ type: 'goblin', armor: "none", weapon: "club", name: "Club Goblin", weights: {attack: 0.0, defend: 1.0, focus: 0.0}},
{ type: 'goblin', armor: "none", weapon: "stick", name: "Stick Goblin", attributes: { constitution: 1, defense: 2}, weights: {attack: 0.0, defend: 0.0, focus: 1.0}}
]);
generateAllies([
{ type: 'goblin', armor: "none", weapon: "stone", name: "Stone Goblin", weights: {attack: 1.0, defend: 0.0, focus: 0.0}},
{ type: 'goblin', armor: "none", weapon: "club", name: "Club Goblin", weights: {attack: 0.0, defend: 1.0, focus: 0.0}},
{ type: 'goblin', armor: "none", weapon: "stick", name: "Stick Goblin", attributes: { constitution: 1, defense: 2}, weights: {attack: 0.0, defend: 0.0, focus: 1.0}}
]);
<</script>>
Combat stats:
Speed: Turn order + if 2x speed, 2x turns etc
Agility: Anti Critical Strike chance
Perception: Critical Strike chance (Crit when Perception - Agility > 0 & roll dice (+ luck?))
Defense: Blocks against Strength and Precision
Strength: Damages against Defense (melee) (Damage done: Strength - Defense [+- dice damage])
Constitution: Sets Stamina level
Precision: Damages against Defense (ranged) (Damage done: Precision - Defense [+- dice damage])
Luck: Higher luck = wins ties
Loss when Stamina reaches 0. Let make decision if enemy loss.
If player loss, enemy can hurt or spare randomly weighted based on type.
Lose state: time passes, player is moved to another story point.
<</link>>
<<link "Camp">>
<<goto "CraterCamp">>
<</link>><dialogue-wheel id="myWheel" style="display: block; margin: 0 auto;"></dialogue-wheel>
<<done>>
<<script>>
const wheel = document.getElementById('myWheel');
wheel.options = [
{ text: "Stagger toward the pod", color: "#00785E" },
{ text: "Stagger toward the river", color: "#019e5c" },
{ text: "Stagger toward the forest", color: "#D8F5EF" },
{ text: "Stagger toward the lake", color: "#00c9a7" }
];
wheel.wheelRadius = 95;
wheel.ringThickness = 60;
wheel.perspectiveAngleX = 20;
wheel.fontSizeScale = 1.5;
wheel.bevelIntensity = 0.5;
wheel.ringExtrusion = 22;
wheel.disabledOpacity = 0.5;
wheel.disabledSaturation = 0.25;
wheel.disableAffectsText = false;
// Listen for selection events
wheel.addEventListener('option-selected', (event) => {
debugLog('Selected:', event.detail.option);
// Example: Update some UI element
// const selectionDisplay = document.getElementById('selectionInfo');
// if (selectionDisplay) {
// selectionDisplay.textContent = `Selected: ${event.detail.option.text}`;
// }
});
<</script>>
<</done>>
<br>
<div id="peonList">
<h3>Existing Peons:</h3>
</div>
<<script>>
$(document).ready(function () {
function renderPeon(peon) {
return '<div class="peon" style="border:1px solid #ccc; padding:10px; margin:10px;">' +
'<h4>' + peon.Name.First + ' ' + peon.Name.Last + '</h4>' +
'<ul>' +
'<li>Race: ' + peon.Race + '</li>' +
'<li>Ethnicity: ' + peon.Ethnicity + '</li>' +
'<li>Gender: ' + peon.Gender + '</li>' +
'<li>Age: ' + peon.Age + '</li>' +
'<li>Body Type: ' + peon.BodyType + '</li>' +
'<li>Skin: ' + peon.Skintone + ' (' + peon.HexColor + ')</li>' +
'<li>Distinctive Feature: ' + peon.Distinctive + '</li>' +
'</ul>' +
'</div>';
}
if (!State.variables.peons) {
State.variables.peons = {};
}
Object.values(State.variables.peons).forEach(peon => {
$('#peonList').append(renderPeon(peon));
});
const newPeons = [
peonGenerator.generatePeon(1),
peonGenerator.generatePeon(1, {
race: "human",
age: "Elder",
hairStyle: "Bald",
skintone: "light",
firstName: "TestNameFirstOnly"
}),
peonGenerator.generatePeon(1, {
race: "orc",
hexColor: "#2F4F4F",
bodyType: "Hulking",
distinctive: "Ritual Scarring"
})
].flat();
newPeons.forEach(peon => {
State.variables.peons[peon.UUID] = peon;
$('#peonList').append(renderPeon(peon));
});
});
<</script>>
<br>
[[DebugCamp]]<img src="media/locations/riverine_forest_map_no_camp.webp" usemap="#image-map" style="max-height: 90vh; max-width: 90vh;">
<map name="image-map">
<area alt="River" title="River" data-passage="River" coords="0,1699,260,1694,463,1488,658,1276,750,1269,920,1262,1042,1267,1132,1269,1201,1256,1271,1245,1331,1211,1387,1155,1445,1041,1492,940,1516,807,1516,697,1496,599,1442,491,1400,413,1405,350,1476,339,1583,317,1691,288,1765,263,1872,230,1961,201,2042,181,2042,8,1925,8,1883,60,1805,106,1731,106,1628,102,1525,124,1384,136,1326,194,1264,239,1268,324,1263,391,1254,462,1227,528,1248,584,1279,615,1337,657,1364,707,1333,794,1295,865,1248,957,1192,1028,1073,1046,946,1046,820,1042,740,1044,613,1075,510,1118,434,1221,340,1324,266,1391,217,1418,110,1485,62,1518,0,1523" shape="poly">
<area alt="Lake" title="Lake" data-passage="Lake" coords="588,136,684,103,776,96,829,101,863,168,887,244,915,326,944,403,919,472,901,568,865,631,760,664,642,673,550,664,474,680,403,689,342,653,300,584,271,505,271,418,277,326,284,257,311,175,412,123,501,110" shape="poly">
<area alt="Crater" title="Crater" data-passage="Crater" coords="1080,1715,1160,1711,1247,1715,1294,1746,1317,1789,1239,1847,1154,1878,1071,1873,1010,1838,979,1793,1008,1746" shape="poly">
<area alt="Clearing" title="Clearing" data-passage="Camp" coords="1745,1205,1797,1232,1850,1248,1910,1261,1931,1303,1964,1324,2002,1333,2043,1344,2043,1699,2016,1683,1980,1621,1928,1588,1870,1594,1812,1610,1743,1605,1680,1588,1640,1541,1591,1480,1569,1399,1573,1324,1626,1272,1673,1237" shape="poly">
<area alt="Forest" title="Forest" data-passage="Woods" coords="2048,2048,0,0" shape="rect">
</map>
<<done>>
<<script>>
// Apply maphilight to all images that have a usemap attribute, e.g.
// all the maps that are in this twine. This sets the default styling
// for all the maps. These are all the possible styling options.
// Note: for picking colors, check out http://hslpicker.com/. You can
// copy the HEX value as long as you leave off the "#".
$("img[usemap]").maphilight({
fill: true, // Fill the area?
fillColor: '0F0F0F', // HEX format without the starting "#"
fillOpacity: 0.2, // Opacity of the filled area
stroke: true, // Outline the area?
strokeColor: '0F870F',
strokeOpacity: 0.2,
strokeWidth: 3, // Outline width
fade: true, // Animate when hovered with a fade?
alwaysOn: false, // Always show the areas?
neverOn: false,
groupBy: false,
wrapClass: true,
shadow: false,
shadowX: 0,
shadowY: 0,
shadowRadius: 6,
shadowColor: '000000',
shadowOpacity: 0.8,
shadowPosition: 'outside',
shadowFrom: false
});
$("img[usemap]").rwdImageMaps();
<</script>>
<</done>>
[[DebugCamp]]<<nobr>>
<<set $location to $campLoc + " Camp">>
<<set $locationImage to "media/locations/" + $campLoc + "_camp.webp">>
<<set $_target to $campLoc + "Camp">>
<</nobr>>
You collect whatever you can find to create a haphazard shelter. It might at least let you hide from the sun.
[[Go To Camp|$_target]]<div class="camp-status-container">
<div class="resource-row" id="resource-target">
</div>
</div>
<<done>>
<<script>>
const resources = State.variables.resources;
let html = '';
Object.values(resources)
.filter(r => r.visible)
.forEach(r => {
const name = r.name.slice(0,1).toUpperCase() + r.name.slice(1);
html += `<div class="resource-item">
<img src="${r.icon}" title="${name}" class="resource-icon">
<div class="resource-value">${r.amount}</div>
</div>`;
});
$('#resource-target').wiki(html);
<</script>>
<</done>><div class="choice-header">Actions</div>
<br>
<div class="buttongrid">
<<customButton "Travel" "" "CraterExplore" "move-btn" "media/icons/compass.svg">><</customButton>>
<<customButton "Explore" "" "CraterExplore" "move-btn" "media/icons/spyglass.svg">><</customButton>>
<<customButton "Build" "" "" "action-btn" "media/icons/wheelbarrow.svg">><</customButton>>
<<customButton "Rest" "" "Sleep" "move-btn" "media/icons/night-sleep.svg">><</customButton>>
<<customButton "Train" "" "CraterCamp" "move-btn" "media/icons/biceps.svg">><</customButton>>
<<customButton "Craft" "" "CraterCamp" "action-btn" "media/icons/stone-crafting.svg">><</customButton>>
<<customButton "Manage" "" "CraterCamp" "action-btn" "media/icons/pointing.svg">><</customButton>>
<<customButton "Logistics" "" "CraterCamp" "action-btn" "media/icons/warehouse.svg">><</customButton>>
<<customButton "HUNT" "" "CraterCamp" "blood-btn frenzy" "media/icons/bleeding-eye_red.svg">><</customButton>>
</div>
<br>
<<if $exploredLocations.length > 0>>
<div class="choice-header">Explored Locations</div>
<br>
<div class="location-grid" id="explored-locations-grid"></div>
<br>
<<done>>
<<script>>
window.renderExploredLocationButtons();
<</script>>
<</done>>
<</if>><div id="sleep-selection">
<div class="choice-header">How long do you want to sleep?</div>
<br>
<div class="buttongrid">
<<button "1 hour">>
<<set $sleepHours to 1>>
<<goto "SleepStart">>
<</button>>
<<button "8 hours">>
<<set $sleepHours to 8>>
<<goto "SleepStart">>
<</button>>
<<button "2 hours">>
<<set $sleepHours to 2>>
<<goto "SleepStart">>
<</button>>
<<button "12 hours">>
<<set $sleepHours to 12>>
<<goto "SleepStart">>
<</button>>
<<button "4 hours">>
<<set $sleepHours to 4>>
<<goto "SleepStart">>
<</button>>
<<button "Until next morning">>
<<goto "SleepUntilMorning">>
<</button>>
</div>
<br>
<<return "Cancel">>
</div><<set setup.sleepHours to $sleepHours>>
<<set setup.sleepHourCounter to 0>>
<div id="sleep-status">Sleeping...</div>
<<repeat 500ms>>
<<if setup.sleepHours <= 0 or setup.sleepInterrupted>>
<<script>>
setup.finishSleep();
<</script>>
<<else>>
<<set setup.sleepHours to setup.sleepHours - 1>>
<<replace "#sleep-status">>Hour <<print $sleepHours - setup.sleepHours>> of <<print $sleepHours>>...<</replace>>
<<script>>
sleep();
<</script>>
<</if>>
<</repeat>><div id="sleep-status">Sleeping...</div>
<<repeat 500ms>>
<<if setup.sleepHours <= 0>>
<<script>>
setup.finishSleep();
<</script>>
<<else>>
<<set setup.sleepHours to setup.sleepHours - 1>>
<<replace "#sleep-status">>Hour <<print $sleepHours - setup.sleepHours>> of <<print $sleepHours>>...<</replace>>
<<script>>
sleep(true);
<</script>>
<</if>>
<</repeat>><<script>>
const time = Cycle.get('time');
const currentIndex = time.phases.indexOf(time.current());
const targetIndex = time.phases.indexOf("morning");
const distance = (targetIndex - currentIndex + time.phases.length) % time.phases.length;
const phasesBetween = distance === 0 ? time.phases.length - 1 : distance - 1;
const turnsInPhase = time.stack % time.period;
const turnsLeftInCurrentPhase = time.period - turnsInPhase;
const turnsToMorning = turnsLeftInCurrentPhase + (phasesBetween * time.period);
State.variables.sleepHours = turnsToMorning;
<</script>>
<<set setup.sleepHours to $sleepHours>>
<<set setup.sleepHourCounter to 0>>
<div id="sleep-status">Sleeping until morning...</div>
<<repeat 500ms>>
<<if setup.sleepHours <= 0>>
<<script>>
setup.finishSleep();
<</script>>
<<else>>
<<set setup.sleepHours to setup.sleepHours - 1>>
<<replace "#sleep-status">>Hour <<print $sleepHours - setup.sleepHours>> of <<print $sleepHours>>...<</replace>>
<<script>>
sleep();
<</script>>
<</if>>
<</repeat>>Hello<<include "CombatUI">><div class="combat-container">
<div class="enemies-row">
<<for _i, _enemy range $enemies>>
<div class="enemy-container" @data-enemy-index="_i" data-selector>
<h2 class="enemy-name">_enemy.name</h2>
<span class="enemy-stamina">_enemy.stamina.current / _enemy.stamina.max</span>
<div class="portrait-container">
<img @src="_enemy.image" @alt="_enemy.name" class="enemy-portrait">
</div>
</div>
<</for>>
</div>
<<if $allies>>
<hr />
<div class="allies-row">
<<for _i, _ally range $allies>>
<div class="ally-container">
<h2 class="ally-name">_ally.name</h2>
<span class="ally-stamina">_ally.stamina.current / _ally.stamina.max</span>
<div class="portrait-container">
<img @src="_ally.image" @alt="_ally.name" class="ally-portrait">
</div>
</div>
<</for>>
</div>
<</if>>
<div class="action-buttons">
<button onclick="performAttack();">Attack</button>
<<for _item range $inventory>>
<<if _item.equipped && _item.action>>
<button @onclick="`performCombatAction('${_item.equippedSlots[0]}');`"> <<= _item.action.name>> </button>
<</if>>
<</for>>
<button onclick="performDefense();">Defend</button>
<button onclick="performFocus();">Focus</button>
<button onclick="performFlee();">Flee</button>
<div id="combat-loading" style="display: none; text-align: center;">
<em>Enemy is attacking...</em>
</div>
</div>
<div id="easymode" class="action-buttons" style="display: none;">
<button onclick="win();">Win fight</button>
<button onclick="lose();">Lose fight</button>
</div>
<div id="action-description" class="action-description">
$combatIntro
</div>
</div>
<<done>>
<<script>>
if (settings.easymode) {
document.getElementById('easymode').style.display = 'block';
}
if (window.updateTargetSelectionUI) {
updateTargetSelectionUI();
}
if (window.updatePreCalculatedActionDisplays) {
updatePreCalculatedActionDisplays();
}
$(document).on('click', '[data-selector]', function() {
var index = $(this).data('enemy-index');
selectTarget(index);
});
<</script>>
<</done>>You successfully fled from combat!
<<link "Continue" $fleePassage>>
<<unset $winPassage>>
<<unset $losePassage>>
<<unset $fleePassage>>
<</link>><h2>You win!</h2>
<<if $defeatedEnemies && $defeatedEnemies.length > 0>>
<h3>Defeated Enemies:</h3>
<<for _enemy range $defeatedEnemies>>
_enemy.name <br>
<</for>>
<br>
<div id="victory-actions" class="victory-actions">
<<link "Recruit All">>
<<script>>
State.variables.result = handleRecruitAll();
<</script>>
<<replace "#victory-actions">>
<<include $result>>
<</replace>>
<</link>>
<<link "Murder All">>
<<script>>
State.variables.result = handleMurderAll();
<</script>>
<<replace "#victory-actions">>
<<include $result>>
<</replace>>
<</link>>
<<link "Leave All">>
<<script>>
State.variables.result = handleLeaveAll();
<</script>>
<<replace "#victory-actions">>
<<include $result>>
<</replace>>
<</link>>
</div>
<</if>>
<<if !$defeatedEnemies || $defeatedEnemies.length === 0>>
<<link "Continue" $winPassage>>
<<unset $winPassage>>
<<unset $losePassage>>
<<unset $fleePassage>>
<</link>>
<</if>><h2>Recruitment Results</h2>
<<if $recruitedPeons && $recruitedPeons.length > 0>>
<p>You successfully recruited:</p>
<<for _peon range $recruitedPeons>>
<div class="recruited-peon">
<h4>_peon.Name.First _peon.Name.Last</h4>
<p>Race: _peon.Race | Gender: _peon.Gender | Age: _peon.Age</p>
</div>
<</for>>
<</if>>
<<if $failedRecruits && $failedRecruits.length > 0>>
<p>Failed to recruit: $failedRecruits</p>
<</if>>
<<link "Continue" $winPassage>>
<<unset $winPassage>>
<<unset $losePassage>>
<<unset $fleePassage>>
<</link>><h2>Recruitment Failed</h2>
<p>None of the enemies were willing to join you.</p>
<<link "Continue" $winPassage>>
<<unset $winPassage>>
<<unset $losePassage>>
<<unset $fleePassage>>
<</link>><h2>You murdered the enemies</h2>
<p>All defeated enemies have been killed.</p>
<p>I hope you feel bad.</p>
<<link "Continue" $winPassage>>
<<unset $winPassage>>
<<unset $losePassage>>
<<unset $fleePassage>>
<</link>><p>You leave your enemies lying on the ground.</p>
<<link "Continue" $winPassage>>
<<unset $winPassage>>
<<unset $losePassage>>
<<unset $fleePassage>>
<</link>><h2>You lose...</h2>
<<link "Continue" $losePassage>>
<<unset $winPassage>>
<<unset $losePassage>>
<<unset $fleePassage>>
<</link>><<script>>
UIBar.stow().hide()
<</script>>
<style>
#passages, #story, #passage-crash-location, .passage {
width: 100% !important;
max-width: 100% !important;
padding: 0 !important;
margin: 0 !important;
}
body {
min-height: 100vh;
background-color: var(--black);
margin: 0;
color: white;
overflow-x: hidden;
}
h1 {
margin-bottom: 20px;
font-size: 2.5em;
text-align: center;
}
#passages {
padding: 0 !important;
margin: 0 !important;
min-height: 100vh !important;
display: flex;
flex-direction: column;
justify-content: center;
}
body {
padding-top: 0 !important;
margin: 0 !important;
}
.ui-dialog {
top: 0 !important;
}
.container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
justify-content: center;
gap: 100px;
padding: 20px 5%;
width: 100%;
max-width: 100% !important;
box-sizing: border-box;
}
.choice-box {
justify-self: center;
width: 100%;
min-height: 600px;
height: auto;
border-radius: 30px;
box-sizing: border-box;
overflow: hidden;
display: flex;
flex-direction: column;
text-align: center;
background-color: var(--dark);
transition: all 0.3s ease;
position: relative;
cursor: pointer;
}
.choice-box:hover {
transform: scale(1.05);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.5);
}
.choice-box img {
width: 100%;
height: 65vh;
max-height: 400px;
min-height: 300px;
object-fit: cover;
}
.choice-box p {
margin: 0;
padding: 15px;
font-size: 1.2em;
background-color: transparent;
transition: color 0.3s ease;
flex-grow: 1;
display: flex;
align-items: center;
justify-content: center;
}
.choice-box:hover p {
color: var(--light);
}
.expanded-content {
display: none;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(30, 30, 30, 0.9);
padding: 20px;
box-sizing: border-box;
overflow-y: auto;
}
.choice-box.expanded {
transform: scale(1.2);
z-index: 10;
transition: all 0.3s ease-out;
}
.choice-box.expanded .expanded-content {
display: block;
padding: 15px;
}
.link-button {
display: inline-block;
margin-top: 20px;
padding: 10px 20px;
background-color: #ffd700;
color: #121212;
text-decoration: none;
border-radius: 5px;
transition: background-color 0.3s ease;
}
.link-button:hover {
background-color: #ffea00;
}
@media (min-width: 2000px) {
.container {
grid-template-columns: repeat(4, minmax(400px, 1fr));
}
}
@media (max-width: 1999px) and (min-width: 900px) {
.container {
grid-template-columns: repeat(2, minmax(400px, 1fr));
}
}
@media (max-width: 899px) {
.container {
grid-template-columns: minmax(300px, 1fr);
}
.choice-box {
width: 90%;
max-width: 500px;
}
}
@media (max-width: 768px) {
.choice-box img {
height: 50vh;
}
h1 {
font-size: 2em;
}
.choice-box.expanded {
transform: scale(1.05);
margin: 1rem;
}
}
</style>
<body>
<h1>Select your landing location</h1>
<div class="container">
<div class="choice-box" onclick="expandBox(this)">
<img src="media/images/riverine_forest.jpg" alt="Riverine Forest">
<p>Riverine Forest</p>
<div class="expanded-content">
<h3>Riverine Forest</h3>
<p>From high above, a dark green ribbon winds through the landscape, following the serpentine path of a glistening river. <br><br> The dense canopy of the riverine forest creates a stark contrast with the surrounding terrain, its lush vegetation fed by the life-giving waters below.</p>
<<customButton "Aim at the Riverine Forest" "" "ForestCrash" "move-btn pulse-animation">><<script>>UIBar.unstow().show();<</script>><</customButton>>
</div>
</div>
<div class="choice-box" onclick="expandBox(this)">
<img src="media/images/rainforest.jpg" alt="Rainforest">
<p>Rainforest</p>
<div class="expanded-content">
<h3>Rainforest</h3>
<p>A vast, unbroken carpet of emerald green stretches across the curvature of the planet, occasionally obscured by swirling white clouds. <br><br> The immense scale of this verdant expanse is evident from space, hinting at the incredible biodiversity hidden beneath its canopy.</p>
<<link "Not Implemented">><</link>>
</div>
</div>
<div class="choice-box" onclick="expandBox(this)">
<img src="media/images/savannah.jpg" alt="Savannah">
<p>Savannah</p>
<div class="expanded-content">
<h3>Savannah</h3>
<p>Golden-hued plains dominate the view, punctuated by scattered dark spots representing solitary trees and small copses. <br><br> From this celestial vantage point, herds of wildlife appear as tiny moving specks, traversing the sun-baked landscape in search of water and grazing grounds.</p>
<<link "Not Implemented">><</link>>
</div>
</div>
<div class="choice-box" onclick="expandBox(this)">
<img src="media/images/islands.jpg" alt="Islands">
<p>Islands</p>
<div class="expanded-content">
<h3>Islands</h3>
<p>The islands form a stunning archipelago of emerald and turquoise gems against the deep blue ocean. <br><br> Their varied shapes and sizes create a mesmerizing pattern, from rugged volcanic peaks to smooth curved shorelines, all pristine and untouched.</p>
<<link "Not Implemented">><</link>>
</div>
</div>
</div>
<script>
function expandBox(box) {
document.querySelectorAll('.choice-box').forEach(el => el.classList.remove('expanded'));
box.classList.add('expanded');
event.stopPropagation();
}
document.addEventListener('click', function() {
document.querySelectorAll('.choice-box').forEach(el => el.classList.remove('expanded'));
});
</script>
</body>Placeholder.
You are from a very advanced galaxy spanning culture.
You were accused of a crime you did not commit and sentenced to eternal pain.
Injected with a strange serum, you were put into a stasis pod and launched into the void towards a barren planet.
You have the opportunity to guide your sentencing pod slightly.
[[Continue|Crash Location]]
<<link "DebugtoCamp" "CampSetup">>
<<set $campLoc to "Crater">>
<<set $dayIndex to 1>>
<<set $totalDays to 1>>
<<set $timeIndex to 0>>
<<set $actions to 3>>
<<set $showTime to true>>
<<editcycle 'time' resume>>
<<editcycle 'thirst' resume>>
<<editcycle 'hunger' resume>>
<<editcycle 'sleep' resume>>
<<script>>
equipItem(addItemToInventory(0));
equipItem(addItemToInventory(1));
equipItem(addItemToInventory(2));
<</script>>
<</link>>
<<set $healthIndex to 2>>
<<set $intro to false>>
<<set $location to "Unknown">>
<<set $dayIndex to -2>>
<<set $weekIndex to 1>>
<<set $monthIndex to 1>>
<<set $yearIndex to 1>>
<<set $placesArray to ["Woods", "River", "Lake", "Clearing"]>><<set $time to ["morning", "noon", "evening", "night"]>>
<<set $health to ["dying", "wounded", "bruised", "fine", "good", "divine"]>>
<<script>>
if (typeof State.variables.inventory === 'undefined') {
State.variables.inventory = [];
}
if (typeof State.variables.profileImage === 'undefined') {
State.variables.profileImage = 'media/portraits/commander.png';
}
if (typeof State.variables.activeBuffs === 'undefined') {
State.variables.activeBuffs = new Set();
}
if (typeof State.variables.exploredLocations === 'undefined') {
State.variables.exploredLocations = [];
}
gender.setPronouns("other");
const unitDefaults = {
image: "media/portraits/portrait.svg",
};
fetch('data/units.json')
.then(response => {
if (!response.ok) throw new Error('Failed to load units.json');
return response.json();
})
.then(data => {
const processedUnits = data.map(unit => {
const mergedUnit = { ...unitDefaults, ...unit };
if (!unit.hasOwnProperty('image')) {
switch (mergedUnit.faction) {
case 'enemy':
mergedUnit.image = "media/portraits/ogre.svg";
break;
case 'friendly':
mergedUnit.image = "media/portraits/invisible-face.svg";
break;
}
}
return mergedUnit;
});
setup.units = processedUnits;
State.variables.units = processedUnits;
debugLog('Processed units with faction-based defaults:', processedUnits);
})
.catch(error => console.error('Error loading units.json:', error));
fetch('data/combat.json')
.then(response => {
if (!response.ok) throw new Error('Failed to load combat.json');
return response.json();
})
.then(data => {
setup.combatDescriptions = data.descriptions;
debugLog('Combat descriptions loaded:', setup.combatDescriptions);
})
.catch(error => {
console.error('Error loading combat.json:', error);
setup.combatDescriptions = {
melee: ["You attack {enemy}!"],
ranged: ["You attack {enemy}!"],
defeated: ["{enemy} is defeated!"]
};
});
fetch('data/resources.json')
.then(response => {
if (!response.ok) throw new Error('Failed to load resources.json');
return response.json();
})
.then(data => {
const resourcesMap = data.reduce((map, resource) => {
map[resource.name] = resource;
return map;
}, {});
setup.resources = resourcesMap;
State.variables.resources = resourcesMap;
debugLog('Processed resources as map:', resourcesMap);
})
.catch(error => console.error('Error loading resources.json:', error));
fetch('media/faces/_faces.json')
.then(response => {
if (!response.ok) throw new Error('Failed to load faces.json');
return response.json();
})
.then(data => {
setup.faceData = data;
const featureCategories = ['faces', 'eyes', 'eyebrows', 'nose', 'mouth', 'ears', 'hair', 'detail'];
const files = featureCategories.flatMap(cat =>
(data[cat] || []).map(file => ({ category: cat, filename: file }))
);
debugLog(`Preloading ${files.length} face SVGs...`);
return Promise.all(
files.map(({ filename }) => {
const path = `media/faces/${filename}.svg`;
return fetch(path)
.then(response => {
if (!response.ok) throw new Error(`Failed to load ${path}`);
return response.text().then(svg => [path, svg]);
});
})
).then(entries => {
setup.faceMap = new Map(entries);
});
})
.then(() => debugLog('All face SVGs preloaded successfully'))
.catch(error => console.error('Error loading faces:', error));
<</script>>
<<run loadItems()>>
<<set $timeImage to "media/images/sunrise.png">>
<<set $exploredPassages to {}>>
<<set $showTime to false>>
<<set $physicalAge to 25>>
<<set $chronologicalAge to 25>>
<<set $timeSave to false>>
<<set $inventoryCount to 0>>
<<newcycle 'time' 5 1 suspend>>
<<phase morning>>
<<phase noon>>
<<phase evening>>
<<phase night>>
<</newcycle>>
<<newcycle "hunger" 20 1 suspend>>
<<phase full>>
<<phase hungry>>
<<phase famished>>
<<phase starving>>
<<phase malnourished>>
<</newcycle>>
<<newcycle "thirst" 10 1 suspend>>
<<phase quenched>>
<<phase thirsty>>
<<phase parched>>
<<phase dehydrated>>
<<phase desiccated>>
<</newcycle>>
<<newcycle "sleep" 20 1 suspend>>
<<phase rested>>
<<phase drowsy>>
<<phase tired>>
<<phase lethargic>>
<<phase fatigued>>
<</newcycle>><<nobr>>
<<set $healthClass to "health-" + $health[$healthIndex]>>
<<if $location>>
Location: $location
<</if>>
<<if $dayIndex > -1>>
<<if Cycle.check('time', 'morning')>>
<<set $timeImage to "media/time/sunrise.png">>
<<elseif Cycle.check('time', 'noon')>>
<<set $timeImage to "media/time/noon.png">>
<<elseif Cycle.check('time', 'evening')>>
<<set $timeImage to "media/time/dusk.png">>
<<elseif Cycle.check('time', 'night')>>
<<set $timeImage to "media/time/night.png">>
<<elseif $showTime>>
<<unset $timeImage>>
<</if>>
<<if $timeImage>>
<div class="time-location-wrapper">
<<if $locationImage>>
<img src="$locationImage" class="location-image">
<</if>>
<<if $showTime>>
<img src="$timeImage" class="time-indicator">
<</if>>
</div>
<</if>>
<br>
<<if $showTime>>
Time of day: <<showcycle 'time' upperfirst>>
<br><br>
Year $yearIndex <br>
Month $monthIndex <br>
Week $weekIndex <br>
Day $dayIndex
<br><br>
<</if>>
<div class="health-display-container">
You are <span id="health-status"></span>
</div>
<div id="player-stamina" style="display:none;">
<span class="player-stamina">Stamina: 0 / 0</span>
</div>
<br><br>
<div id="buffs-container"></div>
<img src="media/images/borders/divider/fade-divider/divider-fade-001.png" class="divider">
<a href="#" class="link-internal" id="characterLink">Character</a> <br> <br>
<a href="#" class="link-internal" id="inventoryLink">Inventory</a> <br> <br>
<<if $codexUnlocked is true>>
<a href="#" class="link-internal" id="codexLink">PHIL</a> <br> <br>
<</if>>
<<if $mapUnlocked is true>>
<a href="#" class="link-internal" id="mapLink">Map</a> <br> <br>
<</if>>
<<if Config.debug>>
<a href="#" class="link-internal" id="debugLink">Debug menu</a>
<</if>>
<img src="media/images/borders/divider/fade-divider/divider-fade-001.png" class="divider">
<</if>>
<</nobr>>Media from:
https://game-icons.net/ https://www.flaticon.com https://opengameart.org/
from authors:
Viscious Speed https://viscious-speed.deviantart.com/ [CC0 PDD]
Delapouite https://delapouite.com/ [CC-BY 3.0]
Lorc https://lorcblog.blogspot.com/ [CC-BY 3.0]
sbed https://opengameart.org/users/sbed [CC-BY 3.0]
adekto https://opengameart.org/users/adekto [CC-BY-SA 3.0]
Justin Nichol https://opengameart.org/users/justin-nichol [CC-BY-SA 3.0]
Aeynit https://opengameart.org/users/aeynit [CC-BY-SA 4.0]
Freepik https://www.freepik.com/
Flat Icons https://flat-icons.com/<<link "Credits">>
<<script>>
Dialog.create("Credits")
.wikiPassage("Credits")
.open();
<</script>>
<</link>><div id="start-container">
<h1>Character Creation</h1>
<p>Set your name, pronouns, and formal title</p>
<div class="input-group">
<label for="first-name">First Name:</label>
<<textbox "$first_name" "Dion">>
</div>
<div class="input-group">
<label for="last-name">Last Name:</label>
<<textbox "$last_name" "Cyrellis">>
</div>
<div class="input-group">
<label>Pronouns:</label>
<<link 'Configure your gender and pronouns.'>>
<<pronouns>>
<</link>>
This can be changed at any time in the settings menu.
</div>
<div class="input-group">
<label for="title">Title:</label>
<<textbox "$formal" "Seres">>
</div>
<div class="input-group">
<label for="profile-image">Custom Profile Image:</label>
<input type="file" id="profile-image" accept="image/*">
</div>
<div class="input-group">
<img id="profile-preview" src="" alt="Profile Image Preview" style="display: none; max-width: 100%; height: auto; border-radius: 10px; margin-top: 10px;">
</div>
<<customButton "Submit" "" "Prologue" "move-btn pulse-animation" "media/icons/play-button.svg" "large" "Welcome $formal $first_name $last_name !">><</customButton>>
</div>
<<set $intro to true>><h1>Uh oh you reached a passage only possible if there was an error</h1>
<<back "Go Back">>
<br><br>
<h3>Export Debug Data</h3>
<p>Please add some information on what your previous actions were.</p>
<p>Copy the text below and email it to me:</p>
<textarea readonly id="save-export" style="width:100%; height:150px; font-family:monospace; font-size:11px;"></textarea>
<<done>>
<<script>>
document.getElementById("save-export").value = Save.base64.save();
<</script>>
<</done>>
<<button "Copy to Clipboard">>
<<script>>
var textarea = document.getElementById("save-export");
textarea.select();
document.execCommand("copy");
var btn = this;
btn.textContent = "✓ Copied!";
btn.disabled = true;
setTimeout(() => {
btn.textContent = "Copy to Clipboard";
btn.disabled = false;
}, 2000);
<</script>>
<</button>><<nobr>>
<<set $location to "Clearing">>
<<set $locationImage to "media/locations/riverine_forest_map_no_camp.webp">>
The forest spits you into a clearing; a cruel parody of sanctuary. Your vision swims, each breath a razor-drawn gamble, as you collapse against a moss-veined rock. The air reeks of damp earth and your own blood.
<br><br>
Somewhere ahead, water rasps over stone, its taunting chorus sharper than the wind's hollow dirge through the pines.
<br><br>
<i>Move</i>, your failing body screams, but the clearing's soft moss yawns like a grave. Shadows thicken at the edges of your sight.
<br><br>
Even the sunlight feels accusatory here, dappling the ground as if to say: <i>This is where you'll bleed out. This is where the planet swallows you whole.</i>
<br><br>
[[You pass out|PassOut]]
<</nobr>><<nobr>>
<<script>>
UIBar.stow().hide();
increaseDate(5);
<</script>>
<<unset $locationImage>>
<<unset $location>>
<<unset $showTime>>
<<editcycle 'time' change 20 resume>>
<<timed 2s>><<fadein 3s>>
<span style="font-size: 2em; font-weight: bold;">.</span>
<</fadein>>
<<next 1s>><<fadein 2s>>
<span style="font-size: 2em; font-weight: bold;">.</span>
<</fadein>>
<<next 1s>><<fadein 2s>>
<span style="font-size: 2em; font-weight: bold;">.</span>
<</fadein>>
<</timed>>
<</nobr>>
<<timed 8s>><<fadein 4s>>
Consciousness claws its way back. Above you, the stars glare like shattered glass; cold, sharp, indifferent. The forest presses closer now, its silhouette jagged and breathing. Something wet soaks your tattered clothes; your blood, pooling in the moss. The air reeks of copper and rot.
<</fadein>><<next 5s>><<fadein 2s>>
<span>You are alive.</span>
Your ribs scream. Your leg bends wrong. A gash in your side pulses in time with your heartbeat, each throb a whisper: <i>This is how you die here. This is how the soil drinks you.</i> The hunger is a feral thing, gnawing at your stomach. Thirst cracks your tongue. Every breath tastes like failure.
<span style="font-style: italic;">You are alive.</span>
The ground hums. Roots curl around your wrists. <i>Stay</i>, they hiss. <i>Lie still. Let the planet peel you apart. Let the crows feast.</i> Your eyelids flutter. The stars blur.
<span style="font-weight: bold; font-style: italic;">You are alive.</span>
<<linkreplace "Defy the forest">><<fadein 1s>>
You lurch sideways, retching bile. The world tilts. Your mangled leg buckles, you claw at a sapling, snapping it in your grip. Blood slicks your palms. Shelter. Water. Fire. The words echo, brittle and distant. The forest leers. <i>You'll die crawling</i>, it taunts. But crawl anyway.
You slump against the gnarled trunk, its bark gouging your spine like teeth. The forest's laughter dies to a venomous hum, but its eyes linger; glints of starlight caught in the leaves above. Your breath rasps, a broken rhythm chanting: <i>Alive. Alive. Alive.</i> Blood slicks the roots beneath you, black in the moonlight. Somewhere, the river still mocks. Somewhere, your body still obeys.
The cold seeps into your bones. The shadows thicken. But the tree stands. But your heart beats. But the night is not eternal.
[[Rest|ClearingRested]]
<</fadein>><</linkreplace>>
<</fadein>>
<</timed>><<nobr>>
<<set $healthIndex to 1>>
<<editcycle 'time' change 1>>
<<set $location to "Clearing">>
<<set $locationImage to "media/locations/riverine_forest_map_no_camp.webp">>
<<set $showTime to true>>
<<editcycle 'thirst' change 20 resume>>
<<editcycle 'hunger' change 40 resume>>
<<editcycle 'sleep' change 20 resume>>
<<script>>
increaseDate(2);
removeBuff(3);
<</script>>
<</nobr>><<fadein 5s>><h2>The sun's light is a liar.</h2>
It paints your skin in gold while your body screams; every muscle a knotted rope, every bone ground to dust.
Your leg pulses: a forge-hot hammer striking iron.
<i>
Strike.
Strike.
Strike.
</i>
Relentless as the planet's heartbeat.
<<linkreplace "Open your eyes">><<nobr>><<script>>UIBar.unstow().show();<</script>><</nobr>><<fadein 1s>>The forest greets you in a haze of chlorophyll and cruelty. Sunlight stabs through the canopy, needling your pupils. Blurred shapes sharpen: ferns bowed like mourners, roots coiled like snakes. Then, movement. A small, feathered thing jabs its beak into your calf. Again. <i>Again.</i> It tears at dried blood, at scab-flecked skin. You twitch. It flutters back, beady eyes glinting; <i>hungry</i>, not afraid.
Your clothes cling, stiff with filth and old gore. But the wounds… gone. In their place: scars, pale and ropey, as if the forest itself stitched you back together. <i>Wrong.</i> Your throat cracks. Your stomach gnaws itself. You taste bile and pine resin.
<i>You should be dead.</i>
The trees creak in agreement. The bird watches, waiting. Above, the sky pales into dawn, its light thin and grudging. Cold still clings to the air. Predators will wake.
And the pod, rusted relic of a dead hope, still lurks somewhere in this green hell. It has metal. Circuitry. Data. Corroded teeth to pry loose. Dead hope to scavenge. <i>Lies.</i>
<<linkreplace "Kick the bird">>You lash out, leg screaming as you kick toward the creature; a mangled, graceless arc. The bird hops sideways, unbothered. Its head tilts. Black eyes lock onto yours, unblinking. <i>Pathetic,</i> they seem to say. A guttural croak escapes its throat, more rasp than song. Then it pecks again. <i>Harder.</i> Blood blooms fresh on your calf. The forest holds its breath.<</linkreplace>>
The clearing's false peace shatters. <i>You cannot stay.</i>
<<nobr>>
<<linkreplace "Leave this place">>
<<fadein 1s>>
<div class="choice-header">Where will you go?</div>
<br>
<div class="buttongrid">
<<customButton "Stagger toward the pod"
"The pod's carcass lies behind; rusted metal, frayed wires, data corroded to gibberish."
"PodRoute"
"move-btn"
"media/icons/apollo-capsule.svg"
"large">>
<<script>>debugLog("Moving to pod");<</script>>
<</customButton>>
/*
<<customButton "Stagger toward the river"
"The river hisses your name. Its water is a liquid grave, but your throat burns like ash."
"RiverRoute"
"move-btn"
"media/icons/river.svg"
"large">>
<</customButton>>
<<customButton "Stagger toward the forest"
"Branches sag like gallows. Roots writhe like nooses. The forest offers shelter in exchange for flesh."
"ForestRoute"
"move-btn"
"media/icons/forest.svg"
"large">>
<</customButton>>
<<customButton "Stagger toward the lake"
"The lake's breath reeks of algae and rot. Fish bones litter the shallows; salvation or slow poison?"
"LakeRoute"
"move-btn"
"media/icons/at-sea.svg"
"large">>
<</customButton>>
*/
</div>
<br>
<div class="disclaimer">
Your choice is not permanent, all locations will remain accessible.
</div>
<</fadein>>
<</linkreplace>>
<</nobr>>
<</fadein>><</linkreplace>><</fadein>><div class="choice-header">Where will you go?</div>
<br>
<div class="buttongrid">
<<customButton "Stagger toward the pod"
"The pod's carcass lies behind; rusted metal, frayed wires, data corroded to gibberish."
"PodRoute"
"move-btn"
"media/icons/apollo-capsule.svg"
"large">>
<<script>>debugLog("Moving to pod");<</script>>
<</customButton>>
/*
<<customButton "Stagger toward the river"
"The river hisses your name. Its water is a liquid grave, but your throat burns like ash."
"RiverRoute"
"move-btn"
"media/icons/river.svg"
"large">>
<</customButton>>
<<customButton "Stagger toward the forest"
"Branches sag like gallows. Roots writhe like nooses. The forest offers shelter in exchange for flesh."
"ForestRoute"
"move-btn"
"media/icons/forest.svg"
"large">>
<</customButton>>
<<customButton "Stagger toward the lake"
"The lake's breath reeks of algae and rot. Fish bones litter the shallows; salvation or slow poison?"
"LakeRoute"
"move-btn"
"media/icons/at-sea.svg"
"large">>
<</customButton>>
*/
</div>
<br>
<div class="disclaimer">
Your choice is not permanent, all locations will remain accessible.
</div>You awaken in a smoldering crater, the aftermath of your own catastrophic descent. The air is thick with the scent of scorched earth and the acrid tang of burnt metal. Every fiber of your being throbs with a relentless, all-encompassing pain, as if the very essence of your existence has been shattered and reassembled in the most agonizing manner possible.
<<timed 2s>><<fadein 2s>>[[Look around]]<</fadein>><</timed>>
<<set $healthIndex to 0>>
<<set $intro to false>>
<<set $location to "Unknown">>
<<set $dayIndex to 0>>
<<set $totalDays to 0>>
<<set $timeIndex to 0>>
<<set $actions to 3>>
<<set $placesArray to ["Woods", "River", "Lake"]>>
<<done>>
<<script>>
equipItem(addItemToInventory(0));
equipItem(addItemToInventory(1));
equipItem(addItemToInventory(2));
addBuff(3);
<</script>>
<</done>>
<<shakescreen 1.5s>>Vision bleeds into clarity. The crater walls loom; jagged teeth slick with ash. To your left, a slope <i>almost</i> navigable: dirt scarred by your crash, roots exposed like broken ribs. Beyond it, the forest waits. Not trees; <i>sentinel pines</i>, their trunks blackened by firelight, branches tangled into a cage. The air thrums. Insects shriek. Somewhere, water hisses. A taunt. A <i>dare</i>.
[[Drag yourself upward, toward the slope|LandingCraterEdge]]<<nobr>>
<<set $showTime to true>>
<<set $location to "Crater's Edge">>
<<set $locationImage to "media/locations/crater-edge.jpg">>
<</nobr>>
Every inch is a battle. Elbows gouge dirt. Knees shred against stone. Halfway up, you retch; bile and blood flecking the soil. The crater <i>clings</i>, as if the planet resists your escape. But you crawl. You crawl until fingernails split and breath frays to nothing.
<<timed 2s>><<fadein 1s>>
At the rim, the world unfurls in shades of green and ruin. Behind you: the pod's carcass, still spitting embers into the dawn. Ahead: a primordial forest, so dense it swallows light. To the east, a clearing glows; a liar's promise of safety. Beyond it, water glints, sharp as a blade.
[[Stare at the clearing|ClearingCrash]]
<</fadein>><<next 1s>><<fadein 3s>>
...a river's voice rises. Not a murmur. A <i>laugh</i>.
<</fadein>><</timed>><<include "CampStatus" "div">>
<<include "CampActions" "div">>
<<set setup.restMultiplier to 1>>
<<set setup.restLocationTag to "crater">>
<<set setup.restingWeights to {
combat: 0.4,
theft: 0.3,
neutral: 0.15,
post: 0.15
}>>
<<set setup.restInterruptChance to 0.05>>
<<set setup.restReturnPassage to "CraterCamp">>
<<print getPassageSize(passage());>><<set _next to exploreArea("crater")>>
<<if _next>>
<<goto _next>>
<<else>>
There are no more unexplored locations in the crater.
<</if>>
<<return "Return">>Sniffing around a large rock is another of the scavengers, it has not yet seen you.
<<set $combatIntro to "A small $enemy.name emerges from behind a rock, clutching a crude weapon!">>
<<script>>
var goblinCount = Math.random() < 0.5 ? 1 : 2;
setup.enemies = [];
for (var i = 0; i < goblinCount; i++) {
setup.enemies.push({
type: 'goblin',
armor: "none",
weapon: "stone",
name: "Scavenger",
weights: {attack: 0.8, defend: 0.1, focus: 0.1}
});
}
generateEnemies(setup.enemies);
<</script>>
<<link "Sneak away">>
<<script>>
if (sneakCheck(setup.enemies).success) {
Engine.play("CraterCamp");
}
else {
generateEnemies(setup.enemies);
startCombat("CraterCombatEventEasyWin", "CraterCombatEventEasyLoss", "CraterCombatEventEasyFlee", false, true, false);
}
<</script>>
<</link>>
<<link "Attack the scavenger" "StartCombat">>
<<script>>
generateEnemies(setup.enemies);
startCombat("CraterCombatEventEasyWin", "CraterCombatEventEasyLoss", "CraterCombatEventEasyFlee", false, true, false);
<</script>>
<</link>>The scavenger lies motionless on the dusty ground of the crater.
[[Return|CraterCamp]]You were unable to dodge the last stone thrown by the scavenger, and the world turns bright white before going dark.
[[Return|CraterCamp]]You figure that the scavengers stubby legs are no match for yours and you sprint away, leaving the scavenger jogging after you meekly throwing stones until their interest fail.
[[Return|CraterCamp]]The large scavenger collapses with a guttural groan, its club rolling from lifeless fingers. The remaining scavengers freeze for a moment, then scatter into the rocky crevices of the crater, their whooping cries echoing off the walls.
[[Return|CraterCamp]]The club catches you square in the chest, knocking the wind from your lungs. As you stumble backward, the smaller scavengers close in. The last thing you see is the large one's yellowed teeth bared in a triumphant snarl.
[[Return|CraterCamp]]You spot a gap between the scavengers and bolt for it. The large one bellows and gives chase, but its bulk slows it down on the uneven crater floor. You scramble up a rocky slope and duck behind a boulder, holding your breath until their frustrated snarls fade into the distance.
[[Return|CraterCamp]]You can see three shadowy figures in the distance, one larger than the others and carrying a large club. They seem to be searching the crater for something. <br>
<<set $combatIntro to "A larger scavenger gnarls at you as it is flanked by two smaller ones. It lifts its club and slams it into the ground, creating a silicate dust cloud.">>
<<script>>
setup.enemies = [
{
type: "goblin", name: "Scavenger", armor: "cloth", weapon: "stick", weights: {attack: 0.6, defend: 0.3, focus: 0.1}
},
{
type: "goblin", name: "Large scavenger", armor: "hide", weapon: "club", attributes: { constitution: 6, perception: 8 }, weights: {attack: 0.8, defend: 0.1, focus: 0.1}
},
{
type: "goblin", name: "Scavenger", armor: "cloth", weapon: "stick", weights: {attack: 0.6, defend: 0.3, focus: 0.1}
}
]
<</script>>
<<link "Sneak away">>
<<script>>
if (sneakCheck(setup.enemies).success) {
Engine.play("CraterCamp");
}
else {
generateEnemies(setup.enemies);
startCombat("CraterCombatEventMediumWin", "CraterCombatEventMediumLoss", "CraterCombatEventMediumFlee", false, true, false);
}
<</script>>
<</link>>
<<link "Attack the scavengers" "StartCombat">>
<<script>>
generateEnemies(setup.enemies);
startCombat("CraterCombatEventMediumWin", "CraterCombatEventMediumLoss", "CraterCombatEventMediumFlee", false, true, false);
<</script>>
<</link>><<explored "Small Stream" "media/icons/water-drop.svg">>
<<first>>
<<if Cycle.check('time', 'night')>>
You wander around the perimiter of the crater, the freezing cold of night embracing you.
<<else>>
You wander around the perimeter of the crater, the scorching rays of the sun burning your skin.
<</if>>
<<finally>>
You find the small stream, pouring down the crater wall.
<</first>>
<<linkreplace "Quench your thirst">>
<<editcycle 'thirst' reset>>
<</linkreplace>>
[[Return|CraterCamp]]<<explored "Mushroom Grove" "media/icons/grass-mushroom.svg">>
<<first>>
Near the damp shadow of the crater wall, you discover a cluster of pale, fleshy mushrooms growing from the cracks in the stone. They look edible, though distinctly alien.
<<finally>>
The mushroom grove continues to thrive in the damp shade, their caps glistening with moisture.
<</first>>
<<if not $noMushrooms>>
<<linkreplace "Gather mushrooms">>
You pick a handful of the rubbery fungi and shove them in your pocket.
<<script>>
addItemToInventory(7);
<</script>>
<<set $noMushrooms to true>>
<</linkreplace>>
<</if>>
[[Return|CraterCamp]]Half-buried in the silicate sand near the crater's center, you spot a glint of polished metal - wreckage from your descent pod. Among the twisted fragments, you find an intact solar plasma pistol, its power cells slowly recharging in the daylight.
<<script>>
window.addItemToInventory(10);
<</script>>
[[Return|CraterCamp]]<<first>>
A narrow fissure in the crater wall opens into a modest cave, dry and sheltered from the wind. The floor is covered in soft sand, and the walls show signs of previous habitation - ancient charcoal marks and scattered bones.
<<explored "Cave Shelter" "media/icons/cave-entrance.svg">>
<<finally>>
The cave remains a reliable shelter from the harsh weather outside.
<</first>>
<<nobr>>
<<set setup.restMultiplier to 2>>
<<set setup.restLocationTag to "cratercave">>
<<set setup.restingWeights to {
combat: 0.2,
theft: 0.2,
neutral: 0.30,
post: 0.30
}>>
<<set setup.restInterruptChance to 0.02>>
<<set setup.restReturnPassage to "CraterCave">>
<<customButton "Rest" "" "Sleep" "move-btn" "media/icons/night-sleep.svg">><</customButton>>
<</nobr>>
[[Return|CraterCamp]]<<first>>
Following a vein of exposed rock, you discover a shallow quarry where the crater walls have crumbled to reveal layers of workable stone. Sharp flakes and fragments litter the ground, suggesting this site has been used before.
<<explored "Stone Mine" "media/icons/mining.svg">>
<<finally>>
The quarry offers abundant raw stone for gathering.
<</first>>
[[Return|CraterCamp]]<<first>>
You scramble up the crater's rim and find yourself looking out over a vast riverine forest. Towering trees with silver bark line the banks of a wide, slow-moving river. The air is humid and thick with the calls of unfamiliar creatures. A narrow trail leads down from the rim toward the treeline.
<<explored "Forest Edge" "media/icons/forest.svg">>
<<finally>>
The path to the forest remains clear, winding down from the crater rim to the river below.
<</first>>
[[Return|CraterCamp]]You wake to the sound of skittering pebbles. A small scavenger is rummaging through your belongings! Before you can react, it grabs something and bolts into the darkness.
<<script>>
//var foodItems = $inventory.filter(item => item.id === 'mushroom' || item.id === 'raw_meat');
//if (foodItems.length > 0) {
// var stolen = foodItems.random();
// deleteItemFromInventory(stolen.instanceId);
// State.variables.theftResult = "It made off with your " + stolen.name + ".";
//} else {
State.variables.theftResult = "It found nothing worth taking and fled empty-handed.";
//}
<</script>>
<<print $theftResult>>
[[Continue|CraterCamp]]A rustling near your camp stirs you from half-sleep. You catch a glimpse of glowing eyes in the darkness before whatever it was slips away. You notice your supplies have been disturbed.
<<script>>
if (Math.random() < 0.3) {
State.variables.theftResult = "A waterskin has been chewed through and emptied.";
} else {
State.variables.theftResult = "Fortunately, nothing seems to be missing.";
}
<</script>>
<<print $theftResult>>
[[Continue|CraterCamp]]Strange patterns dance behind your eyelids - geometric shapes and symbols that seem almost familiar, yet slip away when you try to focus on them. You wake with a lingering sense of something important forgotten.
[[Go back to sleep|SleepContinue]]
[[Get up|CraterCamp]]The crater is alive with sound at night. Distant howls echo off the walls, accompanied by the clicking of insectoids and the occasional crash of shifting rocks. You drift in and out of sleep, kept alert by the alien symphony.
[[Try to sleep through it|SleepContinue]]
[[Get up|CraterCamp]]A brilliant streak of light splits the night sky, illuminating the crater in flashes of blue-white. The meteor burns up in the atmosphere, leaving a faint trail that fades slowly. You mark the spot where it seemed to land - somewhere beyond the crater rim.
[[Go back to sleep|SleepContinue]]
[[Get up|CraterCamp]]You wake to find a thin layer of frost has formed on your bedding. The crater floor is painted in shades of silver and grey, and your breath mists in the chill air. The sun has not yet cleared the crater's edge.
<<script>>
addBuff(17);
<</script>>
[[Get up|CraterCamp]]During the night, a dust storm swept through the crater. Everything is coated in a fine layer of silicate powder. You cough and wipe the grit from your eyes as you emerge from your shelter.
<<script>>
Cycle.get('thirst').update(10);
<</script>>
[[Clean yourself off|CraterCamp]]You wake to the gentle warmth of dawn sunlight spilling over the crater rim. The silence is broken only by the distant chirping of creatures in the forest beyond. For a moment, this alien world feels almost like home.
[[Start the day|CraterCamp]]Scratches at the cave entrance rouse you from sleep. Something small and opportunistic was investigating your camp, but the cave's narrow opening kept it at bay. You find fresh claw marks in the stone just outside.
[[Continue|CraterCave]]The wind shifts outside, causing the cave to resonate with low, thrumming tones. The sound seems to emanate from deep within the rock itself - a natural phenomenon, yet oddly melodic.
[[Go back to sleep|SleepContinue]]
[[Get up|CraterCave]]The cave provided excellent shelter through the night. You emerge well-rested, the harsh crater weather having passed you by completely.
<<script>>
Cycle.get('sleep').reset();
<</script>>
[[Leave the cave|CraterCamp]]<<nobr>>
<<first>>
Placeholder first exploration of the lake.
<<run $placesArray.delete(passage())>>
<<run $exploredPlaces.push(passage())>>
<<finally>>
Placeholder returning to the lake.
<</first>>
<</nobr>>
[[Camp]]Uh oh, you fell in the water!
<<link "Continue" $continue>><</link>><<nobr>>
<<first>>
Placeholder first exploration of the river.
<<run $placesArray.delete(passage())>>
<<run $exploredPlaces.push(passage())>>
<<finally>>
Placeholder returning to the river.
<</first>>
<</nobr>>
[[Camp]] [[Wade out into the river|RiverMiddle]]You are standing in the middle of a river.
You can see a small fire on the other side of the river.
[[Back to the river bank|River]] [[Go to the other side|Other Side of the River]]You are on the other side of the river.
[[Back to camp|Camp]] [[Go back in the river|RiverMiddle]]
<<first>>
<<run $exploredPlaces.push(passage())>>
<</first>><<nobr>>
<<set $location to "Craters Edge">>
<<set $locationImage to "media/locations/crater-edge.jpg">>
<</nobr>>
The pod. Your only anchor in this green hell. <i>It WILL save you.</i> The thought claws through the pain, a lifeline thrown to drowning flesh.
You rise. Joints scream like rusted hinges. Muscles tear like rotten fabric. The forest watches - a thousand leafy eyes judging your stumbling betrayal of its mossy grave.
The earth tilts. Roots snake around your ankles, whispering <i>stay, decay, become soil</i>. You wrench free. Blood blooms fresh on your calf where the bird feasted.
The path ahead: a cathedral of destruction. Fallen giants lie splayed like broken ribs, their splintered bones jutting skyward. You crawl through their graveyard:
Bark scrapes raw palms.
Rot stings nostrils with the sweet stench of dying worlds.
Shadows pool like blood beneath shattered trunks.
Your fingers sink into pulpy wood. Beetles swarm from the wound, black carapaces gleaming like spilled oil. At the crest, the crater gapes below - your past and future screaming from its depths.
There. At the epicenter of ruin:
The pod's carcass.
Twisted metal breathing smoke.
Your origin story written in scorch marks.
<i>Promises rusting in alien rain.</i>
<<linkreplace "Lean over the edge">>
<<fadein 2s>>
Acrid smoke drills into your sinuses - the sacred incense of shattered technology. Below lies your genesis-turned-tomb:
Twisted metal ribs curl skyward like a skeletal prayer.
Molten cables bleed black ichor into the soil.
Heat shimmers dance above the wreckage, mocking mirages of safety.
<i>This rusted corpse birthed you. Now it calls you home.</i> The crater's edge crumbles beneath your weight. Pebbles skitter down the slope, each click echoing like a countdown.
[[Climb down the craters edge|Crater]]
<</fadein>>
<</linkreplace>><<nobr>>
<<set $location to "Crater">>
<</nobr>>
The earth betrays your every step. Stones roll like conspirators beneath your <<print window.getItemInSlot("feet").name.toLowerCase();>>, sending you skidding down the slope in a miniature avalanche of scree. Each impact hammers through shattered ribs. <<set $painLevel += 1>>
A choking shroud of dust swallows the world; grave dirt clotting in your throat, blinding you to everything but the pod's grim silhouette. Every breath rasps the same warning: <i>you shouldn't be here</i>.
Your <<print window.getItemInSlot("feet").name.toLowerCase();>> sink into ash fine as cremated bones. Residual heat radiates through, cooking your soles.
The pod's carcass reveals itself in horrific intimacy: hull plates peeled back like rotting fruit skin; support struts snapped like femurs; the cockpit window webbed with cracks - a dead insect eye staring at heaven.
The air thrums with dying energy. Old scars pulse in time with the machinery's final heartbeats.
In front on the wretched carcass, an impish creature is moving, tearing, biting the metal corpse.
<<nobr>>
<<linkreplace "Pick up a rock">>
<<script>>
equipItem(addItemToInventory(8));
<</script>>
You find a small rock, holding it with your right hand.
<</linkreplace>>
<</nobr>>
<<link "Get Closer" "CraterGoblin">>
<<script>>
generateEnemies([{ type: "goblin",
name: "impish creature",
armor: "none",
weapon: "stone",
attributes: {
constitution: 6,
defense: 2
}
}]);
<</script>>
<</link>><<set $combatIntro to "It freezes. You can see its hand shaking fiercely. Slowly, it turns... and you see yourself reflected in eyes like fractured obsidian. A guttural hiss escapes its throat as it raises a $enemy.weapon carved from the crater's bones.">>
<<done>>
<<script>>window.startCombat('CraterGoblinWin', 'CraterGoblinLoss', 'CraterConsideration', true, true, false);<</script>>
<</done>>
<<include "CombatUI">>Your blow lands with sickening finality - the crunch of bone beneath desperate strength. The creature staggers, a wet rattle bubbling in its throat. For one suspended moment, its obsidian eyes hold yours... then it collapses face-first into the dust. Engine oil spills out of the creatures mouth, blooming around its skull like a black halo.
The pod stands unchallenged now - your steel womb, your shattered ark. Its wounds glow dully in the fading light.
<<nobr>>
<<linkreplace "Search the pods carcass">>
<<addclass "a[data-passage='CraterConsideration']" "hidden">>
<<fadein 1s>>
Ruin greets you. The pod's electronic heart lies eviscerated; silicon entrails spilled across scorched earth.
<</fadein>>
<br>
<<fadein 1s 3s>>
Torn wiring snakes through ash, copper veins ripped raw and weeping energy.
<</fadein>>
<br>
<<fadein 1s 6s>>
Hull plates curl like dead petals, revealing structural bones snapped clean through.
<</fadein>>
<br>
<<fadein 1s 9s>>
Inside the coffin-cage: restraints hang like gutted serpents, still smelling of your terror.
<</fadein>>
<br>
<<fadein 1s 12s>>
There. A hull plate hangs by a metallic sinew - cracked edge gleaming like a promise.
<br>
<<fadein 1s 15s>>
<<linkreplace "Claim the shard">>
<<script>>addItemToInventory(4);<</script>>
Metal screams as you wrench it free. The broken edge cuts your palm - a blood pact sealed.
<<removeclass "a[data-passage='CraterConsideration']" "hidden">>
<br><br>
<<linkreplace "Search inside the pod">>
Digging around in the oil and blood on the pods' inside, there is a small piece of tarp. Barely reaching it through the wiring and hanging restraints, you grasp the edge and is able to lift it out of the pod.
<br><br>
Inside the tarp is a small rectangular machine, backside covered in photovoltaic cells. Trying to power it on, nothing happens. <i>"It must be discharged"</i> you realize. <<set $codexGet to $totalDays>>
<<script>>addItemToInventory(9);<</script>>
<</linkreplace>>
<</linkreplace>>
<</fadein>>
<</fadein>>
<</linkreplace>>
<</nobr>>
[[Inspect the crater|CraterConsideration]]The world detonates in white agony as stone meets temple. You taste blood and silicate dust. Through your swimming vision, the creature looms - a shrieking gargoyle carved from nightmares. Its $enemy.weapon descends again...
<<fadein 200ms 2s>>
You wake to the pod's corpse groaning above you. Blood crusts your eyelashes. The creature is gone, but its guttural chittering echoes over the crater's rim. Your chance at salvation bleeds into the hungry soil.
<<if !settings.easyMode>>
How did you lose this battle? 😮💨
There is a toggle for easy combat mode in the settings.
<</if>>
<</fadein>>
<<link "Retry combat with more health?" "CraterGoblin">><<set $healthIndex to 3>><</link>>
[[Drag yourself to shelter and skip the battle|CraterConsideration]]The crater holds you in its dusty hand. You taste metal on the wind, the pod's dying breath. This scar in the earth birthed you; now it offers brutal sanctuary. Your gaze surveys the ashes of your arrival:
<<linkreplace "Consider the crater as shelter">>
<<fadein 1s>>
The pod's carcass lies broken but rich; veins of wiring to harvest, hull plates to salvage. Days could be spent tearing secrets from its ribs.
No reprieve from the sun's hammerblow... but when rain comes, that twisted metal roof will sing. And the crater walls? They'll break the wind's teeth.
No berries. No streams. Just dust that coats your tongue like ash. Every hour spent scavenging metal is an hour your stomach screams.
Shadows pool thickest where wreckage meets earth. That impish creature came from somewhere. How many more eyes study you from the rim?
<</fadein>>
<<fadein 1s 2s>>
<<linkreplace "Claim the crater as shelter">>
<<fadein 2s>>
You press a palm against the pod's still-warm skin. <i>"We're both stranded here,"</i> you whisper to the dead metal. <i>"At least one of us will survive." </i>
<<link "Explore the crater" "CampSetup">><<set $campLoc to "Crater">><</link>>
<</fadein>>
<</linkreplace>>
[[Retreat to the forest's edge|Clearing]]
<</fadein>>
<</linkreplace>><<nobr>>
<<set $location to 'Dark forest'>>
<<set $locationImage to 'media/locations/forest.webp'>>
<</nobr>>
The forest contains the necessary material for shelter and game to hunt.You decide that a day at the lake sounds perfect.
Standing up, aching joints makes themselves known, you listen for the soft sound of water and start your journey.You decide that drinkable water and possibly food is your primary goal.
Standing up, aching joints makes themselves known, you lumber towards the rivers murmur.<<nobr>>
<<first>>
Placeholder first exploration of the woods.
<<run $placesArray.delete(passage())>>
<<run $exploredPlaces.push(passage())>>
<<finally>>
Placeholder returning to the woods.
<</first>>
<</nobr>>
[[Camp]]
<<set $playerX = 57>>
<<set $playerY = 53>>
<<set $location to "Woods">>