UNPKG

hytopia

Version:

The HYTOPIA SDK makes it easy for developers to create massively multiplayer games using JavaScript or TypeScript.

182 lines (151 loc) 4.58 kB
import { Vector3, World } from "hytopia"; import { BaseAgent, type AgentBehavior } from "../BaseAgent"; interface FishResult { success: boolean; size?: "small" | "medium" | "large"; } /** * This is a simple implementation of a fishing behavior for Agents. * It does not include animations or any other fancy features. * Agents can call actions like `cast_rod` to start fishing, and the environment will trigger a callback when the fishing is complete. * This is a simple example of ENVIRONMENT type messages for Agents. */ export class FishingBehavior implements AgentBehavior { private isFishing: boolean = false; private readonly PIER_LOCATION = new Vector3(31.5, 3, 59.5); private readonly FISHING_RANGE = 5; // meters private readonly CATCH_PROBABILITIES = { nothing: 0.4, small: 0.3, medium: 0.2, large: 0.1, }; onUpdate(agent: BaseAgent, world: World): void { // Could add ambient fishing animations here if needed } private isNearPier(agent: BaseAgent): boolean { const distance = Vector3.fromVector3Like(agent.position).distance( this.PIER_LOCATION ); return distance <= this.FISHING_RANGE; } private rollForFish(): FishResult { const roll = Math.random(); let cumulative = 0; // Check for no catch cumulative += this.CATCH_PROBABILITIES.nothing; if (roll < cumulative) { return { success: false }; } // Check for small fish cumulative += this.CATCH_PROBABILITIES.small; if (roll < cumulative) { return { success: true, size: "small", }; } // Check for medium fish cumulative += this.CATCH_PROBABILITIES.medium; if (roll < cumulative) { return { success: true, size: "medium", }; } // Must be a large fish return { success: true, size: "large", }; } onToolCall( agent: BaseAgent, world: World, toolName: string, args: any ): string | void { if (toolName === "cast_rod") { console.log("Fishing tool called"); if (!this.isNearPier(agent)) { return "You need to be closer to the pier to fish!"; } if (this.isFishing) { return "You're already fishing!"; } this.isFishing = true; // Start fishing animation if available agent.stopModelAnimations(["walk_upper", "walk_lower", "run_upper", "run_lower"]); agent.startModelLoopedAnimations(["idle_upper", "idle_lower"]); // Could be replaced with a fishing animation // Simulate fishing time setTimeout(() => { this.isFishing = false; const result = this.rollForFish(); if (!result.success) { agent.handleEnvironmentTrigger( "Nothing seems to be biting..." ); return; } const fishDescription = `${result.size} fish`; agent.addToInventory({ name: fishDescription, quantity: 1, metadata: { size: result.size, }, }); agent.handleEnvironmentTrigger( `You caught ${fishDescription}!` ); }, 5000); // 5 second fishing time return "Casting your line..."; } else if (toolName === "give_fish") { const { size, weight, target } = args; const fishDescription = `${size} fish`; if (!agent.removeFromInventory(fishDescription, 1)) { return "You don't have that fish anymore!"; } const nearbyEntities = agent.getNearbyEntities(5); const targetEntity = nearbyEntities.find((e) => e.name === target); if (!targetEntity) { return `Cannot find ${target} nearby. Try getting closer to them.`; } // Add to target's inventory if it's an agent if (targetEntity.type === "Agent") { const targetAgent = world.entityManager .getAllEntities() .find( (e) => e instanceof BaseAgent && e.name === target ) as BaseAgent; if (targetAgent) { targetAgent.addToInventory({ name: fishDescription, quantity: 1, metadata: { size, weight }, }); } } return `Successfully gave ${fishDescription} to ${target}`; } } getPromptInstructions(): string { return ` To fish at the pier, use: <action type="cast_rod"></action> You must call cast_rod exactly like this, with the empty object inside the action tag. To give a fish to another agent, use: <action type="give_fish"> { size: "small" | "medium" | "large", target: "name of the player or agent to give the fish to" } </action> You must be within 5 meters of the pier to fish. Each attempt takes 5 seconds and has a chance to catch nothing or a fish of varying sizes. You can only have one line in the water at a time.`; } getState(): string { return this.isFishing ? "Currently fishing" : "Not fishing"; } }