/*
* Copyright (c) 2014 tabletoptool.com team.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*
* Contributors:
* rptools.com team - initial implementation
* tabletoptool.com team - further development
*/
package com.t3.client.ui.commandpanel;
import java.util.List;
import org.apache.log4j.Logger;
import com.t3.chatparser.ChatPart;
import com.t3.chatparser.ExpressionPart;
import com.t3.chatparser.ParsedChat;
import com.t3.chatparser.UnknownCommandException;
import com.t3.chatparser.generated.ParseException;
import com.t3.client.TabletopTool;
import com.t3.guid.GUID;
import com.t3.language.I18N;
import com.t3.macro.MacroEngine;
import com.t3.macro.MacroException;
import com.t3.model.CellPoint;
import com.t3.model.LookupTable;
import com.t3.model.LookupTable.LookupEntry;
import com.t3.model.Player;
import com.t3.model.chat.PlayerSpeaker;
import com.t3.model.chat.Speaker;
import com.t3.model.chat.TextMessage;
import com.t3.model.chat.TokenSpeaker;
import com.t3.model.Token;
import com.t3.util.guidreference.TokenReference;
/**
* This is the class responsible for starting the chat parser on a given string and executing
* the results. It can also parse arbitrary dice expressions.
* @author Virenerus
*/
public class ChatExecutor {
private static final Logger log=Logger.getLogger(ChatExecutor.class);
/**
* This parses the given text and returns the list of understood parts that you can work with.
* @param str the text to parse
* @return a list of the understood words
* @throws UnknownCommandException if you try to call an unknown chat command
* @throws ParseException when the command could not be parsed correctly (e.g. invalid parameters)
*/
public static ParsedChat parseChat(String text) throws UnknownCommandException, ParseException {
return new com.t3.chatparser.generated.ChatParser(text).parse();
}
public static void executeChat(String text, Speaker identity) {
try {
ParsedChat parts=parseChat(text);
if(parts.getChatCommand()!=null) {
switch(parts.getChatCommand()) {
case CLEAR_CHAT:
clearChat();
break;
case EMIT:
emit(buildDefaultStringRepresentation(parts), identity);
break;
case EMOTE:
emote(buildDefaultStringRepresentation(parts),identity);
break;
case GM:
gm(buildDefaultStringRepresentation(parts),identity);
break;
case GOTO: {
try {
String[] args=parts.getArguments();
int x=Integer.parseInt(args[0]);
int y=Integer.parseInt(args[1]);
TabletopTool.getFrame().getCurrentZoneRenderer().centerOn(new CellPoint(x, y));
} catch(Exception e) {
throw new IllegalArgumentException("goto expects a coordinate x y");
}
}
break;
case IMPERSONATE:
try {
GUID guid = new GUID(parts.getArguments()[0]);
TabletopTool.getFrame().getCommandPanel().setImpersonatedToken(new TokenReference(TabletopTool.getFrame().getCurrentZoneRenderer().getZone(), guid));
} catch(Exception e) {
throw new IllegalArgumentException("impersonate expects one 16 characters token id");
}
break;
case CLEAR_IMPERSONATE:
TabletopTool.getFrame().getCommandPanel().setImpersonatedToken(null);
break;
case MACRO_EXEC:
List<Token> l=TabletopTool.getFrame().getCurrentZoneRenderer().getSelectedTokensList();
if(l==null || l.isEmpty())
MacroEngine.getInstance().evaluate(buildDefaultStringRepresentation(parts));
else
MacroEngine.getInstance().evaluate(buildDefaultStringRepresentation(parts),l.get(0));
break;
case OOC:
outOfCharacter(buildDefaultStringRepresentation(parts));
break;
case REPLY:
reply(buildDefaultStringRepresentation(parts), identity);
break;
case ROLL:
TabletopTool.addMessage(TextMessage.say(((ExpressionPart)parts.get(0)).getDiceExpression().toCompleteChatString(),identity));
break;
case ROLL_GM:
TabletopTool.addMessage(TextMessage.gm(((ExpressionPart)parts.get(0)).getDiceExpression().toCompleteChatString(),identity));
break;
case ROLL_ME:
TabletopTool.addMessage(TextMessage.me(((ExpressionPart)parts.get(0)).getDiceExpression().toCompleteChatString()));
break;
case ROLL_SECRET:
String roll=((ExpressionPart)parts.get(0)).getDiceExpression().toCompleteChatString();
if (!TabletopTool.getPlayer().isGM()) {
TabletopTool.addMessage(new TextMessage(TextMessage.Channel.GM, null, TabletopTool.getPlayer().getName(), "* " +
I18N.getText("rollsecret.gm.string", identity, roll)));
TabletopTool.addMessage(new TextMessage(TextMessage.Channel.ME, null, TabletopTool.getPlayer().getName(),
I18N.getText("rollsecret.self.string")));
} else {
TabletopTool.addMessage(new TextMessage(TextMessage.Channel.GM, null, TabletopTool.getPlayer().getName(), "* " +
I18N.getText("rollsecret.gmself.string", roll)));
}
break;
case SELF:
self(buildDefaultStringRepresentation(parts));
break;
case TABLE:
try {
String[] args=parts.getArguments();
String tableName=args[0];
String value=(args.length==1?null:args[1]);
LookupTable lookupTable = TabletopTool.getCampaign().getLookupTableMap().get(tableName);
if(!TabletopTool.getPlayer().isGM() && !lookupTable.getAllowLookup()) {
if(lookupTable.getVisible()) {
TabletopTool.addLocalMessage(I18N.getText("msg.error.tableDoesNotExist") + " '" + tableName + "'");
} else {
TabletopTool.showError(I18N.getText("msg.error.tableAccessProhibited") + ": " + tableName);
}
return;
}
if (lookupTable == null) {
TabletopTool.addLocalMessage(I18N.getText("msg.error.tableDoesNotExist") + " '" + tableName + "'");
return;
}
LookupEntry result = lookupTable.getLookup(value);
String lookupValue = result.getValue();
// Command handling
if (result != null && lookupValue.startsWith("/")) {
MacroEngine.getInstance().evaluate(lookupValue);
return;
}
StringBuilder sb=new StringBuilder();
sb.append("Table ").append(tableName).append(" (");
sb.append(identity);
sb.append("): ");
if (result.getImageId() != null) {
sb.append("<img src=\"asset://").append(result.getImageId()).append("\" alt=\"").append(result.getValue()).append("\">");
} else {
sb.append("<span style='color:red'>");
sb.append(lookupValue);
sb.append("</span>");
}
TabletopTool.addMessage(TextMessage.say(sb.toString(),identity));
} catch (Exception pe) {
TabletopTool.addLocalMessage("lookuptable.couldNotPerform" + pe.getMessage());
}
break;
case TOKEN_SPEECH:
List<Token> selectedTokens = TabletopTool.getFrame().getCurrentZoneRenderer().getSelectedTokensList();
if (selectedTokens.isEmpty()) {
TabletopTool.addLocalMessage(I18N.getText("msg.error.noTokensSelected"));
return;
}
for (Token token : selectedTokens) {
String speechKey = token.getSpeech(parts.getArguments()[0]);
String speech=token.getSpeech(speechKey);
if(text!=null)
TabletopTool.addMessage(TextMessage.say(speech, new TokenSpeaker(token.getId())));
}
break;
case WHISPER:
String playerName=null;
String part1=parts.get(0).getDefaultTextRepresentation();
for(Player p:TabletopTool.getPlayerList()) {
if(part1.startsWith(p.getName()))
playerName=p.getName();
}
String message=buildDefaultStringRepresentation(parts).substring(playerName.length()).trim();
whisper(message, identity, playerName);
break;
default:
break;
}
}
else
say(buildDefaultStringRepresentation(parts), identity);
} catch (MacroException | IllegalArgumentException | UnknownCommandException | ParseException e) {
TabletopTool.addLocalMessage("<font color=\"red\">"+e.getMessage()+"</font>");
log.error(e);
e.printStackTrace();
}
}
public static void gm(String message, Speaker identity) {
TabletopTool.addMessage(TextMessage.gm(message, identity));
}
public static void say(String message, Speaker identity) {
TabletopTool.addMessage(TextMessage.say(message, identity));
}
public static void whisper(String message, Speaker identity, String targetPlayer) {
try {
// Validate
if (!TabletopTool.isPlayerConnected(targetPlayer)) {
TabletopTool.addMessage(TextMessage.me(I18N.getText("msg.error.playerNotConnected", targetPlayer)));
return;
}
if (TabletopTool.getPlayer().getName().equalsIgnoreCase(targetPlayer)) {
TabletopTool.addMessage(TextMessage.me(I18N.getText("whisper.toSelf")));
return;
}
// Send
TabletopTool.addMessage(TextMessage.whisper(targetPlayer, "<span class='whisper' style='color:blue'>"
+ I18N.getText("whisper.string", identity, message)+"</span>"));
TabletopTool.addMessage(TextMessage.me("<span class='whisper' style='color:blue'>" +
I18N.getText("whisper.you.string", targetPlayer, message) + "</span>"));
} catch(Exception e) {
e.printStackTrace();
}
}
public static void self(String message) {
TabletopTool.addMessage(TextMessage.me(message));
}
public static void reply(String message, Speaker identity) {
String playerName = TabletopTool.getLastWhisperer();
if (playerName == null)
{
TabletopTool.addMessage(TextMessage.me("<b>You have no one to which to reply.</b>"));
}
// Validate
if (!TabletopTool.isPlayerConnected(playerName)) {
TabletopTool.addMessage(TextMessage.me(I18N.getText("msg.error.playerNotConnected", playerName)));
return;
}
if (TabletopTool.getPlayer().getName().equalsIgnoreCase(playerName)) {
TabletopTool.addMessage(TextMessage.me(I18N.getText("whisper.toSelf")));
return;
}
// Send
TabletopTool.addMessage(TextMessage.whisper(playerName, "<span class='whisper' style='color:blue'>"
+ I18N.getText("whisper.string", identity, message)+"</span>"));
TabletopTool.addMessage(TextMessage.me("<span class='whisper' style='color:blue'>" +
I18N.getText("whisper.you.string", playerName, message) + "</span>"));
}
public static void outOfCharacter(String message) {
TabletopTool.addMessage(TextMessage.say("(( "+message+" ))", new PlayerSpeaker(TabletopTool.getPlayer())));
}
public static void emote(String message, Speaker identity) {
TabletopTool.addGlobalMessage("<span color=\"green\" style=\"font-style: italic;\">"
+identity+" "+message+"</span>");
}
public static void emit(String message, Speaker identity) {
if(TabletopTool.getPlayer().isGM())
TabletopTool.addGlobalMessage(message);
else
TabletopTool.addMessage(TextMessage.say(message,identity));
}
public static void clearChat() {
TabletopTool.getFrame().getCommandPanel().clearMessagePanel();
}
private static String buildDefaultStringRepresentation(List<? extends ChatPart> parts) {
StringBuilder sb=new StringBuilder();
for(ChatPart cp:parts)
sb.append(cp.getDefaultTextRepresentation());
return sb.toString();
}
}