Skip to main content
Foundation’s SimpleConversation class allows you to create interactive chat conversations with players, isolating their chat and processing their messages as conversation input.

Creating a conversation

Extend SimpleConversation and implement getFirstPrompt():
public class WarpCreationConversation extends SimpleConversation {

    public WarpCreationConversation() {
        // Optional: return to menu when done
        super(null);
    }

    @Override
    protected Prompt getFirstPrompt() {
        return new NamePrompt();
    }

    private class NamePrompt extends SimplePrompt {

        @Override
        protected String getPrompt(ConversationContext context) {
            return "&6Enter a name for your warp:";
        }

        @Override
        protected boolean isInputValid(ConversationContext context, String input) {
            return input.length() >= 3 && input.length() <= 16;
        }

        @Override
        protected String getFailedValidationText(ConversationContext context, String input) {
            return "&cWarp name must be between 3 and 16 characters!";
        }

        @Override
        protected Prompt acceptValidatedInput(ConversationContext context, String input) {
            context.setSessionData("name", input);
            
            return new ConfirmPrompt();
        }
    }

    private class ConfirmPrompt extends SimplePrompt {

        @Override
        protected String getPrompt(ConversationContext context) {
            String name = (String) context.getSessionData("name");
            return "&aCreate warp '&e" + name + "&a'? (yes/no)";
        }

        @Override
        protected Prompt acceptValidatedInput(ConversationContext context, String input) {
            if (input.equalsIgnoreCase("yes")) {
                String name = (String) context.getSessionData("name");
                Player player = (Player) context.getForWhom();
                
                // Create warp
                Warp.create(name, player.getLocation());
                
                tell(context.getForWhom(), "&aWarp created successfully!");
            } else {
                tell(context.getForWhom(), "&cWarp creation cancelled.");
            }
            
            return END_OF_CONVERSATION;
        }
    }
}

Starting conversations

new WarpCreationConversation().start(player);

Prompt types

Simple prompt

private class MyPrompt extends SimplePrompt {

    @Override
    protected String getPrompt(ConversationContext ctx) {
        return "Enter something:";
    }

    @Override
    protected Prompt acceptValidatedInput(ConversationContext ctx, String input) {
        ctx.setSessionData("data", input);
        return new NextPrompt();
    }
}

Numeric prompt

private class AmountPrompt extends NumericPrompt {

    @Override
    public String getPromptText(ConversationContext ctx) {
        return "Enter an amount:";
    }

    @Override
    protected Prompt acceptValidatedInput(ConversationContext ctx, Number input) {
        ctx.setSessionData("amount", input.intValue());
        return new NextPrompt();
    }

    @Override
    protected String getFailedValidationText(ConversationContext ctx, String input) {
        return "&c'" + input + "' is not a valid number!";
    }
}

Boolean prompt

private class ConfirmPrompt extends BooleanPrompt {

    @Override
    public String getPromptText(ConversationContext ctx) {
        return "Are you sure? (yes/no)";
    }

    @Override
    protected Prompt acceptValidatedInput(ConversationContext ctx, boolean input) {
        if (input) {
            // User said yes
            tell(ctx.getForWhom(), "Confirmed!");
        } else {
            // User said no
            tell(ctx.getForWhom(), "Cancelled.");
        }
        
        return END_OF_CONVERSATION;
    }
}

Fixed set prompt

private class ModePrompt extends FixedSetPrompt {

    public ModePrompt() {
        super("creative", "survival", "adventure");
    }

    @Override
    public String getPromptText(ConversationContext ctx) {
        return "&6Choose a mode: &ecreative&7, &esurvival&7, or &eadventure";
    }

    @Override
    protected Prompt acceptValidatedInput(ConversationContext ctx, String input) {
        ctx.setSessionData("mode", input);
        return END_OF_CONVERSATION;
    }
}

Session data

Store and retrieve data throughout the conversation:
// Store data
context.setSessionData("key", value);

// Retrieve data
String name = (String) context.getSessionData("name");
int amount = (int) context.getSessionData("amount");

// Check if exists
if (context.getSessionData("key") != null) {
    // ...
}

Messaging

tell(conversable, "&aMessage to player");
tellLater(5, conversable, "&eDelayed message");

Configuration

Prefix

@Override
protected ConversationPrefix getPrefix() {
    return new SimplePrefix("&6[Warp] &r");
}

@Override
protected boolean insertPrefix() {
    return true; // Show prefix before messages
}

Timeout

@Override
protected int getTimeout() {
    return 120; // 2 minutes of inactivity
}

Exit words

@Override
protected ConversationCanceller getCanceller() {
    return new SimpleCanceller("quit", "cancel", "exit", "stop");
}
@Override
protected boolean isModal() {
    return true; // Hide other plugin messages during conversation
}
Return to a menu after conversation ends:
public class MyConversation extends SimpleConversation {

    public MyConversation(Menu menuToReturnTo) {
        super(menuToReturnTo);
    }

    @Override
    protected boolean reopenMenu() {
        return true; // Reopen menu after conversation
    }

    @Override
    public String getMenuAnimatedTitle() {
        return "&aConversation completed!";
    }
}

// Start from menu
new MyConversation(this).start(player);

Conversation lifecycle

On end

@Override
protected void onConversationEnd(ConversationAbandonedEvent event, boolean timeout) {
    if (timeout) {
        tell(event.getContext().getForWhom(), "&cConversation timed out!");
    } else if (event.gracefulExit()) {
        tell(event.getContext().getForWhom(), "&aConversation completed!");
    } else {
        tell(event.getContext().getForWhom(), "&cConversation cancelled.");
    }
}

Prompt end

private class MyPrompt extends SimplePrompt {

    @Override
    public void onConversationEnd(SimpleConversation conversation, 
                                   ConversationAbandonedEvent event) {
        // Cleanup for this specific prompt
    }
}

Example: Multi-step setup

public class ShopSetupConversation extends SimpleConversation {

    @Override
    protected Prompt getFirstPrompt() {
        return new NamePrompt();
    }

    private class NamePrompt extends SimplePrompt {
        @Override
        protected String getPrompt(ConversationContext ctx) {
            tellBoxed((Conversable) ctx.getForWhom(),
                "&6&lShop Setup Wizard",
                "",
                "&7Step 1/3: Enter shop name",
                "&7Type 'cancel' to quit"
            );
            return "&eShop name:";
        }

        @Override
        protected boolean isInputValid(ConversationContext ctx, String input) {
            return input.length() >= 3;
        }

        @Override
        protected Prompt acceptValidatedInput(ConversationContext ctx, String input) {
            ctx.setSessionData("name", input);
            return new LocationPrompt();
        }
    }

    private class LocationPrompt extends SimplePrompt {
        @Override
        protected String getPrompt(ConversationContext ctx) {
            return "&7Step 2/3: Type 'here' to set location, or 'skip':";
        }

        @Override
        protected Prompt acceptValidatedInput(ConversationContext ctx, String input) {
            if (input.equalsIgnoreCase("here")) {
                Player player = (Player) ctx.getForWhom();
                ctx.setSessionData("location", player.getLocation());
            }
            return new PricePrompt();
        }
    }

    private class PricePrompt extends NumericPrompt {
        @Override
        public String getPromptText(ConversationContext ctx) {
            return "&7Step 3/3: Enter price:";
        }

        @Override
        protected Prompt acceptValidatedInput(ConversationContext ctx, Number input) {
            String name = (String) ctx.getSessionData("name");
            Location loc = (Location) ctx.getSessionData("location");
            double price = input.doubleValue();
            
            // Create shop
            Shop.create(name, loc, price);
            
            tellBoxed(ctx.getForWhom(),
                "&a&lShop Created!",
                "",
                "&7Name: &f" + name,
                "&7Price: &f$" + price
            );
            
            return END_OF_CONVERSATION;
        }
    }
}
Conversations automatically close the player’s inventory to allow typing.
Always validate user input - never trust it directly without checking!

Build docs developers (and LLMs) love