From 9c7ae89dd2fc3b7e6f542b4caf876b87d84429dc Mon Sep 17 00:00:00 2001 From: roberts Date: Mon, 30 Mar 2026 13:34:34 -0500 Subject: [PATCH] Fix craft parsing, sustained combat, combat cooldown - Craft regex now captures only 1-2 words (not entire sentence) - Mine regex same fix - Combat is now sustained: bridge keeps attacking every 500ms until target dies, leaves range, or 10s timeout - Combat has 12-second cooldown to prevent spam - Bot chases target if too far for melee during combat Co-Authored-By: Claude Opus 4.6 (1M context) --- bridge/src/index.js | 46 ++++++++++++++++++++++++++++++++-- dougbot/core/behaviors.py | 14 ++++++++--- dougbot/core/command_parser.py | 4 +-- 3 files changed, 56 insertions(+), 8 deletions(-) diff --git a/bridge/src/index.js b/bridge/src/index.js index 9ddf713..4ffc66e 100644 --- a/bridge/src/index.js +++ b/bridge/src/index.js @@ -583,8 +583,50 @@ async function handleAction(action, params = {}) { if (hostiles.length === 0) return { attacked: false, reason: 'no_hostiles' }; hostiles.sort((a, b) => a.dist - b.dist); const target = hostiles[0].entity; - bot.attack(target); - return { attacked: true, target: target.name || target.type, distance: hostiles[0].dist }; + + // Sustained combat: keep attacking until target is dead or out of range + let hits = 0; + const maxHits = 20; + const combatPromise = new Promise((resolve) => { + const attackInterval = setInterval(() => { + // Check if target is still alive and in range + const ent = bot.entities[target.id]; + if (!ent || !ent.position) { + clearInterval(attackInterval); + resolve({ attacked: true, hits, result: 'target_gone' }); + return; + } + const d = ent.position.distanceTo(bot.entity.position); + if (d > range + 2) { + clearInterval(attackInterval); + resolve({ attacked: true, hits, result: 'out_of_range' }); + return; + } + if (hits >= maxHits) { + clearInterval(attackInterval); + resolve({ attacked: true, hits, result: 'max_hits' }); + return; + } + + // Look at target and attack + bot.lookAt(ent.position.offset(0, ent.height * 0.8, 0)).then(() => { + try { bot.attack(ent); hits++; } catch (e) {} + }); + + // Move toward target if too far for melee + if (d > 3) { + bot.pathfinder.setGoal(new GoalNear(ent.position.x, ent.position.y, ent.position.z, 2)); + } + }, 500); // Attack every 500ms + + // Safety timeout + setTimeout(() => { + clearInterval(attackInterval); + resolve({ attacked: true, hits, result: 'timeout' }); + }, 10000); + }); + + return await combatPromise; } // --- Crafting --- diff --git a/dougbot/core/behaviors.py b/dougbot/core/behaviors.py index 042d8b8..41e8853 100644 --- a/dougbot/core/behaviors.py +++ b/dougbot/core/behaviors.py @@ -41,6 +41,7 @@ class BehaviorEngine: self._last_scan_time = 0.0 self._last_chat_time = 0.0 self._last_wander_time = 0.0 + self._last_combat_time = 0.0 self._explored_positions: list[dict] = [] # Places we've been self._known_containers: list[dict] = [] # Containers we've found self._relationships: dict[str, float] = {} # Player name → fondness (-1 to 1) @@ -215,19 +216,24 @@ class BehaviorEngine: if bravery < 30: return None # Too scared to fight + # Cooldown — don't spam combat tasks + if time.time() - self._last_combat_time < 12: + return None + # Find attackable hostile within melee range for hostile in self.nearby_hostiles: dist = hostile.get("distance", 99) - if dist < 4 and self.health > 8: + if dist < 5 and self.health > 8: # Brave Dougs attack, others might not if bravery > 60 or (bravery > 40 and self.health > 14): + self._last_combat_time = time.time() return Task( - name=f"attack_{hostile['type']}", + name="combat", priority=Priority.HIGH, action="attack_nearest_hostile", - params={"range": 5}, + params={"range": 6}, description=f"Fighting a {hostile['type']}!", - timeout=10, + timeout=15, ) return None diff --git a/dougbot/core/command_parser.py b/dougbot/core/command_parser.py index b0295d9..53ae1fb 100644 --- a/dougbot/core/command_parser.py +++ b/dougbot/core/command_parser.py @@ -57,11 +57,11 @@ class CommandParser: ] CRAFT_PATTERNS = [ - r"(?:craft|make|build|create)\s+(?:a\s+|an\s+|some\s+)?(.+)", + r"(?:craft|make|build|create)\s+(?:a\s+|an\s+|some\s+|me\s+)?(\w+(?:\s+\w+)?)", ] MINE_PATTERNS = [ - r"(?:mine|dig|break|destroy)\s+(?:that|this|the)?\s*(.+)", + r"(?:mine|dig|break|destroy)\s+(?:that|this|the|some)?\s*(\w+(?:\s+\w+)?)", ] GIVE_PATTERNS = [