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'
6
 const gameState = {
6
 const gameState = {
7
   capturedKoi: parseInt(localStorage.getItem(STORAGE_KEY) || '0'),
7
   capturedKoi: parseInt(localStorage.getItem(STORAGE_KEY) || '0'),
8
 
8
 
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
+
9
   addKoi(n = 1) {
98
   addKoi(n = 1) {
10
     this.capturedKoi += n
99
     this.capturedKoi += n
11
     this.save()
100
     this.save()
12
     this.notifyListeners()
101
     this.notifyListeners()
102
+    // Check if new items became affordable
103
+    this.updateAffordableItems()
13
   },
104
   },
14
 
105
 
15
   spendKoi(n) {
106
   spendKoi(n) {
@@ -17,6 +108,8 @@ const gameState = {
17
       this.capturedKoi -= n
108
       this.capturedKoi -= n
18
       this.save()
109
       this.save()
19
       this.notifyListeners()
110
       this.notifyListeners()
111
+      // Recalculate affordable items after spending
112
+      this.updateAffordableItems()
20
       return true
113
       return true
21
     }
114
     }
22
     return false
115
     return false
src/renderers/three/index.jsmodified
@@ -17,7 +17,7 @@ import { openShop, closeShop, isShopOpen } from './shop/shopUI.js'
17
 import { showDialog, closeDialog, isDialogOpen } from './shop/dialogUI.js'
17
 import { showDialog, closeDialog, isDialogOpen } from './shop/dialogUI.js'
18
 import { getDialogForCharacter, getReturnDialog } from './shop/dialogScripts.js'
18
 import { getDialogForCharacter, getReturnDialog } from './shop/dialogScripts.js'
19
 import inventory from './shop/inventory.js'
19
 import inventory from './shop/inventory.js'
20
-import { getItem, CHARACTERS } from './shop/items.js'
20
+import { getItem, CHARACTERS, getAllItems } from './shop/items.js'
21
 
21
 
22
 let scene, camera, renderer, composer, outlinePass
22
 let scene, camera, renderer, composer, outlinePass
23
 let doug, pond, breadManager, donny, koiSchool, ollie, placementManager
23
 let doug, pond, breadManager, donny, koiSchool, ollie, placementManager
@@ -160,6 +160,9 @@ export function start(container) {
160
   placementManager = new PlacementManager(scene, pond, camera, toonGradient)
160
   placementManager = new PlacementManager(scene, pond, camera, toonGradient)
161
   placementManager.initForbiddenZones(pond.getInitialForbiddenZones())
161
   placementManager.initForbiddenZones(pond.getInitialForbiddenZones())
162
 
162
 
163
+  // Initialize shop approach tracking (for smart creature behavior)
164
+  gameState.initShopTracking(getAllItems(), inventory)
165
+
163
   // Post-processing
166
   // Post-processing
164
   composer = new EffectComposer(renderer)
167
   composer = new EffectComposer(renderer)
165
   composer.addPass(new RenderPass(scene, camera))
168
   composer.addPass(new RenderPass(scene, camera))
src/renderers/three/narwhal.jsmodified
@@ -1,5 +1,6 @@
1
 // Donny the Narwhal - distinguished gentleman of the deep
1
 // Donny the Narwhal - distinguished gentleman of the deep
2
 import * as THREE from 'three'
2
 import * as THREE from 'three'
3
+import gameState from './gameState.js'
3
 
4
 
4
 export function createDonny(scene, gradientMap) {
5
 export function createDonny(scene, gradientMap) {
5
   const group = new THREE.Group()
6
   const group = new THREE.Group()
@@ -470,13 +471,20 @@ export function createDonny(scene, gradientMap) {
470
   }
471
   }
471
 
472
 
472
   // Try to trigger shop approach (called from main loop)
473
   // Try to trigger shop approach (called from main loop)
474
+  // Only approaches Doug on first emergence or when player unlocked new affordable items
473
   function tryTriggerShop(koiCount, pond, doug) {
475
   function tryTriggerShop(koiCount, pond, doug) {
474
     if (state.shopCooldown > 0) return false
476
     if (state.shopCooldown > 0) return false
475
     if (koiCount < SHOP_KOI_THRESHOLD) return false
477
     if (koiCount < SHOP_KOI_THRESHOLD) return false
476
 
478
 
479
+    // Check if there's a reason to approach Doug
480
+    if (!gameState.shouldCreatureApproach('donny')) {
481
+      return false
482
+    }
483
+
477
     // If waiting, do full approach sequence
484
     // If waiting, do full approach sequence
478
     if (state.mode === 'waiting') {
485
     if (state.mode === 'waiting') {
479
       startShopApproach(pond, doug)
486
       startShopApproach(pond, doug)
487
+      gameState.markApproachComplete('donny')
480
       return true
488
       return true
481
     }
489
     }
482
 
490
 
@@ -485,6 +493,7 @@ export function createDonny(scene, gradientMap) {
485
       state.mode = 'shop_ready'
493
       state.mode = 'shop_ready'
486
       state.shopMode = true
494
       state.shopMode = true
487
       state.timer = 0
495
       state.timer = 0
496
+      gameState.markApproachComplete('donny')
488
       if (state.onShopReady) {
497
       if (state.onShopReady) {
489
         state.onShopReady('donny')
498
         state.onShopReady('donny')
490
       }
499
       }
src/renderers/three/octopus.jsmodified
@@ -1,5 +1,6 @@
1
 // Ollie the Octopus - curious inspector of the pond
1
 // Ollie the Octopus - curious inspector of the pond
2
 import * as THREE from 'three'
2
 import * as THREE from 'three'
3
+import gameState from './gameState.js'
3
 
4
 
4
 export function createOllie(scene, gradientMap) {
5
 export function createOllie(scene, gradientMap) {
5
   const group = new THREE.Group()
6
   const group = new THREE.Group()
@@ -554,13 +555,20 @@ export function createOllie(scene, gradientMap) {
554
   }
555
   }
555
 
556
 
556
   // Try to trigger shop approach
557
   // Try to trigger shop approach
558
+  // Only approaches Doug on first emergence or when player unlocked new affordable items
557
   function tryTriggerShop(koiCount, pond, doug) {
559
   function tryTriggerShop(koiCount, pond, doug) {
558
     if (state.shopCooldown > 0) return false
560
     if (state.shopCooldown > 0) return false
559
     if (koiCount < SHOP_KOI_THRESHOLD) return false
561
     if (koiCount < SHOP_KOI_THRESHOLD) return false
560
 
562
 
563
+    // Check if there's a reason to approach Doug
564
+    if (!gameState.shouldCreatureApproach('ollie')) {
565
+      return false
566
+    }
567
+
561
     // If waiting, do full approach sequence
568
     // If waiting, do full approach sequence
562
     if (state.mode === 'waiting') {
569
     if (state.mode === 'waiting') {
563
       startShopApproach(pond, doug)
570
       startShopApproach(pond, doug)
571
+      gameState.markApproachComplete('ollie')
564
       return true
572
       return true
565
     }
573
     }
566
 
574
 
@@ -569,6 +577,7 @@ export function createOllie(scene, gradientMap) {
569
       state.mode = 'shop_ready'
577
       state.mode = 'shop_ready'
570
       state.shopMode = true
578
       state.shopMode = true
571
       state.timer = 0
579
       state.timer = 0
580
+      gameState.markApproachComplete('ollie')
572
       if (state.onShopReady) {
581
       if (state.onShopReady) {
573
         state.onShopReady('ollie')
582
         state.onShopReady('ollie')
574
       }
583
       }
src/renderers/three/shop/items.jsmodified
@@ -1561,3 +1561,8 @@ export function getAllBuildings() {
1561
 export function getItem(itemId) {
1561
 export function getItem(itemId) {
1562
   return OUTFITS[itemId] || BUILDINGS[itemId] || null
1562
   return OUTFITS[itemId] || BUILDINGS[itemId] || null
1563
 }
1563
 }
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) {
414
       if (action === 'buy') {
414
       if (action === 'buy') {
415
         if (gameState.spendKoi(item.price)) {
415
         if (gameState.spendKoi(item.price)) {
416
           inventory.purchase(itemId)
416
           inventory.purchase(itemId)
417
+          // Recalculate affordable items after purchase
418
+          gameState.onPurchase()
417
           // Auto-equip after buying (this also unequips same-type items)
419
           // Auto-equip after buying (this also unequips same-type items)
418
           const unequippedIds = inventory.equip(item.character, itemId)
420
           const unequippedIds = inventory.equip(item.character, itemId)
419
           // Notify about unequipped items first
421
           // Notify about unequipped items first
@@ -458,6 +460,8 @@ function attachBuildingListeners(content) {
458
       if (action === 'buy') {
460
       if (action === 'buy') {
459
         if (gameState.spendKoi(item.price)) {
461
         if (gameState.spendKoi(item.price)) {
460
           inventory.purchase(itemId)
462
           inventory.purchase(itemId)
463
+          // Recalculate affordable items after purchase
464
+          gameState.onPurchase()
461
           if (onPurchaseCallback) onPurchaseCallback(item)
465
           if (onPurchaseCallback) onPurchaseCallback(item)
462
           playPurchase()
466
           playPurchase()
463
           updateShopContent()
467
           updateShopContent()