import G from './state.js'; import { STAFF_TRAITS } from '../data/furniture.js'; import { clamp, chance, randi, choice, emit, toast } from '../util.js'; // ============================================================ // STAFF — hiring, daily work effects, loyalty, drama // ============================================================ export function hireCandidate(candId) { const d = G.data; const cap = G.levelInfo().staffCap; if (d.staff.length >= cap) return { ok: false, reason: `Staff cap is ${cap}. Expand the shop!` }; const idx = d.candidates.findIndex(c => c.id === candId); if (idx < 0) return { ok: false, reason: 'Candidate gone' }; const c = d.candidates[idx]; d.staff.push({ ...c, hiredDay: d.shop.day }); d.candidates.splice(idx, 1); toast(`${c.name} joins as your ${c.role}! Say hi! 👋`, 'good', '🤝'); emit('staffChanged'); return { ok: true }; } export function fireStaff(id) { const i = G.data.staff.findIndex(s => s.id === id); if (i < 0) return; const s = G.data.staff[i]; G.data.staff.splice(i, 1); G.addRep(-1); toast(`${s.name} was let go. Word gets around… (-1 reputation)`, 'bad', '📦'); // friends get sad for (const o of G.data.staff) o.loyalty = clamp(o.loyalty - 6, 5, 100); emit('staffChanged'); } export function giveRaise(s) { s.salary = Math.round(s.salary * 1.15); s.loyalty = clamp(s.loyalty + 18, 0, 100); toast(`${s.name} beams — “Best boss ever!” (+loyalty)`, 'good', '🥰'); emit('staffChanged'); } // called each morning export function staffMorning() { const d = G.data; for (const s of [...d.staff]) { // loyalty drift toward fairness const fairPay = 18 + s.skill * 0.42; if (s.salary < fairPay * 0.85) s.loyalty -= 2; else s.loyalty += 1; s.loyalty = clamp(s.loyalty + randi(-1, 1), 0, 100); // quit? if (s.loyalty < 22 && chance(0.5)) { d.staff.splice(d.staff.indexOf(s), 1); toast(`${s.name} packed up and QUIT! “I deserve better…”`, 'bad', '🚪'); G.addRep(-0.5); continue; } // raise request if (s.loyalty < 45 && s.skill > 60 && chance(0.3)) { emit('raiseRequest', s); } // grumpy clashes const otherGrump = d.staff.find(o => o !== s && o.traits.includes('grumpy') && s.traits.includes('grumpy')); if (otherGrump && chance(0.25)) { toast(`${s.name} and ${otherGrump.name} argue about shelf organization… again.`, '', '😤'); s.loyalty -= 1; otherGrump.loyalty -= 1; } if (s.traits.includes('sticky') && !s.traits.includes('honest') && chance(0.12)) { const loss = Math.min(G.gold, randi(4, 14)); G.addGold(-loss); toast(`You catch ${s.name} pocketing ${loss}g from the till!`, 'bad', '🖐️'); } } // friendship boosts between friendly folks const friendlies = d.staff.filter(s => s.traits.includes('friendly')); if (friendlies.length === d.staff.length && d.staff.length >= 2) { for (const s of friendlies) s.loyalty = clamp(s.loyalty + 1, 0, 100); } } // speed multiplier a role benefits from export function traitSpeed(s) { let m = 1; for (const t of s.traits) m *= STAFF_TRAITS[t]?.speed ?? 1; return m; } export function traitSatBonus(s) { let b = 0; for (const t of s.traits) b += STAFF_TRAITS[t]?.satBonus ?? 0; return b; } // stocker auto-restock tick (during open hours) let restockTimer = 0; export function updateStocker(dtMin) { const stockers = G.staffByRole('stocker'); if (!stockers.length || !G.shop.isOpen) return; restockTimer += dtMin; const interval = 30 / traitSpeed(stockers[0]); if (restockTimer < interval) return; restockTimer = 0; // move most-demanded item from storage to any shelf with room for (const [pid, lot] of Object.entries(G.data.inventory)) { if (lot.qty <= 0) continue; for (const f of G.shelves()) { const cap = f.type === 'table' ? 6 : f.type === 'pedestal' ? 1 : 12; const stock = G.data.shelfStock[f.id] ||= {}; const used = Object.values(stock).reduce((a, b) => a + b, 0); if (used >= cap) continue; const move = Math.min(lot.qty, cap - used); lot.qty -= move; stock[pid] = (stock[pid] || 0) + move; if (lot.qty <= 0) delete G.data.inventory[pid]; emit('stockChanged'); return; // one box at a time — they're chill } } } // cleaner keeps beauty up (visual sparkle handled by gfx) export function cleanerBonus() { const cleaners = G.staffByRole('cleaner'); if (!cleaners.length) return 0; return Math.round(cleaners.reduce((a, c) => a + c.skill / 40, 0)); }