package tradeskills import ( "fmt" "log" "math" ) // PacketBuilder handles the construction of tradeskill-related packets. type PacketBuilder interface { // SendCreateFromRecipe builds and sends the recipe crafting UI packet SendCreateFromRecipe(clientID uint32, recipeID uint32) error // SendItemCreationUI builds and sends the item creation/crafting progress UI packet SendItemCreationUI(clientID uint32, recipeID uint32) error // StopCrafting sends the stop crafting packet StopCrafting(clientID uint32) error // CounterReaction sends the event counter reaction packet CounterReaction(clientID uint32, countered bool) error // UpdateCreateItem sends crafting progress update packet UpdateCreateItem(clientID uint32, update CraftingUpdate) error } // CraftingUpdate represents data for a crafting progress update packet. type CraftingUpdate struct { TableSpawnID uint32 // ID of the crafting table spawn Effect int8 // Effect type (1=crit success, 2=success, 3=failure, 4=crit failure) TotalDurability int32 // Current total durability TotalProgress int32 // Current total progress DurabilityChange int32 // Change in durability this update ProgressChange int32 // Change in progress this update ProgressLevel int8 // Progress level (0-4) Event *TradeskillEvent // Active event (if any) } // RecipeComponentInfo represents component information for recipe UI. type RecipeComponentInfo struct { ItemID uint32 // Item ID UniqueID uint32 // Unique item instance ID Name string // Item name Icon uint32 // Item icon Quantity int16 // Available quantity QuantityUsed int16 // Quantity being used } // RecipeUIData represents all data needed for the recipe creation UI. type RecipeUIData struct { RecipeID uint32 // Recipe ID RecipeName string // Recipe name CraftingStation string // Required crafting station Tier int8 // Recipe tier ProductName string // Product name ProductIcon uint32 // Product icon ProductQuantity int16 // Product quantity PrimaryTitle string // Primary component title PrimaryQuantityNeeded int16 // Primary component quantity needed PrimaryComponents []RecipeComponentInfo // Available primary components PrimarySelected []RecipeComponentInfo // Selected primary components BuildComponents [][]RecipeComponentInfo // Build components (slots 1-4) BuildSelected [][]RecipeComponentInfo // Selected build components BuildTitles []string // Build component titles BuildQuantitiesNeeded []int16 // Build component quantities needed FuelTitle string // Fuel component title FuelQuantityNeeded int16 // Fuel component quantity needed FuelComponents []RecipeComponentInfo // Available fuel components FuelSelected []RecipeComponentInfo // Selected fuel components MassProductionChoices []int32 // Mass production quantity choices } // ItemCreationUIData represents data for the item creation progress UI. type ItemCreationUIData struct { MaxPossibleDurability int32 // Maximum durability (1000) MaxPossibleProgress int32 // Maximum progress (1000) ProductProgressNeeded int32 // Progress needed for completion (1000) ProgressLevelsKnown int8 // Highest stage known by player ProcessStages []ItemCreationStage // Process stages (0-3) ProductItem ItemCreationProduct // Final product SkillIDs []uint32 // Available skill IDs for crafting } // ItemCreationStage represents a stage in the item creation process. type ItemCreationStage struct { ProgressNeeded int32 // Progress needed for this stage Product ItemCreationProduct // Product for this stage Byproduct ItemCreationProduct // Byproduct for this stage (optional) } // ItemCreationProduct represents a product in item creation. type ItemCreationProduct struct { Name string // Product name Icon uint32 // Product icon } // DefaultPacketBuilder is a basic implementation of PacketBuilder. // In a real implementation, this would interface with the actual packet system. type DefaultPacketBuilder struct { // TODO: Add dependencies for actual packet building (client manager, item manager, etc.) } // NewDefaultPacketBuilder creates a new default packet builder. func NewDefaultPacketBuilder() *DefaultPacketBuilder { return &DefaultPacketBuilder{} } // SendCreateFromRecipe builds and sends the recipe crafting UI packet. func (pb *DefaultPacketBuilder) SendCreateFromRecipe(clientID uint32, recipeID uint32) error { // TODO: Implement actual packet building // This is a placeholder implementation showing the data structure log.Printf("Building WS_CreateFromRecipe packet for client %d, recipe %d", clientID, recipeID) // In the real implementation, this would: // 1. Get recipe from master recipe list // 2. Validate player has recipe // 3. Validate crafting table // 4. Build component lists // 5. Calculate mass production options // 6. Create and send packet uiData := RecipeUIData{ RecipeID: recipeID, RecipeName: "Example Recipe", CraftingStation: "Forge", Tier: 1, ProductName: "Iron Sword", ProductIcon: 12345, ProductQuantity: 1, PrimaryTitle: "Metal", PrimaryQuantityNeeded: 2, MassProductionChoices: []int32{1, 5, 10, 15, 20, 25}, } // Add example primary components uiData.PrimaryComponents = []RecipeComponentInfo{ {ItemID: 1001, UniqueID: 5001, Name: "Iron Ingot", Icon: 100, Quantity: 5, QuantityUsed: 2}, {ItemID: 1002, UniqueID: 5002, Name: "Steel Ingot", Icon: 101, Quantity: 3, QuantityUsed: 2}, } // TODO: Send actual packet to client log.Printf("Would send recipe UI data: %+v", uiData) return nil } // SendItemCreationUI builds and sends the item creation/crafting progress UI packet. func (pb *DefaultPacketBuilder) SendItemCreationUI(clientID uint32, recipeID uint32) error { log.Printf("Building WS_ShowItemCreation packet for client %d, recipe %d", clientID, recipeID) // TODO: Implement actual packet building // This would build the crafting progress window with stages uiData := ItemCreationUIData{ MaxPossibleDurability: MaxDurability, MaxPossibleProgress: MaxProgress, ProductProgressNeeded: MaxProgress, ProgressLevelsKnown: 0, // Player's highest known stage } // Add process stages (0-3, stage 4 is completion) for i := 0; i < 4; i++ { stage := ItemCreationStage{ Product: ItemCreationProduct{ Name: fmt.Sprintf("Stage %d Product", i), Icon: uint32(1000 + i), }, } switch i { case 0: stage.ProgressNeeded = 0 // Stage 0 is fuel/byproduct case 1: stage.ProgressNeeded = ProgressStage1 case 2: stage.ProgressNeeded = ProgressStage2 case 3: stage.ProgressNeeded = ProgressStage3 } uiData.ProcessStages = append(uiData.ProcessStages, stage) } // Final product (stage 4) uiData.ProductItem = ItemCreationProduct{ Name: "Completed Item", Icon: 2000, } // TODO: Add skills for tradeskill techniques uiData.SkillIDs = []uint32{ // These would be populated from player's spellbook based on recipe technique } log.Printf("Would send item creation UI data: %+v", uiData) return nil } // StopCrafting sends the stop crafting packet. func (pb *DefaultPacketBuilder) StopCrafting(clientID uint32) error { log.Printf("Sending OP_StopItemCreationMsg to client %d", clientID) // TODO: Send actual packet // This would be a simple packet with opcode OP_StopItemCreationMsg and no data return nil } // CounterReaction sends the event counter reaction packet. func (pb *DefaultPacketBuilder) CounterReaction(clientID uint32, countered bool) error { log.Printf("Sending WS_TSEventReaction to client %d, countered=%v", clientID, countered) // TODO: Build and send WS_TSEventReaction packet // packet->setDataByName("counter_reaction", countered ? 1 : 0); return nil } // UpdateCreateItem sends crafting progress update packet. func (pb *DefaultPacketBuilder) UpdateCreateItem(clientID uint32, update CraftingUpdate) error { log.Printf("Sending WS_UpdateCreateItem to client %d: progress=%d, durability=%d, effect=%d", clientID, update.TotalProgress, update.TotalDurability, update.Effect) // Calculate progress level based on current progress progressLevel := int8(0) if update.TotalProgress >= MaxProgress { progressLevel = 4 } else if update.TotalProgress >= ProgressStage3 { progressLevel = 3 } else if update.TotalProgress >= ProgressStage2 { progressLevel = 2 } else if update.TotalProgress >= ProgressStage1 { progressLevel = 1 } update.ProgressLevel = progressLevel // TODO: Build and send actual WS_UpdateCreateItem packet // This would include: // - spawn_id (table spawn ID) // - effect (1=crit success, 2=success, 3=failure, 4=crit failure) // - total_durability // - total_progress // - durability_change // - progress_change // - progress_level // - reaction_icon (if event) // - reaction_name (if event) if update.Event != nil { log.Printf("Including event in update: %s (icon: %d)", update.Event.Name, update.Event.Icon) } return nil } // PacketHelper provides utility functions for packet building. type PacketHelper struct{} // CalculateProgressStage determines the progress stage based on current progress. func (ph *PacketHelper) CalculateProgressStage(progress int32) int8 { if progress >= MaxProgress { return 4 } else if progress >= ProgressStage3 { return 3 } else if progress >= ProgressStage2 { return 2 } else if progress >= ProgressStage1 { return 1 } return 0 } // GetMassProductionQuantities returns the available mass production quantities. func (ph *PacketHelper) GetMassProductionQuantities(maxLevel int) []int32 { // Base quantities quantities := []int32{1} // Add additional quantities based on achievement/level // This matches the C++ logic: v{1,2,4,6,11,21}[mp] where mp is 5 maxQuantities := []int32{1, 2, 4, 6, 11, 21} for i := 1; i < len(maxQuantities) && i <= maxLevel; i++ { quantities = append(quantities, maxQuantities[i]*5) } return quantities } // ValidateRecipeComponents checks if the player has the required components. func (ph *PacketHelper) ValidateRecipeComponents(playerID uint32, components []ComponentUsage) error { // TODO: Implement component validation // This would check player inventory for required items and quantities if len(components) == 0 { return fmt.Errorf("no components provided") } for _, component := range components { if component.ItemUniqueID == 0 { return fmt.Errorf("invalid component unique ID") } if component.Quantity <= 0 { return fmt.Errorf("invalid component quantity: %d", component.Quantity) } } log.Printf("Component validation passed for player %d", playerID) return nil } // CalculateItemPacketType determines the packet type based on client version. func (ph *PacketHelper) CalculateItemPacketType(clientVersion int16) int16 { // TODO: Implement version-specific packet type calculation // This would match the GetItemPacketType function from the C++ code if clientVersion < 860 { return 1 // Older packet format } else if clientVersion < 1193 { return 2 // Middle packet format } else { return 3 // Newer packet format } } // GetClientItemPacketOffset returns the item packet offset for a client version. func (ph *PacketHelper) GetClientItemPacketOffset(clientVersion int16) int32 { // TODO: Implement version-specific offset calculation // This matches client->GetClientItemPacketOffset() from C++ if clientVersion < 860 { return -1 // Use default offset } return 2 // Standard offset for newer clients } // ComponentSlotInfo represents information about recipe component slots. type ComponentSlotInfo struct { SlotID int8 // Component slot (0=primary, 1-4=build, 5=fuel) Title string // Component title/name QuantityReq int16 // Required quantity QuantityHave int16 // Available quantity Items []RecipeComponentInfo // Available items for this slot } // BuildComponentSlots creates component slot information for a recipe. func (ph *PacketHelper) BuildComponentSlots(recipeID uint32, playerID uint32) ([]ComponentSlotInfo, error) { // TODO: Implement actual component slot building // This would query the recipe and player inventory to build component options slots := []ComponentSlotInfo{ { SlotID: ComponentSlotPrimary, Title: "Primary Component", QuantityReq: 2, QuantityHave: 5, Items: []RecipeComponentInfo{ {ItemID: 1001, UniqueID: 5001, Name: "Iron Ingot", Icon: 100, Quantity: 5}, }, }, { SlotID: ComponentSlotFuel, Title: "Fuel", QuantityReq: 1, QuantityHave: 3, Items: []RecipeComponentInfo{ {ItemID: 2001, UniqueID: 6001, Name: "Coal", Icon: 200, Quantity: 3}, }, }, } return slots, nil } // EffectTypeFromOutcome converts a crafting outcome to an effect type. func (ph *PacketHelper) EffectTypeFromOutcome(outcome CraftingOutcome) int8 { if outcome.CriticalSuccess { return 1 // Critical success } else if outcome.Success { return 2 // Regular success } else if outcome.CriticalFailure { return 4 // Critical failure } else { return 3 // Regular failure } } // ClampProgress ensures progress values are within valid ranges. func (ph *PacketHelper) ClampProgress(progress int32) int32 { return int32(math.Max(float64(MinProgress), math.Min(float64(MaxProgress), float64(progress)))) } // ClampDurability ensures durability values are within valid ranges. func (ph *PacketHelper) ClampDurability(durability int32) int32 { return int32(math.Max(float64(MinDurability), math.Min(float64(MaxDurability), float64(durability)))) }