JavaScript · 4707 bytes Raw Blame History
1 // Inventory persistence for dougk shop system
2 // Tracks owned items, equipped outfits, and placed buildings
3
4 import { getItem } from './items.js'
5
6 const STORAGE_KEYS = {
7 OWNED: 'dougk-owned-items',
8 EQUIPPED: 'dougk-equipped',
9 BUILDINGS: 'dougk-buildings',
10 BUILDINGS_FREEFORM: 'dougk-buildings-freeform'
11 }
12
13 function loadJSON(key, defaultValue) {
14 try {
15 const data = localStorage.getItem(key)
16 return data ? JSON.parse(data) : defaultValue
17 } catch {
18 return defaultValue
19 }
20 }
21
22 function saveJSON(key, value) {
23 localStorage.setItem(key, JSON.stringify(value))
24 }
25
26 const inventory = {
27 // Owned items (array of item IDs)
28 ownedItems: loadJSON(STORAGE_KEYS.OWNED, []),
29
30 // Equipped outfits per character
31 equipped: loadJSON(STORAGE_KEYS.EQUIPPED, {
32 doug: [],
33 donny: [],
34 ollie: []
35 }),
36
37 // Placed buildings (legacy zone-based format)
38 buildings: loadJSON(STORAGE_KEYS.BUILDINGS, []),
39
40 // Placed buildings (new freeform format with coordinates)
41 buildingsFreeform: loadJSON(STORAGE_KEYS.BUILDINGS_FREEFORM, []),
42
43 // Check if an item is owned
44 owns(itemId) {
45 return this.ownedItems.includes(itemId)
46 },
47
48 // Purchase an item (add to owned)
49 purchase(itemId) {
50 if (!this.owns(itemId)) {
51 this.ownedItems.push(itemId)
52 this.save()
53 return true
54 }
55 return false
56 },
57
58 // Equip an outfit to a character
59 // Automatically unequips any other outfit of the same type
60 equip(character, itemId) {
61 if (!this.equipped[character]) {
62 this.equipped[character] = []
63 }
64
65 // Get the type of the item being equipped
66 const newItem = getItem(itemId)
67 if (!newItem) return
68
69 // Find and unequip any item of the same type
70 const sameTypeItems = this.equipped[character].filter(equippedId => {
71 const equippedItem = getItem(equippedId)
72 return equippedItem && equippedItem.type === newItem.type
73 })
74
75 // Remove items of the same type
76 for (const oldId of sameTypeItems) {
77 this.equipped[character] = this.equipped[character].filter(id => id !== oldId)
78 }
79
80 // Now equip the new item
81 if (!this.equipped[character].includes(itemId)) {
82 this.equipped[character].push(itemId)
83 }
84 this.save()
85
86 // Return the unequipped items so the caller can update visuals
87 return sameTypeItems
88 },
89
90 // Unequip an outfit from a character
91 unequip(character, itemId) {
92 if (this.equipped[character]) {
93 this.equipped[character] = this.equipped[character].filter(id => id !== itemId)
94 this.save()
95 }
96 },
97
98 // Check if an outfit is equipped on a character
99 isEquipped(character, itemId) {
100 return this.equipped[character]?.includes(itemId) || false
101 },
102
103 // Get all equipped items for a character
104 getEquipped(character) {
105 return this.equipped[character] || []
106 },
107
108 // Place a building
109 placeBuilding(buildingType, zoneId) {
110 this.buildings.push({ type: buildingType, zoneId })
111 this.save()
112 },
113
114 // Check if a zone is occupied
115 isZoneOccupied(zoneId) {
116 return this.buildings.some(b => b.zoneId === zoneId)
117 },
118
119 // Get all placed buildings
120 getBuildings() {
121 return [...this.buildings]
122 },
123
124 // Alias for getBuildings (used by PlacementManager)
125 getPlacedBuildings() {
126 return this.getBuildings()
127 },
128
129 // Remove a placed building by zone ID
130 removeBuilding(zoneId) {
131 const index = this.buildings.findIndex(b => b.zoneId === zoneId)
132 if (index !== -1) {
133 this.buildings.splice(index, 1)
134 this.save()
135 return true
136 }
137 return false
138 },
139
140 // === Freeform placement methods (new system) ===
141
142 // Place a building with freeform coordinates
143 placeBuildingFreeform(data) {
144 // data: { type, x, z, rotation, placementId }
145 this.buildingsFreeform.push(data)
146 this.save()
147 },
148
149 // Get all freeform placed buildings
150 getPlacedBuildingsFreeform() {
151 return [...this.buildingsFreeform]
152 },
153
154 // Remove a freeform building by placement ID
155 removeBuildingFreeform(placementId) {
156 const index = this.buildingsFreeform.findIndex(b => b.placementId === placementId)
157 if (index !== -1) {
158 this.buildingsFreeform.splice(index, 1)
159 this.save()
160 return true
161 }
162 return false
163 },
164
165 // Save all state
166 save() {
167 saveJSON(STORAGE_KEYS.OWNED, this.ownedItems)
168 saveJSON(STORAGE_KEYS.EQUIPPED, this.equipped)
169 saveJSON(STORAGE_KEYS.BUILDINGS, this.buildings)
170 saveJSON(STORAGE_KEYS.BUILDINGS_FREEFORM, this.buildingsFreeform)
171 },
172
173 // Clear all data (for testing)
174 reset() {
175 this.ownedItems = []
176 this.equipped = { doug: [], donny: [], ollie: [] }
177 this.buildings = []
178 this.buildingsFreeform = []
179 this.save()
180 }
181 }
182
183 export default inventory