A couple more quests added.

This commit is contained in:
Storm Dragon
2026-02-05 03:06:25 -05:00
parent a8ab10b31a
commit 11c64625cc
6 changed files with 278 additions and 4 deletions

BIN
sounds/quests/boomerang.ogg LFS Normal file

Binary file not shown.

View File

@@ -222,7 +222,7 @@ const int GOOSE_FALL_SPEED = 100; // ms per foot
const int GOOSE_FLY_AWAY_CHANCE = 0; // Chance out of 1000 per tick to fly away const int GOOSE_FLY_AWAY_CHANCE = 0; // Chance out of 1000 per tick to fly away
const int GOOSE_MAX_DIST_FROM_WATER = 4; // How far they can wander from water const int GOOSE_MAX_DIST_FROM_WATER = 4; // How far they can wander from water
const int GOOSE_MAX_COUNT = 3; const int GOOSE_MAX_COUNT = 3;
const int GOOSE_HOURLY_SPAWN_CHANCE = 35; // Percent chance per hour to spawn a goose const int GOOSE_HOURLY_SPAWN_CHANCE = 40; // Percent chance per hour to spawn a goose
const int GOOSE_SIGHT_RANGE = 0; const int GOOSE_SIGHT_RANGE = 0;
// Weather settings // Weather settings

View File

@@ -19,6 +19,22 @@ string get_base_equipment_name(int equip_type) {
return "Unknown"; return "Unknown";
} }
string get_base_equipment_name_plural(int equip_type) {
if (equip_type == EQUIP_SPEAR) return "Spears";
if (equip_type == EQUIP_AXE) return "Stone Axes";
if (equip_type == EQUIP_SLING) return "Slings";
if (equip_type == EQUIP_BOW) return "Bows";
if (equip_type == EQUIP_HAT) return "Skin Hats";
if (equip_type == EQUIP_GLOVES) return "Skin Gloves";
if (equip_type == EQUIP_PANTS) return "Skin Pants";
if (equip_type == EQUIP_TUNIC) return "Skin Tunics";
if (equip_type == EQUIP_MOCCASINS) return "Moccasins";
if (equip_type == EQUIP_POUCH) return "Skin Pouches";
if (equip_type == EQUIP_BACKPACK) return "Backpacks";
if (equip_type == EQUIP_FISHING_POLE) return "Fishing Poles";
return "Items";
}
// Get inventory count for an equipment type // Get inventory count for an equipment type
int get_unruned_equipment_count(int equip_type) { int get_unruned_equipment_count(int equip_type) {
if (equip_type == EQUIP_SPEAR) return get_personal_count(ITEM_SPEARS); if (equip_type == EQUIP_SPEAR) return get_personal_count(ITEM_SPEARS);
@@ -158,6 +174,12 @@ void run_rune_equipment_menu(int rune_type) {
engrave_rune(equip_type, rune_type); engrave_rune(equip_type, rune_type);
break; break;
} }
if (key_pressed(KEY_TAB)) {
int equip_type = equipment_types[selection];
engrave_rune_max(equip_type, rune_type);
break;
}
} }
} }
@@ -194,3 +216,48 @@ void engrave_rune(int equip_type, int rune_type) {
speak_with_history("Missing: " + missing, true); speak_with_history("Missing: " + missing, true);
} }
} }
void engrave_rune_max(int equip_type, int rune_type) {
// Validate requirements
string missing = "";
if (get_personal_count(ITEM_KNIVES) < 1) missing += "Stone Knife ";
int unruned_count = get_unruned_equipment_count(equip_type);
if (unruned_count < 1) {
speak_with_history("No " + get_base_equipment_name(equip_type) + " available.", true);
return;
}
int clay_count = get_personal_count(ITEM_CLAY);
int favor_count = int(favor);
int max_craft = unruned_count;
if (clay_count < max_craft) max_craft = clay_count;
if (favor_count < max_craft) max_craft = favor_count;
if (missing != "") {
speak_with_history("Missing: " + missing, true);
return;
}
if (max_craft <= 0) {
if (clay_count < 1) missing += "1 clay ";
if (favor < 1.0) missing += "1 favor ";
if (missing == "") missing = "resources";
speak_with_history("Missing: " + missing, true);
return;
}
// Consume materials (knife is not consumed, it's a tool)
add_personal_count(ITEM_CLAY, -max_craft);
favor -= double(max_craft);
for (int i = 0; i < max_craft; i++) {
decrement_unruned_equipment(equip_type);
add_runed_item(equip_type, rune_type);
}
simulate_crafting(6 * max_craft);
string item_name = (max_craft == 1) ? get_base_equipment_name(equip_type) : get_base_equipment_name_plural(equip_type);
speak_with_history("Engraved " + max_craft + " " + item_name + " with " + get_rune_name(rune_type) + ".", true);
}

View File

@@ -1,20 +1,26 @@
// Quest system // Quest system
#include "src/quests/bat_invasion_game.nvgt" #include "src/quests/bat_invasion_game.nvgt"
#include "src/quests/catch_the_boomerang_game.nvgt"
#include "src/quests/enchanted_melody_game.nvgt" #include "src/quests/enchanted_melody_game.nvgt"
#include "src/quests/escape_from_hel_game.nvgt" #include "src/quests/escape_from_hel_game.nvgt"
#include "src/quests/skeletal_bard_game.nvgt"
const int QUEST_BAT_INVASION = 0; const int QUEST_BAT_INVASION = 0;
const int QUEST_ENCHANTED_MELODY = 1; const int QUEST_CATCH_BOOMERANG = 1;
const int QUEST_ESCAPE_FROM_HEL = 2; const int QUEST_ENCHANTED_MELODY = 2;
const int QUEST_TYPE_COUNT = 3; const int QUEST_ESCAPE_FROM_HEL = 3;
const int QUEST_SKELETAL_BARD = 4;
const int QUEST_TYPE_COUNT = 5;
int[] quest_queue; int[] quest_queue;
bool quest_roll_done_today = false; bool quest_roll_done_today = false;
string get_quest_name(int quest_type) { string get_quest_name(int quest_type) {
if (quest_type == QUEST_BAT_INVASION) return "Bat Invasion"; if (quest_type == QUEST_BAT_INVASION) return "Bat Invasion";
if (quest_type == QUEST_CATCH_BOOMERANG) return "Catch the Boomerang";
if (quest_type == QUEST_ENCHANTED_MELODY) return "Enchanted Melody"; if (quest_type == QUEST_ENCHANTED_MELODY) return "Enchanted Melody";
if (quest_type == QUEST_ESCAPE_FROM_HEL) return "Escape from Hel"; if (quest_type == QUEST_ESCAPE_FROM_HEL) return "Escape from Hel";
if (quest_type == QUEST_SKELETAL_BARD) return "Skeletal Bard";
return "Unknown Quest"; return "Unknown Quest";
} }
@@ -22,12 +28,18 @@ string get_quest_description(int quest_type) {
if (quest_type == QUEST_BAT_INVASION) { if (quest_type == QUEST_BAT_INVASION) {
return "Bat Invasion. Giant killer bats are attacking. Press space to throw when the bat is centered."; return "Bat Invasion. Giant killer bats are attacking. Press space to throw when the bat is centered.";
} }
if (quest_type == QUEST_CATCH_BOOMERANG) {
return "Catch the Boomerang. Throw and catch the boomerang as it returns for up to 4 points.";
}
if (quest_type == QUEST_ENCHANTED_MELODY) { if (quest_type == QUEST_ENCHANTED_MELODY) {
return "Enchanted Melody. Repeat the pattern using E R D F or U I J K. Lowest to highest pitch."; return "Enchanted Melody. Repeat the pattern using E R D F or U I J K. Lowest to highest pitch.";
} }
if (quest_type == QUEST_ESCAPE_FROM_HEL) { if (quest_type == QUEST_ESCAPE_FROM_HEL) {
return "Escape from Hel. Press space to jump over open graves. The pace quickens."; return "Escape from Hel. Press space to jump over open graves. The pace quickens.";
} }
if (quest_type == QUEST_SKELETAL_BARD) {
return "Skeletal Bard. A skeleton named Billy Bones is practicing to become a bard. Count the notes.";
}
return "Unknown quest."; return "Unknown quest.";
} }
@@ -38,6 +50,14 @@ int get_quest_chance_from_favor() {
return chance; return chance;
} }
void quest_menu_background_tick() {
menu_background_tick();
}
void quest_boomerang_hit_sound() {
play_player_damage_sound();
}
void add_quest(int quest_type) { void add_quest(int quest_type) {
if (quest_queue.length() >= QUEST_MAX_ACTIVE) return; if (quest_queue.length() >= QUEST_MAX_ACTIVE) return;
quest_queue.insert_last(quest_type); quest_queue.insert_last(quest_type);
@@ -139,8 +159,10 @@ void run_quest(int quest_type) {
p.pause_all(); p.pause_all();
int score = 0; int score = 0;
if (quest_type == QUEST_BAT_INVASION) score = run_bat_invasion(); if (quest_type == QUEST_BAT_INVASION) score = run_bat_invasion();
else if (quest_type == QUEST_CATCH_BOOMERANG) score = run_catch_the_boomerang();
else if (quest_type == QUEST_ENCHANTED_MELODY) score = run_enchanted_melody(); else if (quest_type == QUEST_ENCHANTED_MELODY) score = run_enchanted_melody();
else if (quest_type == QUEST_ESCAPE_FROM_HEL) score = run_escape_from_hel(); else if (quest_type == QUEST_ESCAPE_FROM_HEL) score = run_escape_from_hel();
else if (quest_type == QUEST_SKELETAL_BARD) score = run_skeletal_bard();
apply_quest_reward(score); apply_quest_reward(score);
p.resume_all(); p.resume_all();
} }

View File

@@ -0,0 +1,89 @@
// Catch the Boomerang quest game
int run_catch_the_boomerang() {
string[] instructions;
instructions.insert_last("=== Catch the Boomerang ===");
instructions.insert_last("");
instructions.insert_last("Throw a boomerang and catch it on the return.");
instructions.insert_last("");
instructions.insert_last("How to play:");
instructions.insert_last(" - Press Space to throw");
instructions.insert_last(" - Press Space again when it sounds close");
instructions.insert_last(" - There are 5 turns");
instructions.insert_last(" - Better timing earns more points (up to 4)");
instructions.insert_last("");
instructions.insert_last("Close this screen to begin.");
text_reader_lines(instructions, "Quest Instructions", true);
wait(500);
const string boomerangSound = "sounds/quests/boomerang.ogg";
const int turnsTotal = 5;
int score = 0;
int caughtCount = 0;
string[] skillLevel = {"", "almost dropped", "managed to catch", "caught", "expertly caught"};
bool firstTurnPrompt = true;
for (int turn = 0; turn < turnsTotal; turn++) {
if (firstTurnPrompt) {
speak_with_history("Press Space to throw.", true);
firstTurnPrompt = false;
}
while (true) {
wait(5);
if (key_pressed(KEY_SPACE)) {
break;
}
}
int maxDistance = random(40, 70);
int boomerangY = -2;
bool returning = false;
bool resolved = false;
int stepDelay = random(180, 280);
int stepSize = 2;
while (!resolved) {
wait(stepDelay);
p.play_2d(boomerangSound, 0.0, 0.0, 0.0, boomerangY, false);
if (key_pressed(KEY_SPACE)) {
if (returning && boomerangY > -5) {
int points = 5 + boomerangY;
if (points < 1) points = 1;
if (points > 4) points = 4;
score += points;
caughtCount++;
string promptSuffix = (turn < turnsTotal - 1) ? " Press Space to throw." : "";
speak_with_history("You " + skillLevel[points] + " the boomerang. " + points + " points." + promptSuffix, true);
} else {
string promptSuffix = (turn < turnsTotal - 1) ? " Press Space to throw." : "";
speak_with_history("You missed the boomerang." + promptSuffix, true);
}
resolved = true;
}
if (!resolved) {
if (!returning && boomerangY <= -maxDistance) {
returning = true;
}
boomerangY += returning ? stepSize : -stepSize;
if (boomerangY >= 0) {
string promptSuffix = (turn < turnsTotal - 1) ? " Press Space to throw." : "";
speak_with_history("The boomerang hit you." + promptSuffix, true);
quest_boomerang_hit_sound();
resolved = true;
}
}
}
wait(400);
}
speak_with_history("You caught " + caughtCount + " boomerangs for a total of " + score + " points.", true);
return score;
}

View File

@@ -0,0 +1,93 @@
// Skeletal Bard quest game
string[] skeletalBardNotes = {
"sounds/quests/bone1.ogg",
"sounds/quests/bone2.ogg",
"sounds/quests/bone3.ogg",
"sounds/quests/bone4.ogg",
"sounds/quests/bone5.ogg",
"sounds/quests/bone6.ogg",
"sounds/quests/bone7.ogg",
"sounds/quests/bone8.ogg"
};
void play_skeletal_bard_note() {
int noteIndex = random(0, skeletalBardNotes.length() - 1);
p.play_stationary(skeletalBardNotes[noteIndex], false);
}
int select_note_count(int minNotes, int maxNotes, int startValue) {
int selection = startValue;
speak_with_history("Select number of notes. " + selection + ".", true);
while (true) {
wait(5);
quest_menu_background_tick();
if (key_pressed(KEY_UP)) {
if (selection < maxNotes) selection++;
speak_with_history("" + selection + ".", true);
}
if (key_pressed(KEY_DOWN)) {
if (selection > minNotes) selection--;
speak_with_history("" + selection + ".", true);
}
if (key_pressed(KEY_RETURN)) {
return selection;
}
}
return selection;
}
int run_skeletal_bard() {
string[] instructions;
instructions.insert_last("=== Skeletal Bard ===");
instructions.insert_last("");
instructions.insert_last("A skeleton named Billy Bones is practicing to become a bard.");
instructions.insert_last("Listen to the melody and count the notes.");
instructions.insert_last("");
instructions.insert_last("How to play:");
instructions.insert_last(" - After the tune, choose the number of notes");
instructions.insert_last(" - Use Up/Down to change the number");
instructions.insert_last(" - Press Enter to confirm");
instructions.insert_last(" - There are 5 rounds, up to 4 points each");
instructions.insert_last("");
instructions.insert_last("Close this screen to begin.");
text_reader_lines(instructions, "Quest Instructions", true);
wait(500);
int score = 0;
const int rounds = 5;
for (int round = 0; round < rounds; round++) {
int length = random(5, 20);
for (int i = 0; i < length; i++) {
play_skeletal_bard_note();
wait(random(200, 400));
}
wait(400);
int guess = select_note_count(5, 20, 5);
int diff = length - guess;
if (diff < 0) diff = -diff;
int points = 4 - diff;
if (points < 0) points = 0;
score += points;
speak_with_history("You entered " + guess + " notes and the actual number was " + length + ". " + points + " points. Press Enter to continue.", true);
while (true) {
wait(5);
if (key_pressed(KEY_RETURN)) {
break;
}
}
}
speak_with_history("Skeletal Bard complete. Your score for all five tunes is " + score + ".", true);
return score;
}