phrase = world.conversationLoader.loadPhrase(phraseID, conversationCollection, getResources());
if (AndorsTrailApplication.DEVELOPMENT_DEBUGMESSAGES) {
- if (phrase == null) phrase = new Phrase("(phrase \"" + phraseID + "\" not implemented yet)", null, null, null);
+ if (phrase == null) phrase = new Phrase("(phrase \"" + phraseID + "\" not implemented yet)", null, null);
}
- Loot loot = ConversationController.applyPhraseEffect(player, phrase, world.quests, world.dropLists);
+ Loot loot = ConversationController.applyPhraseRewards(player, phrase, world);
if (phrase.message == null) {
for (Reply r : phrase.replies) {
if (resultCode != RESULT_OK) break;
int skillID = data.getExtras().getInt("skillID");
- player.addSkillLevel(skillID);
+ player.addSkillLevel(skillID, true);
break;
}
}
}
}
- private static void applyActorCondition(Actor actor, ActorConditionEffect e) { applyActorCondition(actor, e, e.duration); }
+ public static void applyActorCondition(Actor actor, ActorConditionEffect e) { applyActorCondition(actor, e, e.duration); }
private static void applyActorCondition(Actor actor, ActorConditionEffect e, int duration) {
- final ActorConditionType type = e.conditionType;
if (e.isRemovalEffect()) {
- removeAllConditionsOfType(actor, type.conditionTypeID);
+ removeAllConditionsOfType(actor, e.conditionType.conditionTypeID);
} else if (e.magnitude > 0) {
- if (!e.conditionType.isStacking) {
- if (actor.hasCondition(type.conditionTypeID)) return;
- //TODO: Maybe only keep the one with the highest magnitude?
+ if (e.conditionType.isStacking) {
+ addStackableActorCondition(actor, e, duration);
+ } else {
+ addNonStackableActorCondition(actor, e, duration);
}
- actor.conditions.add(e.createCondition(duration));
}
}
+ private static void addStackableActorCondition(Actor actor, ActorConditionEffect e, int duration) {
+ final ActorConditionType type = e.conditionType;
+ int magnitude = e.magnitude;
+
+ for(int i = actor.conditions.size() - 1; i >= 0; --i) {
+ ActorCondition c = actor.conditions.get(i);
+ if (!type.conditionTypeID.equals(c.conditionType.conditionTypeID)) continue;
+ if (c.duration == duration) {
+ // If the actor already has a condition of this type and the same duration, just increase the magnitude instead.
+ actor.conditions.remove(i);
+ magnitude += c.magnitude;
+ break;
+ }
+ }
+ actor.conditions.add(new ActorCondition(type, magnitude, duration));
+ }
+ private static void addNonStackableActorCondition(Actor actor, ActorConditionEffect e, int duration) {
+ final ActorConditionType type = e.conditionType;
+
+ for(int i = actor.conditions.size() - 1; i >= 0; --i) {
+ ActorCondition c = actor.conditions.get(i);
+ if (!type.conditionTypeID.equals(c.conditionType.conditionTypeID)) continue;
+ if (c.magnitude > e.magnitude) return;
+ // If the actor already has this condition, but of a lower magnitude, we remove the old one and add this higher magnitude.
+ actor.conditions.remove(i);
+ }
+ actor.conditions.add(e.createCondition(duration));
+ }
+
public static void removeAllTemporaryConditions(final Actor actor) {
for(int i = actor.conditions.size() - 1; i >= 0; --i) {
if (!actor.conditions.get(i).isTemporaryEffect()) continue;
package com.gpl.rpg.AndorsTrail.controller;\r
\r
+import com.gpl.rpg.AndorsTrail.context.WorldContext;\r
import com.gpl.rpg.AndorsTrail.conversation.Phrase;\r
import com.gpl.rpg.AndorsTrail.conversation.Phrase.Reply;\r
+import com.gpl.rpg.AndorsTrail.conversation.Phrase.Reward;\r
+import com.gpl.rpg.AndorsTrail.model.ability.ActorCondition;\r
+import com.gpl.rpg.AndorsTrail.model.ability.ActorConditionEffect;\r
+import com.gpl.rpg.AndorsTrail.model.ability.ActorConditionType;\r
import com.gpl.rpg.AndorsTrail.model.actor.Player;\r
-import com.gpl.rpg.AndorsTrail.model.item.DropListCollection;\r
import com.gpl.rpg.AndorsTrail.model.item.ItemTypeCollection;\r
import com.gpl.rpg.AndorsTrail.model.item.Loot;\r
-import com.gpl.rpg.AndorsTrail.model.quest.QuestCollection;\r
import com.gpl.rpg.AndorsTrail.model.quest.QuestLogEntry;\r
import com.gpl.rpg.AndorsTrail.model.quest.QuestProgress;\r
+import com.gpl.rpg.AndorsTrail.util.ConstRange;\r
\r
public final class ConversationController {\r
\r
- public static Loot applyPhraseEffect(final Player player, final Phrase phrase, final QuestCollection questcollection, final DropListCollection dropListCollection) {\r
- if (phrase.rewardDropListID == null && phrase.progressQuest == null) return null;\r
+ private static final ConstRange always = new ConstRange(1, 1);\r
+ \r
+ public static Loot applyPhraseRewards(final Player player, final Phrase phrase, final WorldContext world) {\r
+ if (phrase.rewards == null || phrase.rewards.length == 0) return null;\r
\r
final Loot loot = new Loot();\r
- if (phrase.rewardDropListID != null) {\r
- dropListCollection.getDropList(phrase.rewardDropListID).createRandomLoot(loot, player);\r
- player.inventory.add(loot);\r
- }\r
- if (phrase.progressQuest != null) {\r
- boolean added = player.addQuestProgress(phrase.progressQuest);\r
- if (added) { // Only apply exp reward if the quest stage was reached just now (and not re-reached)\r
- QuestLogEntry stage = questcollection.getQuestLogEntry(phrase.progressQuest);\r
- if (stage != null) {\r
- loot.exp = stage.rewardExperience;\r
- player.addExperience(stage.rewardExperience);\r
+ for (Reward reward : phrase.rewards) {\r
+ switch (reward.rewardType) {\r
+ case Reward.REWARD_TYPE_ACTOR_CONDITION:\r
+ int magnitude = 1;\r
+ int duration = reward.value;\r
+ if (reward.value == ActorCondition.DURATION_FOREVER) duration = ActorCondition.DURATION_FOREVER;\r
+ else if (reward.value == ActorCondition.MAGNITUDE_REMOVE_ALL) magnitude = ActorCondition.MAGNITUDE_REMOVE_ALL;\r
+ \r
+ ActorConditionType conditionType = world.actorConditionsTypes.getActorConditionType(reward.rewardID);\r
+ ActorConditionEffect e = new ActorConditionEffect(conditionType, magnitude, duration, always);\r
+ ActorStatsController.applyActorCondition(player, e);\r
+ break;\r
+ case Reward.REWARD_TYPE_SKILL_INCREASE:\r
+ player.addSkillLevel(Integer.parseInt(reward.rewardID), false);\r
+ break;\r
+ case Reward.REWARD_TYPE_DROPLIST:\r
+ world.dropLists.getDropList(reward.rewardID).createRandomLoot(loot, player);\r
+ break;\r
+ case Reward.REWARD_TYPE_QUEST_PROGRESS:\r
+ QuestProgress progress = new QuestProgress(reward.rewardID, reward.value);\r
+ boolean added = player.addQuestProgress(progress);\r
+ if (added) { // Only apply exp reward if the quest stage was reached just now (and not re-reached)\r
+ QuestLogEntry stage = world.quests.getQuestLogEntry(progress);\r
+ if (stage != null) {\r
+ loot.exp += stage.rewardExperience;\r
+ }\r
}\r
+ break;\r
}\r
}\r
+ \r
+ player.inventory.add(loot);\r
+ player.addExperience(loot.exp);\r
return loot;\r
}\r
\r
public static void applyReplyEffect(final Player player, final Reply reply) {\r
if (!reply.requiresItem()) return;\r
\r
- if (ItemTypeCollection.isGoldItemType(reply.requiresItemTypeID)) {\r
- player.inventory.gold -= reply.requiresItemQuantity;\r
- } else {\r
- player.inventory.removeItem(reply.requiresItemTypeID, reply.requiresItemQuantity);\r
+ if (reply.itemRequirementType == Reply.ITEM_REQUIREMENT_TYPE_INVENTORY_REMOVE) {\r
+ if (ItemTypeCollection.isGoldItemType(reply.requiresItemTypeID)) {\r
+ player.inventory.gold -= reply.requiresItemQuantity;\r
+ } else {\r
+ player.inventory.removeItem(reply.requiresItemTypeID, reply.requiresItemQuantity);\r
+ }\r
}\r
}\r
\r
public static boolean canSelectReply(final Player player, final Reply reply) {\r
if (!hasRequiredQuestProgress(player, reply.requiresProgress)) return false;\r
- if (reply.requiresItem()) {\r
- if (!hasRequiredItems(player, reply.requiresItemTypeID, reply.requiresItemQuantity)) return false;\r
- }\r
+ if (!hasRequiredItems(player, reply)) return false;\r
return true;\r
}\r
\r
return player.hasExactQuestProgress(progress);\r
}\r
\r
- private static boolean hasRequiredItems(final Player player, String requiresItemTypeID, int requiresItemQuantity) {\r
- if (ItemTypeCollection.isGoldItemType(requiresItemTypeID)) return player.inventory.gold >= requiresItemQuantity;\r
- \r
- return player.inventory.hasItem(requiresItemTypeID, requiresItemQuantity);\r
+ private static boolean hasRequiredItems(final Player player, Reply reply) {\r
+ if (!reply.requiresItem()) return true;\r
+ \r
+ if (ItemTypeCollection.isGoldItemType(reply.requiresItemTypeID)) { \r
+ return player.inventory.gold >= reply.requiresItemQuantity;\r
+ } else if (reply.itemRequirementType == Reply.ITEM_REQUIREMENT_TYPE_WEAR_KEEP) {\r
+ return player.inventory.isWearing(reply.requiresItemTypeID);\r
+ } else {\r
+ return player.inventory.hasItem(reply.requiresItemTypeID, reply.requiresItemQuantity);\r
+ }\r
}\r
\r
public static String getDisplayMessage(Phrase phrase, Player player) { return replacePlayerName(phrase.message, player); }\r
import com.gpl.rpg.AndorsTrail.AndorsTrailApplication;
import com.gpl.rpg.AndorsTrail.conversation.Phrase.Reply;
+import com.gpl.rpg.AndorsTrail.conversation.Phrase.Reward;
import com.gpl.rpg.AndorsTrail.model.actor.MonsterTypeCollection;
import com.gpl.rpg.AndorsTrail.model.item.DropList;
import com.gpl.rpg.AndorsTrail.model.item.DropListCollection;
import com.gpl.rpg.AndorsTrail.model.item.ItemTypeCollection;
import com.gpl.rpg.AndorsTrail.model.map.MapCollection;
import com.gpl.rpg.AndorsTrail.model.quest.QuestCollection;
+import com.gpl.rpg.AndorsTrail.model.quest.QuestProgress;
import com.gpl.rpg.AndorsTrail.resource.parsers.ConversationListParser;
import com.gpl.rpg.AndorsTrail.util.L;
public void verifyData(QuestCollection quests) {
if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
for (Phrase p : phrases.values()) {
- if (p.progressQuest != null) {
- quests.getQuestLogEntry(p.progressQuest); // Will warn inside if invalid.
- }
+ if (p.rewards == null) continue;
+ for (Reward r : p.rewards) {
+ if (r.rewardType != Reward.REWARD_TYPE_QUEST_PROGRESS) continue;
+ quests.getQuestLogEntry(new QuestProgress(r.rewardID, r.value)); // Will warn inside if invalid.
+ }
}
}
}
for (Reply r : e.getValue().replies) {
if (!r.requiresItem()) continue;
ItemType itemType = itemTypes.getItemType(r.requiresItemTypeID);
+
+ if (r.itemRequirementType == Reply.ITEM_REQUIREMENT_TYPE_WEAR_KEEP) {
+ if (!itemType.isEquippable()) L.log("WARNING: Phrase \"" + e.getKey() + "\" has a reply that requires a worn \"" + itemType + "\", but the item is not wearable.");
+ }
+
if (!itemType.isQuestItem()) continue;
Phrase nextPhrase = getPhrase(r.nextPhrase);
- if (nextPhrase.progressQuest == null) {
+ if (!hasQuestProgressReward(nextPhrase)) {
L.log("WARNING: Phrase \"" + e.getKey() + "\" has a reply that requires a questitem, but the next phrase does not add quest progress.");
}
}
}
}
+ private boolean hasQuestProgressReward(Phrase nextPhrase) {
+ if (nextPhrase.rewards == null) return false;
+ for (Reward r : nextPhrase.rewards) {
+ if (r.rewardType == Reward.REWARD_TYPE_QUEST_PROGRESS) return true;
+ }
+ return false;
+ }
+
// Selftest method. Not part of the game logic.
public void DEBUG_getSuppliedQuestStages(HashSet<String> suppliedStages) {
if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
for (Phrase p : phrases.values()) {
- if (p.progressQuest == null) continue;
- suppliedStages.add(p.progressQuest.toString());
+ if (p.rewards == null) continue;
+ for (Reward r : p.rewards) {
+ if (r.rewardType != Reward.REWARD_TYPE_QUEST_PROGRESS) continue;
+ QuestProgress progressQuest = new QuestProgress(r.rewardID, r.value);
+ suppliedStages.add(progressQuest.toString());
+ }
}
}
}
public void DEBUG_getUsedDroplists(HashSet<DropList> usedDropLists, final DropListCollection dropListCollection) {
if (AndorsTrailApplication.DEVELOPMENT_VALIDATEDATA) {
for (Phrase p : phrases.values()) {
- if (p.rewardDropListID != null) {
- DropList d = dropListCollection.getDropList(p.rewardDropListID);
+ if (p.rewards == null) continue;
+ for (Reward r : p.rewards) {
+ if (r.rewardType != Reward.REWARD_TYPE_DROPLIST) continue;
+
+ DropList d = dropListCollection.getDropList(r.rewardID);
if (d != null) usedDropLists.add(d);
}
}
public final String message;
public final Reply[] replies;
- public final QuestProgress progressQuest; // If this phrase is reached, this quest will be updated.
- public final String rewardDropListID; // If this phrase is reached, player will be awarded all these items
+ public final Reward[] rewards; // If this phrase is reached, player will be awarded all these rewards
- public Phrase(String message, Reply[] replies, QuestProgress progressQuest, String rewardDropListID) {
+ public Phrase(String message, Reply[] replies, Reward[] rewards) {
this.message = message;
if (replies == null) replies = NO_REPLIES;
this.replies = replies;
- this.progressQuest = progressQuest;
- this.rewardDropListID = rewardDropListID;
+ this.rewards = rewards;
}
public static final class Reply {
+ public static final int ITEM_REQUIREMENT_TYPE_INVENTORY_REMOVE = 0; // Player must have item(s) in inventory. Items will be removed when selecting reply.
+ public static final int ITEM_REQUIREMENT_TYPE_INVENTORY_KEEP = 1; // Player must have item(s) in inventory. Items will NOT be removed when selecting reply.
+ public static final int ITEM_REQUIREMENT_TYPE_WEAR_KEEP = 2; // Player must be wearing item(s). Items will NOT be removed when selecting reply.
+
public final String text;
public final String nextPhrase;
public final QuestProgress requiresProgress;
public final String requiresItemTypeID;
public final int requiresItemQuantity;
+ public final int itemRequirementType;
public boolean requiresItem() {
if (requiresItemTypeID == null) return false;
return true;
}
- public Reply(String text, String nextPhrase, QuestProgress requiresProgress, String requiresItemTypeID, int requiresItemQuantity) {
+ public Reply(String text, String nextPhrase, QuestProgress requiresProgress, String requiresItemTypeID, int requiresItemQuantity, int itemRequirementType) {
this.text = text;
this.nextPhrase = nextPhrase;
this.requiresProgress = requiresProgress;
this.requiresItemTypeID = requiresItemTypeID;
this.requiresItemQuantity = requiresItemQuantity;
+ this.itemRequirementType = itemRequirementType;
+ }
+ }
+
+ public static final class Reward {
+ public static final int REWARD_TYPE_QUEST_PROGRESS = 0;
+ public static final int REWARD_TYPE_DROPLIST = 1;
+ public static final int REWARD_TYPE_SKILL_INCREASE = 2;
+ public static final int REWARD_TYPE_ACTOR_CONDITION = 3;
+
+ public final int rewardType;
+ public final String rewardID;
+ public final int value;
+
+ public Reward(int rewardType, String rewardID, int value) {
+ this.rewardType = rewardType;
+ this.rewardID = rewardID;
+ this.value = value;
}
}
}
public boolean hasSkill(int skillID) {
return getSkillLevel(skillID) > 0;
}
- public void addSkillLevel(int skillID) {
- if (!hasAvailableSkillpoints()) return;
+ public void addSkillLevel(int skillID, boolean requireAvailableSkillpoint) {
+ if (requireAvailableSkillpoint) {
+ if (!hasAvailableSkillpoints()) return;
+ --availableSkillIncreases;
+ }
if (!skillLevels.containsKey(skillID)) skillLevels.put(skillID, 1);
else skillLevels.put(skillID, skillLevels.get(skillID) + 1);
- --availableSkillIncreases;
ActorStatsController.recalculatePlayerCombatTraits(this);
}
public boolean nextLevelAddsNewSkillpoint() {
public boolean isEmptySlot(int slot) {\r
return wear[slot] == null;\r
}\r
+\r
+ public boolean isWearing(String itemTypeID) {\r
+ for(int i = 0; i < NUM_WORN_SLOTS; ++i) {\r
+ if (wear[i] == null) continue;\r
+ if (wear[i].id.equals(itemTypeID)) return true;\r
+ }\r
+ return false;\r
+ }\r
\r
\r
// ====== PARCELABLE ===================================================================\r
import com.gpl.rpg.AndorsTrail.conversation.Phrase;
import com.gpl.rpg.AndorsTrail.conversation.Phrase.Reply;
+import com.gpl.rpg.AndorsTrail.conversation.Phrase.Reward;
import com.gpl.rpg.AndorsTrail.model.quest.QuestProgress;
import com.gpl.rpg.AndorsTrail.resource.ResourceFileTokenizer;
import com.gpl.rpg.AndorsTrail.resource.ResourceFileTokenizer.ResourceParserFor;
, QuestProgress.parseQuestProgress(parts[2]) // requiresProgress
, ResourceParserUtils.parseNullableString(parts[3]) // requiresItemType
, ResourceParserUtils.parseInt(parts[4], 0) // requiresItemQuantity
+ , Reply.ITEM_REQUIREMENT_TYPE_INVENTORY_REMOVE // itemRequirementType
);
}
};
replyResourceTokenizer.tokenizeArray(parts[4], replies, replyParser);
final Reply[] _replies = replies.toArray(new Reply[replies.size()]);
+ final ArrayList<Reward> rewards = new ArrayList<Reward>();
+ QuestProgress questProgress = QuestProgress.parseQuestProgress(parts[2]);
+ if (questProgress != null) rewards.add(new Reward(Reward.REWARD_TYPE_QUEST_PROGRESS, questProgress.questID, questProgress.progress));
+ String rewardDroplist = ResourceParserUtils.parseNullableString(parts[3]);
+ if (rewardDroplist != null) rewards.add(new Reward(Reward.REWARD_TYPE_DROPLIST, rewardDroplist, 0));
+ final Reward[] _rewards = rewards.toArray(new Reward[rewards.size()]);
+
return new Pair<String, Phrase>(parts[0], new Phrase(
ResourceParserUtils.parseNullableString(parts[1]) // message
, _replies // replies
- , QuestProgress.parseQuestProgress(parts[2]) // questProgress
- , ResourceParserUtils.parseNullableString(parts[3]) // rewardDroplist
+ , _rewards // rewards
));
}
}