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 STEPS = 40;
|
||||
const DURATION_MS = 30 * 1000;
|
||||
const STEPS = Math.ceil(DURATION_MS / STEP_MS);
|
||||
|
||||
module.exports = {
|
||||
id: 'cameraWhiplash',
|
||||
@@ -29,13 +30,23 @@ module.exports = {
|
||||
});
|
||||
if (step >= STEPS) {
|
||||
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);
|
||||
|
||||
ctx.sendAlert({
|
||||
color: '#00bcd4',
|
||||
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 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() {
|
||||
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 = {
|
||||
id: 'chatSpam',
|
||||
name: 'Chat Spam',
|
||||
goal: 320,
|
||||
async run(ctx) {
|
||||
const nickname = randLetter();
|
||||
const messageCount = 100;
|
||||
for (let i = 0; i < messageCount; i += 1) {
|
||||
for (let burst = 0; burst < BURST_COUNT; burst += 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);
|
||||
let text = '';
|
||||
for (let j = 0; j < len; j += 1) {
|
||||
@@ -28,5 +42,7 @@ module.exports = {
|
||||
// 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 BURSTS = 200;
|
||||
const TICK_MS = 180;
|
||||
const BURSTS = 120;
|
||||
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 = {
|
||||
id: 'ghostTypingSpam',
|
||||
name: 'Typing Spam',
|
||||
goal: 220,
|
||||
async run(ctx) {
|
||||
let tick = 0;
|
||||
const active = new Set();
|
||||
const timer = setInterval(() => {
|
||||
tick += 1;
|
||||
for (let burst = 0; burst < BURSTS; burst += 1) {
|
||||
const name = NAMES[Math.floor(Math.random() * NAMES.length)] + String(Math.floor(Math.random() * 10));
|
||||
const on = Math.random() > 0.35;
|
||||
try {
|
||||
@@ -24,8 +31,9 @@ module.exports = {
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
if (tick >= BURSTS) {
|
||||
clearInterval(timer);
|
||||
await sleep(randInt(MIN_BURST_DELAY_MS, MAX_BURST_DELAY_MS));
|
||||
}
|
||||
|
||||
active.forEach((ghost) => {
|
||||
try {
|
||||
ctx.sendExternalTyping({ nickname: ghost, role: 'spectator', roverId: null, isTyping: false });
|
||||
@@ -33,8 +41,6 @@ module.exports = {
|
||||
// ignore
|
||||
}
|
||||
});
|
||||
}
|
||||
}, TICK_MS);
|
||||
|
||||
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;
|
||||
|
||||
@@ -36,7 +37,7 @@ async function restore(ctx, effect = {}) {
|
||||
|
||||
function scheduleRestore(ctx, effect = {}) {
|
||||
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 next = { ...effect, endsAt };
|
||||
ctx.saveEffect('modeJam', next);
|
||||
@@ -49,12 +50,15 @@ function scheduleRestore(ctx, effect = {}) {
|
||||
|
||||
module.exports = {
|
||||
id: 'modeJam',
|
||||
name: 'Admin Mode',
|
||||
goal: 1000,
|
||||
name: 'Admin Lock',
|
||||
goal: 900,
|
||||
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 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 {
|
||||
ctx.setMode('admin', 'buttonbox:modeJam');
|
||||
@@ -70,7 +74,7 @@ module.exports = {
|
||||
scheduleRestore(ctx, {
|
||||
prevMode,
|
||||
prevReason,
|
||||
endsAt: Date.now() + DURATION_MS,
|
||||
endsAt,
|
||||
});
|
||||
|
||||
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();
|
||||
if (!allRewards.length) throw new Error('No rewards configured');
|
||||
const excludeRewardId = typeof options.excludeRewardId === 'string' ? options.excludeRewardId : null;
|
||||
|
||||
const usedByOtherButtons = new Set(
|
||||
state.buttons
|
||||
@@ -139,8 +140,15 @@ function assignNewReward(button) {
|
||||
.map((entry) => entry.rewardId)
|
||||
.filter((rewardId) => typeof rewardId === 'string' && rewardId.length > 0),
|
||||
);
|
||||
const uniqueCandidates = allRewards.filter((reward) => !usedByOtherButtons.has(reward.id));
|
||||
const pool = uniqueCandidates.length ? uniqueCandidates : allRewards;
|
||||
const uniqueCandidates = allRewards.filter(
|
||||
(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;
|
||||
if (!reward) throw new Error('No rewards configured');
|
||||
|
||||
@@ -260,7 +268,7 @@ async function runRewardForButton(button) {
|
||||
});
|
||||
button.lastRewardAt = Date.now();
|
||||
button.count = 0;
|
||||
assignNewReward(button);
|
||||
assignNewReward(button, { excludeRewardId: reward.id });
|
||||
}
|
||||
|
||||
async function applyPress(buttonId) {
|
||||
|
||||
Reference in New Issue
Block a user