/* $Id: ThePiedPiper.java,v 1.60 2011/05/01 19:50:05 martinfuchs Exp $ */
/***************************************************************************
* (C) Copyright 2003-2011 - Stendhal *
***************************************************************************
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
package games.stendhal.server.maps.quests;
import games.stendhal.common.Rand;
import games.stendhal.common.grammar.Grammar;
import games.stendhal.common.parser.Sentence;
import games.stendhal.server.core.engine.SingletonRepository;
import games.stendhal.server.core.events.TurnListener;
import games.stendhal.server.core.events.TurnNotifier;
import games.stendhal.server.entity.creature.Creature;
import games.stendhal.server.entity.item.StackableItem;
import games.stendhal.server.entity.npc.ChatAction;
import games.stendhal.server.entity.npc.ConversationStates;
import games.stendhal.server.entity.npc.EventRaiser;
import games.stendhal.server.entity.npc.SpeakerNPC;
import games.stendhal.server.entity.npc.condition.AndCondition;
import games.stendhal.server.entity.npc.condition.NotCondition;
import games.stendhal.server.entity.player.Player;
import games.stendhal.server.maps.Region;
import games.stendhal.server.maps.quests.piedpiper.AwaitingPhase;
import games.stendhal.server.maps.quests.piedpiper.ITPPQuest;
import games.stendhal.server.maps.quests.piedpiper.ITPPQuestConstants;
import games.stendhal.server.maps.quests.piedpiper.InactivePhase;
import games.stendhal.server.maps.quests.piedpiper.InvasionPhase;
import games.stendhal.server.maps.quests.piedpiper.TPPQuestInPhaseCondition;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import org.apache.log4j.Logger;
/**
* QUEST: The Pied Piper
*
* PARTICIPANTS: <ul>
* <li> Mayor Chalmers
* <li> George
* <li> Anna
* <li> Jens
* <li> Susi
* <li> Finn Farmer
* <li> Pied Piper
* </ul>
*
* STEPS:<ul>
* <li> PART I.
* <li> Mayor will activate the quest by telling to all about Ados city rats problem.
* <li> Kill (all) rats in city and go back to mayor for your reward.
* </ul>
*
* REWARD:<ul>
* <li> PART I.
* <li> 10 moneys for each rat
* <li> 20 moneys for each caverat
* <li> 100 moneys for each venomrat
* <li> 160 moneys for each razorrat
* <li> 360 moneys for each giantrat
* <li> 800 moneys for each archrat
* <li> 5 karma in total
* </ul>
*
* REPETITIONS: <ul><li> once between a week and two weeks.</ul>
*/
public class ThePiedPiper extends AbstractQuest implements ITPPQuestConstants {
protected static final Logger logger = Logger.getLogger(ThePiedPiper.class);
private LinkedList<Creature> rats = new LinkedList<Creature>();
private static LinkedList<ITPPQuest> phases = new LinkedList<ITPPQuest>();
private static TPP_Phase phase = INACTIVE;
protected LinkedHashMap<String, Integer> timings = new LinkedHashMap<String, Integer>();
public static SpeakerNPC getMainNPC() {
return SingletonRepository.getNPCList().get("Mayor Chalmers");
}
/**
* function will set timings to either test server or game server.
*/
private void setTimings() {
if (System.getProperty("stendhal.testserver") == null) {
// game timings */
timings.put(INACTIVE_TIME_MAX, 60 * 60 * 24 * 21);
timings.put(INACTIVE_TIME_MIN, 60 * 60 * 24 * 2);
timings.put(INVASION_TIME_MAX, 60 * 60 * 2);
timings.put(INVASION_TIME_MIN, 60 * 60 * 2);
timings.put(AWAITING_TIME_MAX, 60 * -1);
timings.put(AWAITING_TIME_MIN, 60 * -1);
timings.put(OUTGOING_TIME_MAX, 60 * -1);
timings.put(OUTGOING_TIME_MIN, 60 * -1);
timings.put(CHILDRENS_TIME_MAX, 60 * -1);
timings.put(CHILDRENS_TIME_MIN, 60 * -1);
timings.put(FINAL_TIME_MAX, 60 * 60 * 6);
timings.put(FINAL_TIME_MIN, 60 * 60 * 4);
timings.put(SHOUT_TIME, 60 * 10);
}
else {
// test timings
timings.put(INACTIVE_TIME_MAX, 60 * 2);
timings.put(INACTIVE_TIME_MIN, 60 * 1);
timings.put(INVASION_TIME_MAX, 60 * 20);
timings.put(INVASION_TIME_MIN, 60 * 20);
timings.put(AWAITING_TIME_MAX, 60 * -1);
timings.put(AWAITING_TIME_MIN, 60 * -1);
timings.put(OUTGOING_TIME_MAX, 60 * -1);
timings.put(OUTGOING_TIME_MIN, 60 * -1);
timings.put(CHILDRENS_TIME_MAX, 60 * -1);
timings.put(CHILDRENS_TIME_MIN, 60 * -1);
timings.put(FINAL_TIME_MAX, 60 * 60 * 6);
timings.put(FINAL_TIME_MIN, 60 * 60 * 4);
timings.put(SHOUT_TIME, 60 * 2);
}
}
/**
*
* @param ph
* @return phase index
*/
public static int getPhaseIndex(TPP_Phase ph) {
for (int i=0; i<getPhases().size(); i++) {
if(getPhases().get(i).getPhase().compareTo(ph)==0) {
return(i);
}
}
// didnt found it!
logger.warn("Using improper phase for quest ("+ph.name()+"). size: "+getPhases().size());
return (-1);
}
/**
* return next available quest phase
* @param ph -
* @return next phase
*/
public static TPP_Phase getNextPhase(TPP_Phase ph) {
int pos=getPhaseIndex(ph);
if(pos!=(getPhases().size()-1)) {
return (getPhases().get(pos+1).getPhase());
}
return(getDefaultPhaseClass().getPhase());
}
/**
* return next instance of quest phases classes from list
* @param ph
* @return next phase class
*/
public static ITPPQuest getNextPhaseClass(TPP_Phase ph) {
return getPhases().get(getPhaseIndex(getNextPhase(ph)));
}
/**
* return instance of quest phases classes
* @param ph
* @return phase class
*/
public static ITPPQuest getPhaseClass(TPP_Phase ph) {
/*
if(getPhaseIndex(ph)==-1) {
return getDefaultPhaseClass();
}
*/
return getPhases().get(getPhaseIndex(ph));
}
/**
* function return default phase class
* @return default phase class
*/
public static ITPPQuest getDefaultPhaseClass() {
return getPhases().get(getPhaseIndex(INACTIVE));
}
/**
* switching quest to next available phase.
*/
public static void switchToNextPhase() {
getPhaseClass(
getPhase()).phaseToNextPhase(
getNextPhaseClass(
getPhase()), new LinkedList<String>());
}
private static QuestTimer questTimer;
/**
* Timings logic of quest.
*/
private static final class QuestTimer implements TurnListener {
public void onTurnReached(final int currentTurn) {
final ITPPQuest i = getPhaseClass(getPhase());
i.phaseToNextPhase(getNextPhaseClass(getPhase()),
new LinkedList<String>(Arrays.asList("pied piper")));
}
}
public int getRatsCount() {
return(getRats().size());
}
/**
* NPC's actions when player asks for his reward.
*/
class RewardPlayerAction implements ChatAction {
public void fire(final Player player, final Sentence sentence, final EventRaiser mayor) {
final int quantity = calculateReward(player);
// to avoid giving karma without job
if(quantity==0) {
mayor.say("You didn't kill any rats which invaded the city, so you don't deserve a reward.");
return;
}
player.addKarma(5);
final StackableItem moneys = (StackableItem) SingletonRepository.getEntityManager()
.getItem("money");
moneys.setQuantity(quantity);
player.equipOrPutOnGround(moneys);
mayor.say("Please take "+quantity+" money, thank you very much for your help.");
player.setQuest(QUEST_SLOT, "done");
}
}
/**
* NPC's answers when player ask for details.
*/
class DetailsKillingsAction implements ChatAction {
public void fire(final Player player, final Sentence sentence, final EventRaiser mayor) {
if (calculateReward(player)==0) {
mayor.say("You killed no rats during the #rats invasion. "+
"To get a #reward you have to kill at least "+
"one rat at that time.");
return;
}
final StringBuilder sb = new StringBuilder("Well, from the last reward, you killed ");
long moneys = 0;
int kills = 0;
for(int i=0; i<RAT_TYPES.size(); i++) {
try {
kills=Integer.parseInt(player.getQuest(QUEST_SLOT,i+1));
} catch (NumberFormatException nfe) {
// Have no records about this creature in player's slot.
// Treat it as he never killed this creature.
kills=0;
}
// must add 'and' word before last creature in list
if(i==(RAT_TYPES.size()-1)) {
sb.append("and ");
}
sb.append(Grammar.quantityplnoun(kills, RAT_TYPES.get(i), "a"));
sb.append(", ");
moneys = moneys + kills*RAT_REWARDS.get(i);
}
sb.append("so I will give you ");
sb.append(moneys);
sb.append(" money as a #reward for that job.");
mayor.say(sb.toString());
}
}
/**
* function for calculating reward's moneys for player
*
* @param player
* - player which must be rewarded
* @return
* gold amount for hunting rats.
*/
private int calculateReward(Player player) {
int moneys = 0;
int kills = 0;
for(int i=0; i<RAT_TYPES.size(); i++) {
try {
final String killed = player.getQuest(QUEST_SLOT,i+1);
// have player quest slot or not yet?
if (killed != null) {
kills=Integer.decode(killed);
}
} catch (NumberFormatException nfe) {
// player's quest slot don't contain valid number
// so he didn't killed such creatures.
}
moneys = moneys + kills*RAT_REWARDS.get(i);
}
return(moneys);
}
/**
* Set new time period for quest timer (time to next quest phase).
* @param max - maximal time in seconds
* @param min - minimal time in seconds
*/
public static void setNewNotificationTime(int max, int min) {
TurnNotifier.get().dontNotify(questTimer);
if((max >= 0) && (min >= 0)) {
TurnNotifier.get().notifyInSeconds(
Rand.randUniform(max, min), questTimer);
}
}
/**
* add states to NPC's FSM
*/
private void prepareNPC() {
// Player asking about rats when quest is inactive
getMainNPC().add(
ConversationStates.ATTENDING,
Arrays.asList("rats", "rats!"),
new TPPQuestInPhaseCondition(INACTIVE),
ConversationStates.ATTENDING,
"Ados isn't being invaded by rats right now. You can still "+
"get a #reward for the last time you helped. You can ask for #details "+
"if you want.",
null);
// Player asking about rats at invasion time.
getMainNPC().add(
ConversationStates.ATTENDING,
Arrays.asList("rats", "rats!"),
new TPPQuestInPhaseCondition(INVASION),
ConversationStates.ATTENDING,
null,
new ChatAction() {
public void fire(Player player, Sentence sentence, EventRaiser npc) {
npc.say("There " + Grammar.isare(getRats().size()) +
" still about "+Integer.toString(getRats().size())+
" rats alive.");
}
});
// Player asking about rats when quest is neither inactive nor invasion phase
getMainNPC().add(
ConversationStates.ATTENDING,
Arrays.asList("rats", "rats!"),
new AndCondition(
new NotCondition(new TPPQuestInPhaseCondition(INACTIVE)),
new NotCondition(new TPPQuestInPhaseCondition(INVASION))),
ConversationStates.ATTENDING,
"The rats are gone. "+
"You can get #reward for your help now, ask about #details "+
"if you want to know more.",
null);
// Player asked about reward at invasion time
getMainNPC().add(
ConversationStates.ATTENDING,
"reward",
new TPPQuestInPhaseCondition(INVASION),
ConversationStates.ATTENDING,
"Ados is being invaded by rats! "+
"I dont want to reward you now, "+
" until all rats are dead.",
null);
// Player asked about reward not at invasion time
getMainNPC().add(
ConversationStates.ATTENDING,
"reward",
new NotCondition(new TPPQuestInPhaseCondition(INVASION)),
ConversationStates.ATTENDING,
null,
new RewardPlayerAction());
//Player asked about details at invasion time
getMainNPC().add(
ConversationStates.ATTENDING,
"details",
new TPPQuestInPhaseCondition(INVASION),
ConversationStates.ATTENDING,
"Ados is being invaded by rats! "+
"I dont want to either reward you or "+
"explain details to you now,"+
" until all rats are dead.",
null);
getMainNPC().add(
ConversationStates.ATTENDING,
"details",
new NotCondition(new TPPQuestInPhaseCondition(INVASION)),
ConversationStates.ATTENDING,
null,
new DetailsKillingsAction());
}
/**
* first start
*/
private void startQuest() {
setTimings();
getPhases().add(new InactivePhase(timings));
getPhases().add(new InvasionPhase(timings, getRats()));
getPhases().add(new AwaitingPhase(timings));
setNewNotificationTime(
getDefaultPhaseClass().getMinTimeOut(),
getDefaultPhaseClass().getMaxTimeOut());
}
@Override
public String getSlotName() {
return QUEST_SLOT;
}
@Override
public List<String> getHistory(final Player player) {
LinkedList<String> history = new LinkedList<String>();
if (!player.hasQuest(QUEST_SLOT)) {
return history;
}
final String questState = player.getQuest(QUEST_SLOT, 0);
if ("rats".equals(questState)) {
history.add("I have killed some rats in Ados city already, and am trying to kill more.");
}
if ("done".equals(questState)) {
history.add("I have killed some rats in Ados city and got a reward from Mayor Chalmers!");
}
return history;
}
@Override
public String getName() {
return "ThePiedPiper";
}
@Override
public void addToWorld() {
questTimer = new QuestTimer();
fillQuestInfo(
"The Pied Piper",
"Ados City has a rat problem from time to time.",
true);
prepareNPC();
super.addToWorld();
startQuest();
}
public static void setPhase(TPP_Phase phase) {
ThePiedPiper.phase = phase;
}
public static TPP_Phase getPhase() {
return phase;
}
public static void setPhases(LinkedList<ITPPQuest> phases) {
ThePiedPiper.phases = phases;
}
public static LinkedList<ITPPQuest> getPhases() {
return phases;
}
public void setRats(LinkedList<Creature> rats) {
this.rats = rats;
}
public LinkedList<Creature> getRats() {
return rats;
}
@Override
public String getRegion() {
return Region.ADOS_CITY;
}
}