mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 01:21:20 -04:00
button reward updates
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
const STEP_MS = 220;
|
const STEP_MS = 220;
|
||||||
const STEPS = 40;
|
const DURATION_MS = 30 * 1000;
|
||||||
|
const STEPS = Math.ceil(DURATION_MS / STEP_MS);
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
id: 'cameraWhiplash',
|
id: 'cameraWhiplash',
|
||||||
@@ -29,13 +30,23 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
if (step >= STEPS) {
|
if (step >= STEPS) {
|
||||||
clearInterval(timer);
|
clearInterval(timer);
|
||||||
|
rovers.forEach((rover) => {
|
||||||
|
try {
|
||||||
|
ctx.issueCommand(String(rover.id), {
|
||||||
|
type: 'servo',
|
||||||
|
servo: { angle: 0 },
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
ctx.logger.warn('cameraWhiplash servo reset failed', { roverId: rover.id, error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}, STEP_MS);
|
}, STEP_MS);
|
||||||
|
|
||||||
ctx.sendAlert({
|
ctx.sendAlert({
|
||||||
color: '#00bcd4',
|
color: '#00bcd4',
|
||||||
title: 'Camera Wiggle',
|
title: 'Camera Wiggle',
|
||||||
message: `Wobble running on ${rovers.length} rover(s).`,
|
message: `Wobble running for 30 seconds on ${rovers.length} rover(s).`,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,17 +1,31 @@
|
|||||||
const LETTERS = 'abcdefghijklmnopqrstuvwxyz';
|
const LETTERS = 'abcdefghijklmnopqrstuvwxyz';
|
||||||
|
const BURST_COUNT = 18;
|
||||||
|
const MIN_BURST_SIZE = 2;
|
||||||
|
const MAX_BURST_SIZE = 8;
|
||||||
|
const MIN_BURST_DELAY_MS = 80;
|
||||||
|
const MAX_BURST_DELAY_MS = 420;
|
||||||
|
|
||||||
function randLetter() {
|
function randLetter() {
|
||||||
return LETTERS[Math.floor(Math.random() * LETTERS.length)];
|
return LETTERS[Math.floor(Math.random() * LETTERS.length)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function randInt(min, max) {
|
||||||
|
return min + Math.floor(Math.random() * (max - min + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
function sleep(ms) {
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
id: 'chatSpam',
|
id: 'chatSpam',
|
||||||
name: 'Chat Spam',
|
name: 'Chat Spam',
|
||||||
goal: 320,
|
goal: 320,
|
||||||
async run(ctx) {
|
async run(ctx) {
|
||||||
const nickname = randLetter();
|
const nickname = randLetter();
|
||||||
const messageCount = 100;
|
for (let burst = 0; burst < BURST_COUNT; burst += 1) {
|
||||||
for (let i = 0; i < messageCount; i += 1) {
|
const burstSize = randInt(MIN_BURST_SIZE, MAX_BURST_SIZE);
|
||||||
|
for (let i = 0; i < burstSize; i += 1) {
|
||||||
const len = 3 + Math.floor(Math.random() * 14);
|
const len = 3 + Math.floor(Math.random() * 14);
|
||||||
let text = '';
|
let text = '';
|
||||||
for (let j = 0; j < len; j += 1) {
|
for (let j = 0; j < len; j += 1) {
|
||||||
@@ -28,5 +42,7 @@ module.exports = {
|
|||||||
// ignore spam errors
|
// ignore spam errors
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
await sleep(randInt(MIN_BURST_DELAY_MS, MAX_BURST_DELAY_MS));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,16 +1,23 @@
|
|||||||
const NAMES = ['ross', 'david', 'chirpet', 'caydu', 'meow', 'wawa'];
|
const NAMES = ['ross', 'david', 'chirpet', 'caydu', 'meow', 'wawa'];
|
||||||
const BURSTS = 200;
|
const BURSTS = 120;
|
||||||
const TICK_MS = 180;
|
const MIN_BURST_DELAY_MS = 70;
|
||||||
|
const MAX_BURST_DELAY_MS = 350;
|
||||||
|
|
||||||
|
function randInt(min, max) {
|
||||||
|
return min + Math.floor(Math.random() * (max - min + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
function sleep(ms) {
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
id: 'ghostTypingSpam',
|
id: 'ghostTypingSpam',
|
||||||
name: 'Typing Spam',
|
name: 'Typing Spam',
|
||||||
goal: 220,
|
goal: 220,
|
||||||
async run(ctx) {
|
async run(ctx) {
|
||||||
let tick = 0;
|
|
||||||
const active = new Set();
|
const active = new Set();
|
||||||
const timer = setInterval(() => {
|
for (let burst = 0; burst < BURSTS; burst += 1) {
|
||||||
tick += 1;
|
|
||||||
const name = NAMES[Math.floor(Math.random() * NAMES.length)] + String(Math.floor(Math.random() * 10));
|
const name = NAMES[Math.floor(Math.random() * NAMES.length)] + String(Math.floor(Math.random() * 10));
|
||||||
const on = Math.random() > 0.35;
|
const on = Math.random() > 0.35;
|
||||||
try {
|
try {
|
||||||
@@ -24,8 +31,9 @@ module.exports = {
|
|||||||
} catch {
|
} catch {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
if (tick >= BURSTS) {
|
await sleep(randInt(MIN_BURST_DELAY_MS, MAX_BURST_DELAY_MS));
|
||||||
clearInterval(timer);
|
}
|
||||||
|
|
||||||
active.forEach((ghost) => {
|
active.forEach((ghost) => {
|
||||||
try {
|
try {
|
||||||
ctx.sendExternalTyping({ nickname: ghost, role: 'spectator', roverId: null, isTyping: false });
|
ctx.sendExternalTyping({ nickname: ghost, role: 'spectator', roverId: null, isTyping: false });
|
||||||
@@ -33,8 +41,6 @@ module.exports = {
|
|||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}, TICK_MS);
|
|
||||||
|
|
||||||
ctx.sendAlert({ color: '#9c27b0', title: 'Typing Spam', message: 'Ghost typing burst started.' });
|
ctx.sendAlert({ color: '#9c27b0', title: 'Typing Spam', message: 'Ghost typing burst started.' });
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
const DURATION_MS = 300 * 1000;
|
const MIN_DURATION_MS = 5 * 60 * 1000;
|
||||||
|
const MAX_DURATION_MS = 10 * 60 * 1000;
|
||||||
|
|
||||||
let activeTimer = null;
|
let activeTimer = null;
|
||||||
|
|
||||||
@@ -36,7 +37,7 @@ async function restore(ctx, effect = {}) {
|
|||||||
|
|
||||||
function scheduleRestore(ctx, effect = {}) {
|
function scheduleRestore(ctx, effect = {}) {
|
||||||
clearTimer();
|
clearTimer();
|
||||||
const endsAt = Number(effect.endsAt || Date.now() + DURATION_MS);
|
const endsAt = Number(effect.endsAt || Date.now() + MIN_DURATION_MS);
|
||||||
const remaining = Math.max(0, endsAt - Date.now());
|
const remaining = Math.max(0, endsAt - Date.now());
|
||||||
const next = { ...effect, endsAt };
|
const next = { ...effect, endsAt };
|
||||||
ctx.saveEffect('modeJam', next);
|
ctx.saveEffect('modeJam', next);
|
||||||
@@ -49,12 +50,15 @@ function scheduleRestore(ctx, effect = {}) {
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
id: 'modeJam',
|
id: 'modeJam',
|
||||||
name: 'Admin Mode',
|
name: 'Admin Lock',
|
||||||
goal: 1000,
|
goal: 900,
|
||||||
async run(ctx) {
|
async run(ctx) {
|
||||||
|
const durationMs =
|
||||||
|
MIN_DURATION_MS + Math.floor(Math.random() * (MAX_DURATION_MS - MIN_DURATION_MS + 1));
|
||||||
|
const endsAt = Date.now() + durationMs;
|
||||||
const prevMode = ctx.getMode();
|
const prevMode = ctx.getMode();
|
||||||
const prevReason = ctx.getAdminReasonText();
|
const prevReason = ctx.getAdminReasonText();
|
||||||
const jamReason = `Locked by button box until ${new Date(Date.now() + DURATION_MS).toLocaleTimeString()}`;
|
const jamReason = `Locked by button box until ${new Date(endsAt).toLocaleTimeString()}`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ctx.setMode('admin', 'buttonbox:modeJam');
|
ctx.setMode('admin', 'buttonbox:modeJam');
|
||||||
@@ -70,7 +74,7 @@ module.exports = {
|
|||||||
scheduleRestore(ctx, {
|
scheduleRestore(ctx, {
|
||||||
prevMode,
|
prevMode,
|
||||||
prevReason,
|
prevReason,
|
||||||
endsAt: Date.now() + DURATION_MS,
|
endsAt,
|
||||||
});
|
});
|
||||||
|
|
||||||
ctx.sendAlert({ color: '#ff5722', title: 'Admin Mode', message: 'Server forced into admin mode temporarily.' });
|
ctx.sendAlert({ color: '#ff5722', title: 'Admin Mode', message: 'Server forced into admin mode temporarily.' });
|
||||||
|
|||||||
@@ -129,9 +129,10 @@ function ensureRewardAssignments() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function assignNewReward(button) {
|
function assignNewReward(button, options = {}) {
|
||||||
const allRewards = listRewards();
|
const allRewards = listRewards();
|
||||||
if (!allRewards.length) throw new Error('No rewards configured');
|
if (!allRewards.length) throw new Error('No rewards configured');
|
||||||
|
const excludeRewardId = typeof options.excludeRewardId === 'string' ? options.excludeRewardId : null;
|
||||||
|
|
||||||
const usedByOtherButtons = new Set(
|
const usedByOtherButtons = new Set(
|
||||||
state.buttons
|
state.buttons
|
||||||
@@ -139,8 +140,15 @@ function assignNewReward(button) {
|
|||||||
.map((entry) => entry.rewardId)
|
.map((entry) => entry.rewardId)
|
||||||
.filter((rewardId) => typeof rewardId === 'string' && rewardId.length > 0),
|
.filter((rewardId) => typeof rewardId === 'string' && rewardId.length > 0),
|
||||||
);
|
);
|
||||||
const uniqueCandidates = allRewards.filter((reward) => !usedByOtherButtons.has(reward.id));
|
const uniqueCandidates = allRewards.filter(
|
||||||
const pool = uniqueCandidates.length ? uniqueCandidates : allRewards;
|
(reward) => !usedByOtherButtons.has(reward.id) && (!excludeRewardId || reward.id !== excludeRewardId),
|
||||||
|
);
|
||||||
|
const fallbackUniqueCandidates = allRewards.filter((reward) => !usedByOtherButtons.has(reward.id));
|
||||||
|
const pool = uniqueCandidates.length
|
||||||
|
? uniqueCandidates
|
||||||
|
: fallbackUniqueCandidates.length
|
||||||
|
? fallbackUniqueCandidates
|
||||||
|
: allRewards.filter((reward) => !excludeRewardId || reward.id !== excludeRewardId);
|
||||||
const reward = pool[Math.floor(Math.random() * pool.length)] || null;
|
const reward = pool[Math.floor(Math.random() * pool.length)] || null;
|
||||||
if (!reward) throw new Error('No rewards configured');
|
if (!reward) throw new Error('No rewards configured');
|
||||||
|
|
||||||
@@ -260,7 +268,7 @@ async function runRewardForButton(button) {
|
|||||||
});
|
});
|
||||||
button.lastRewardAt = Date.now();
|
button.lastRewardAt = Date.now();
|
||||||
button.count = 0;
|
button.count = 0;
|
||||||
assignNewReward(button);
|
assignNewReward(button, { excludeRewardId: reward.id });
|
||||||
}
|
}
|
||||||
|
|
||||||
async function applyPress(buttonId) {
|
async function applyPress(buttonId) {
|
||||||
|
|||||||
Reference in New Issue
Block a user