Hooks Reference - xopherdeep/do-it-for-the-xp GitHub Wiki
Custom Vue 3 Composables for the XP App
Hooks (composables) encapsulate reusable reactive logic following Vue 3's Composition API patterns. They provide clean interfaces to complex subsystems like audio, temples, and abilities.
Location: src/hooks/
| Hook | Purpose | Key Features |
|---|---|---|
useTemple |
Temple navigation | Movement, doors, chests, map |
useAudio |
Sound & music | Play, stop, volume control |
useAbilities |
Get abilities | Fetch, filter, status |
useAbilitySystem |
Ability actions | Use, unlock, cooldowns |
useDialog |
Dialog state | Open, close, message queue |
useMerchant |
Shop system | Buy, sell, inventory |
useQuests |
Quest tracking | Active, complete, filter |
useToast |
Notifications | Show toast messages |
Temple navigation and dungeon state management.
import { useTemple } from '@/hooks/useTemple';
const {
// === State ===
currentRoom, // Current room data
position, // [row, col] coordinates
templeState, // Full temple state object
showMap, // Map visibility toggle
// === Navigation ===
move, // (direction: 'north'|'south'|'east'|'west')
// === Interactions ===
isDoorLocked, // Check if door is locked
unlockDoor, // Unlock a door (uses key)
processChestItems, // Handle chest contents
handleTeleport, // Use teleport room
// === Map System ===
toggleMap, // Toggle map visibility
openMap, // Show map
closeMap, // Hide map
getRoomVisibility, // Get visibility status
isCurrentRoom, // Check if position is current
getRoomIcon, // Get icon for room type
getMapTileClass, // Get CSS classes for map tile
isRoomVisited, // Check if room was visited
showRoomDetails, // Should show room details?
// === Message Display ===
registerMessageDisplay, // Register message callback
displayMessage, // Show a message
} = useTemple('water-temple', [0, 0]);<script setup>
import { useTemple } from '@/hooks/useTemple';
const { move, currentRoom, templeState } = useTemple('fire-temple');
function handleMove(direction) {
const success = move(direction);
if (!success) {
console.log('Movement blocked!');
}
}
</script>Sound effects and music playback control.
import { useAudio } from '@/hooks/useAudio';
const {
// === Playback ===
play, // (soundId, options?) - Play a sound
playBGM, // (trackId) - Play background music
stopBGM, // Stop current BGM
playUI, // (uiSoundId) - Play UI sound
// === Volume ===
masterVolume, // Ref<number> - Master volume (0-1)
sfxVolume, // Ref<number> - SFX volume
bgmVolume, // Ref<number> - BGM volume
// === Controls ===
muted, // Ref<boolean> - Mute state
toggleMute, // Toggle mute
// === Sound Libraries ===
getSoundLibrary, // Get available sounds
} = useAudio();The audio system supports multiple themes:
| Theme | UI Sounds | RPG Sounds |
|---|---|---|
nintendo |
Classic Nintendo-style | N/A |
earthbound |
N/A | Earthbound-inspired |
Access and manage player abilities.
import { useAbilities } from '@/hooks/useAbilities';
const {
abilities, // All abilities
unlockedAbilities, // Abilities player has unlocked
availableAbilities, // Abilities ready to use
coolingAbilities, // Abilities on cooldown
getAbilityById, // (id) => Ability
filterByFrequency, // (freq) => Ability[]
} = useAbilities(userId);Execute ability actions.
import { useAbilitySystem } from '@/hooks/useAbilitySystem';
const {
useAbility, // (abilityId) => Promise<Result>
unlockAbility, // (abilityId) => Promise<void>
getRemainingCooldown,// (abilityId) => number (seconds)
canUseAbility, // (abilityId) => boolean
getAbilityCost, // (abilityId) => { ap?: number, mp?: number }
} = useAbilitySystem(userId);Locked → Unlock (AP cost) → Unlocked → Use (MP cost) → Cooling → Available
Dialog state management for RPG-style text boxes.
import { useDialog } from '@/hooks/useDialog';
const {
isOpen, // Ref<boolean>
currentMessage, // Ref<string>
messageQueue, // Ref<string[]>
open, // () - Open dialog
close, // () - Close dialog
showMessage, // (message) - Show single message
queueMessages, // (messages[]) - Queue multiple
nextMessage, // () - Advance to next message
clearQueue, // () - Clear all messages
} = useDialog();Shop interaction system.
import { useMerchant } from '@/hooks/useMerchant';
const {
inventory, // Available items
playerGP, // Current gold
buyItem, // (itemId, quantity?) => Result
sellItem, // (itemId, quantity?) => Result
canAfford, // (itemId) => boolean
getPrice, // (itemId) => number
} = useMerchant(shopId);Quest tracking and management.
import { useQuests } from '@/hooks/useQuests';
const {
activeQuests, // Currently active quests
completedQuests, // Finished quests
getQuestById, // (id) => Quest
markComplete, // (questId) => void
} = useQuests(userId);Toast notification system.
import { useToast } from '@/hooks/useToast';
const { showToast, showSuccess, showError, showWarning } = useToast();
// Basic usage
showToast('Item collected!');
// With options
showSuccess('Quest completed!', { duration: 3000 });
showError('Not enough GP!');
showWarning('Low HP!');Follow these patterns when creating new hooks:
// src/hooks/useCustomHook.ts
import { ref, computed, onMounted, onUnmounted } from 'vue';
export function useCustomHook(options?: CustomHookOptions) {
// === Internal State ===
const internalValue = ref<string>('');
// === Computed ===
const derivedValue = computed(() =>
internalValue.value.toUpperCase()
);
// === Actions ===
function setValue(value: string) {
internalValue.value = value;
}
// === Lifecycle ===
onMounted(() => {
// Setup logic
});
onUnmounted(() => {
// Cleanup logic
});
// === Return API ===
return {
// State (readonly when possible)
value: computed(() => internalValue.value),
derivedValue,
// Actions
setValue,
};
}-
Prefix with
use- Follow Vue convention - Return computed refs - Prevent external mutation
-
Handle cleanup - Use
onUnmountedfor cleanup - Type everything - Full TypeScript definitions
- Document inputs/outputs - Clear JSDoc comments
- Architecture - Technical overview
-
Temple System -
useTempledetails - Battle System Engine - Combat hooks