Skip to content

Commit 21b2a38

Browse files
committed
Fixed tracking of quest objectives
1 parent f6fbf45 commit 21b2a38

5 files changed

Lines changed: 91 additions & 11 deletions

File tree

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515

1616
---
1717

18+
## [1.0.4] - 2026-02-04
19+
20+
Building completion now updates quest objectives., daily quests now properly repeat after cooldown expires, Training system now properly updates quest objectives.
21+
22+
### Fixed
23+
- **Repeatable Quests**: Fixed bug where the "Accept Quest" button was hidden for completed repeatable quests, preventing players from accepting them again
24+
- **Daily Quest Visibility**: Daily quests now always show in the Daily tab, even when on cooldown
25+
- **Building Quest Tracking**: Completing construction of a building now properly updates quest objectives with `type: 'build'` (e.g., "Build a training facility" in Building a Reputation quest)
26+
- **Training Quest Tracking**: Assigning a training regimen to a gladiator now correctly updates quest objectives with `type: 'train'` (e.g., "Daily Training" quest)
27+
28+
### Added
29+
- **Cooldown Indicator**: Daily quests on cooldown now display remaining days (e.g., "⏳ 1 day left")
30+
- **Accept Again Button**: Repeatable quests show "Accept Again" button instead of "Accept Quest" when re-accepting
31+
32+
---
33+
1834
## [1.0.3] - 2026-02-04
1935

2036
Quest system fix for proper progress initialization.
@@ -128,6 +144,7 @@ This is the first public release of **Ludus Magnus: Reborn**, a complete Roman g
128144

129145
| Version | Date | Highlights |
130146
|---------|------|------------|
147+
| 1.0.4 | 2026-02-04 | Fixed tracking of quest objectives |
131148
| 1.0.3 | 2026-02-04 | Fixed quest progress initialization timing issue |
132149
| 1.0.2 | 2026-02-04 | Quest progress initialization recognizes existing game state |
133150
| 1.0.1 | 2026-02-04 | Bug fixes, training/nutrition systems, balance improvements |

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ludus-magnus-reborn",
3-
"version": "1.0.3",
3+
"version": "1.0.4",
44
"description": "A Roman gladiator ludus management simulation game. Build your gladiator school, train legendary fighters, and conquer the arena.",
55
"author": "Ludus Magnus: Reborn Contributors",
66
"license": "MIT",

src/components/screens/DashboardScreen.tsx

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ import {
1010
} from '@features/game/gameSlice';
1111
import { addGold, spendGold, consumeResource } from '@features/player/playerSlice';
1212
import { NUTRITION_OPTIONS, type NutritionQuality } from '@data/training';
13-
import { tickCooldowns as tickQuestCooldowns } from '@features/quests/questsSlice';
13+
import { tickCooldowns as tickQuestCooldowns, incrementObjective } from '@features/quests/questsSlice';
1414
import { tickCooldowns as tickFactionCooldowns } from '@features/factions/factionsSlice';
15+
import { getQuestById } from '@data/quests';
1516
import {
1617
updateConstructionProgress,
1718
completeConstruction,
@@ -54,6 +55,9 @@ export const DashboardScreen: React.FC = () => {
5455
const staffState = useAppSelector(state => state.staff);
5556
const employees = staffState?.employees || [];
5657
const totalDailyWages = staffState?.totalDailyWages || 0;
58+
59+
const questsState = useAppSelector(state => state.quests);
60+
const activeQuests = questsState?.activeQuests || [];
5761

5862
const [processingDay, setProcessingDay] = useState(false);
5963

@@ -224,6 +228,24 @@ export const DashboardScreen: React.FC = () => {
224228
if (newDays <= 0) {
225229
dispatch(completeConstruction(building.id));
226230
events.push(`Construction complete: ${building.type}`);
231+
232+
// Update quest objectives for building completion
233+
activeQuests.forEach(activeQuest => {
234+
const questDef = getQuestById(activeQuest.questId);
235+
if (!questDef) return;
236+
237+
questDef.objectives.forEach(objective => {
238+
// Check if this objective is for building this specific type
239+
if (objective.type === 'build' && objective.target === building.type) {
240+
dispatch(incrementObjective({
241+
questId: activeQuest.questId,
242+
objectiveId: objective.id,
243+
amount: 1,
244+
required: objective.required,
245+
}));
246+
}
247+
});
248+
});
227249
} else {
228250
dispatch(updateConstructionProgress({ id: building.id, daysRemaining: newDays }));
229251
}
@@ -258,7 +280,7 @@ export const DashboardScreen: React.FC = () => {
258280
// Advance day
259281
dispatch(advanceDay());
260282
setProcessingDay(false);
261-
}, [dispatch, currentDay, totalDailyWages, foodCosts, ludusFame, gold, roster, employees, buildings, resources]);
283+
}, [dispatch, currentDay, totalDailyWages, foodCosts, ludusFame, gold, roster, employees, buildings, resources, activeQuests]);
262284

263285
// Get phase icon
264286
const getPhaseIcon = (phase: string) => {

src/components/screens/QuestsScreen.tsx

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
getQuestById,
2020
getAvailableQuests,
2121
calculateQuestProgress,
22+
DAILY_QUESTS,
2223
type Quest,
2324
type QuestDialogue,
2425
type QuestObjective,
@@ -86,7 +87,8 @@ export const QuestsScreen: React.FC = () => {
8687
case 'side':
8788
return availableQuests.filter(q => q.type === 'side' || q.type === 'event');
8889
case 'daily':
89-
return availableQuests.filter(q => q.type === 'daily');
90+
// Show all daily quests (available + on cooldown) so users can see when they'll be ready
91+
return DAILY_QUESTS;
9092
case 'completed':
9193
return completedQuestIds.map(id => getQuestById(id)).filter((q): q is Quest => q !== undefined);
9294
default:
@@ -411,6 +413,7 @@ export const QuestsScreen: React.FC = () => {
411413
quest={quest}
412414
isCompleted={activeTab === 'completed'}
413415
isActive={activeQuests.some(q => q.questId === quest.id)}
416+
cooldownDays={questCooldowns[quest.id]}
414417
onSelect={() => {
415418
setSelectedQuest(quest);
416419
setShowQuestModal(true);
@@ -432,6 +435,7 @@ export const QuestsScreen: React.FC = () => {
432435
quest={selectedQuest}
433436
activeData={getActiveQuestData(selectedQuest.id)}
434437
isCompleted={completedQuestIds.includes(selectedQuest.id)}
438+
canAccept={availableQuests.some(q => q.id === selectedQuest.id)}
435439
onAccept={() => handleAcceptQuest(selectedQuest)}
436440
onClose={() => setShowQuestModal(false)}
437441
/>
@@ -463,25 +467,31 @@ interface QuestCardProps {
463467
quest: Quest;
464468
isCompleted: boolean;
465469
isActive: boolean;
470+
cooldownDays?: number;
466471
onSelect: () => void;
467472
}
468473

469474
const QuestCard: React.FC<QuestCardProps> = ({
470475
quest,
471476
isCompleted,
472477
isActive,
478+
cooldownDays,
473479
onSelect,
474480
}) => {
481+
const isOnCooldown = cooldownDays !== undefined && cooldownDays > 0;
482+
475483
return (
476484
<div
477485
onClick={onSelect}
478486
className={clsx(
479487
'p-4 rounded-lg border cursor-pointer transition-all',
480-
isCompleted
481-
? 'border-health-high bg-health-high/10'
482-
: isActive
483-
? 'border-roman-gold-500 bg-roman-gold-500/10'
484-
: 'border-roman-marble-600 bg-roman-marble-800 hover:border-roman-marble-500'
488+
isOnCooldown
489+
? 'border-roman-marble-700 bg-roman-marble-900 opacity-75'
490+
: isCompleted
491+
? 'border-health-high bg-health-high/10'
492+
: isActive
493+
? 'border-roman-gold-500 bg-roman-gold-500/10'
494+
: 'border-roman-marble-600 bg-roman-marble-800 hover:border-roman-marble-500'
485495
)}
486496
>
487497
<div className="flex items-start gap-3">
@@ -497,6 +507,11 @@ const QuestCard: React.FC<QuestCardProps> = ({
497507
{isActive && (
498508
<span className="text-xs px-2 py-0.5 bg-blue-600 rounded">Active</span>
499509
)}
510+
{isOnCooldown && (
511+
<span className="text-xs px-2 py-0.5 bg-roman-marble-600 rounded">
512+
{cooldownDays} day{cooldownDays > 1 ? 's' : ''} left
513+
</span>
514+
)}
500515
</div>
501516
<p className="text-sm text-roman-marble-400 mt-1 line-clamp-2">
502517
{quest.description}
@@ -591,6 +606,7 @@ interface QuestDetailViewProps {
591606
quest: Quest;
592607
activeData?: { objectives: { id: string; current: number; completed: boolean }[] };
593608
isCompleted: boolean;
609+
canAccept: boolean; // true if quest can be accepted (not on cooldown, available)
594610
onAccept: () => void;
595611
onClose: () => void;
596612
}
@@ -599,6 +615,7 @@ const QuestDetailView: React.FC<QuestDetailViewProps> = ({
599615
quest,
600616
activeData,
601617
isCompleted,
618+
canAccept,
602619
onAccept,
603620
onClose,
604621
}) => {
@@ -671,9 +688,9 @@ const QuestDetailView: React.FC<QuestDetailViewProps> = ({
671688
<Button variant="ghost" className="flex-1" onClick={onClose}>
672689
Close
673690
</Button>
674-
{!isActive && !isCompleted && (
691+
{!isActive && canAccept && (
675692
<Button variant="gold" className="flex-1" onClick={onAccept}>
676-
Accept Quest
693+
{isCompleted && quest.repeatable ? 'Accept Again' : 'Accept Quest'}
677694
</Button>
678695
)}
679696
</div>

src/components/screens/TrainingScreen.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
setNutrition,
77
learnSkill,
88
} from '@features/gladiators/gladiatorsSlice';
9+
import { incrementObjective } from '@features/quests/questsSlice';
910
import { MainLayout } from '@components/layout';
1011
import { Card, CardHeader, CardTitle, CardContent, Button, ProgressBar } from '@components/ui';
1112
import {
@@ -22,6 +23,7 @@ import {
2223
canLearnSkill,
2324
} from '@data/skillTrees';
2425
import { GLADIATOR_CLASSES } from '@data/gladiatorClasses';
26+
import { getQuestById } from '@data/quests';
2527
import type { Gladiator } from '@/types';
2628
import { clsx } from 'clsx';
2729

@@ -32,6 +34,8 @@ export const TrainingScreen: React.FC = () => {
3234
const { roster } = useAppSelector(state => state.gladiators);
3335
const { buildings } = useAppSelector(state => state.ludus);
3436
const { resources } = useAppSelector(state => state.player);
37+
const questsState = useAppSelector(state => state.quests);
38+
const activeQuests = questsState?.activeQuests || [];
3539

3640
const [selectedGladiatorId, setSelectedGladiatorId] = useState<string | null>(
3741
roster.length > 0 ? roster[0].id : null
@@ -44,6 +48,26 @@ export const TrainingScreen: React.FC = () => {
4448
// Handle training selection
4549
const handleSetTraining = (gladiatorId: string, trainingType: TrainingType | null) => {
4650
dispatch(setTrainingRegimen({ gladiatorId, trainingType }));
51+
52+
// Update quest objectives for training if a training type is selected (not null/stopping)
53+
if (trainingType) {
54+
activeQuests.forEach(activeQuest => {
55+
const questDef = getQuestById(activeQuest.questId);
56+
if (!questDef) return;
57+
58+
questDef.objectives.forEach(objective => {
59+
if (objective.type === 'train') {
60+
// For 'train' objectives, increment by 1 when assigning training
61+
dispatch(incrementObjective({
62+
questId: activeQuest.questId,
63+
objectiveId: objective.id,
64+
amount: 1,
65+
required: objective.required,
66+
}));
67+
}
68+
});
69+
});
70+
}
4771
};
4872

4973
// Handle nutrition selection

0 commit comments

Comments
 (0)