diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/Dialogs.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/Dialogs.java index e09d249b7..6e50bd1a7 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/Dialogs.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/Dialogs.java @@ -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"); diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/activity/ConversationActivity.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/activity/ConversationActivity.java index af90520a2..e003a31d1 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/activity/ConversationActivity.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/activity/ConversationActivity.java @@ -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 conversationHistory = new ArrayList(); + 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(); - - // 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(); + 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); + } } diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/ConversationController.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/ConversationController.java index a6894f81c..c043b591e 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/ConversationController.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/ConversationController.java @@ -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 = new ArrayList(); } - 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; + } + } }