mirror of
https://github.com/OMGeeky/andors-trail.git
synced 2026-02-23 15:38:29 +01:00
WIP moving conversation flow logic from Activity to testable state machine.
This commit is contained in:
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user