Hooks Reference - xopherdeep/do-it-for-the-xp GitHub Wiki

Hooks Reference

Custom Vue 3 Composables for the XP App


Overview

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/


Available 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

useTemple

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]);

Usage Example

<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>

useAudio

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();

Sound Themes

The audio system supports multiple themes:

Theme UI Sounds RPG Sounds
nintendo Classic Nintendo-style N/A
earthbound N/A Earthbound-inspired

useAbilities

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);

useAbilitySystem

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);

Ability Lifecycle

Locked → Unlock (AP cost) → Unlocked → Use (MP cost) → Cooling → Available

useDialog

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();

useMerchant

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);

useQuests

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);

useToast

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!');

Creating Custom Hooks

Follow these patterns when creating new hooks:

Basic Template

// 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,
  };
}

Best Practices

  1. Prefix with use - Follow Vue convention
  2. Return computed refs - Prevent external mutation
  3. Handle cleanup - Use onUnmounted for cleanup
  4. Type everything - Full TypeScript definitions
  5. Document inputs/outputs - Clear JSDoc comments

Related Documentation

⚠️ **GitHub.com Fallback** ⚠️