First attempt at scripted map areas

This commit is contained in:
Oskar Wiksten
2013-08-07 15:42:11 +02:00
parent 8d91c7375d
commit dc278fb5da
11 changed files with 170 additions and 70 deletions

View File

@@ -58,10 +58,18 @@ public final class Dialogs {
showConversation(currentActivity, context, phraseID, null);
}
public static void showMapScriptMessage(final MainActivity currentActivity, final ControllerContext context, String phraseID) {
showConversation(currentActivity, context, phraseID, null, false);
}
public static void showConversation(final MainActivity currentActivity, final ControllerContext context, final String phraseID, final Monster npc) {
showConversation(currentActivity, context, phraseID, npc, true);
}
private static void showConversation(final MainActivity currentActivity, final ControllerContext context, final String phraseID, final Monster npc, boolean giveRewardsForFirstPhrase) {
context.gameRoundController.pause();
Intent intent = new Intent(currentActivity, ConversationActivity.class);
intent.setData(Uri.parse("content://com.gpl.rpg.AndorsTrail/conversation/" + phraseID));
intent.putExtra("giveRewardsForFirstPhrase", giveRewardsForFirstPhrase);
addMonsterIdentifiers(intent, npc);
currentActivity.startActivityForResult(intent, MainActivity.INTENTREQUEST_CONVERSATION);
}

View File

@@ -36,10 +36,11 @@ import com.gpl.rpg.AndorsTrail.resource.tiles.TileManager;
import java.util.ArrayList;
public final class ConversationActivity extends Activity implements OnKeyListener
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 playerNameColor = Color.argb(255, 0xbb, 0x22, 0x22);
private static final int NPCNameColor = Color.argb(255, 0xbb, 0xbb, 0x22);
private static final int playerPhraseColor = 0;
@@ -64,7 +65,7 @@ public final class ConversationActivity extends Activity implements OnKeyListene
if (!app.isInitialized()) { finish(); return; }
this.world = app.getWorld();
this.player = world.model.player;
this.conversationState = new ConversationController.ConversationStatemachine(world, app.getControllerContext(), getResources(), this);
this.conversationState = new ConversationController.ConversationStatemachine(world, app.getControllerContext(), this);
requestWindowFeature(Window.FEATURE_NO_TITLE);
@@ -106,17 +107,22 @@ public final class ConversationActivity extends Activity implements OnKeyListene
statementList.setFocusable(false);
statementList.setFocusableInTouchMode(false);
String phraseID;
boolean giveRewardsForFirstPhrase;
boolean displayLastMessage = true;
if (savedInstanceState != null) {
conversationState.setCurrentNPC(Dialogs.getMonsterFromBundle(savedInstanceState, world));
ArrayList<ConversationStatement> savedConversationHistory = savedInstanceState.getParcelableArrayList("conversationHistory");
if (savedConversationHistory != null) conversationHistory.addAll(savedConversationHistory);
final String phraseID = savedInstanceState.getString("phraseID");
conversationState.proceedToRestoredState(phraseID);
phraseID = savedInstanceState.getString("phraseID");
giveRewardsForFirstPhrase = false;
displayLastMessage = false;
} else {
conversationState.setCurrentNPC(Dialogs.getMonsterFromIntent(getIntent(), world));
final String phraseID = getIntent().getData().getLastPathSegment();
conversationState.proceedToPhrase(phraseID);
phraseID = getIntent().getData().getLastPathSegment();
giveRewardsForFirstPhrase = getIntent().getBooleanExtra("giveRewardsForFirstPhrase", true);
}
conversationState.proceedToPhrase(getResources(), phraseID, giveRewardsForFirstPhrase, displayLastMessage);
}
@Override
@@ -205,12 +211,12 @@ public final class ConversationActivity extends Activity implements OnKeyListene
replyGroup.removeAllViews();
nextButton.setEnabled(false);
if (conversationState.hasOnlyOneNextReply()) {
conversationState.playerSelectedNextStep();
conversationState.playerSelectedNextStep(getResources());
} else {
if (rb == null) return;
Reply r = (Reply) rb.getTag();
addConversationStatement(player, rb.getText().toString(), playerPhraseColor);
conversationState.playerSelectedReply(r);
conversationState.playerSelectedReply(getResources(), r);
}
}
@@ -340,7 +346,7 @@ public final class ConversationActivity extends Activity implements OnKeyListene
}
@Override
public void onTextPhraseReached(String message, Actor actor) {
public void onTextPhraseReached(String message, Actor actor, String phraseID) {
addConversationStatement(actor, message, NPCPhraseColor);
}
@@ -395,13 +401,11 @@ public final class ConversationActivity extends Activity implements OnKeyListene
@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();
}

View File

@@ -17,7 +17,6 @@ import com.gpl.rpg.AndorsTrail.context.ControllerContext;
import com.gpl.rpg.AndorsTrail.context.WorldContext;
import com.gpl.rpg.AndorsTrail.controller.AttackResult;
import com.gpl.rpg.AndorsTrail.controller.CombatController;
import com.gpl.rpg.AndorsTrail.controller.MovementController;
import com.gpl.rpg.AndorsTrail.controller.listeners.CombatActionListener;
import com.gpl.rpg.AndorsTrail.controller.listeners.CombatTurnListener;
import com.gpl.rpg.AndorsTrail.controller.listeners.PlayerMovementListener;
@@ -36,7 +35,13 @@ import com.gpl.rpg.AndorsTrail.view.QuickButton.QuickButtonContextMenuInfo;
import java.lang.ref.WeakReference;
import java.util.Collection;
public final class MainActivity extends Activity implements PlayerMovementListener, CombatActionListener, CombatTurnListener, WorldEventListener {
public final class MainActivity
extends Activity
implements
PlayerMovementListener
, CombatActionListener
, CombatTurnListener
, WorldEventListener {
public static final int INTENTREQUEST_MONSTERENCOUNTER = 2;
public static final int INTENTREQUEST_CONVERSATION = 4;
@@ -115,7 +120,6 @@ public final class MainActivity extends Activity implements PlayerMovementListen
}
break;
case INTENTREQUEST_CONVERSATION:
MovementController.refreshMonsterAggressiveness(world.model.currentMap, world.model.player);
controllers.mapController.applyCurrentMapReplacements(getResources(), true);
break;
case INTENTREQUEST_SAVEGAME:
@@ -340,6 +344,11 @@ public final class MainActivity extends Activity implements PlayerMovementListen
Dialogs.showConversation(this, controllers, phraseID, m);
}
@Override
public void onScriptAreaStartedConversation(String phraseID) {
Dialogs.showMapScriptMessage(this, controllers, phraseID);
}
@Override
public void onPlayerSteppedOnMonster(Monster m) {
Dialogs.showMonsterEncounter(this, controllers, m);

View File

@@ -63,7 +63,7 @@ public final class ConversationController {
addQuestProgressReward(player, reward.rewardID, reward.value, result);
break;
case alignmentChange:
player.addAlignment(reward.rewardID, reward.value);
addAlignmentReward(player, reward.rewardID, reward.value);
break;
}
}
@@ -76,16 +76,20 @@ public final class ConversationController {
return result;
}
private void addAlignmentReward(Player player, String faction, int delta) {
player.addAlignment(faction, delta);
MovementController.refreshMonsterAggressiveness(world.model.currentMap, world.model.player);
}
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);
}
if (stage == null) return;
result.loot.exp += stage.rewardExperience;
result.questProgress.add(progress);
}
private void addDropListReward(Player player, String droplistID, PhraseRewards result) {
@@ -169,17 +173,15 @@ public final class ConversationController {
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) {
public ConversationStatemachine(WorldContext world, ControllerContext controllers, ConversationStateListener listener) {
this.world = world;
this.player = world.model.player;
this.controllers = controllers;
this.res = res;
this.listener = listener;
}
@@ -187,17 +189,17 @@ public final class ConversationController {
public Monster getCurrentNPC() { return npc; }
public String getCurrentPhraseID() { return phraseID; }
public void playerSelectedReply(Reply r) {
public void playerSelectedReply(final Resources res, Reply r) {
applyReplyEffect(player, r);
proceedToPhrase(r.nextPhrase);
proceedToPhrase(res, r.nextPhrase, true, true);
}
public void playerSelectedNextStep() {
playerSelectedReply(currentPhrase.replies[0]);
public void playerSelectedNextStep(final Resources res) {
playerSelectedReply(res, currentPhrase.replies[0]);
}
public interface ConversationStateListener {
void onTextPhraseReached(String message, Actor actor);
void onTextPhraseReached(String message, Actor actor, String phraseID);
void onConversationEnded();
void onConversationEndedWithShop(Monster npc);
void onConversationEndedWithCombat(Monster npc);
@@ -207,7 +209,7 @@ public final class ConversationController {
void onConversationHasReply(Reply r, String message);
}
private void setCurrentPhrase(String phraseID) {
private void setCurrentPhrase(final Resources res, String phraseID) {
this.phraseID = phraseID;
this.currentPhrase = world.conversationLoader.loadPhrase(phraseID, conversationCollection, res);
if (AndorsTrailApplication.DEVELOPMENT_DEBUGMESSAGES) {
@@ -218,7 +220,7 @@ public final class ConversationController {
}
}
public void proceedToPhrase(String phraseID) {
public void proceedToPhrase(final Resources res, String phraseID, boolean giveRewards, boolean displayPhraseMessage) {
if (phraseID.equalsIgnoreCase(ConversationCollection.PHRASE_CLOSE)) {
listener.onConversationEnded();
return;
@@ -233,26 +235,36 @@ public final class ConversationController {
return;
}
setCurrentPhrase(phraseID);
setCurrentPhrase(res, phraseID);
ConversationController.PhraseRewards phraseRewards = controllers.conversationController.applyPhraseRewards(player, currentPhrase);
if (phraseRewards != null) {
listener.onPlayerReceivedRewards(phraseRewards);
if (giveRewards) {
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(world, r)) continue;
applyReplyEffect(player, r);
proceedToPhrase(r.nextPhrase);
proceedToPhrase(res, r.nextPhrase, giveRewards, displayPhraseMessage);
return;
}
} else if (displayPhraseMessage) {
String message = getDisplayMessage(currentPhrase, player);
listener.onTextPhraseReached(message, npc, phraseID);
}
String message = getDisplayMessage(currentPhrase, player);
listener.onTextPhraseReached(message, npc);
if (hasOnlyOneNextReply()) {
listener.onConversationCanProceedWithNext();
return;
}
requestReplies();
for (Reply r : currentPhrase.replies) {
if (!canSelectReply(world, r)) continue;
listener.onConversationHasReply(r, getDisplayMessage(r, player));
}
}
private void endConversationWithRemovingNPC() {
@@ -267,23 +279,6 @@ public final class ConversationController {
listener.onConversationEndedWithCombat(npc);
}
private void requestReplies() {
if (hasOnlyOneNextReply()) {
listener.onConversationCanProceedWithNext();
return;
}
for (Reply r : currentPhrase.replies) {
if (!canSelectReply(world, r)) continue;
listener.onConversationHasReply(r, getDisplayMessage(r, player));
}
}
public void proceedToRestoredState(String phraseID) {
setCurrentPhrase(phraseID);
requestReplies();
}
public boolean hasOnlyOneNextReply() {
if (currentPhrase.replies == null) return false;
if (currentPhrase.replies.length != 1) return false;

View File

@@ -0,0 +1,11 @@
package com.gpl.rpg.AndorsTrail.controller;
/**
* Created with IntelliJ IDEA.
* User: oskar
* Date: 8/7/13
* Time: 3:26 PM
* To change this template use File | Settings | File Templates.
*/
public class ConversationStatemachine {
}

View File

@@ -5,7 +5,9 @@ import com.gpl.rpg.AndorsTrail.context.ControllerContext;
import com.gpl.rpg.AndorsTrail.context.WorldContext;
import com.gpl.rpg.AndorsTrail.controller.listeners.MapLayoutListeners;
import com.gpl.rpg.AndorsTrail.controller.listeners.WorldEventListeners;
import com.gpl.rpg.AndorsTrail.conversation.Phrase;
import com.gpl.rpg.AndorsTrail.model.ability.SkillCollection;
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.map.LayeredTileMap;
@@ -20,6 +22,7 @@ public final class MapController {
private final WorldContext world;
public final WorldEventListeners worldEventListeners = new WorldEventListeners();
public final MapLayoutListeners mapLayoutListeners = new MapLayoutListeners();
private ConversationController.ConversationStatemachine mapScriptExecutor;
public MapController(ControllerContext controllers, WorldContext world) {
this.controllers = controllers;
@@ -41,9 +44,15 @@ public final class MapController {
case rest:
steppedOnRestArea(o);
break;
case script:
runScriptArea(o);
}
}
private void runScriptArea(MapObject o) {
mapScriptExecutor.proceedToPhrase(controllers.getResources(), o.id, true, true);
}
private void steppedOnRestArea(MapObject area) {
if (controllers.preferences.confirmRest) {
worldEventListeners.onPlayerSteppedOnRestArea(area);
@@ -139,4 +148,21 @@ public final class MapController {
public boolean satisfiesCondition(ReplaceableMapSection replacement) {
return world.model.player.hasExactQuestProgress(replacement.requireQuestStage);
}
private final ConversationController.ConversationStatemachine.ConversationStateListener conversationStateListener = new ConversationController.ConversationStatemachine.ConversationStateListener() {
@Override
public void onTextPhraseReached(String message, Actor actor, String phraseID) {
worldEventListeners.onScriptAreaStartedConversation(phraseID);
}
@Override public void onPlayerReceivedRewards(ConversationController.PhraseRewards phraseRewards) { }
@Override public void onConversationEnded() { }
@Override public void onConversationEndedWithShop(Monster npc) { }
@Override public void onConversationEndedWithCombat(Monster npc) { }
@Override public void onConversationEndedWithRemoval(Monster npc) { }
@Override public void onConversationCanProceedWithNext() { }
@Override public void onConversationHasReply(Phrase.Reply r, String message) { }
};
public void prepareScriptsOnCurrentMap() {
mapScriptExecutor = new ConversationController.ConversationStatemachine(world, controllers, conversationStateListener);
}
}

View File

@@ -96,6 +96,7 @@ public final class MovementController implements TimedMessageTask.Callback {
}
}
controllers.mapController.applyCurrentMapReplacements(res, false);
controllers.mapController.prepareScriptsOnCurrentMap();
newMap.visited = true;
newMap.updateLastVisitTime();
moveBlockedActors(newMap, model.currentTileMap);

View File

@@ -8,6 +8,7 @@ import java.util.Collection;
public interface WorldEventListener {
void onPlayerStartedConversation(Monster m, String phraseID);
void onScriptAreaStartedConversation(String phraseID);
void onPlayerSteppedOnMonster(Monster m);
void onPlayerSteppedOnMapSignArea(MapObject area);
void onPlayerSteppedOnKeyArea(MapObject area);

View File

@@ -13,6 +13,10 @@ public final class WorldEventListeners extends ListOfListeners<WorldEventListene
@Override public void call(WorldEventListener listener, Monster m, String phraseID) { listener.onPlayerStartedConversation(m, phraseID); }
};
private final Function1<WorldEventListener, String> onScriptAreaStartedConversation = new Function1<WorldEventListener, String>() {
@Override public void call(WorldEventListener listener, String phraseID) { listener.onScriptAreaStartedConversation(phraseID); }
};
private final Function1<WorldEventListener, Monster> onPlayerSteppedOnMonster = new Function1<WorldEventListener, Monster>() {
@Override public void call(WorldEventListener listener, Monster m) { listener.onPlayerSteppedOnMonster(m); }
};
@@ -58,6 +62,11 @@ public final class WorldEventListeners extends ListOfListeners<WorldEventListene
callAllListeners(this.onPlayerStartedConversation, m, phraseID);
}
@Override
public void onScriptAreaStartedConversation(String phraseID) {
callAllListeners(this.onScriptAreaStartedConversation, phraseID);
}
@Override
public void onPlayerSteppedOnMonster(Monster m) {
callAllListeners(this.onPlayerSteppedOnMonster, m);

View File

@@ -11,6 +11,14 @@ public final class MapObject {
,rest
,keyarea
,container
,script
}
public static enum MapObjectEvaluationType {
whenEntering
,onEveryStep
,afterEveryTurn
,continuously
}
public final CoordRect position;
@@ -20,6 +28,7 @@ public final class MapObject {
public final String place;
public final QuestProgress requireQuestProgress;
public final DropList dropList;
public final MapObjectEvaluationType evaluateWhen;
private MapObject(
final CoordRect position
@@ -28,7 +37,9 @@ public final class MapObject {
, final String map
, final String place
, final QuestProgress requireQuestProgress
, final DropList dropList) {
, final DropList dropList
, final MapObjectEvaluationType evaluateWhen
) {
this.position = new CoordRect(position);
this.type = type;
this.id = id;
@@ -36,21 +47,25 @@ public final class MapObject {
this.place = place;
this.requireQuestProgress = requireQuestProgress;
this.dropList = dropList;
this.evaluateWhen = evaluateWhen;
}
public static MapObject createMapSignEvent(final CoordRect position, final String phraseID) {
return new MapObject(position, MapObjectType.sign, phraseID, null, null, null, null);
return new MapObject(position, MapObjectType.sign, phraseID, null, null, null, null, MapObjectEvaluationType.whenEntering);
}
public static MapObject createNewMapEvent(final CoordRect position, final String thisMapTitle, final String destinationMap, final String destinationPlace) {
return new MapObject(position, MapObjectType.newmap, thisMapTitle, destinationMap, destinationPlace, null, null);
public static MapObject createMapChangeArea(final CoordRect position, final String thisMapTitle, final String destinationMap, final String destinationPlace) {
return new MapObject(position, MapObjectType.newmap, thisMapTitle, destinationMap, destinationPlace, null, null, MapObjectEvaluationType.whenEntering);
}
public static MapObject createNewRest(final CoordRect position, final String placeId) {
return new MapObject(position, MapObjectType.rest, placeId, null, null, null, null);
public static MapObject createRestArea(final CoordRect position, final String placeId) {
return new MapObject(position, MapObjectType.rest, placeId, null, null, null, null, MapObjectEvaluationType.whenEntering);
}
public static MapObject createNewKeyArea(final CoordRect position, final String phraseID, final QuestProgress requireQuestStage) {
return new MapObject(position, MapObjectType.keyarea, phraseID, null, null, requireQuestStage, null);
public static MapObject createKeyArea(final CoordRect position, final String phraseID, final QuestProgress requireQuestStage) {
return new MapObject(position, MapObjectType.keyarea, phraseID, null, null, requireQuestStage, null, MapObjectEvaluationType.whenEntering);
}
public static MapObject createNewContainerArea(final CoordRect position, final DropList dropList) {
return new MapObject(position, MapObjectType.container, null, null, null, null, dropList);
public static MapObject createContainerArea(final CoordRect position, final DropList dropList) {
return new MapObject(position, MapObjectType.container, null, null, null, null, dropList, MapObjectEvaluationType.whenEntering);
}
public static MapObject createScriptArea(final CoordRect position, final String phraseID, final MapObjectEvaluationType evaluateWhen) {
return new MapObject(position, MapObjectType.script, phraseID, null, null, null, null, evaluateWhen);
}
}

View File

@@ -71,7 +71,7 @@ public final class TMXMapTranslator {
else if(p.name.equalsIgnoreCase("place")) place = p.value;
else if(AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) L.log("OPTIMIZE: Map " + m.name + ", mapchange " + object.name + "@" + topLeft.toString() + " has unrecognized property \"" + p.name + "\".");
}
mapObjects.add(MapObject.createNewMapEvent(position, object.name, map, place));
mapObjects.add(MapObject.createMapChangeArea(position, object.name, map, place));
} else if (object.type.equalsIgnoreCase("spawn")) {
ArrayList<MonsterType> types = monsterTypes.getMonsterTypesFromSpawnGroup(object.name);
int maxQuantity = 1;
@@ -129,15 +129,36 @@ public final class TMXMapTranslator {
}
}
mapObjects.add(MapObject.createNewKeyArea(position, phraseID, requireQuestStage));
mapObjects.add(MapObject.createKeyArea(position, phraseID, requireQuestStage));
} else if (object.type.equals("rest")) {
mapObjects.add(MapObject.createNewRest(position, object.name));
mapObjects.add(MapObject.createRestArea(position, object.name));
} else if (object.type.equals("container")) {
DropList dropList = dropLists.getDropList(object.name);
if (dropList == null) continue;
mapObjects.add(MapObject.createNewContainerArea(position, dropList));
mapObjects.add(MapObject.createContainerArea(position, dropList));
} else if (object.type.equals("replace")) {
// Do nothing. Will be handled when reading map layers instead.
} else if (object.type.equalsIgnoreCase("script")) {
String phraseID = object.name;
MapObject.MapObjectEvaluationType evaluateWhen = MapObject.MapObjectEvaluationType.whenEntering;
for (TMXProperty p : object.properties) {
if (p.name.equalsIgnoreCase("when")) {
if (p.value.equalsIgnoreCase("enter")) {
evaluateWhen = MapObject.MapObjectEvaluationType.whenEntering;
} else if (p.value.equalsIgnoreCase("step")) {
evaluateWhen = MapObject.MapObjectEvaluationType.onEveryStep;
} else if (p.value.equalsIgnoreCase("turn")) {
evaluateWhen = MapObject.MapObjectEvaluationType.afterEveryTurn;
} else if (p.value.equalsIgnoreCase("always")) {
evaluateWhen = MapObject.MapObjectEvaluationType.continuously;
} else if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
L.log("OPTIMIZE: Map " + m.name + ", script " + object.name + "@" + topLeft.toString() + " has unrecognized value for \"when\" property: \"" + p.value + "\".");
}
} else if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
L.log("OPTIMIZE: Map " + m.name + ", script " + object.name + "@" + topLeft.toString() + " has unrecognized property \"" + p.name + "\".");
}
}
mapObjects.add(MapObject.createScriptArea(position, phraseID, evaluateWhen));
} else if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
L.log("OPTIMIZE: Map " + m.name + ", has unrecognized object type \"" + object.type + "\" for name \"" + object.name + "\".");
}