Package games.stendhal.server.maps.quests

Source Code of games.stendhal.server.maps.quests.SolveRiddles$Riddles$RiddleLoader

/* $Id: SolveRiddles.java,v 1.22 2011/05/01 19:50:05 martinfuchs Exp $ */
/***************************************************************************
*                   (C) Copyright 2003-2010 - 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.Direction;
import games.stendhal.common.NotificationType;
import games.stendhal.common.Rand;
import games.stendhal.common.parser.ConversationParser;
import games.stendhal.common.parser.Sentence;
import games.stendhal.common.parser.SimilarExprMatcher;
import games.stendhal.server.core.engine.SingletonRepository;
import games.stendhal.server.core.engine.StendhalRPZone;
import games.stendhal.server.entity.npc.ChatAction;
import games.stendhal.server.entity.npc.ConversationPhrases;
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.QuestNotStartedCondition;
import games.stendhal.server.entity.npc.condition.QuestStartedCondition;
import games.stendhal.server.entity.player.Player;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.log4j.Logger;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;

/**
* QUEST: Quest to solve a riddle to leave hell
* <p>
*
* PARTICIPANTS: <ul><li>Grim Reaper</ul>
*
*
* STEPS: <ul><li> Reaper sets you a riddle
* <li> Player tries to answer
* <li> Reaper compares answer to configuration file on server
* </ul>
*
*
* REWARD: <ul><li>100 XP - Leaving hell</ul>
*
* REPETITIONS: <ul><li>Any time you wish to leave hell, but if you ask for a riddle when you didn't solve the previous
*  one yet, nor asked the other NPC to let you leave with karma loss, then you have to solve same one still</ul>
*
* @author kymara
*/

public class SolveRiddles extends AbstractQuest {
  private static final String QUEST_SLOT = "solve_riddles";
  private static final int xpreward = 100;
  private Riddles riddles;

  private static class Riddles {
    private static Logger logger = Logger.getLogger(Riddles.class);

    private static final String RIDDLES_XML = "/data/conf/riddles.xml";
    private static final String RIDDLES_EXAMPLE_XML = "/data/conf/riddles-example.xml";

    Map<String, Collection<String>> riddleMap;

    public Riddles() {
      riddleMap = new HashMap<String, Collection<String>>();
      new RiddleLoader().load(riddleMap);
    }

    /**
     * Check if an answer matches the riddle.
     *
     * @param riddle The riddle to be answered
     * @param sentence The answer given by the player
     * @return <code>true</code> iff the answer is correct
     */
    public boolean matches(String riddle, Sentence sentence) {
      final Sentence answer = sentence.parseAsMatchingSource();

      // if the riddle is unknown, teleport the player out.
      // this can happen if the riddle was removed from the xml file
      // (or if the character is copied to a testserver with a different file)
      if (riddleMap.get(riddle) == null) {
        logger.warn("Accepting any answer for unknown riddle: " + riddle);
        return true;
      }
      for (String correct : riddleMap.get(riddle)) {
        final Sentence expected = ConversationParser.parse(correct, new SimilarExprMatcher());
        if (answer.matchesFull(expected)) {
          return true;
        }
      }

      return false;
    }

    /**
     * Get a random riddle.
     *
     * @return A random ridde
     */
    String getRiddle() {
      return Rand.rand(riddleMap.keySet());
    }

    /**
     * Loader for the riddles xml format.
     */
    private static class RiddleLoader extends DefaultHandler {
      Map<String, Collection<String>> riddles;
      String currentKey;
      String currentAnswer;

      public void load(Map<String, Collection<String>> riddles) {
        this.riddles = riddles;

        InputStream in = getClass().getResourceAsStream(RIDDLES_XML);

        if (in == null) {
          logger.warn(RIDDLES_XML + " not found. Using " + RIDDLES_EXAMPLE_XML);
          in = getClass().getResourceAsStream(RIDDLES_EXAMPLE_XML);
          if (in == null) {
            logger.error("Failed to load " + RIDDLES_EXAMPLE_XML);
            return;
          }
        }

        SAXParser parser;

        // Use the default (non-validating) parser
        final SAXParserFactory factory = SAXParserFactory.newInstance();
        try {
          parser = factory.newSAXParser();
          parser.parse(in, this);
        } catch (final Exception e) {
          logger.error(e);
        } finally {
          try {
            in.close();
          } catch (IOException e) {
            logger.error(e);
          }
        }
      }

      /**
       * Add an answer to a riddle. Add the riddle too if it did not exist before.
       * @param riddle The riddle to add an answer to
       * @param answer Asnwer to the riddle
       */
      private void addAnswer(String riddle, String answer) {
        Collection<String> answers = riddles.get(riddle);
        if (answers == null) {
          answers = new LinkedList<String>();
          riddles.put(riddle, answers);
        }
        answers.add(answer);
      }

      @Override
      public void startElement(final String uri, final String localName, final String qName, final Attributes attrs) {
        if (qName.equals("entry")) {
          final String key = attrs.getValue("key");
          if (key == null) {
            logger.warn("An entry without a key");
          } else {
            currentKey = key;
          }
        } else if (!(qName.equals("riddles") || qName.equals("comment"))) {
          currentKey = null;
          logger.warn("Unknown XML element: " + qName);
        }
      }

      @Override
      public void endElement(final String uri, final String lName, final String qName) {
        if (qName.equals("entry")) {
          if ((currentKey != null) && (currentAnswer != null)) {
            addAnswer(currentKey, currentAnswer);
          } else {
            logger.error("Error reading riddles, Key=" + currentKey + " " + " Answer=" + currentAnswer);
          }
        } else {
          currentKey = null;
          currentAnswer = null;
        }
      }

      @Override
      public void characters(char[] ch, int start, int length) {
        if (currentKey != null) {
          currentAnswer = new String(ch, start, length);
        } else {
          currentAnswer = null;
        }
      }
    }
  }

  public SolveRiddles() {
    riddles = new Riddles();
  }

  @Override
  public String getSlotName() {
    return QUEST_SLOT;
  }

  private void setRiddle() {
    final SpeakerNPC reaper = npcs.get("Grim Reaper");
   
    // player has no unsolved riddle active
    reaper.add(ConversationStates.ATTENDING,
        "leave",
        new QuestNotStartedCondition(QUEST_SLOT),
        ConversationStates.QUESTION_1,
        null,
        new ChatAction() {
          public void fire(final Player player, final Sentence sentence, final EventRaiser npc) {
            // randomly choose from available riddles
            final String riddle = riddles.getRiddle();
            npc.say("Try this riddle: " + riddle);
            player.setQuest(QUEST_SLOT, riddle);
          }
        });
   
    // player already was set a riddle he couldn't solve
    reaper.add(ConversationStates.ATTENDING,
        "leave",
        new QuestStartedCondition(QUEST_SLOT),
        ConversationStates.QUESTION_1,
        null,
        new ChatAction() {
        public void fire(final Player player, final Sentence sentence, final EventRaiser npc) {
          final String riddle = player.getQuest(QUEST_SLOT);
          npc.say("You must solve the riddle which I previously set you: " + riddle);
        }
    });
   
    reaper.add(ConversationStates.QUESTION_1, "", null,
      ConversationStates.QUESTION_1, null,
      new ChatAction() {
        public void fire(final Player player, final Sentence sentence, final EventRaiser npc) {
          final String riddle = player.getQuest(QUEST_SLOT);
          final String triggerText = sentence.getTriggerExpression().getNormalized();

          if (riddles.matches(riddle, sentence)) {
            final StendhalRPZone zone = SingletonRepository.getRPWorld().getZone("int_afterlife");
            player.teleport(zone, 31, 23, Direction.UP, player);
            // clear quest slot so riddle is chosen randomly for player next time
            player.removeQuest(QUEST_SLOT);
            player.sendPrivateText(NotificationType.POSITIVE, "You solved the riddle correctly and earned " + xpreward + " XP.");
            player.addXP(xpreward);
            player.notifyWorldAboutChanges();
            npc.setCurrentState(ConversationStates.IDLE);
          } else if (ConversationPhrases.GOODBYE_MESSAGES.contains(triggerText)) {
            npc.say("The old order of things has passed away ... ");
            npc.setCurrentState(ConversationStates.IDLE);
          } else if (triggerText.equals("leave") || triggerText.equals("riddle")) {
            // player didn't answer riddle but tried saying riddle/leave again (to get another maybe?)
            npc.say("You can ask my mirror to let you leave, or you must solve the riddle which I previously set you: " + riddle);
          } else {
            npc.say("Incorrect! Try again, or ask my mirror to let you leave.");
            player.subXP(10 * xpreward);
          }
        }
      });
  }

  @Override
  public void addToWorld() {
    super.addToWorld();
    fillQuestInfo(
        "Solve Riddles",
        "The Grim Reapers pose tricky riddles for those who want to leave the hottest place in Faiumoni.",
        false);
    setRiddle();
  }

  @Override
  public String getName() {
    return "SolveRiddles";
  }
 
  // there is a minimum level requirement to get into hell - this quest is in hell
  @Override
  public int getMinLevel() {
    return 200;
  }
 
  @Override
  public boolean isVisibleOnQuestStatus() {
    return false;
  }
 
  @Override
  public List<String> getHistory(final Player player) {
    return new ArrayList<String>();
  }
 
}
TOP

Related Classes of games.stendhal.server.maps.quests.SolveRiddles$Riddles$RiddleLoader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.