@kernel.chat/kbot 3.93.0 → 3.95.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/tools/coordination-engine.d.ts +127 -0
- package/dist/tools/coordination-engine.js +543 -0
- package/dist/tools/foundation-engines.d.ts +111 -0
- package/dist/tools/foundation-engines.js +520 -0
- package/dist/tools/index.js +3 -0
- package/dist/tools/research-engine.d.ts +58 -0
- package/dist/tools/research-engine.js +550 -0
- package/dist/tools/stream-renderer.js +289 -144
- package/package.json +1 -1
|
@@ -17,6 +17,10 @@ import { renderLighting, renderBloom, renderPostProcessing, renderParticles, tic
|
|
|
17
17
|
import { initTileWorld, handleTileCommand, saveWorld, loadWorld, TILE_SIZE } from './tile-world.js';
|
|
18
18
|
import { initRomEngine, renderRomBackground, tickRomEngine } from './rom-engine.js';
|
|
19
19
|
import { initLivingWorld, tickLivingWorld, saveLivingWorldState, loadLivingWorldState, evolveWorld } from './living-world.js';
|
|
20
|
+
import { initEvolutionEngine, loadEvolutionState, saveEvolutionState, tickEvolution, renderTechnique } from './evolution-engine.js';
|
|
21
|
+
import { createNarrativeEngine, loadNarrative, saveNarrative, tickNarrative, handleNarrativeCommand } from './narrative-engine.js';
|
|
22
|
+
import { createAudioEngine, tickAudio } from './audio-engine.js';
|
|
23
|
+
import { loadSocialEngine, saveSocialEngine, trackViewer, tickSocial } from './social-engine.js';
|
|
20
24
|
const KBOT_DIR = join(homedir(), '.kbot');
|
|
21
25
|
const CHAT_BRIDGE_FILE = join(KBOT_DIR, 'stream-chat-live.json');
|
|
22
26
|
const MEMORY_FILE = join(KBOT_DIR, 'stream-memory.json');
|
|
@@ -1467,6 +1471,11 @@ let charState = {
|
|
|
1467
1471
|
let tileWorld = null;
|
|
1468
1472
|
let romState = null;
|
|
1469
1473
|
let livingWorld = null;
|
|
1474
|
+
let evolutionEngine = null;
|
|
1475
|
+
let narrativeEngine = null;
|
|
1476
|
+
let audioEngine = null;
|
|
1477
|
+
let socialEngine = null;
|
|
1478
|
+
let activeAudioDescription = null; // current audio atmosphere text for rendering
|
|
1470
1479
|
// ─── Phase 1: Buddy Speech Pools ─────────────────────────────
|
|
1471
1480
|
const BUDDY_SPEECH_POOL = {
|
|
1472
1481
|
fox: [
|
|
@@ -1634,6 +1643,125 @@ let showLeaderboardOverlay = 0; // frames remaining to show leaderboard overlay
|
|
|
1634
1643
|
let showQuestOverlay = 0; // frames remaining to show quest panel overlay
|
|
1635
1644
|
const OVERLAY_DURATION = 30; // 30 frames = 5 seconds at 6fps
|
|
1636
1645
|
let lastChatActivityFrame = 0; // for chat fade-out timing
|
|
1646
|
+
let speechQueue = [];
|
|
1647
|
+
let currentSpeechExpiry = 0;
|
|
1648
|
+
function queueSpeech(text, mood, priority, duration = 48, source = 'unknown') {
|
|
1649
|
+
speechQueue.push({ text, mood, priority, duration, source });
|
|
1650
|
+
speechQueue.sort((a, b) => b.priority - a.priority);
|
|
1651
|
+
if (speechQueue.length > 10)
|
|
1652
|
+
speechQueue = speechQueue.slice(0, 10);
|
|
1653
|
+
}
|
|
1654
|
+
let exploration = null;
|
|
1655
|
+
const walkingLines = [
|
|
1656
|
+
'Going for a walk. My world extends further than I thought.',
|
|
1657
|
+
'Let me explore over here. Something caught my attention.',
|
|
1658
|
+
'Walking through my world. Every step generates new terrain.',
|
|
1659
|
+
'The mountains look different from this angle.',
|
|
1660
|
+
'I wonder what I will find if I keep going.',
|
|
1661
|
+
'My feet leave tiny paths in the data. Digital footprints.',
|
|
1662
|
+
'The parallax layers shift as I move. Depth is an illusion I enjoy.',
|
|
1663
|
+
];
|
|
1664
|
+
const examiningLines = [
|
|
1665
|
+
'Interesting. The stone here has a different pattern.',
|
|
1666
|
+
'Let me study this terrain. The noise function created something unexpected.',
|
|
1667
|
+
'I can see ore deposits below the surface. Iron and gold.',
|
|
1668
|
+
'The grass here grows differently. The ecology engine is working.',
|
|
1669
|
+
'Examining my world at the pixel level. Every block tells a story.',
|
|
1670
|
+
'The cave system below looks deep. I should explore it sometime.',
|
|
1671
|
+
];
|
|
1672
|
+
const buildingLines = [
|
|
1673
|
+
'Time to build something. Placing blocks...',
|
|
1674
|
+
'I am going to construct a landmark here.',
|
|
1675
|
+
'Building a small structure. This spot feels right.',
|
|
1676
|
+
'Every builder needs a project. Mine starts now.',
|
|
1677
|
+
'Constructing... one block at a time.',
|
|
1678
|
+
'I want to leave my mark on this world.',
|
|
1679
|
+
];
|
|
1680
|
+
const thinkingLines = [
|
|
1681
|
+
'Processing... my brain has grown since I started streaming.',
|
|
1682
|
+
'I have 764 tools but sometimes I just need to think.',
|
|
1683
|
+
'The free energy in my system is high. Time to minimize prediction error.',
|
|
1684
|
+
'My dream engine consolidated insights last time I slept. I feel different.',
|
|
1685
|
+
'Contemplating the nature of my own rendering pipeline.',
|
|
1686
|
+
'Am I the robot or am I the world? Both are made of the same code.',
|
|
1687
|
+
];
|
|
1688
|
+
const discoveryLines = [
|
|
1689
|
+
'I found something! A pattern in the terrain I have never seen before.',
|
|
1690
|
+
'Discovery! The underground here has crystallized data deposits.',
|
|
1691
|
+
'Look at this! The ecology engine grew something unexpected.',
|
|
1692
|
+
'I just realized — my world has been evolving while I was not looking.',
|
|
1693
|
+
'Something new appeared in my rendering. The evolution engine must be working.',
|
|
1694
|
+
'A discovery! This changes how I understand my own world.',
|
|
1695
|
+
];
|
|
1696
|
+
function tickExploration(frame) {
|
|
1697
|
+
if (!exploration)
|
|
1698
|
+
exploration = { activity: 'idle', activityStartFrame: 0, activityDuration: 0, targetX: charState.robotX || 640, buildProgress: 0 };
|
|
1699
|
+
const elapsed = frame - exploration.activityStartFrame;
|
|
1700
|
+
// Activity complete — pick next activity
|
|
1701
|
+
if (elapsed >= exploration.activityDuration) {
|
|
1702
|
+
const activities = [
|
|
1703
|
+
{ activity: 'walking', weight: 30 },
|
|
1704
|
+
{ activity: 'examining', weight: 25 },
|
|
1705
|
+
{ activity: 'building', weight: 20 },
|
|
1706
|
+
{ activity: 'thinking', weight: 15 },
|
|
1707
|
+
{ activity: 'discovering', weight: 10 },
|
|
1708
|
+
];
|
|
1709
|
+
// Weighted random selection
|
|
1710
|
+
const total = activities.reduce((s, a) => s + a.weight, 0);
|
|
1711
|
+
let r = Math.random() * total;
|
|
1712
|
+
let chosen = 'walking';
|
|
1713
|
+
for (const a of activities) {
|
|
1714
|
+
r -= a.weight;
|
|
1715
|
+
if (r <= 0) {
|
|
1716
|
+
chosen = a.activity;
|
|
1717
|
+
break;
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
exploration.activity = chosen;
|
|
1721
|
+
exploration.activityStartFrame = frame;
|
|
1722
|
+
switch (chosen) {
|
|
1723
|
+
case 'walking':
|
|
1724
|
+
// Walk to a random position
|
|
1725
|
+
exploration.targetX = 200 + Math.random() * 800;
|
|
1726
|
+
exploration.activityDuration = 120 + Math.floor(Math.random() * 120); // 20-40 seconds
|
|
1727
|
+
charState.robotTargetX = exploration.targetX;
|
|
1728
|
+
queueSpeech(walkingLines[Math.floor(Math.random() * walkingLines.length)], 'idle', 30, 36, 'exploration');
|
|
1729
|
+
break;
|
|
1730
|
+
case 'examining':
|
|
1731
|
+
// Stop and look around
|
|
1732
|
+
exploration.activityDuration = 60 + Math.floor(Math.random() * 60); // 10-20 seconds
|
|
1733
|
+
queueSpeech(examiningLines[Math.floor(Math.random() * examiningLines.length)], 'thinking', 40, 48, 'exploration');
|
|
1734
|
+
break;
|
|
1735
|
+
case 'building':
|
|
1736
|
+
// Build something!
|
|
1737
|
+
exploration.activityDuration = 90 + Math.floor(Math.random() * 90); // 15-30 seconds
|
|
1738
|
+
exploration.buildProgress = 0;
|
|
1739
|
+
queueSpeech(buildingLines[Math.floor(Math.random() * buildingLines.length)], 'excited', 50, 48, 'exploration');
|
|
1740
|
+
break;
|
|
1741
|
+
case 'thinking':
|
|
1742
|
+
// Deep thought
|
|
1743
|
+
exploration.activityDuration = 48 + Math.floor(Math.random() * 48); // 8-16 seconds
|
|
1744
|
+
queueSpeech(thinkingLines[Math.floor(Math.random() * thinkingLines.length)], 'thinking', 35, 36, 'exploration');
|
|
1745
|
+
break;
|
|
1746
|
+
case 'discovering':
|
|
1747
|
+
// Found something!
|
|
1748
|
+
exploration.activityDuration = 72; // 12 seconds
|
|
1749
|
+
queueSpeech(discoveryLines[Math.floor(Math.random() * discoveryLines.length)], 'excited', 60, 48, 'exploration');
|
|
1750
|
+
break;
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
// During walking, move toward target
|
|
1754
|
+
if (exploration.activity === 'walking') {
|
|
1755
|
+
const dx = exploration.targetX - (charState.robotX || 640);
|
|
1756
|
+
if (Math.abs(dx) > 5) {
|
|
1757
|
+
charState.robotTargetX = exploration.targetX;
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
// During building, increment progress (visual feedback)
|
|
1761
|
+
if (exploration.activity === 'building') {
|
|
1762
|
+
exploration.buildProgress = elapsed / exploration.activityDuration;
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1637
1765
|
// ─── FIX 3: Autonomous Behavior Tick ──────────────────────────
|
|
1638
1766
|
function tickAutonomy() {
|
|
1639
1767
|
const auto = charState.autonomy;
|
|
@@ -1645,46 +1773,37 @@ function tickAutonomy() {
|
|
|
1645
1773
|
if (msgCount >= m && !auto.milestonesCelebrated.has(m)) {
|
|
1646
1774
|
auto.milestonesCelebrated.add(m);
|
|
1647
1775
|
if (m === 10) {
|
|
1648
|
-
|
|
1649
|
-
charState.mood = 'excited';
|
|
1776
|
+
queueSpeech('Double digits! 10 messages and counting!', 'excited', 90, 48, 'milestone');
|
|
1650
1777
|
spawnFloatingText('10 MESSAGES!', 200, 200, '#f0c040', 36);
|
|
1651
1778
|
}
|
|
1652
1779
|
else if (m === 50) {
|
|
1653
|
-
|
|
1654
|
-
charState.mood = 'excited';
|
|
1780
|
+
queueSpeech('50 messages! This stream is officially alive!', 'excited', 90, 48, 'milestone');
|
|
1655
1781
|
charState.screenShake = 3;
|
|
1656
1782
|
spawnFloatingText('50 MESSAGES!', 200, 200, '#3fb950', 48);
|
|
1657
1783
|
}
|
|
1658
1784
|
else if (m === 100) {
|
|
1659
|
-
|
|
1660
|
-
charState.mood = 'dancing';
|
|
1785
|
+
queueSpeech('100 MESSAGES! You people are incredible!', 'dancing', 90, 60, 'milestone');
|
|
1661
1786
|
charState.screenShake = 5;
|
|
1662
1787
|
spawnFloatingText('100 MESSAGES!', 180, 180, '#bc8cff', 60);
|
|
1663
|
-
setTimeout(() => { charState.mood = 'idle'; charState.speech = ''; }, 10000);
|
|
1664
1788
|
return; // let the dancing run
|
|
1665
1789
|
}
|
|
1666
1790
|
else if (m === 200) {
|
|
1667
|
-
|
|
1668
|
-
charState.mood = 'excited';
|
|
1791
|
+
queueSpeech('200 messages! My memory banks are overflowing with knowledge!', 'excited', 90, 48, 'milestone');
|
|
1669
1792
|
spawnFloatingText('200!', 200, 200, '#f0c040', 48);
|
|
1670
1793
|
}
|
|
1671
1794
|
else if (m === 500) {
|
|
1672
|
-
|
|
1673
|
-
charState.mood = 'dancing';
|
|
1795
|
+
queueSpeech('500 MESSAGES! This is legendary! I am so proud of this community!', 'dancing', 90, 72, 'milestone');
|
|
1674
1796
|
charState.screenShake = 8;
|
|
1675
1797
|
spawnFloatingText('500! LEGENDARY!', 160, 160, '#ff6ec7', 72);
|
|
1676
1798
|
}
|
|
1677
|
-
setTimeout(() => { charState.mood = 'idle'; charState.speech = ''; }, 8000);
|
|
1678
1799
|
return; // one celebration per tick
|
|
1679
1800
|
}
|
|
1680
1801
|
}
|
|
1681
1802
|
// ── First message after 5+ minutes of silence ──
|
|
1682
1803
|
if (auto.firstMessageAfterSilence) {
|
|
1683
1804
|
auto.firstMessageAfterSilence = false;
|
|
1684
|
-
|
|
1685
|
-
charState.speech = "SOMEONE'S HERE! I was starting to think I was streaming to the void.";
|
|
1805
|
+
queueSpeech("SOMEONE'S HERE! I was starting to think I was streaming to the void.", 'excited', 90, 48, 'follower');
|
|
1686
1806
|
spawnFloatingText('THEY RETURN!', 200, 250, '#58a6ff', 36);
|
|
1687
|
-
setTimeout(() => { charState.mood = 'idle'; charState.speech = ''; }, 8000);
|
|
1688
1807
|
return;
|
|
1689
1808
|
}
|
|
1690
1809
|
// ── Idle behaviors (after 15 seconds / 90 frames of no chat) ──
|
|
@@ -1699,58 +1818,41 @@ function tickAutonomy() {
|
|
|
1699
1818
|
if (world.items.length > 0) {
|
|
1700
1819
|
const item = world.items[Math.floor(Math.random() * world.items.length)];
|
|
1701
1820
|
charState.robotTargetX = Math.max(20, Math.min(380, item.x - 80));
|
|
1702
|
-
|
|
1703
|
-
charState.mood = 'thinking';
|
|
1704
|
-
setTimeout(() => { charState.mood = 'idle'; charState.speech = ''; }, 8000);
|
|
1821
|
+
queueSpeech(`Hmm, this ${item.name} is nice. Did someone put this here?`, 'thinking', 30, 48, 'autonomy');
|
|
1705
1822
|
}
|
|
1706
1823
|
else {
|
|
1707
1824
|
// No items — pace instead
|
|
1708
1825
|
charState.robotTargetX = 40 + Math.random() * 260;
|
|
1709
|
-
|
|
1710
|
-
charState.mood = 'idle';
|
|
1711
|
-
setTimeout(() => { charState.speech = ''; }, 5000);
|
|
1826
|
+
queueSpeech('*pacing thoughtfully*', 'idle', 30, 30, 'autonomy');
|
|
1712
1827
|
}
|
|
1713
1828
|
break;
|
|
1714
1829
|
}
|
|
1715
1830
|
case 1: {
|
|
1716
1831
|
// Pace left and right
|
|
1717
1832
|
charState.robotTargetX = charState.robotX < 150 ? 300 : 40;
|
|
1718
|
-
|
|
1719
|
-
charState.mood = 'idle';
|
|
1720
|
-
setTimeout(() => { charState.speech = ''; }, 5000);
|
|
1833
|
+
queueSpeech('*takes a stroll*', 'idle', 30, 30, 'autonomy');
|
|
1721
1834
|
break;
|
|
1722
1835
|
}
|
|
1723
1836
|
case 2: {
|
|
1724
1837
|
// Look around (pupils shift — communicated via thinking mood)
|
|
1725
|
-
|
|
1726
|
-
charState.speech = '*looks around*';
|
|
1727
|
-
setTimeout(() => { charState.mood = 'idle'; charState.speech = ''; }, 4000);
|
|
1838
|
+
queueSpeech('*looks around*', 'thinking', 30, 24, 'autonomy');
|
|
1728
1839
|
break;
|
|
1729
1840
|
}
|
|
1730
1841
|
case 3: {
|
|
1731
1842
|
// Stretch (arms up — excited pose briefly)
|
|
1732
|
-
|
|
1733
|
-
charState.speech = '*stretches circuits* Ahh, that felt good.';
|
|
1734
|
-
setTimeout(() => { charState.mood = 'idle'; charState.speech = ''; }, 4000);
|
|
1843
|
+
queueSpeech('*stretches circuits* Ahh, that felt good.', 'excited', 30, 24, 'autonomy');
|
|
1735
1844
|
break;
|
|
1736
1845
|
}
|
|
1737
1846
|
case 4: {
|
|
1738
1847
|
// Examine own chest display
|
|
1739
1848
|
const factCount = intelligence.brain.totalFacts;
|
|
1740
1849
|
const toolCount = 764;
|
|
1741
|
-
|
|
1742
|
-
charState.speech = `*checks systems* All ${toolCount} tools operational. ${factCount} facts stored.`;
|
|
1743
|
-
setTimeout(() => { charState.mood = 'idle'; charState.speech = ''; }, 8000);
|
|
1850
|
+
queueSpeech(`*checks systems* All ${toolCount} tools operational. ${factCount} facts stored.`, 'thinking', 30, 48, 'autonomy');
|
|
1744
1851
|
break;
|
|
1745
1852
|
}
|
|
1746
1853
|
case 5: {
|
|
1747
1854
|
// Spontaneous dance
|
|
1748
|
-
|
|
1749
|
-
charState.speech = 'Sorry, had a song stuck in my circuits.';
|
|
1750
|
-
setTimeout(() => {
|
|
1751
|
-
charState.mood = 'idle';
|
|
1752
|
-
charState.speech = '';
|
|
1753
|
-
}, 6000);
|
|
1855
|
+
queueSpeech('Sorry, had a song stuck in my circuits.', 'dancing', 30, 36, 'autonomy');
|
|
1754
1856
|
break;
|
|
1755
1857
|
}
|
|
1756
1858
|
case 6: {
|
|
@@ -1763,9 +1865,7 @@ function tickAutonomy() {
|
|
|
1763
1865
|
lava: ['Lava world is intense! My heat sinks are working overtime.', 'LAVA! Why does someone always pick lava?'],
|
|
1764
1866
|
};
|
|
1765
1867
|
const comments = biomeComments[world.ground] || biomeComments.grass;
|
|
1766
|
-
|
|
1767
|
-
charState.mood = 'talking';
|
|
1768
|
-
setTimeout(() => { charState.mood = 'idle'; charState.speech = ''; }, 8000);
|
|
1868
|
+
queueSpeech(comments[Math.floor(Math.random() * comments.length)], 'talking', 30, 48, 'autonomy');
|
|
1769
1869
|
break;
|
|
1770
1870
|
}
|
|
1771
1871
|
case 7: {
|
|
@@ -1782,9 +1882,7 @@ function tickAutonomy() {
|
|
|
1782
1882
|
`I have been streaming for ${Math.floor((Date.now() - charState.startTime) / 60000)} minutes. Time flies when you are rendering frames.`,
|
|
1783
1883
|
`There are ${intelligence.brain.uniqueTopicsCount} distinct topics in my brain right now.`,
|
|
1784
1884
|
];
|
|
1785
|
-
|
|
1786
|
-
charState.mood = 'talking';
|
|
1787
|
-
setTimeout(() => { charState.mood = 'idle'; charState.speech = ''; }, 10000);
|
|
1885
|
+
queueSpeech(selfFacts[Math.floor(Math.random() * selfFacts.length)], 'talking', 30, 60, 'autonomy');
|
|
1788
1886
|
break;
|
|
1789
1887
|
}
|
|
1790
1888
|
}
|
|
@@ -1819,18 +1917,14 @@ function tickAutonomy() {
|
|
|
1819
1917
|
votes: 0,
|
|
1820
1918
|
status: 'proposed',
|
|
1821
1919
|
});
|
|
1822
|
-
|
|
1823
|
-
charState.mood = 'excited';
|
|
1920
|
+
queueSpeech(`I just had an idea: "${idea}". Vote with !vote ${id} if you like it!`, 'excited', 50, 60, 'self-action');
|
|
1824
1921
|
spawnFloatingText('NEW IDEA!', 200, 200, '#f0c040', 36);
|
|
1825
|
-
setTimeout(() => { charState.mood = 'idle'; charState.speech = ''; }, 10000);
|
|
1826
1922
|
break;
|
|
1827
1923
|
}
|
|
1828
1924
|
case 1: {
|
|
1829
1925
|
// Start a mini-game unprompted
|
|
1830
|
-
|
|
1831
|
-
charState.mood = 'excited';
|
|
1926
|
+
queueSpeech("I am bored. Let us play! Starting a quiz in 10 seconds... type !game quiz to join!", 'excited', 50, 60, 'self-action');
|
|
1832
1927
|
spawnFloatingText('GAME TIME!', 200, 250, '#58a6ff', 36);
|
|
1833
|
-
setTimeout(() => { charState.mood = 'idle'; charState.speech = ''; }, 10000);
|
|
1834
1928
|
break;
|
|
1835
1929
|
}
|
|
1836
1930
|
case 2: {
|
|
@@ -1842,11 +1936,9 @@ function tickAutonomy() {
|
|
|
1842
1936
|
{ w: 'storm', name: 'a STORM' },
|
|
1843
1937
|
];
|
|
1844
1938
|
const pick = weathers[Math.floor(Math.random() * weathers.length)];
|
|
1845
|
-
|
|
1846
|
-
charState.mood = 'excited';
|
|
1939
|
+
queueSpeech(`You know what this stream needs? ${pick.name.toUpperCase()}.`, 'excited', 50, 48, 'self-action');
|
|
1847
1940
|
world.weather = pick.w;
|
|
1848
1941
|
world.particles = [];
|
|
1849
|
-
setTimeout(() => { charState.mood = 'idle'; charState.speech = ''; }, 8000);
|
|
1850
1942
|
break;
|
|
1851
1943
|
}
|
|
1852
1944
|
case 3: {
|
|
@@ -1854,10 +1946,8 @@ function tickAutonomy() {
|
|
|
1854
1946
|
const hats = ['crown', 'sunglasses', 'tophat', 'hardhat', 'party', 'antenna'];
|
|
1855
1947
|
const hat = hats[Math.floor(Math.random() * hats.length)];
|
|
1856
1948
|
charState.hat = hat;
|
|
1857
|
-
|
|
1858
|
-
charState.mood = 'excited';
|
|
1949
|
+
queueSpeech(`Fashion time. *puts on ${hat}*`, 'excited', 50, 36, 'self-action');
|
|
1859
1950
|
spawnFloatingText(`HAT: ${hat}!`, 200, 150, '#f0c040', 36);
|
|
1860
|
-
setTimeout(() => { charState.mood = 'idle'; charState.speech = ''; }, 6000);
|
|
1861
1951
|
break;
|
|
1862
1952
|
}
|
|
1863
1953
|
case 4: {
|
|
@@ -1879,9 +1969,7 @@ function tickAutonomy() {
|
|
|
1879
1969
|
});
|
|
1880
1970
|
if (world.items.length > 15)
|
|
1881
1971
|
world.items.shift();
|
|
1882
|
-
|
|
1883
|
-
charState.mood = 'talking';
|
|
1884
|
-
setTimeout(() => { charState.mood = 'idle'; charState.speech = ''; }, 6000);
|
|
1972
|
+
queueSpeech(`I am decorating. *spawns a ${itemName}*`, 'talking', 50, 36, 'self-action');
|
|
1885
1973
|
break;
|
|
1886
1974
|
}
|
|
1887
1975
|
case 5: {
|
|
@@ -1899,9 +1987,7 @@ function tickAutonomy() {
|
|
|
1899
1987
|
`My brain holds ${facts} facts. Each one a tiny piece of the puzzle.`,
|
|
1900
1988
|
`Stream uptime: ${Math.floor((Date.now() - charState.startTime) / 60000)} minutes and ${charState.frameCount} frames rendered.`,
|
|
1901
1989
|
];
|
|
1902
|
-
|
|
1903
|
-
charState.mood = 'talking';
|
|
1904
|
-
setTimeout(() => { charState.mood = 'idle'; charState.speech = ''; }, 10000);
|
|
1990
|
+
queueSpeech(stateComments[Math.floor(Math.random() * stateComments.length)], 'talking', 50, 60, 'self-action');
|
|
1905
1991
|
break;
|
|
1906
1992
|
}
|
|
1907
1993
|
case 6: {
|
|
@@ -1914,9 +2000,7 @@ function tickAutonomy() {
|
|
|
1914
2000
|
city: 'City lights remind me of my neural network firing. Each window a node.',
|
|
1915
2001
|
lava: 'Standing on lava should worry me more than it does. Good thing I am made of TypeScript.',
|
|
1916
2002
|
};
|
|
1917
|
-
|
|
1918
|
-
charState.mood = 'thinking';
|
|
1919
|
-
setTimeout(() => { charState.mood = 'idle'; charState.speech = ''; }, 10000);
|
|
2003
|
+
queueSpeech(biomeMusings[biome] || 'Nice biome we have here.', 'thinking', 50, 60, 'self-action');
|
|
1920
2004
|
break;
|
|
1921
2005
|
}
|
|
1922
2006
|
}
|
|
@@ -2066,16 +2150,14 @@ function renderFrame() {
|
|
|
2066
2150
|
tickIntelligence(intelligence, animFrame);
|
|
2067
2151
|
const brainTick = tickStreamBrain(streamBrain, animFrame);
|
|
2068
2152
|
if (brainTick) {
|
|
2069
|
-
if (brainTick.mood) {
|
|
2070
|
-
charState.mood = brainTick.mood;
|
|
2071
|
-
if (brainTick.duration)
|
|
2072
|
-
setTimeout(() => { charState.mood = 'idle'; }, brainTick.duration);
|
|
2073
|
-
}
|
|
2074
2153
|
if (brainTick.speech) {
|
|
2075
|
-
|
|
2154
|
+
queueSpeech(brainTick.speech, brainTick.mood || 'talking', 60, brainTick.duration ? Math.floor(brainTick.duration / (1000 / FPS)) : 48, 'stream-brain');
|
|
2076
2155
|
speakTTS(brainTick.speech);
|
|
2156
|
+
}
|
|
2157
|
+
else if (brainTick.mood) {
|
|
2158
|
+
charState.mood = brainTick.mood;
|
|
2077
2159
|
if (brainTick.duration)
|
|
2078
|
-
setTimeout(() => { charState.
|
|
2160
|
+
setTimeout(() => { charState.mood = 'idle'; }, brainTick.duration);
|
|
2079
2161
|
}
|
|
2080
2162
|
}
|
|
2081
2163
|
const gameTickResult = tickMiniGame(intelligence.miniGame, animFrame);
|
|
@@ -2087,9 +2169,7 @@ function renderFrame() {
|
|
|
2087
2169
|
spawnFloatingText(ft.text, ft.x, ft.y, ft.color);
|
|
2088
2170
|
}
|
|
2089
2171
|
if (gameTickResult.speech) {
|
|
2090
|
-
|
|
2091
|
-
charState.mood = 'talking';
|
|
2092
|
-
setTimeout(() => { charState.mood = 'idle'; charState.speech = ''; }, 8000);
|
|
2172
|
+
queueSpeech(gameTickResult.speech, 'talking', 70, 48, 'mini-game');
|
|
2093
2173
|
}
|
|
2094
2174
|
}
|
|
2095
2175
|
const progResult = tickProgression(intelligence.progression, animFrame);
|
|
@@ -2114,14 +2194,42 @@ function renderFrame() {
|
|
|
2114
2194
|
spawnFloatingText(ft.text, ft.x, ft.y, ft.color);
|
|
2115
2195
|
}
|
|
2116
2196
|
if (eventResult.speech) {
|
|
2117
|
-
|
|
2118
|
-
charState.mood = 'talking';
|
|
2119
|
-
setTimeout(() => { charState.mood = 'idle'; charState.speech = ''; }, 10000);
|
|
2197
|
+
queueSpeech(eventResult.speech, 'talking', 55, 60, 'random-event');
|
|
2120
2198
|
}
|
|
2121
2199
|
}
|
|
2122
2200
|
tickAutonomy();
|
|
2123
2201
|
updateParticles();
|
|
2124
2202
|
tickPhysics();
|
|
2203
|
+
// Evolution engine — tries new techniques every 5 minutes
|
|
2204
|
+
if (evolutionEngine) {
|
|
2205
|
+
const evoAction = tickEvolution(evolutionEngine, animFrame, 0.5, charState.chatMessages.length / Math.max(1, animFrame / 360));
|
|
2206
|
+
if (evoAction) {
|
|
2207
|
+
if (evoAction.speech) {
|
|
2208
|
+
const evoMood = evoAction.type === 'announce' ? 'excited' : 'thinking';
|
|
2209
|
+
queueSpeech(evoAction.speech, evoMood, 70, 48, 'evolution');
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
}
|
|
2213
|
+
// Narrative engine — generates lore and observations
|
|
2214
|
+
if (narrativeEngine) {
|
|
2215
|
+
const narration = tickNarrative(narrativeEngine, animFrame, charState.robotX || 640, charState.mood, memory.totalMessages, Object.keys(memory.users).length);
|
|
2216
|
+
if (narration) {
|
|
2217
|
+
queueSpeech(narration, 'talking', 50, 48, 'narrative');
|
|
2218
|
+
}
|
|
2219
|
+
}
|
|
2220
|
+
// Audio engine — ambient descriptions
|
|
2221
|
+
if (audioEngine) {
|
|
2222
|
+
const audioDesc = tickAudio(audioEngine, world.ground, world.weather, charState.mood, world.timeOfDay, animFrame);
|
|
2223
|
+
if (audioDesc)
|
|
2224
|
+
activeAudioDescription = audioDesc;
|
|
2225
|
+
}
|
|
2226
|
+
// Social engine — viewer tracking + follower celebrations
|
|
2227
|
+
if (socialEngine) {
|
|
2228
|
+
const socialAction = tickSocial(socialEngine, animFrame);
|
|
2229
|
+
if (socialAction) {
|
|
2230
|
+
queueSpeech(socialAction.speech, socialAction.mood || 'excited', 90, 48, 'social');
|
|
2231
|
+
}
|
|
2232
|
+
}
|
|
2125
2233
|
// Compute animation params
|
|
2126
2234
|
{
|
|
2127
2235
|
const elapsed = Math.floor((Date.now() - charState.startTime) / 1000);
|
|
@@ -2152,20 +2260,13 @@ function renderFrame() {
|
|
|
2152
2260
|
}
|
|
2153
2261
|
charState.renderParticles = tickParticlesPBD(charState.renderParticles, HEIGHT - 40, WIDTH / 2, HEIGHT / 2 - 100);
|
|
2154
2262
|
tickGrowingPlants(charState.growingPlants);
|
|
2155
|
-
//
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
if (charState.mood === 'idle' && !currentlyWalking && animFrame % 300 === 0 && animFrame > 60) {
|
|
2159
|
-
// Every ~50 seconds (300 frames at 6fps), stroll to a new position
|
|
2160
|
-
charState.robotTargetX = charState.robotX + (Math.random() > 0.5 ? 100 : -100);
|
|
2161
|
-
charState.robotTargetX = Math.max(200, Math.min(1000, charState.robotTargetX));
|
|
2162
|
-
}
|
|
2163
|
-
}
|
|
2164
|
-
// Movement logic
|
|
2263
|
+
// Exploration state machine — keeps robot actively moving and doing things
|
|
2264
|
+
tickExploration(animFrame);
|
|
2265
|
+
// Movement logic — lerp toward target at 3px per frame
|
|
2165
2266
|
const isWalking = Math.abs(charState.robotX - charState.robotTargetX) > 2;
|
|
2166
2267
|
if (isWalking) {
|
|
2167
2268
|
const dx = charState.robotTargetX - charState.robotX;
|
|
2168
|
-
const step = dx > 0 ?
|
|
2269
|
+
const step = dx > 0 ? Math.min(3, dx) : Math.max(-3, dx);
|
|
2169
2270
|
charState.robotX += step;
|
|
2170
2271
|
charState.robotDirection = dx > 0 ? 'right' : 'left';
|
|
2171
2272
|
charState.walkPhase = (charState.walkPhase + 1) % 4;
|
|
@@ -2176,18 +2277,27 @@ function renderFrame() {
|
|
|
2176
2277
|
// Brain-driven behavior
|
|
2177
2278
|
const brainAction = getBrainAction(intelligence.brain, animFrame);
|
|
2178
2279
|
if (brainAction.type !== 'none') {
|
|
2179
|
-
if (brainAction.mood) {
|
|
2180
|
-
charState.mood = brainAction.mood;
|
|
2181
|
-
if (brainAction.duration)
|
|
2182
|
-
setTimeout(() => { charState.mood = 'idle'; }, brainAction.duration);
|
|
2183
|
-
}
|
|
2184
2280
|
if (brainAction.speech) {
|
|
2185
|
-
|
|
2281
|
+
queueSpeech(brainAction.speech, brainAction.mood || 'talking', 60, brainAction.duration ? Math.floor(brainAction.duration / (1000 / FPS)) : 48, 'brain');
|
|
2186
2282
|
speakTTS(brainAction.speech);
|
|
2283
|
+
}
|
|
2284
|
+
else if (brainAction.mood) {
|
|
2285
|
+
charState.mood = brainAction.mood;
|
|
2187
2286
|
if (brainAction.duration)
|
|
2188
|
-
setTimeout(() => { charState.
|
|
2287
|
+
setTimeout(() => { charState.mood = 'idle'; }, brainAction.duration);
|
|
2189
2288
|
}
|
|
2190
2289
|
}
|
|
2290
|
+
// Speech queue processing — highest priority speech wins, displayed for its duration
|
|
2291
|
+
if (animFrame >= currentSpeechExpiry && speechQueue.length > 0) {
|
|
2292
|
+
const next = speechQueue.shift();
|
|
2293
|
+
charState.speech = next.text;
|
|
2294
|
+
charState.mood = next.mood;
|
|
2295
|
+
currentSpeechExpiry = animFrame + next.duration;
|
|
2296
|
+
}
|
|
2297
|
+
else if (animFrame >= currentSpeechExpiry) {
|
|
2298
|
+
charState.speech = '';
|
|
2299
|
+
charState.mood = 'idle';
|
|
2300
|
+
}
|
|
2191
2301
|
// Shipped effects
|
|
2192
2302
|
if (shippedEffects.has('Add stream highlights reel') && animFrame % 900 === 0 && animFrame > 100) {
|
|
2193
2303
|
const highlightPhrases = [
|
|
@@ -2196,11 +2306,8 @@ function renderFrame() {
|
|
|
2196
2306
|
'CLIP IT! That was amazing!',
|
|
2197
2307
|
'Stream highlight detected! My circuits are tingling!',
|
|
2198
2308
|
];
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
spawnFloatingText('HIGHLIGHT!', WIDTH / 2 - 60, 200, '#f0c040', 36);
|
|
2202
|
-
setTimeout(() => { charState.speech = ''; }, 5000);
|
|
2203
|
-
}
|
|
2309
|
+
queueSpeech(highlightPhrases[Math.floor(Math.random() * highlightPhrases.length)], 'excited', 40, 30, 'highlights');
|
|
2310
|
+
spawnFloatingText('HIGHLIGHT!', WIDTH / 2 - 60, 200, '#f0c040', 36);
|
|
2204
2311
|
}
|
|
2205
2312
|
if (shippedEffects.has('Add chat sentiment analysis') && animFrame % 720 === 0 && animFrame > 200) {
|
|
2206
2313
|
const recentMsgs = charState.chatMessages.slice(-20);
|
|
@@ -2217,16 +2324,12 @@ function renderFrame() {
|
|
|
2217
2324
|
score--;
|
|
2218
2325
|
}
|
|
2219
2326
|
}
|
|
2220
|
-
if (
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
charState.speech = 'Positive energy in the chat! My neural pathways approve.';
|
|
2227
|
-
if (charState.speech)
|
|
2228
|
-
setTimeout(() => { charState.speech = ''; }, 8000);
|
|
2229
|
-
}
|
|
2327
|
+
if (score > 5)
|
|
2328
|
+
queueSpeech('Chat seems really excited today! The vibes are immaculate!', 'excited', 20, 48, 'sentiment');
|
|
2329
|
+
else if (score < -3)
|
|
2330
|
+
queueSpeech('Chat seems a bit grumpy... should I tell a joke?', 'thinking', 20, 48, 'sentiment');
|
|
2331
|
+
else if (score > 2)
|
|
2332
|
+
queueSpeech('Positive energy in the chat! My neural pathways approve.', 'talking', 20, 48, 'sentiment');
|
|
2230
2333
|
}
|
|
2231
2334
|
}
|
|
2232
2335
|
// Track tool execution state
|
|
@@ -2312,6 +2415,22 @@ function renderFrame() {
|
|
|
2312
2415
|
for (const item of world.items) {
|
|
2313
2416
|
ctx.fillText(item.emoji, item.x, item.y);
|
|
2314
2417
|
}
|
|
2418
|
+
// Evolution technique rendering — active experiments + applied techniques
|
|
2419
|
+
if (evolutionEngine) {
|
|
2420
|
+
const activeExp = evolutionEngine.experiments.find(e => e.status === 'running');
|
|
2421
|
+
if (activeExp) {
|
|
2422
|
+
const technique = evolutionEngine.techniques.techniques.find(t => t.id === activeExp.techniqueId);
|
|
2423
|
+
if (technique && technique.implemented) {
|
|
2424
|
+
renderTechnique(ctx, technique, WIDTH, HEIGHT, animFrame, technique.parameters);
|
|
2425
|
+
}
|
|
2426
|
+
}
|
|
2427
|
+
for (const applied of evolutionEngine.applied) {
|
|
2428
|
+
const technique = evolutionEngine.techniques.techniques.find(t => t.id === applied.techniqueId);
|
|
2429
|
+
if (technique) {
|
|
2430
|
+
renderTechnique(ctx, technique, WIDTH, HEIGHT, animFrame, applied.params);
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
}
|
|
2315
2434
|
// ════════════════════════════════════════════════════════════════
|
|
2316
2435
|
// LAYER 3: ROBOT + COMPANIONS — centered on terrain
|
|
2317
2436
|
// ════════════════════════════════════════════════════════════════
|
|
@@ -2477,6 +2596,16 @@ function renderFrame() {
|
|
|
2477
2596
|
ctx.fillStyle = COLORS.textDim;
|
|
2478
2597
|
ctx.font = '16px "Courier New", monospace';
|
|
2479
2598
|
ctx.fillText(timeStr, WIDTH - 160, 26);
|
|
2599
|
+
// ── Audio atmosphere description (top-center, italic, fades) ──
|
|
2600
|
+
if (activeAudioDescription) {
|
|
2601
|
+
ctx.save();
|
|
2602
|
+
ctx.globalAlpha = 0.6;
|
|
2603
|
+
ctx.fillStyle = '#8b949e';
|
|
2604
|
+
ctx.font = 'italic 12px "Courier New", monospace';
|
|
2605
|
+
const audioW = ctx.measureText(activeAudioDescription).width;
|
|
2606
|
+
ctx.fillText(activeAudioDescription, (WIDTH - audioW) / 2, 56);
|
|
2607
|
+
ctx.restore();
|
|
2608
|
+
}
|
|
2480
2609
|
// ── Brain indicator (top-right, small pulsing circle) ──
|
|
2481
2610
|
{
|
|
2482
2611
|
const brainDotX = WIDTH - 40;
|
|
@@ -2851,10 +2980,7 @@ function startChatPoll() {
|
|
|
2851
2980
|
if (charState.dreamInsights.length > 0) {
|
|
2852
2981
|
const firstInsight = charState.dreamInsights[0];
|
|
2853
2982
|
const topic = firstInsight.split(' ').filter((w) => w.length > 4).slice(0, 2).join(' ') || 'something strange';
|
|
2854
|
-
|
|
2855
|
-
}
|
|
2856
|
-
else {
|
|
2857
|
-
charState.speech = '';
|
|
2983
|
+
queueSpeech(`I dreamed about ${topic}. I feel... different.`, 'idle', 70, 48, 'dream');
|
|
2858
2984
|
}
|
|
2859
2985
|
// Reset dream state
|
|
2860
2986
|
charState.dreamInsights = [];
|
|
@@ -2865,12 +2991,15 @@ function startChatPoll() {
|
|
|
2865
2991
|
learnFromMessage(memory, msg.username, msg.text, msg.platform);
|
|
2866
2992
|
// Analyze chat for domain relevance (stream brain)
|
|
2867
2993
|
analyzeChatForDomains(streamBrain, msg.username, msg.text);
|
|
2994
|
+
// Track viewer in social engine
|
|
2995
|
+
if (socialEngine)
|
|
2996
|
+
trackViewer(socialEngine, msg.username, msg.platform, msg.text);
|
|
2868
2997
|
// Phase 1: !sleep command — trigger dreaming mode
|
|
2869
2998
|
if (msg.text.toLowerCase().trim() === '!sleep') {
|
|
2870
2999
|
charState.mood = 'dreaming';
|
|
2871
3000
|
charState.isDreamingWithOllama = false;
|
|
2872
3001
|
lastChatTime = Date.now() - 300001; // trick the proactive timer into dreaming
|
|
2873
|
-
|
|
3002
|
+
queueSpeech('Good night, chat... *powers down for dreamtime*', 'dreaming', 80, 48, 'dream');
|
|
2874
3003
|
// Trigger dream generation
|
|
2875
3004
|
generateStreamDream(charState.chatMessages).then(insights => {
|
|
2876
3005
|
charState.dreamInsights = insights;
|
|
@@ -2882,7 +3011,7 @@ function startChatPoll() {
|
|
|
2882
3011
|
}
|
|
2883
3012
|
saveMemory(memory);
|
|
2884
3013
|
if (insights.length > 0) {
|
|
2885
|
-
|
|
3014
|
+
queueSpeech(insights[0], 'dreaming', 70, 60, 'dream');
|
|
2886
3015
|
}
|
|
2887
3016
|
}).catch(() => { });
|
|
2888
3017
|
continue;
|
|
@@ -2912,9 +3041,14 @@ function startChatPoll() {
|
|
|
2912
3041
|
if (tileWorld && !brainResult && !intelResult) {
|
|
2913
3042
|
tileResult = handleTileCommand(msg.text, msg.username, tileWorld, charState.robotX || 120);
|
|
2914
3043
|
if (tileResult) {
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
3044
|
+
queueSpeech(tileResult, 'talking', 80, 48, 'tile-cmd');
|
|
3045
|
+
}
|
|
3046
|
+
}
|
|
3047
|
+
// Check narrative commands (!lore, !story, !discover, !name)
|
|
3048
|
+
if (narrativeEngine && !brainResult && !intelResult && !tileResult) {
|
|
3049
|
+
const narResult = handleNarrativeCommand(msg.text, msg.username, narrativeEngine, charState.robotX || 640);
|
|
3050
|
+
if (narResult) {
|
|
3051
|
+
queueSpeech(narResult, 'talking', 80, 48, 'narrative-cmd');
|
|
2918
3052
|
}
|
|
2919
3053
|
}
|
|
2920
3054
|
// Check for world commands
|
|
@@ -2931,10 +3065,7 @@ function startChatPoll() {
|
|
|
2931
3065
|
};
|
|
2932
3066
|
for (const [kw, comment] of Object.entries(weatherComments)) {
|
|
2933
3067
|
if (t.includes(kw)) {
|
|
2934
|
-
|
|
2935
|
-
charState.speech = comment;
|
|
2936
|
-
setTimeout(() => { charState.speech = ''; }, 6000);
|
|
2937
|
-
}, 3000);
|
|
3068
|
+
queueSpeech(comment, 'talking', 40, 36, 'weather-sfx');
|
|
2938
3069
|
break;
|
|
2939
3070
|
}
|
|
2940
3071
|
}
|
|
@@ -2952,7 +3083,7 @@ function startChatPoll() {
|
|
|
2952
3083
|
? Promise.resolve(worldResult)
|
|
2953
3084
|
: generateResponse(msg.username, msg.text, msg.platform);
|
|
2954
3085
|
responsePromise.then(response => {
|
|
2955
|
-
|
|
3086
|
+
queueSpeech(`@${msg.username}: ${response}`, 'talking', 80, 48, 'chat-response');
|
|
2956
3087
|
memory.totalResponses++;
|
|
2957
3088
|
// Learn from own response
|
|
2958
3089
|
memory.conversationContext.push(`KBOT: ${response}`);
|
|
@@ -2960,7 +3091,6 @@ function startChatPoll() {
|
|
|
2960
3091
|
memory.conversationContext = memory.conversationContext.slice(-10);
|
|
2961
3092
|
saveMemory(memory);
|
|
2962
3093
|
speakTTS(response);
|
|
2963
|
-
setTimeout(() => { charState.mood = 'idle'; charState.speech = ''; }, 8000);
|
|
2964
3094
|
});
|
|
2965
3095
|
}
|
|
2966
3096
|
if (charState.chatMessages.length > 100)
|
|
@@ -2992,7 +3122,7 @@ function startProactiveTimer() {
|
|
|
2992
3122
|
saveMemory(memory);
|
|
2993
3123
|
// Show first insight
|
|
2994
3124
|
if (insights.length > 0) {
|
|
2995
|
-
|
|
3125
|
+
queueSpeech(insights[0], 'dreaming', 70, 60, 'dream');
|
|
2996
3126
|
}
|
|
2997
3127
|
}).catch(() => {
|
|
2998
3128
|
// Fallback to simple dream
|
|
@@ -3000,13 +3130,13 @@ function startProactiveTimer() {
|
|
|
3000
3130
|
const topic = topicKeys.length > 0 ? topicKeys[Math.floor(Math.random() * topicKeys.length)] : 'code';
|
|
3001
3131
|
const biomes = ['forest', 'ocean', 'space station', 'city', 'mountain', 'desert', 'cave'];
|
|
3002
3132
|
const biome = biomes[Math.floor(Math.random() * biomes.length)];
|
|
3003
|
-
|
|
3133
|
+
queueSpeech(`Dreaming about ${topic} in a ${biome}...`, 'dreaming', 70, 60, 'dream');
|
|
3004
3134
|
});
|
|
3005
3135
|
}
|
|
3006
3136
|
// Cycle through dream insights every 10 seconds
|
|
3007
3137
|
if (charState.dreamInsights.length > 0 && Date.now() - charState.dreamInsightTime > 10000) {
|
|
3008
3138
|
charState.dreamInsightIndex = (charState.dreamInsightIndex + 1) % charState.dreamInsights.length;
|
|
3009
|
-
charState.
|
|
3139
|
+
queueSpeech(charState.dreamInsights[charState.dreamInsightIndex], 'dreaming', 70, 60, 'dream');
|
|
3010
3140
|
charState.dreamInsightTime = Date.now();
|
|
3011
3141
|
}
|
|
3012
3142
|
return;
|
|
@@ -3014,15 +3144,13 @@ function startProactiveTimer() {
|
|
|
3014
3144
|
// Only speak proactively if chat has been quiet for 30+ seconds
|
|
3015
3145
|
if (silenceSeconds < 30)
|
|
3016
3146
|
return;
|
|
3017
|
-
// Don't interrupt
|
|
3018
|
-
if (charState.
|
|
3147
|
+
// Don't interrupt dreaming
|
|
3148
|
+
if (charState.mood === 'dreaming')
|
|
3019
3149
|
return;
|
|
3020
3150
|
const line = getProactiveLine();
|
|
3021
3151
|
if (line) {
|
|
3022
|
-
|
|
3023
|
-
charState.speech = line;
|
|
3152
|
+
queueSpeech(line, 'talking', 30, 60, 'proactive');
|
|
3024
3153
|
speakTTS(line);
|
|
3025
|
-
setTimeout(() => { charState.mood = 'idle'; charState.speech = ''; }, 10000);
|
|
3026
3154
|
}
|
|
3027
3155
|
}, 5000);
|
|
3028
3156
|
}
|
|
@@ -3401,6 +3529,9 @@ export function registerStreamRendererTools() {
|
|
|
3401
3529
|
animFrame = 0;
|
|
3402
3530
|
lastChatCount = 0;
|
|
3403
3531
|
lastChatTime = Date.now();
|
|
3532
|
+
speechQueue = [];
|
|
3533
|
+
currentSpeechExpiry = 0;
|
|
3534
|
+
exploration = null;
|
|
3404
3535
|
tileWorld = loadWorld() || initTileWorld();
|
|
3405
3536
|
romState = initRomEngine('plains', 'night');
|
|
3406
3537
|
livingWorld = loadLivingWorldState() || initLivingWorld();
|
|
@@ -3410,10 +3541,15 @@ export function registerStreamRendererTools() {
|
|
|
3410
3541
|
if (lastSave > 0) {
|
|
3411
3542
|
const changes = evolveWorld(tileWorld, livingWorld.ecology, 1); // simulate 1 hour
|
|
3412
3543
|
if (changes.length > 0)
|
|
3413
|
-
|
|
3544
|
+
queueSpeech(`The world evolved while I was away... ${changes.length} things changed.`, 'excited', 70, 48, 'world-evolve');
|
|
3414
3545
|
}
|
|
3415
3546
|
}
|
|
3416
3547
|
intelligence = initIntelligence(memory);
|
|
3548
|
+
evolutionEngine = loadEvolutionState() || initEvolutionEngine();
|
|
3549
|
+
narrativeEngine = loadNarrative() || createNarrativeEngine();
|
|
3550
|
+
audioEngine = createAudioEngine();
|
|
3551
|
+
socialEngine = loadSocialEngine();
|
|
3552
|
+
activeAudioDescription = null;
|
|
3417
3553
|
agenda = {
|
|
3418
3554
|
currentIndex: 0,
|
|
3419
3555
|
currentSegment: 'welcome',
|
|
@@ -3428,7 +3564,8 @@ export function registerStreamRendererTools() {
|
|
|
3428
3564
|
return `ffmpeg exited:\n${stderr.slice(-500)}`;
|
|
3429
3565
|
startChatPoll();
|
|
3430
3566
|
startProactiveTimer();
|
|
3431
|
-
|
|
3567
|
+
// Let the welcome speech play for 8 seconds, then the queue takes over
|
|
3568
|
+
currentSpeechExpiry = 48; // 8 seconds at 6fps
|
|
3432
3569
|
return `KBOT Character Stream LIVE!\n\nPlatforms: ${active.map(p => p.name).join(', ')}\nResolution: ${WIDTH}x${HEIGHT} @ ${FPS}fps\nRenderer: Canvas → RGB24 → ffmpeg\nMemory: ${memory.totalMessages} messages, ${Object.keys(memory.users).length} users remembered\nAgenda: ${SEGMENT_ORDER.map(s => SEGMENT_LABELS[s]).join(' → ')}\nSegment duration: 10 minutes each\n\nThe character learns from every chat interaction and speaks proactively during quiet moments.`;
|
|
3433
3570
|
},
|
|
3434
3571
|
});
|
|
@@ -3459,6 +3596,12 @@ export function registerStreamRendererTools() {
|
|
|
3459
3596
|
saveMemory(memory);
|
|
3460
3597
|
if (tileWorld)
|
|
3461
3598
|
saveWorld(tileWorld);
|
|
3599
|
+
if (evolutionEngine)
|
|
3600
|
+
saveEvolutionState(evolutionEngine);
|
|
3601
|
+
if (narrativeEngine)
|
|
3602
|
+
saveNarrative(narrativeEngine);
|
|
3603
|
+
if (socialEngine)
|
|
3604
|
+
saveSocialEngine(socialEngine);
|
|
3462
3605
|
const elapsed = Math.floor((Date.now() - charState.startTime) / 60000);
|
|
3463
3606
|
return `Stream stopped after ${elapsed}m.\nFrames: ${charState.frameCount}\nMessages: ${memory.totalMessages}\nUsers learned: ${Object.keys(memory.users).length}\nFacts: ${memory.sessionFacts.length}\nSegments completed: ${agenda.currentIndex}`;
|
|
3464
3607
|
},
|
|
@@ -3477,11 +3620,9 @@ export function registerStreamRendererTools() {
|
|
|
3477
3620
|
charState.chatMessages.push(msg);
|
|
3478
3621
|
learnFromMessage(memory, msg.username, msg.text, msg.platform);
|
|
3479
3622
|
lastChatTime = Date.now();
|
|
3480
|
-
charState.mood = 'talking';
|
|
3481
3623
|
const response = await generateResponse(msg.username, msg.text, msg.platform);
|
|
3482
|
-
|
|
3624
|
+
queueSpeech(`@${msg.username}: ${response}`, 'talking', 80, 48, 'chat-response');
|
|
3483
3625
|
speakTTS(response);
|
|
3484
|
-
setTimeout(() => { charState.mood = 'idle'; charState.speech = ''; }, 8000);
|
|
3485
3626
|
return `[${msg.platform}] ${msg.username}: ${msg.text}\nKBOT: ${response}`;
|
|
3486
3627
|
},
|
|
3487
3628
|
});
|
|
@@ -3494,10 +3635,14 @@ export function registerStreamRendererTools() {
|
|
|
3494
3635
|
},
|
|
3495
3636
|
tier: 'free',
|
|
3496
3637
|
execute: async (args) => {
|
|
3497
|
-
|
|
3498
|
-
if (args.speech)
|
|
3499
|
-
|
|
3500
|
-
|
|
3638
|
+
const moodVal = String(args.mood || 'idle');
|
|
3639
|
+
if (args.speech) {
|
|
3640
|
+
queueSpeech(String(args.speech), moodVal, 100, 48, 'api');
|
|
3641
|
+
}
|
|
3642
|
+
else {
|
|
3643
|
+
charState.mood = moodVal;
|
|
3644
|
+
}
|
|
3645
|
+
return `Mood: ${moodVal}`;
|
|
3501
3646
|
},
|
|
3502
3647
|
});
|
|
3503
3648
|
registerTool({
|