WIP moving conversation flow logic from Activity to testable state machine.

This commit is contained in:
Oskar Wiksten
2013-02-20 16:07:01 +00:00
parent 6261c499ff
commit 37ae3b6c69
3 changed files with 330 additions and 248 deletions

View File

@@ -85,15 +85,20 @@ public final class Dialogs {
addMonsterIdentifiers(intent, npc);
currentActivity.startActivityForResult(intent, MainActivity.INTENTREQUEST_CONVERSATION);
}
public static void addMonsterIdentifiers(Intent intent, Monster monster) {
if (monster == null) return;
intent.putExtra("x", monster.position.x);
intent.putExtra("y", monster.position.y);
addMonsterIdentifiers(intent.getExtras(), monster);
}
public static void addMonsterIdentifiers(Bundle bundle, Monster monster) {
if (monster == null) return;
bundle.putInt("x", monster.position.x);
bundle.putInt("y", monster.position.y);
}
public static Monster getMonsterFromIntent(Intent intent, final WorldContext world) {
Bundle params = intent.getExtras();
return getMonsterFromBundle(intent.getExtras(), world);
}
public static Monster getMonsterFromBundle(Bundle params, final WorldContext world) {
if (params == null) return null;
if (!params.containsKey("x")) return null;
int x = params.getInt("x");

View File

@@ -1,13 +1,10 @@
package com.gpl.rpg.AndorsTrail.activity;
import java.util.ArrayList;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -15,28 +12,18 @@ import android.text.Spannable;
import android.text.style.ForegroundColorSpan;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.View.OnClickListener;
import android.view.View.OnKeyListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.ListView;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.*;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.TextView.BufferType;
import com.gpl.rpg.AndorsTrail.AndorsTrailApplication;
import com.gpl.rpg.AndorsTrail.Dialogs;
import com.gpl.rpg.AndorsTrail.R;
import com.gpl.rpg.AndorsTrail.context.ControllerContext;
import com.gpl.rpg.AndorsTrail.context.WorldContext;
import com.gpl.rpg.AndorsTrail.controller.ConversationController;
import com.gpl.rpg.AndorsTrail.conversation.ConversationCollection;
import com.gpl.rpg.AndorsTrail.conversation.Phrase;
import com.gpl.rpg.AndorsTrail.conversation.Phrase.Reply;
import com.gpl.rpg.AndorsTrail.model.actor.Actor;
import com.gpl.rpg.AndorsTrail.model.actor.Monster;
@@ -44,79 +31,55 @@ import com.gpl.rpg.AndorsTrail.model.actor.Player;
import com.gpl.rpg.AndorsTrail.model.item.Loot;
import com.gpl.rpg.AndorsTrail.resource.tiles.TileManager;
public final class ConversationActivity extends Activity implements OnKeyListener {
import java.util.ArrayList;
public final class ConversationActivity extends Activity implements OnKeyListener
, ConversationController.ConversationStatemachine.ConversationStateListener {
public static final int ACTIVITYRESULT_ATTACK = Activity.RESULT_FIRST_USER + 1;
public static final int ACTIVITYRESULT_REMOVE = Activity.RESULT_FIRST_USER + 2;
private static final int playerConversationColor = Color.argb(255, 0xbb, 0x22, 0x22);
private static final int NPCConversationColor = Color.argb(255, 0xbb, 0xbb, 0x22);
private WorldContext world;
private ControllerContext controllers;
private Player player;
private String phraseID;
private Phrase phrase;
private ArrayList<ConversationStatement> conversationHistory = new ArrayList<ConversationStatement>();
private ConversationController.ConversationStatemachine conversationState;
private StatementContainerAdapter listAdapter;
private Button nextButton;
private ListView statementList;
private Monster npc;
private RadioGroup replyGroup;
private OnCheckedChangeListener radioButtonListener;
private boolean displayActors = true;
private boolean applyPhraseRewards = true;
private boolean hasResumed = false;
private final ConversationCollection conversationCollection = new ConversationCollection();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AndorsTrailApplication app = AndorsTrailApplication.getApplicationFromActivity(this);
if (!app.isInitialized()) { finish(); return; }
this.world = app.getWorld();
this.controllers = app.getControllerContext();
this.player = world.model.player;
requestWindowFeature(Window.FEATURE_NO_TITLE);
Uri uri = getIntent().getData();
this.npc = Dialogs.getMonsterFromIntent(getIntent(), world);
displayActors = (npc != null);
phraseID = uri.getLastPathSegment();
if (savedInstanceState != null) {
applyPhraseRewards = false;
phraseID = savedInstanceState.getString("phraseID");
conversationHistory = savedInstanceState.getParcelableArrayList("conversationHistory");
if (conversationHistory == null) conversationHistory = new ArrayList<ConversationStatement>();
// Remove the last item since it will be re-added inside setPhrase()
int lastIndex = conversationHistory.size() - 1;
if (lastIndex >= 0) {
conversationHistory.remove(lastIndex);
}
}
setContentView(R.layout.conversation);
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AndorsTrailApplication app = AndorsTrailApplication.getApplicationFromActivity(this);
if (!app.isInitialized()) { finish(); return; }
WorldContext world = app.getWorld();
this.player = world.model.player;
this.conversationState = new ConversationController.ConversationStatemachine(world, app.getControllerContext(), getResources(), this);
replyGroup = new RadioGroup(this);
replyGroup.setLayoutParams(new ListView.LayoutParams(ListView.LayoutParams.MATCH_PARENT, ListView.LayoutParams.WRAP_CONTENT));
statementList = (ListView) findViewById(R.id.conversation_statements);
statementList.addFooterView(replyGroup);
listAdapter = new StatementContainerAdapter(this, conversationHistory, world.tileManager);
statementList.setAdapter(listAdapter);
nextButton = (Button) findViewById(R.id.conversation_next);
Button leaveButton = (Button) findViewById(R.id.conversation_leave);
leaveButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
ConversationActivity.this.finish();
}
});
radioButtonListener = new OnCheckedChangeListener() {
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.conversation);
replyGroup = new RadioGroup(this);
replyGroup.setLayoutParams(new ListView.LayoutParams(ListView.LayoutParams.MATCH_PARENT, ListView.LayoutParams.WRAP_CONTENT));
statementList = (ListView) findViewById(R.id.conversation_statements);
statementList.addFooterView(replyGroup);
listAdapter = new StatementContainerAdapter(this, conversationHistory, world.tileManager);
statementList.setAdapter(listAdapter);
nextButton = (Button) findViewById(R.id.conversation_next);
Button leaveButton = (Button) findViewById(R.id.conversation_leave);
leaveButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
ConversationActivity.this.finish();
}
});
radioButtonListener = new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton arg0, boolean arg1) {
nextButton.setEnabled(true);
@@ -128,28 +91,34 @@ public final class ConversationActivity extends Activity implements OnKeyListene
nextButtonClicked();
}
});
statementList.setOnKeyListener(this);
statementList.setSelected(false);
statementList.setFocusable(false);
statementList.setFocusableInTouchMode(false);
}
statementList.setOnKeyListener(this);
statementList.setSelected(false);
statementList.setFocusable(false);
statementList.setFocusableInTouchMode(false);
if (savedInstanceState != null) {
conversationState.setCurrentNPC(Dialogs.getMonsterFromBundle(savedInstanceState, world));
conversationHistory = savedInstanceState.getParcelableArrayList("conversationHistory");
if (conversationHistory == null) conversationHistory = new ArrayList<ConversationStatement>();
final String phraseID = savedInstanceState.getString("phraseID");
conversationState.proceedToRestoredState(phraseID);
} else {
conversationState.setCurrentNPC(Dialogs.getMonsterFromIntent(getIntent(), world));
final String phraseID = getIntent().getData().getLastPathSegment();
conversationState.proceedToPhrase(phraseID);
}
}
@Override
protected void onResume() {
super.onResume();
if (!hasResumed) {
setPhrase(phraseID);
hasResumed = true;
}
applyPhraseRewards = true;
nextButton.requestFocus();
}
private int getSelectedReplyIndex() {
for (int i = 0; i < phrase.replies.length; ++i) {
for (int i = 0; i < conversationState.getReplyCount(); ++i) {
final View v = replyGroup.getChildAt(i);
if (v == null) continue;
final RadioButton rb = (RadioButton) v;
@@ -159,10 +128,10 @@ public final class ConversationActivity extends Activity implements OnKeyListene
}
private void setSelectedReplyIndex(int i) {
if (phrase.replies == null) return;
if (phrase.replies.length <= 0) return;
int replyCount = conversationState.getReplyCount();
if (replyCount <= 0) return;
if (i < 0) i = 0;
else if (i >= phrase.replies.length) i = phrase.replies.length - 1;
else if (i >= replyCount) i = replyCount - 1;
View v = replyGroup.getChildAt(i);
if (v == null) return;
@@ -210,111 +179,9 @@ public final class ConversationActivity extends Activity implements OnKeyListene
default: return false;
}
}
private void setPhrase(String phraseID) {
this.phraseID = phraseID;
if (phraseID.equalsIgnoreCase(ConversationCollection.PHRASE_CLOSE)) {
ConversationActivity.this.finish();
return;
} else if (phraseID.equalsIgnoreCase(ConversationCollection.PHRASE_SHOP)) {
assert(npc != null);
assert(npc.getDropList() != null);
Intent intent = new Intent(this, ShopActivity.class);
Dialogs.addMonsterIdentifiers(intent, npc);
startActivity(intent);
ConversationActivity.this.finish();
return;
} else if (phraseID.equalsIgnoreCase(ConversationCollection.PHRASE_ATTACK)) {
ConversationActivity.this.setResult(ACTIVITYRESULT_ATTACK);
ConversationActivity.this.finish();
return;
} else if (phraseID.equalsIgnoreCase(ConversationCollection.PHRASE_REMOVE)) {
ConversationActivity.this.setResult(ACTIVITYRESULT_REMOVE);
ConversationActivity.this.finish();
return;
}
phrase = world.conversationLoader.loadPhrase(phraseID, conversationCollection, getResources());
if (AndorsTrailApplication.DEVELOPMENT_DEBUGMESSAGES) {
if (phrase == null) phrase = new Phrase("(phrase \"" + phraseID + "\" not implemented yet)", null, null);
}
ConversationController.PhraseRewards phraseRewards = null;
if (applyPhraseRewards) {
phraseRewards = controllers.conversationController.applyPhraseRewards(player, phrase);
}
if (phrase.message == null) {
for (Reply r : phrase.replies) {
if (!ConversationController.canSelectReply(player, r)) continue;
ConversationController.applyReplyEffect(player, r);
setPhrase(r.nextPhrase);
return;
}
}
String message = ConversationController.getDisplayMessage(phrase, player);
if (applyPhraseRewards && phraseRewards != null) {
Loot loot = phraseRewards.loot;
if (loot.hasItemsOrExp()) {
message += "\n";
if (loot.exp > 0) {
message += "\n" + getString(R.string.conversation_rewardexp, loot.exp);
}
if (loot.gold > 0) {
message += "\n" + getString(R.string.conversation_rewardgold, loot.gold);
} else if (loot.gold < 0) {
message += "\n" + getString(R.string.conversation_lostgold, -loot.gold);
}
if (!loot.items.isEmpty()) {
final int len = loot.items.countItems();
if (len == 1) {
message += "\n" + getString(R.string.conversation_rewarditem);
} else {
message += "\n" + getString(R.string.conversation_rewarditems, len);
}
}
}
}
addConversationStatement(npc, message, NPCConversationColor);
if (isPhraseOnlyNextReply(phrase)) {
nextButton.setEnabled(true);
} else {
replyGroup.removeAllViews();
for (Reply r : phrase.replies) {
addReply(phrase, r);
}
nextButton.setEnabled(false);
}
}
private void addReply(final Phrase p, final Reply r) {
if (!ConversationController.canSelectReply(player, r)) return;
RadioGroup.LayoutParams layoutParams = new RadioGroup.LayoutParams(RadioGroup.LayoutParams.MATCH_PARENT, RadioGroup.LayoutParams.WRAP_CONTENT);
RadioButton rb = new RadioButton(this);
rb.setLayoutParams(layoutParams);
rb.setText(ConversationController.getDisplayMessage(r, player));
rb.setOnCheckedChangeListener(radioButtonListener);
rb.setTag(r);
rb.setShadowLayer(1, 1, 1, Color.BLACK);
rb.setFocusable(false);
rb.setFocusableInTouchMode(false);
replyGroup.addView(rb, layoutParams);
}
private static boolean isPhraseOnlyNextReply(Phrase p) {
if (p.replies.length != 1) return false;
if (p.replies[0].text.equals(ConversationCollection.REPLY_NEXT)) return true;
return false;
}
private RadioButton getSelectedReplyButton() {
for (int i = 0; i < phrase.replies.length; ++i) {
for (int i = 0; i < conversationState.getReplyCount(); ++i) {
final View v = replyGroup.getChildAt(i);
if (v == null) continue;
final RadioButton rb = (RadioButton) v;
@@ -326,33 +193,28 @@ public final class ConversationActivity extends Activity implements OnKeyListene
}
private void nextButtonClicked() {
Reply r;
if (isPhraseOnlyNextReply(phrase)) {
// If there is only a "Next" as reply, we don't need to add it to the conversation history.
r = phrase.replies[0];
replyGroup.removeAllViews();
if (conversationState.hasOnlyOneNextReply()) {
conversationState.playerSelectedNextStep();
} else {
RadioButton rb = getSelectedReplyButton();
if (rb == null) return;
r = (Reply) rb.getTag();
addConversationStatement(player, rb.getText().toString(), playerConversationColor);
Reply r = (Reply) rb.getTag();
addConversationStatement(player, rb.getText().toString());
conversationState.playerSelectedReply(r);
}
replyGroup.removeAllViews();
ConversationController.applyReplyEffect(player, r);
setPhrase(r.nextPhrase);
}
private void addConversationStatement(Actor actor, String text, int color) {
private void addConversationStatement(Actor actor, String text) {
ConversationStatement s = new ConversationStatement();
if (displayActors) {
assert(actor != null);
if (actor != null) {
s.iconID = actor.iconID;
s.actorName = actor.getName();
} else {
s.iconID = ConversationStatement.NO_ICON;
}
s.text = text;
s.color = color;
s.color = actor == player ? playerConversationColor : NPCConversationColor;
s.isPlayerActor = actor != null && actor == player;
conversationHistory.add(s);
statementList.clearFocus();
@@ -362,8 +224,9 @@ public final class ConversationActivity extends Activity implements OnKeyListene
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putString("phraseID", phraseID);
outState.putString("phraseID", conversationState.getCurrentPhraseID());
outState.putParcelableArrayList("conversationHistory", conversationHistory);
Dialogs.addMonsterIdentifiers(outState, conversationState.getCurrentNPC());
}
private static final class ConversationStatement implements Parcelable {
@@ -453,4 +316,82 @@ public final class ConversationActivity extends Activity implements OnKeyListene
return false;
}
}
@Override
public void onTextPhraseReached(String message, Actor actor) {
addConversationStatement(actor, message);
}
@Override
public void onPlayerReceivedRewards(ConversationController.PhraseRewards phraseRewards) {
Loot loot = phraseRewards.loot;
if (!loot.hasItemsOrExp()) return;
if (loot.exp > 0) {
addRewardMessage(getString(R.string.conversation_rewardexp, loot.exp));
}
if (loot.gold > 0) {
addRewardMessage(getString(R.string.conversation_rewardgold, loot.gold));
} else if (loot.gold < 0) {
addRewardMessage(getString(R.string.conversation_lostgold, -loot.gold));
}
if (!loot.items.isEmpty()) {
final int len = loot.items.countItems();
if (len == 1) {
addRewardMessage(getString(R.string.conversation_rewarditem));
} else {
addRewardMessage(getString(R.string.conversation_rewarditems, len));
}
}
}
private void addRewardMessage(String text) {
addConversationStatement(null, text);
}
@Override
public void onConversationEnded() {
ConversationActivity.this.finish();
}
@Override
public void onConversationEndedWithShop(Monster npc) {
Intent intent = new Intent(this, ShopActivity.class);
Dialogs.addMonsterIdentifiers(intent, npc);
startActivity(intent);
ConversationActivity.this.finish();
}
@Override
public void onConversationEndedWithCombat(Monster npc) {
ConversationActivity.this.setResult(ACTIVITYRESULT_ATTACK);
ConversationActivity.this.finish();
}
@Override
public void onConversationEndedWithRemoval(Monster npc) {
ConversationActivity.this.setResult(ACTIVITYRESULT_REMOVE);
ConversationActivity.this.finish();
}
@Override
public void onConversationCanProceedWithNext() {
nextButton.setEnabled(true);
}
@Override
public void onConversationHasReply(Reply r, String message) {
RadioGroup.LayoutParams layoutParams = new RadioGroup.LayoutParams(RadioGroup.LayoutParams.MATCH_PARENT, RadioGroup.LayoutParams.WRAP_CONTENT);
RadioButton rb = new RadioButton(this);
rb.setLayoutParams(layoutParams);
rb.setText(message);
rb.setOnCheckedChangeListener(radioButtonListener);
rb.setTag(r);
rb.setShadowLayer(1, 1, 1, Color.BLACK);
rb.setFocusable(false);
rb.setFocusableInTouchMode(false);
replyGroup.addView(rb, layoutParams);
nextButton.setEnabled(false);
}
}

View File

@@ -1,7 +1,10 @@
package com.gpl.rpg.AndorsTrail.controller;
import android.content.res.Resources;
import com.gpl.rpg.AndorsTrail.context.ControllerContext;
import com.gpl.rpg.AndorsTrail.AndorsTrailApplication;
import com.gpl.rpg.AndorsTrail.context.WorldContext;
import com.gpl.rpg.AndorsTrail.conversation.ConversationCollection;
import com.gpl.rpg.AndorsTrail.conversation.Phrase;
import com.gpl.rpg.AndorsTrail.conversation.Phrase.Reply;
import com.gpl.rpg.AndorsTrail.conversation.Phrase.Reward;
@@ -9,6 +12,8 @@ import com.gpl.rpg.AndorsTrail.model.ability.ActorCondition;
import com.gpl.rpg.AndorsTrail.model.ability.ActorConditionEffect;
import com.gpl.rpg.AndorsTrail.model.ability.ActorConditionType;
import com.gpl.rpg.AndorsTrail.model.ability.SkillInfo;
import com.gpl.rpg.AndorsTrail.model.actor.Actor;
import com.gpl.rpg.AndorsTrail.model.actor.Monster;
import com.gpl.rpg.AndorsTrail.model.actor.Player;
import com.gpl.rpg.AndorsTrail.model.item.ItemTypeCollection;
import com.gpl.rpg.AndorsTrail.model.item.Loot;
@@ -37,44 +42,23 @@ public final class ConversationController {
public final ArrayList<QuestProgress> questProgress = new ArrayList<QuestProgress>();
}
public PhraseRewards applyPhraseRewards(final Player player, final Phrase phrase) {
private PhraseRewards applyPhraseRewards(final Player player, final Phrase phrase) {
if (phrase.rewards == null || phrase.rewards.length == 0) return null;
final PhraseRewards result = new PhraseRewards();
for (Reward reward : phrase.rewards) {
switch (reward.rewardType) {
case Reward.REWARD_TYPE_ACTOR_CONDITION:
int magnitude = 1;
int duration = reward.value;
if (reward.value == ActorCondition.DURATION_FOREVER) duration = ActorCondition.DURATION_FOREVER;
else if (reward.value == ActorCondition.MAGNITUDE_REMOVE_ALL) magnitude = ActorCondition.MAGNITUDE_REMOVE_ALL;
ActorConditionType conditionType = world.actorConditionsTypes.getActorConditionType(reward.rewardID);
ActorConditionEffect e = new ActorConditionEffect(conditionType, magnitude, duration, always);
controllers.actorStatsController.applyActorCondition(player, e);
result.actorConditions.add(e);
addActorConditionReward(player, reward.rewardID, reward.value, result);
break;
case Reward.REWARD_TYPE_SKILL_INCREASE:
int skillID = Integer.parseInt(reward.rewardID);
SkillInfo skill = world.skills.getSkill(skillID);
boolean addedSkill = controllers.skillController.levelUpSkillByQuest(player, skill);
if (addedSkill) {
result.skillIncrease.add(skill);
}
addSkillReward(player, Integer.parseInt(reward.rewardID), result);
break;
case Reward.REWARD_TYPE_DROPLIST:
world.dropLists.getDropList(reward.rewardID).createRandomLoot(result.loot, player);
addDropListReward(player, reward.rewardID, result);
break;
case Reward.REWARD_TYPE_QUEST_PROGRESS:
QuestProgress progress = new QuestProgress(reward.rewardID, reward.value);
boolean added = player.addQuestProgress(progress);
if (added) { // Only apply exp reward if the quest stage was reached just now (and not re-reached)
QuestLogEntry stage = world.quests.getQuestLogEntry(progress);
if (stage != null) {
result.loot.exp += stage.rewardExperience;
result.questProgress.add(progress);
}
}
addQuestProgressReward(player, reward.rewardID, reward.value, result);
break;
case Reward.REWARD_TYPE_ALIGNMENT_CHANGE:
player.addAlignment(reward.rewardID, reward.value);
@@ -86,8 +70,44 @@ public final class ConversationController {
controllers.actorStatsController.addExperience(result.loot.exp);
return result;
}
public static void applyReplyEffect(final Player player, final Reply reply) {
private void addQuestProgressReward(Player player, String questID, int questProgress, PhraseRewards result) {
QuestProgress progress = new QuestProgress(questID, questProgress);
boolean added = player.addQuestProgress(progress);
if (!added) return; // Only apply exp reward if the quest stage was reached just now (and not re-reached)
QuestLogEntry stage = world.quests.getQuestLogEntry(progress);
if (stage != null) {
result.loot.exp += stage.rewardExperience;
result.questProgress.add(progress);
}
}
private void addDropListReward(Player player, String droplistID, PhraseRewards result) {
world.dropLists.getDropList(droplistID).createRandomLoot(result.loot, player);
}
private void addSkillReward(Player player, int skillID, PhraseRewards result) {
SkillInfo skill = world.skills.getSkill(skillID);
boolean addedSkill = controllers.skillController.levelUpSkillByQuest(player, skill);
if (addedSkill) {
result.skillIncrease.add(skill);
}
}
private void addActorConditionReward(Player player, String conditionTypeID, int value, PhraseRewards result) {
int magnitude = 1;
int duration = value;
if (value == ActorCondition.DURATION_FOREVER) duration = ActorCondition.DURATION_FOREVER;
else if (value == ActorCondition.MAGNITUDE_REMOVE_ALL) magnitude = ActorCondition.MAGNITUDE_REMOVE_ALL;
ActorConditionType conditionType = world.actorConditionsTypes.getActorConditionType(conditionTypeID);
ActorConditionEffect e = new ActorConditionEffect(conditionType, magnitude, duration, always);
controllers.actorStatsController.applyActorCondition(player, e);
result.actorConditions.add(e);
}
private static void applyReplyEffect(final Player player, final Reply reply) {
if (!reply.requiresItem()) return;
if (reply.itemRequirementType == Reply.ITEM_REQUIREMENT_TYPE_INVENTORY_REMOVE) {
@@ -98,8 +118,8 @@ public final class ConversationController {
}
}
}
public static boolean canSelectReply(final Player player, final Reply reply) {
private static boolean canSelectReply(final Player player, final Reply reply) {
if (!hasRequiredQuestProgress(player, reply.requiresProgress)) return false;
if (!hasRequiredItems(player, reply)) return false;
return true;
@@ -122,9 +142,125 @@ public final class ConversationController {
}
}
public static String getDisplayMessage(Phrase phrase, Player player) { return replacePlayerName(phrase.message, player); }
public static String getDisplayMessage(Reply reply, Player player) { return replacePlayerName(reply.text, player); }
private static String getDisplayMessage(Phrase phrase, Player player) { return replacePlayerName(phrase.message, player); }
private static String getDisplayMessage(Reply reply, Player player) { return replacePlayerName(reply.text, player); }
private static String replacePlayerName(String s, Player player) {
return s.replace(Constants.PLACEHOLDER_PLAYERNAME, player.getName());
}
public static final class ConversationStatemachine {
private final ConversationCollection conversationCollection = new ConversationCollection();
private final WorldContext world;
private final ControllerContext controllers;
private final Player player;
private final Resources res;
private String phraseID;
private Phrase currentPhrase;
private Monster npc;
public ConversationStateListener listener;
public ConversationStatemachine(WorldContext world, ControllerContext controllers, Resources res, ConversationStateListener listener) {
this.world = world;
this.player = world.model.player;
this.controllers = controllers;
this.res = res;
this.listener = listener;
}
public void setCurrentNPC(Monster currentNPC) { this.npc = currentNPC; }
public Monster getCurrentNPC() { return npc; }
public String getCurrentPhraseID() { return phraseID; }
public void playerSelectedReply(Reply r) {
applyReplyEffect(player, r);
proceedToPhrase(r.nextPhrase);
}
public void playerSelectedNextStep() {
playerSelectedReply(currentPhrase.replies[0]);
}
public interface ConversationStateListener {
void onTextPhraseReached(String message, Actor actor);
void onConversationEnded();
void onConversationEndedWithShop(Monster npc);
void onConversationEndedWithCombat(Monster npc);
void onConversationEndedWithRemoval(Monster npc);
void onPlayerReceivedRewards(ConversationController.PhraseRewards phraseRewards);
void onConversationCanProceedWithNext();
void onConversationHasReply(Reply r, String message);
}
private void setCurrentPhrase(String phraseID) {
this.phraseID = phraseID;
this.currentPhrase = world.conversationLoader.loadPhrase(phraseID, conversationCollection, res);
if (AndorsTrailApplication.DEVELOPMENT_DEBUGMESSAGES) {
if (currentPhrase == null) currentPhrase = new Phrase("(phrase \"" + phraseID + "\" not implemented yet)", null, null);
}
}
public void proceedToPhrase(String phraseID) {
if (phraseID.equalsIgnoreCase(ConversationCollection.PHRASE_CLOSE)) {
listener.onConversationEnded();
return;
} else if (phraseID.equalsIgnoreCase(ConversationCollection.PHRASE_SHOP)) {
listener.onConversationEndedWithShop(npc);
return;
} else if (phraseID.equalsIgnoreCase(ConversationCollection.PHRASE_ATTACK)) {
listener.onConversationEndedWithCombat(npc);
return;
} else if (phraseID.equalsIgnoreCase(ConversationCollection.PHRASE_REMOVE)) {
listener.onConversationEndedWithRemoval(npc);
return;
}
setCurrentPhrase(phraseID);
ConversationController.PhraseRewards phraseRewards = controllers.conversationController.applyPhraseRewards(player, currentPhrase);
if (phraseRewards != null) {
listener.onPlayerReceivedRewards(phraseRewards);
}
if (currentPhrase.message == null) {
for (Reply r : currentPhrase.replies) {
if (!canSelectReply(player, r)) continue;
applyReplyEffect(player, r);
proceedToPhrase(r.nextPhrase);
return;
}
}
String message = getDisplayMessage(currentPhrase, player);
listener.onTextPhraseReached(message, npc);
requestReplies();
}
private void requestReplies() {
if (hasOnlyOneNextReply()) {
listener.onConversationCanProceedWithNext();
return;
}
for (Reply r : currentPhrase.replies) {
if (!canSelectReply(player, r)) continue;
listener.onConversationHasReply(r, getDisplayMessage(r, player));
}
}
public void proceedToRestoredState(String phraseID) {
setCurrentPhrase(phraseID);
requestReplies();
}
public int getReplyCount() {
if (currentPhrase.replies == null) return 0;
return currentPhrase.replies.length;
}
public boolean hasOnlyOneNextReply() {
if (getReplyCount() != 1) return false;
if (currentPhrase.replies[0].text.equals(ConversationCollection.REPLY_NEXT)) return true;
return false;
}
}
}