zeroed-some/dougk / 5a6a9e7

Browse files

improve donnyt and ollie interactions (don't always grab attention)

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
5a6a9e74a7a064ca09667cb3d16a0d9c727a829b
Parents
eef876c
Tree
8373eea

6 changed files

StatusFile+-
M src/renderers/three/gameState.js 93 0
M src/renderers/three/index.js 4 1
M src/renderers/three/narwhal.js 9 0
M src/renderers/three/octopus.js 9 0
M src/renderers/three/shop/items.js 5 0
M src/renderers/three/shop/shopUI.js 4 0
src/renderers/three/gameState.jsmodified
@@ -6,10 +6,101 @@ const STORAGE_KEY = 'dougk-koi'
66
 const gameState = {
77
   capturedKoi: parseInt(localStorage.getItem(STORAGE_KEY) || '0'),
88
 
9
+  // Shop approach tracking - only approach Doug when there's a reason
10
+  shopApproach: {
11
+    // First emergence this session - creatures should greet player
12
+    firstEmergence: {
13
+      donny: true,
14
+      ollie: true
15
+    },
16
+    // Track which items player could afford before last koi capture
17
+    previousAffordableIds: new Set(),
18
+    // Flag: player can now afford something new
19
+    hasNewAffordableItem: false,
20
+    // Items catalog reference (set by initShopTracking)
21
+    allItems: null,
22
+    // Inventory reference for checking owned items
23
+    inventory: null
24
+  },
25
+
26
+  // Initialize shop tracking with items and inventory references
27
+  initShopTracking(allItems, inventory) {
28
+    this.shopApproach.allItems = allItems
29
+    this.shopApproach.inventory = inventory
30
+    // Initialize previous affordable set based on current koi
31
+    this.updateAffordableItems()
32
+  },
33
+
34
+  // Get all purchasable (not owned) items player can afford
35
+  getAffordableItemIds() {
36
+    const { allItems, inventory } = this.shopApproach
37
+    if (!allItems || !inventory) return new Set()
38
+
39
+    const affordable = new Set()
40
+    for (const item of allItems) {
41
+      // Must not already own it and must be able to afford it
42
+      if (!inventory.owns(item.id) && item.price <= this.capturedKoi) {
43
+        affordable.add(item.id)
44
+      }
45
+    }
46
+    return affordable
47
+  },
48
+
49
+  // Update affordable items and check if player unlocked something new
50
+  updateAffordableItems() {
51
+    const currentAffordable = this.getAffordableItemIds()
52
+    const { previousAffordableIds } = this.shopApproach
53
+
54
+    // Check if any new items became affordable
55
+    for (const id of currentAffordable) {
56
+      if (!previousAffordableIds.has(id)) {
57
+        this.shopApproach.hasNewAffordableItem = true
58
+        break
59
+      }
60
+    }
61
+
62
+    // Update the previous set
63
+    this.shopApproach.previousAffordableIds = currentAffordable
64
+  },
65
+
66
+  // Check if a creature should approach Doug
67
+  // Returns true on first emergence or if player can afford something new
68
+  shouldCreatureApproach(creature) {
69
+    const { firstEmergence, hasNewAffordableItem } = this.shopApproach
70
+
71
+    // First emergence this session
72
+    if (firstEmergence[creature]) {
73
+      return true
74
+    }
75
+
76
+    // Player unlocked a new affordable item
77
+    if (hasNewAffordableItem) {
78
+      return true
79
+    }
80
+
81
+    return false
82
+  },
83
+
84
+  // Mark that a creature has approached (used after shop interaction starts)
85
+  markApproachComplete(creature) {
86
+    // Clear first emergence flag for this creature
87
+    this.shopApproach.firstEmergence[creature] = false
88
+
89
+    // Clear the new affordable flag (both creatures share this trigger)
90
+    this.shopApproach.hasNewAffordableItem = false
91
+  },
92
+
93
+  // Called when player purchases an item - recalculate affordable items
94
+  onPurchase() {
95
+    this.updateAffordableItems()
96
+  },
97
+
998
   addKoi(n = 1) {
1099
     this.capturedKoi += n
11100
     this.save()
12101
     this.notifyListeners()
102
+    // Check if new items became affordable
103
+    this.updateAffordableItems()
13104
   },
14105
 
15106
   spendKoi(n) {
@@ -17,6 +108,8 @@ const gameState = {
17108
       this.capturedKoi -= n
18109
       this.save()
19110
       this.notifyListeners()
111
+      // Recalculate affordable items after spending
112
+      this.updateAffordableItems()
20113
       return true
21114
     }
22115
     return false
src/renderers/three/index.jsmodified
@@ -17,7 +17,7 @@ import { openShop, closeShop, isShopOpen } from './shop/shopUI.js'
1717
 import { showDialog, closeDialog, isDialogOpen } from './shop/dialogUI.js'
1818
 import { getDialogForCharacter, getReturnDialog } from './shop/dialogScripts.js'
1919
 import inventory from './shop/inventory.js'
20
-import { getItem, CHARACTERS } from './shop/items.js'
20
+import { getItem, CHARACTERS, getAllItems } from './shop/items.js'
2121
 
2222
 let scene, camera, renderer, composer, outlinePass
2323
 let doug, pond, breadManager, donny, koiSchool, ollie, placementManager
@@ -160,6 +160,9 @@ export function start(container) {
160160
   placementManager = new PlacementManager(scene, pond, camera, toonGradient)
161161
   placementManager.initForbiddenZones(pond.getInitialForbiddenZones())
162162
 
163
+  // Initialize shop approach tracking (for smart creature behavior)
164
+  gameState.initShopTracking(getAllItems(), inventory)
165
+
163166
   // Post-processing
164167
   composer = new EffectComposer(renderer)
165168
   composer.addPass(new RenderPass(scene, camera))
src/renderers/three/narwhal.jsmodified
@@ -1,5 +1,6 @@
11
 // Donny the Narwhal - distinguished gentleman of the deep
22
 import * as THREE from 'three'
3
+import gameState from './gameState.js'
34
 
45
 export function createDonny(scene, gradientMap) {
56
   const group = new THREE.Group()
@@ -470,13 +471,20 @@ export function createDonny(scene, gradientMap) {
470471
   }
471472
 
472473
   // Try to trigger shop approach (called from main loop)
474
+  // Only approaches Doug on first emergence or when player unlocked new affordable items
473475
   function tryTriggerShop(koiCount, pond, doug) {
474476
     if (state.shopCooldown > 0) return false
475477
     if (koiCount < SHOP_KOI_THRESHOLD) return false
476478
 
479
+    // Check if there's a reason to approach Doug
480
+    if (!gameState.shouldCreatureApproach('donny')) {
481
+      return false
482
+    }
483
+
477484
     // If waiting, do full approach sequence
478485
     if (state.mode === 'waiting') {
479486
       startShopApproach(pond, doug)
487
+      gameState.markApproachComplete('donny')
480488
       return true
481489
     }
482490
 
@@ -485,6 +493,7 @@ export function createDonny(scene, gradientMap) {
485493
       state.mode = 'shop_ready'
486494
       state.shopMode = true
487495
       state.timer = 0
496
+      gameState.markApproachComplete('donny')
488497
       if (state.onShopReady) {
489498
         state.onShopReady('donny')
490499
       }
src/renderers/three/octopus.jsmodified
@@ -1,5 +1,6 @@
11
 // Ollie the Octopus - curious inspector of the pond
22
 import * as THREE from 'three'
3
+import gameState from './gameState.js'
34
 
45
 export function createOllie(scene, gradientMap) {
56
   const group = new THREE.Group()
@@ -554,13 +555,20 @@ export function createOllie(scene, gradientMap) {
554555
   }
555556
 
556557
   // Try to trigger shop approach
558
+  // Only approaches Doug on first emergence or when player unlocked new affordable items
557559
   function tryTriggerShop(koiCount, pond, doug) {
558560
     if (state.shopCooldown > 0) return false
559561
     if (koiCount < SHOP_KOI_THRESHOLD) return false
560562
 
563
+    // Check if there's a reason to approach Doug
564
+    if (!gameState.shouldCreatureApproach('ollie')) {
565
+      return false
566
+    }
567
+
561568
     // If waiting, do full approach sequence
562569
     if (state.mode === 'waiting') {
563570
       startShopApproach(pond, doug)
571
+      gameState.markApproachComplete('ollie')
564572
       return true
565573
     }
566574
 
@@ -569,6 +577,7 @@ export function createOllie(scene, gradientMap) {
569577
       state.mode = 'shop_ready'
570578
       state.shopMode = true
571579
       state.timer = 0
580
+      gameState.markApproachComplete('ollie')
572581
       if (state.onShopReady) {
573582
         state.onShopReady('ollie')
574583
       }
src/renderers/three/shop/items.jsmodified
@@ -1561,3 +1561,8 @@ export function getAllBuildings() {
15611561
 export function getItem(itemId) {
15621562
   return OUTFITS[itemId] || BUILDINGS[itemId] || null
15631563
 }
1564
+
1565
+// Get all purchasable items (outfits + buildings)
1566
+export function getAllItems() {
1567
+  return [...Object.values(OUTFITS), ...Object.values(BUILDINGS)]
1568
+}
src/renderers/three/shop/shopUI.jsmodified
@@ -414,6 +414,8 @@ function attachOutfitListeners(content) {
414414
       if (action === 'buy') {
415415
         if (gameState.spendKoi(item.price)) {
416416
           inventory.purchase(itemId)
417
+          // Recalculate affordable items after purchase
418
+          gameState.onPurchase()
417419
           // Auto-equip after buying (this also unequips same-type items)
418420
           const unequippedIds = inventory.equip(item.character, itemId)
419421
           // Notify about unequipped items first
@@ -458,6 +460,8 @@ function attachBuildingListeners(content) {
458460
       if (action === 'buy') {
459461
         if (gameState.spendKoi(item.price)) {
460462
           inventory.purchase(itemId)
463
+          // Recalculate affordable items after purchase
464
+          gameState.onPurchase()
461465
           if (onPurchaseCallback) onPurchaseCallback(item)
462466
           playPurchase()
463467
           updateShopContent()