@kernel.chat/kbot 3.94.0 → 3.97.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/agent.js +30 -0
- package/dist/coordinator.d.ts +132 -0
- package/dist/coordinator.js +682 -0
- package/dist/tools/audio-engine.d.ts +76 -0
- package/dist/tools/audio-engine.js +583 -24
- package/dist/tools/index.js +6 -0
- package/dist/tools/sprite-engine.d.ts +18 -0
- package/dist/tools/sprite-engine.js +435 -1
- package/dist/tools/stream-chat-ai.d.ts +56 -0
- package/dist/tools/stream-chat-ai.js +625 -0
- package/dist/tools/stream-commands.d.ts +91 -0
- package/dist/tools/stream-commands.js +911 -0
- package/dist/tools/stream-overlay.d.ts +53 -0
- package/dist/tools/stream-overlay.js +494 -0
- package/dist/tools/stream-renderer.js +676 -77
- package/dist/tools/stream-vod.d.ts +60 -0
- package/dist/tools/stream-vod.js +449 -0
- package/dist/tools/stream-weather.d.ts +79 -0
- package/dist/tools/stream-weather.js +811 -0
- package/dist/tools/tile-world.d.ts +6 -0
- package/dist/tools/tile-world.js +3 -3
- package/package.json +1 -1
|
@@ -151,7 +151,7 @@ function drawHead(ctx, s, ox, oy, eyeColor, mood, frame, headShiftX) {
|
|
|
151
151
|
// Eye glow background — brighter white-green for alive look, dimmed if dreaming
|
|
152
152
|
const eyeC = mood === 'dreaming'
|
|
153
153
|
? dimColor(eyeColor.startsWith('rgb') ? '#4a6670' : eyeColor, 0.5)
|
|
154
|
-
: '#
|
|
154
|
+
: '#b0ffe0'; // HACK 3: even brighter cyan-white for maximum pop
|
|
155
155
|
px(ctx, hx + 2, eyeY, 4, eyeH, eyeC, s, ox, oy);
|
|
156
156
|
px(ctx, hx + 8, eyeY, 4, eyeH, eyeC, s, ox, oy);
|
|
157
157
|
// Specular highlights on eyes — 2px L-shape catch-light for glassy/alive look (technique 8)
|
|
@@ -895,6 +895,440 @@ function drawWalkingLegs(ctx, s, ox, oy, bodyShiftY, walkPhase) {
|
|
|
895
895
|
px(ctx, 21 + rightLegOffset, footY, 1, 2, PAL.rimLight, s, ox, oy);
|
|
896
896
|
px(ctx, 17 + rightLegOffset, footY + 2, 4, 1, PAL.jetOrange, s, ox, oy);
|
|
897
897
|
}
|
|
898
|
+
// ─── Gorilla Character ────────────────────────────────────────
|
|
899
|
+
const GORILLA = {
|
|
900
|
+
furDark: '#8B6914', // dark brown
|
|
901
|
+
furMain: '#C4943D', // main tan/brown
|
|
902
|
+
furLight: '#DEB860', // light tan highlights
|
|
903
|
+
furChest: '#E8D5A0', // pale chest/face
|
|
904
|
+
skinDark: '#7A5B2E', // darker skin (face creases)
|
|
905
|
+
eyeWhite: '#F0F0E0', // eye whites
|
|
906
|
+
eyePupil: '#1a1a1a', // dark pupils
|
|
907
|
+
mouth: '#3D2B1A', // dark mouth
|
|
908
|
+
fang: '#F0F0E0', // white fangs
|
|
909
|
+
capRed: '#CC2222', // red cap
|
|
910
|
+
capWhite: '#F0F0F0', // white cap panel
|
|
911
|
+
capBrim: '#999999', // brim underside
|
|
912
|
+
claws: '#D0D0D0', // light gray claws
|
|
913
|
+
outline: '#2A1F0A', // dark brown outline
|
|
914
|
+
nose: '#5A3D1E', // nose color
|
|
915
|
+
};
|
|
916
|
+
let _gorillaPrevMood = '';
|
|
917
|
+
let _gorillaSettleFrames = 0;
|
|
918
|
+
/**
|
|
919
|
+
* Draw a stocky gorilla/monkey pixel art character (32x32 grid).
|
|
920
|
+
* Drop-in replacement for drawRobot() with the same signature.
|
|
921
|
+
*
|
|
922
|
+
* @param ctx - Canvas 2D rendering context
|
|
923
|
+
* @param x - Top-left X position in canvas pixels
|
|
924
|
+
* @param y - Top-left Y position in canvas pixels
|
|
925
|
+
* @param scale - Pixel scale multiplier (4-10 recommended)
|
|
926
|
+
* @param mood - Current mood: idle, talking, thinking, excited, dancing, walking
|
|
927
|
+
* @param frame - Animation frame counter (incrementing integer)
|
|
928
|
+
* @param moodColor - Optional RGB override for mood accent color
|
|
929
|
+
*/
|
|
930
|
+
export function drawGorilla(ctx, x, y, scale, mood, frame, moodColor) {
|
|
931
|
+
const s = scale;
|
|
932
|
+
const G = GORILLA;
|
|
933
|
+
// Settle animation on mood change
|
|
934
|
+
if (mood !== _gorillaPrevMood) {
|
|
935
|
+
_gorillaSettleFrames = 3;
|
|
936
|
+
_gorillaPrevMood = mood;
|
|
937
|
+
}
|
|
938
|
+
let settleShift = 0;
|
|
939
|
+
if (_gorillaSettleFrames > 0) {
|
|
940
|
+
settleShift = _gorillaSettleFrames === 3 ? -1 : _gorillaSettleFrames === 2 ? 1 : 0;
|
|
941
|
+
_gorillaSettleFrames--;
|
|
942
|
+
}
|
|
943
|
+
// ── Animation offsets ──
|
|
944
|
+
let bodyY = settleShift;
|
|
945
|
+
let bodyX = 0;
|
|
946
|
+
let headTilt = 0;
|
|
947
|
+
let mouthOpen = 0; // 0=closed, 1=half, 2=open, 3=wide
|
|
948
|
+
let leftArmFwd = 0; // forward offset for walking
|
|
949
|
+
let rightArmFwd = 0;
|
|
950
|
+
let eyeState = 'narrow'; // default grumpy
|
|
951
|
+
let capTilt = 0;
|
|
952
|
+
let questionMark = false;
|
|
953
|
+
// Breathing (idle)
|
|
954
|
+
const breathFrame = frame % 12;
|
|
955
|
+
let breathShift = 0;
|
|
956
|
+
if (mood === 'idle') {
|
|
957
|
+
if (breathFrame >= 1 && breathFrame <= 3)
|
|
958
|
+
breathShift = -1; // rise
|
|
959
|
+
if (breathFrame >= 4 && breathFrame <= 5) {
|
|
960
|
+
breathShift = 0;
|
|
961
|
+
bodyY += 1;
|
|
962
|
+
}
|
|
963
|
+
// Blink every ~24 frames
|
|
964
|
+
if (frame % 24 === 23)
|
|
965
|
+
eyeState = 'blink';
|
|
966
|
+
else if (frame % 24 === 22)
|
|
967
|
+
eyeState = 'blink';
|
|
968
|
+
}
|
|
969
|
+
if (mood === 'talking') {
|
|
970
|
+
// Mouth animation cycles
|
|
971
|
+
mouthOpen = frame % 4; // 0=open, 1=half, 2=wide, 3=closed
|
|
972
|
+
eyeState = 'narrow';
|
|
973
|
+
}
|
|
974
|
+
else if (mood === 'walking') {
|
|
975
|
+
// Walking: arms alternate, body bobs
|
|
976
|
+
const wf = frame % 4;
|
|
977
|
+
if (wf === 0) {
|
|
978
|
+
leftArmFwd = -2;
|
|
979
|
+
rightArmFwd = 2;
|
|
980
|
+
bodyX = -1;
|
|
981
|
+
}
|
|
982
|
+
else if (wf === 1) {
|
|
983
|
+
leftArmFwd = 0;
|
|
984
|
+
rightArmFwd = 0;
|
|
985
|
+
}
|
|
986
|
+
else if (wf === 2) {
|
|
987
|
+
leftArmFwd = 2;
|
|
988
|
+
rightArmFwd = -2;
|
|
989
|
+
bodyX = 1;
|
|
990
|
+
}
|
|
991
|
+
else {
|
|
992
|
+
leftArmFwd = 0;
|
|
993
|
+
rightArmFwd = 0;
|
|
994
|
+
}
|
|
995
|
+
bodyY += (wf % 2 === 0) ? -1 : 0;
|
|
996
|
+
}
|
|
997
|
+
else if (mood === 'excited') {
|
|
998
|
+
const ef = frame % 4;
|
|
999
|
+
if (ef === 0) {
|
|
1000
|
+
bodyY -= 2;
|
|
1001
|
+
}
|
|
1002
|
+
else if (ef === 2) {
|
|
1003
|
+
bodyY -= 3;
|
|
1004
|
+
mouthOpen = 2;
|
|
1005
|
+
eyeState = 'wide';
|
|
1006
|
+
}
|
|
1007
|
+
else {
|
|
1008
|
+
eyeState = 'narrow';
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
else if (mood === 'thinking') {
|
|
1012
|
+
const tf = frame % 3;
|
|
1013
|
+
if (tf === 0) {
|
|
1014
|
+
headTilt = 1;
|
|
1015
|
+
}
|
|
1016
|
+
else if (tf === 1) {
|
|
1017
|
+
eyeState = 'up';
|
|
1018
|
+
}
|
|
1019
|
+
else {
|
|
1020
|
+
questionMark = true;
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
else if (mood === 'dancing') {
|
|
1024
|
+
const df = frame % 4;
|
|
1025
|
+
if (df === 0) {
|
|
1026
|
+
bodyX = -2;
|
|
1027
|
+
bodyY -= 1;
|
|
1028
|
+
}
|
|
1029
|
+
else if (df === 1) {
|
|
1030
|
+
bodyX = 0;
|
|
1031
|
+
}
|
|
1032
|
+
else if (df === 2) {
|
|
1033
|
+
bodyX = 2;
|
|
1034
|
+
bodyY -= 1;
|
|
1035
|
+
}
|
|
1036
|
+
else {
|
|
1037
|
+
bodyX = 0;
|
|
1038
|
+
capTilt = 1;
|
|
1039
|
+
}
|
|
1040
|
+
eyeState = 'narrow';
|
|
1041
|
+
mouthOpen = (df % 2 === 0) ? 1 : 0;
|
|
1042
|
+
}
|
|
1043
|
+
bodyY += breathShift;
|
|
1044
|
+
const ox = x + bodyX * s;
|
|
1045
|
+
const oy = y + bodyY * s;
|
|
1046
|
+
// ── Drop shadow ──
|
|
1047
|
+
ctx.save();
|
|
1048
|
+
ctx.fillStyle = 'rgba(30, 20, 5, 0.3)';
|
|
1049
|
+
ctx.beginPath();
|
|
1050
|
+
ctx.ellipse(ox + 16 * s, oy + 31 * s, 12 * s, 3 * s, 0, 0, Math.PI * 2);
|
|
1051
|
+
ctx.fill();
|
|
1052
|
+
ctx.restore();
|
|
1053
|
+
// ── Tail (draw first, behind body) ──
|
|
1054
|
+
// Curled tail on the right side, rows 14-20
|
|
1055
|
+
px(ctx, 27, 14, 2, 1, G.furMain, s, ox, oy);
|
|
1056
|
+
px(ctx, 28, 15, 2, 1, G.furMain, s, ox, oy);
|
|
1057
|
+
px(ctx, 29, 16, 2, 1, G.furDark, s, ox, oy);
|
|
1058
|
+
px(ctx, 29, 17, 1, 1, G.furDark, s, ox, oy);
|
|
1059
|
+
px(ctx, 28, 18, 1, 1, G.furMain, s, ox, oy);
|
|
1060
|
+
px(ctx, 27, 19, 2, 1, G.furMain, s, ox, oy);
|
|
1061
|
+
// Curl tip
|
|
1062
|
+
px(ctx, 26, 18, 1, 1, G.furLight, s, ox, oy);
|
|
1063
|
+
// Outline
|
|
1064
|
+
px(ctx, 27, 13, 2, 1, G.outline, s, ox, oy);
|
|
1065
|
+
px(ctx, 29, 14, 1, 1, G.outline, s, ox, oy);
|
|
1066
|
+
px(ctx, 30, 15, 1, 2, G.outline, s, ox, oy);
|
|
1067
|
+
px(ctx, 31, 16, 1, 1, G.outline, s, ox, oy);
|
|
1068
|
+
px(ctx, 30, 17, 1, 1, G.outline, s, ox, oy);
|
|
1069
|
+
px(ctx, 30, 18, 1, 1, G.outline, s, ox, oy);
|
|
1070
|
+
px(ctx, 29, 19, 1, 1, G.outline, s, ox, oy);
|
|
1071
|
+
px(ctx, 27, 20, 2, 1, G.outline, s, ox, oy);
|
|
1072
|
+
px(ctx, 25, 18, 1, 1, G.outline, s, ox, oy);
|
|
1073
|
+
// ── Back legs (behind body) ──
|
|
1074
|
+
// Left back leg (rows 22-28)
|
|
1075
|
+
px(ctx, 17, 24, 5, 5, G.furDark, s, ox, oy);
|
|
1076
|
+
px(ctx, 18, 24, 3, 4, G.furMain, s, ox, oy);
|
|
1077
|
+
px(ctx, 17, 29, 6, 2, G.furDark, s, ox, oy); // foot
|
|
1078
|
+
px(ctx, 18, 29, 4, 1, G.furMain, s, ox, oy);
|
|
1079
|
+
// Claws on back foot
|
|
1080
|
+
px(ctx, 17, 31, 1, 1, G.claws, s, ox, oy);
|
|
1081
|
+
px(ctx, 19, 31, 1, 1, G.claws, s, ox, oy);
|
|
1082
|
+
px(ctx, 21, 31, 1, 1, G.claws, s, ox, oy);
|
|
1083
|
+
// Right back leg
|
|
1084
|
+
px(ctx, 22, 24, 5, 5, G.furDark, s, ox, oy);
|
|
1085
|
+
px(ctx, 23, 24, 3, 4, G.furMain, s, ox, oy);
|
|
1086
|
+
px(ctx, 22, 29, 6, 2, G.furDark, s, ox, oy); // foot
|
|
1087
|
+
px(ctx, 23, 29, 4, 1, G.furMain, s, ox, oy);
|
|
1088
|
+
// Claws
|
|
1089
|
+
px(ctx, 22, 31, 1, 1, G.claws, s, ox, oy);
|
|
1090
|
+
px(ctx, 24, 31, 1, 1, G.claws, s, ox, oy);
|
|
1091
|
+
px(ctx, 26, 31, 1, 1, G.claws, s, ox, oy);
|
|
1092
|
+
// ── Body (rows 12-24) ── Very wide, stocky torso
|
|
1093
|
+
// Outline
|
|
1094
|
+
outlineRect(ctx, 5, 12, 22, 14, G.outline, s, ox, oy);
|
|
1095
|
+
// Body fill — dark brown base
|
|
1096
|
+
px(ctx, 5, 12, 22, 14, G.furDark, s, ox, oy);
|
|
1097
|
+
// Main fur color on upper body
|
|
1098
|
+
px(ctx, 6, 13, 20, 8, G.furMain, s, ox, oy);
|
|
1099
|
+
// Light highlights on top (back ridge)
|
|
1100
|
+
px(ctx, 8, 12, 16, 2, G.furLight, s, ox, oy);
|
|
1101
|
+
// Lighter chest/belly underneath
|
|
1102
|
+
px(ctx, 9, 19, 14, 6, G.furChest, s, ox, oy);
|
|
1103
|
+
// Dithered transition from main fur to chest
|
|
1104
|
+
dither(ctx, 9, 18, 14, 1, G.furMain, G.furChest, s, ox, oy);
|
|
1105
|
+
// Dark underside shadow
|
|
1106
|
+
px(ctx, 6, 25, 20, 1, G.skinDark, s, ox, oy);
|
|
1107
|
+
// ── Front arms (rows 14-28): thick, reaching to ground ──
|
|
1108
|
+
// Left front arm
|
|
1109
|
+
const laOff = leftArmFwd;
|
|
1110
|
+
outlineRect(ctx, 2 + laOff, 14, 6, 14, G.outline, s, ox, oy);
|
|
1111
|
+
px(ctx, 2 + laOff, 14, 6, 14, G.furDark, s, ox, oy);
|
|
1112
|
+
px(ctx, 3 + laOff, 14, 4, 12, G.furMain, s, ox, oy);
|
|
1113
|
+
// Shoulder highlight
|
|
1114
|
+
px(ctx, 3 + laOff, 14, 4, 2, G.furLight, s, ox, oy);
|
|
1115
|
+
// Forearm darker
|
|
1116
|
+
px(ctx, 3 + laOff, 22, 4, 4, G.skinDark, s, ox, oy);
|
|
1117
|
+
// Hand/knuckles
|
|
1118
|
+
px(ctx, 1 + laOff, 28, 7, 3, G.furDark, s, ox, oy);
|
|
1119
|
+
px(ctx, 2 + laOff, 28, 5, 2, G.furMain, s, ox, oy);
|
|
1120
|
+
// Claws
|
|
1121
|
+
px(ctx, 1 + laOff, 31, 1, 1, G.claws, s, ox, oy);
|
|
1122
|
+
px(ctx, 3 + laOff, 31, 1, 1, G.claws, s, ox, oy);
|
|
1123
|
+
px(ctx, 5 + laOff, 31, 1, 1, G.claws, s, ox, oy);
|
|
1124
|
+
px(ctx, 7 + laOff, 31, 1, 1, G.claws, s, ox, oy);
|
|
1125
|
+
// Right front arm
|
|
1126
|
+
const raOff = rightArmFwd;
|
|
1127
|
+
outlineRect(ctx, 24 + raOff, 14, 6, 14, G.outline, s, ox, oy);
|
|
1128
|
+
px(ctx, 24 + raOff, 14, 6, 14, G.furDark, s, ox, oy);
|
|
1129
|
+
px(ctx, 25 + raOff, 14, 4, 12, G.furMain, s, ox, oy);
|
|
1130
|
+
// Shoulder highlight
|
|
1131
|
+
px(ctx, 25 + raOff, 14, 4, 2, G.furLight, s, ox, oy);
|
|
1132
|
+
// Forearm darker
|
|
1133
|
+
px(ctx, 25 + raOff, 22, 4, 4, G.skinDark, s, ox, oy);
|
|
1134
|
+
// Hand/knuckles
|
|
1135
|
+
px(ctx, 24 + raOff, 28, 7, 3, G.furDark, s, ox, oy);
|
|
1136
|
+
px(ctx, 25 + raOff, 28, 5, 2, G.furMain, s, ox, oy);
|
|
1137
|
+
// Claws
|
|
1138
|
+
px(ctx, 24 + raOff, 31, 1, 1, G.claws, s, ox, oy);
|
|
1139
|
+
px(ctx, 26 + raOff, 31, 1, 1, G.claws, s, ox, oy);
|
|
1140
|
+
px(ctx, 28 + raOff, 31, 1, 1, G.claws, s, ox, oy);
|
|
1141
|
+
px(ctx, 30 + raOff, 31, 1, 1, G.claws, s, ox, oy);
|
|
1142
|
+
// ── Head (rows 3-12): big round head ──
|
|
1143
|
+
const hx = 7 + headTilt;
|
|
1144
|
+
const hy = 3;
|
|
1145
|
+
// Outline
|
|
1146
|
+
outlineRect(ctx, hx, hy, 18, 10, G.outline, s, ox, oy);
|
|
1147
|
+
// Head fill — dark base
|
|
1148
|
+
px(ctx, hx, hy, 18, 10, G.furDark, s, ox, oy);
|
|
1149
|
+
// Main fur
|
|
1150
|
+
px(ctx, hx + 1, hy + 1, 16, 8, G.furMain, s, ox, oy);
|
|
1151
|
+
// Brow ridge highlight
|
|
1152
|
+
px(ctx, hx + 2, hy + 1, 14, 2, G.furLight, s, ox, oy);
|
|
1153
|
+
// Lighter face area (center)
|
|
1154
|
+
px(ctx, hx + 4, hy + 4, 10, 5, G.furChest, s, ox, oy);
|
|
1155
|
+
// Darker brow ridge above eyes (makes them look grumpy/narrowed)
|
|
1156
|
+
px(ctx, hx + 3, hy + 3, 12, 2, G.skinDark, s, ox, oy);
|
|
1157
|
+
// ── Eyes (rows 7-9 relative, hy+4 to hy+6 in head) ──
|
|
1158
|
+
const eyeY = hy + 5;
|
|
1159
|
+
if (eyeState === 'blink') {
|
|
1160
|
+
// Closed eyes — thin line
|
|
1161
|
+
px(ctx, hx + 5, eyeY, 3, 1, G.outline, s, ox, oy);
|
|
1162
|
+
px(ctx, hx + 11, eyeY, 3, 1, G.outline, s, ox, oy);
|
|
1163
|
+
}
|
|
1164
|
+
else if (eyeState === 'narrow') {
|
|
1165
|
+
// Narrowed/grumpy eyes — 3x2, squinted
|
|
1166
|
+
// Eye whites (narrow slit)
|
|
1167
|
+
px(ctx, hx + 5, eyeY, 3, 2, G.eyeWhite, s, ox, oy);
|
|
1168
|
+
px(ctx, hx + 11, eyeY, 3, 2, G.eyeWhite, s, ox, oy);
|
|
1169
|
+
// Pupils
|
|
1170
|
+
px(ctx, hx + 6, eyeY, 2, 2, G.eyePupil, s, ox, oy);
|
|
1171
|
+
px(ctx, hx + 12, eyeY, 2, 2, G.eyePupil, s, ox, oy);
|
|
1172
|
+
// Heavy brow line pushing down (grumpy)
|
|
1173
|
+
px(ctx, hx + 4, eyeY - 1, 5, 1, G.skinDark, s, ox, oy);
|
|
1174
|
+
px(ctx, hx + 10, eyeY - 1, 5, 1, G.skinDark, s, ox, oy);
|
|
1175
|
+
}
|
|
1176
|
+
else if (eyeState === 'up') {
|
|
1177
|
+
// Looking up
|
|
1178
|
+
px(ctx, hx + 5, eyeY - 1, 3, 2, G.eyeWhite, s, ox, oy);
|
|
1179
|
+
px(ctx, hx + 11, eyeY - 1, 3, 2, G.eyeWhite, s, ox, oy);
|
|
1180
|
+
px(ctx, hx + 6, eyeY - 1, 1, 1, G.eyePupil, s, ox, oy);
|
|
1181
|
+
px(ctx, hx + 12, eyeY - 1, 1, 1, G.eyePupil, s, ox, oy);
|
|
1182
|
+
}
|
|
1183
|
+
else if (eyeState === 'wide') {
|
|
1184
|
+
// Wide/surprised
|
|
1185
|
+
px(ctx, hx + 5, eyeY - 1, 3, 3, G.eyeWhite, s, ox, oy);
|
|
1186
|
+
px(ctx, hx + 11, eyeY - 1, 3, 3, G.eyeWhite, s, ox, oy);
|
|
1187
|
+
px(ctx, hx + 6, eyeY, 1, 1, G.eyePupil, s, ox, oy);
|
|
1188
|
+
px(ctx, hx + 12, eyeY, 1, 1, G.eyePupil, s, ox, oy);
|
|
1189
|
+
}
|
|
1190
|
+
else {
|
|
1191
|
+
// Normal eyes
|
|
1192
|
+
px(ctx, hx + 5, eyeY, 3, 2, G.eyeWhite, s, ox, oy);
|
|
1193
|
+
px(ctx, hx + 11, eyeY, 3, 2, G.eyeWhite, s, ox, oy);
|
|
1194
|
+
px(ctx, hx + 6, eyeY, 2, 2, G.eyePupil, s, ox, oy);
|
|
1195
|
+
px(ctx, hx + 12, eyeY, 2, 2, G.eyePupil, s, ox, oy);
|
|
1196
|
+
}
|
|
1197
|
+
// ── Nose ──
|
|
1198
|
+
px(ctx, hx + 8, hy + 7, 3, 2, G.nose, s, ox, oy);
|
|
1199
|
+
// Nostrils
|
|
1200
|
+
px(ctx, hx + 8, hy + 8, 1, 1, G.outline, s, ox, oy);
|
|
1201
|
+
px(ctx, hx + 10, hy + 8, 1, 1, G.outline, s, ox, oy);
|
|
1202
|
+
// ── Mouth (rows 10-11 of head) ──
|
|
1203
|
+
const mouthY = hy + 9;
|
|
1204
|
+
const mouthX = hx + 6;
|
|
1205
|
+
if (mouthOpen === 0) {
|
|
1206
|
+
// Closed grumpy mouth — wide line with downturned ends
|
|
1207
|
+
px(ctx, mouthX, mouthY, 7, 1, G.mouth, s, ox, oy);
|
|
1208
|
+
// Fangs poking down
|
|
1209
|
+
px(ctx, mouthX + 1, mouthY + 1, 1, 1, G.fang, s, ox, oy);
|
|
1210
|
+
px(ctx, mouthX + 5, mouthY + 1, 1, 1, G.fang, s, ox, oy);
|
|
1211
|
+
}
|
|
1212
|
+
else if (mouthOpen === 1) {
|
|
1213
|
+
// Half open
|
|
1214
|
+
px(ctx, mouthX, mouthY, 7, 1, G.mouth, s, ox, oy);
|
|
1215
|
+
px(ctx, mouthX + 1, mouthY + 1, 5, 1, G.mouth, s, ox, oy);
|
|
1216
|
+
// Fangs
|
|
1217
|
+
px(ctx, mouthX, mouthY + 1, 1, 1, G.fang, s, ox, oy);
|
|
1218
|
+
px(ctx, mouthX + 6, mouthY + 1, 1, 1, G.fang, s, ox, oy);
|
|
1219
|
+
}
|
|
1220
|
+
else if (mouthOpen === 2) {
|
|
1221
|
+
// Wide open — show red inside
|
|
1222
|
+
px(ctx, mouthX - 1, mouthY, 9, 2, G.mouth, s, ox, oy);
|
|
1223
|
+
px(ctx, mouthX, mouthY, 7, 2, '#8B2020', s, ox, oy); // red inner
|
|
1224
|
+
// Big fangs
|
|
1225
|
+
px(ctx, mouthX - 1, mouthY, 1, 2, G.fang, s, ox, oy);
|
|
1226
|
+
px(ctx, mouthX + 7, mouthY, 1, 2, G.fang, s, ox, oy);
|
|
1227
|
+
}
|
|
1228
|
+
else {
|
|
1229
|
+
// Closed tight line
|
|
1230
|
+
px(ctx, mouthX + 1, mouthY, 5, 1, G.mouth, s, ox, oy);
|
|
1231
|
+
}
|
|
1232
|
+
// ── Baseball cap (rows 0-5): red with white front panel ──
|
|
1233
|
+
const capX = hx - 1 + capTilt;
|
|
1234
|
+
const capY = hy - 3;
|
|
1235
|
+
// Cap crown — red
|
|
1236
|
+
px(ctx, capX + 2, capY, 16, 2, G.capRed, s, ox, oy);
|
|
1237
|
+
px(ctx, capX + 1, capY + 2, 18, 2, G.capRed, s, ox, oy);
|
|
1238
|
+
// White front panel (left portion — cap is backwards/sideways)
|
|
1239
|
+
px(ctx, capX + 2, capY, 5, 2, G.capWhite, s, ox, oy);
|
|
1240
|
+
px(ctx, capX + 1, capY + 2, 6, 2, G.capWhite, s, ox, oy);
|
|
1241
|
+
// Brim extending to the right (cap worn sideways)
|
|
1242
|
+
px(ctx, capX + 18, capY + 3, 4, 2, G.capRed, s, ox, oy);
|
|
1243
|
+
px(ctx, capX + 18, capY + 4, 4, 1, G.capBrim, s, ox, oy); // brim underside
|
|
1244
|
+
// Cap outline
|
|
1245
|
+
px(ctx, capX + 2, capY - 1, 16, 1, G.outline, s, ox, oy); // top
|
|
1246
|
+
px(ctx, capX, capY + 2, 1, 2, G.outline, s, ox, oy); // left side
|
|
1247
|
+
px(ctx, capX + 1, capY + 4, 18, 1, G.outline, s, ox, oy); // bottom band
|
|
1248
|
+
px(ctx, capX + 22, capY + 3, 1, 2, G.outline, s, ox, oy); // brim end
|
|
1249
|
+
// Cap button on top
|
|
1250
|
+
px(ctx, capX + 9, capY - 1, 2, 1, G.capRed, s, ox, oy);
|
|
1251
|
+
// ── Question mark particle (thinking frame 2) ──
|
|
1252
|
+
if (questionMark) {
|
|
1253
|
+
const qColor = getMoodColor('thinking', frame, moodColor);
|
|
1254
|
+
px(ctx, hx + 6, hy - 6, 3, 1, qColor, s, ox, oy);
|
|
1255
|
+
px(ctx, hx + 8, hy - 5, 1, 1, qColor, s, ox, oy);
|
|
1256
|
+
px(ctx, hx + 7, hy - 4, 1, 1, qColor, s, ox, oy);
|
|
1257
|
+
px(ctx, hx + 7, hy - 2, 1, 1, qColor, s, ox, oy);
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
/**
|
|
1261
|
+
* Draw animated mood particles around the gorilla.
|
|
1262
|
+
* Same interface as drawMoodParticles but tuned for gorilla position/shape.
|
|
1263
|
+
*/
|
|
1264
|
+
export function drawGorillaParticles(ctx, x, y, scale, mood, frame) {
|
|
1265
|
+
const s = scale;
|
|
1266
|
+
const color = getMoodColor(mood, frame);
|
|
1267
|
+
if (mood === 'dancing') {
|
|
1268
|
+
// Music notes floating up
|
|
1269
|
+
const notes = [
|
|
1270
|
+
{ baseX: -2, baseY: 2, phase: 0 },
|
|
1271
|
+
{ baseX: 30, baseY: 0, phase: 2 },
|
|
1272
|
+
{ baseX: 14, baseY: -2, phase: 4 },
|
|
1273
|
+
];
|
|
1274
|
+
for (const note of notes) {
|
|
1275
|
+
const floatY = ((frame + note.phase) % 8) * -2;
|
|
1276
|
+
const ny = note.baseY + floatY;
|
|
1277
|
+
if (ny > -8) {
|
|
1278
|
+
const c = RAINBOW[(frame + note.phase) % RAINBOW.length];
|
|
1279
|
+
px(ctx, note.baseX, ny + 3, 2, 2, c, s, x, y);
|
|
1280
|
+
px(ctx, note.baseX + 1, ny, 1, 3, c, s, x, y);
|
|
1281
|
+
px(ctx, note.baseX + 1, ny, 2, 1, c, s, x, y);
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
else if (mood === 'excited') {
|
|
1286
|
+
// Sparkle + shapes
|
|
1287
|
+
const positions = [
|
|
1288
|
+
{ x: -2, y: 4 }, { x: 30, y: 2 },
|
|
1289
|
+
{ x: 2, y: -4 }, { x: 28, y: -2 },
|
|
1290
|
+
];
|
|
1291
|
+
for (let i = 0; i < positions.length; i++) {
|
|
1292
|
+
const visible = ((frame + i * 2) % 4) < 2;
|
|
1293
|
+
if (!visible)
|
|
1294
|
+
continue;
|
|
1295
|
+
const p = positions[i];
|
|
1296
|
+
px(ctx, p.x + 1, p.y, 1, 3, color, s, x, y);
|
|
1297
|
+
px(ctx, p.x, p.y + 1, 3, 1, color, s, x, y);
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
else if (mood === 'thinking') {
|
|
1301
|
+
// Thought bubbles
|
|
1302
|
+
const f = frame % 3;
|
|
1303
|
+
if (f === 0) {
|
|
1304
|
+
px(ctx, 20, -6, 3, 1, color, s, x, y);
|
|
1305
|
+
px(ctx, 22, -5, 1, 1, color, s, x, y);
|
|
1306
|
+
px(ctx, 21, -4, 1, 1, color, s, x, y);
|
|
1307
|
+
px(ctx, 21, -2, 1, 1, color, s, x, y);
|
|
1308
|
+
}
|
|
1309
|
+
else if (f === 1) {
|
|
1310
|
+
px(ctx, 19, -4, 1, 1, color, s, x, y);
|
|
1311
|
+
px(ctx, 21, -5, 1, 1, color, s, x, y);
|
|
1312
|
+
px(ctx, 23, -4, 1, 1, color, s, x, y);
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
else if (mood === 'talking') {
|
|
1316
|
+
// Sound waves from mouth area
|
|
1317
|
+
const baseX = 26;
|
|
1318
|
+
const baseY = 14;
|
|
1319
|
+
for (let i = 0; i < 3; i++) {
|
|
1320
|
+
const visible = ((frame + i) % 4) < 3;
|
|
1321
|
+
if (!visible)
|
|
1322
|
+
continue;
|
|
1323
|
+
const dist = i * 2 + ((frame % 2) * 1);
|
|
1324
|
+
const alpha = 1 - i * 0.3;
|
|
1325
|
+
const c = dimColor(color.startsWith('rgb') ? '#58a6ff' : color, alpha);
|
|
1326
|
+
px(ctx, baseX + dist, baseY - 1, 1, 1, c, s, x, y);
|
|
1327
|
+
px(ctx, baseX + dist + 1, baseY, 1, 1, c, s, x, y);
|
|
1328
|
+
px(ctx, baseX + dist, baseY + 1, 1, 1, c, s, x, y);
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
898
1332
|
// ─── Mood Particles ────────────────────────────────────────────
|
|
899
1333
|
/**
|
|
900
1334
|
* Draw animated mood particles around the robot.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export interface ViewerMemory {
|
|
2
|
+
username: string;
|
|
3
|
+
firstSeen: string;
|
|
4
|
+
totalMessages: number;
|
|
5
|
+
topics: string[];
|
|
6
|
+
personality_notes: string;
|
|
7
|
+
lastInteraction: string;
|
|
8
|
+
}
|
|
9
|
+
export interface ChatAIStats {
|
|
10
|
+
totalMessages: number;
|
|
11
|
+
totalResponses: number;
|
|
12
|
+
uniqueViewers: number;
|
|
13
|
+
currentMode: string;
|
|
14
|
+
currentTopic: string;
|
|
15
|
+
uptime: number;
|
|
16
|
+
modelInUse: string;
|
|
17
|
+
queueDepth: number;
|
|
18
|
+
}
|
|
19
|
+
export declare class StreamChatAI {
|
|
20
|
+
private mode;
|
|
21
|
+
private viewers;
|
|
22
|
+
private contextHistory;
|
|
23
|
+
private currentTopic;
|
|
24
|
+
private topicHistory;
|
|
25
|
+
private lastResponseTime;
|
|
26
|
+
private messagesSinceResponse;
|
|
27
|
+
private totalMessages;
|
|
28
|
+
private totalResponses;
|
|
29
|
+
private startTime;
|
|
30
|
+
private modelInUse;
|
|
31
|
+
private queue;
|
|
32
|
+
private activeTriviaQuestion;
|
|
33
|
+
private processing;
|
|
34
|
+
constructor();
|
|
35
|
+
processMessage(username: string, message: string, platform: string): Promise<string | null>;
|
|
36
|
+
private shouldRespond;
|
|
37
|
+
private processQueue;
|
|
38
|
+
private generateChatResponse;
|
|
39
|
+
private handleGreeting;
|
|
40
|
+
private handleCompliment;
|
|
41
|
+
handleCommand(cmd: string, args: string, username: string): Promise<string>;
|
|
42
|
+
private handleAsk;
|
|
43
|
+
private handleJoke;
|
|
44
|
+
private handleTrivia;
|
|
45
|
+
private touchViewer;
|
|
46
|
+
private isNewOrReturning;
|
|
47
|
+
setMode(mode: 'reactive' | 'conversational' | 'entertainer' | 'quiet'): void;
|
|
48
|
+
getMode(): string;
|
|
49
|
+
getViewerMemory(username: string): ViewerMemory | null;
|
|
50
|
+
getTopicSummary(): string;
|
|
51
|
+
getStats(): ChatAIStats;
|
|
52
|
+
saveMemory(): void;
|
|
53
|
+
loadMemory(): void;
|
|
54
|
+
}
|
|
55
|
+
export declare function registerStreamChatAITools(): void;
|
|
56
|
+
//# sourceMappingURL=stream-chat-ai.d.ts.map
|