Package games.stendhal.server.entity.trade

Source Code of games.stendhal.server.entity.trade.Market

/* $Id: Market.java,v 1.57 2011/06/09 12:52:07 madmetzger 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.entity.trade;

import games.stendhal.server.core.engine.GameEvent;
import games.stendhal.server.core.engine.ItemLogger;
import games.stendhal.server.core.engine.SingletonRepository;
import games.stendhal.server.core.engine.dbcommand.LogSimpleItemEventCommand;
import games.stendhal.server.entity.PassiveEntity;
import games.stendhal.server.entity.item.Item;
import games.stendhal.server.entity.item.StackableItem;
import games.stendhal.server.entity.player.Player;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import marauroa.common.game.Definition;
import marauroa.common.game.RPClass;
import marauroa.common.game.RPObject;
import marauroa.common.game.RPSlot;

import org.apache.log4j.Logger;

/**
* A Market handles sales offers of players. Players can place offers or accept
* offers to buy a desired item. Offers last only for a certain amount of time
* before they expire and can be prolonged. Expired Offers get removed
* permanently from the market after another period of time. When an offer has
* been accepted, the offering Player can come and fetch his earnings for that
* sale.
*
* @author madmetzger, kiheru
*/
public class Market extends PassiveEntity {
  private static Logger logger = Logger.getLogger(Market.class);

  /**
   * the RPClass name for the market
   */
  public static final String MARKET_RPCLASS_NAME = "market";
  /**
   * the name of the slot where the earnings are stored
   */
  public static final String EARNINGS_SLOT_NAME = "earnings";
  /**
   * the name of the slot where the offers are stored
   */
  public static final String OFFERS_SLOT_NAME = "offers";
  /**
   * the name of the slot where the expired offers are stored
   */
  public static final String EXPIRED_OFFERS_SLOT_NAME = "expired_offers";

  /**
   * Generate the RPClass for the Market
   */
  public static void generateRPClass() {
    final RPClass shop = new RPClass(MARKET_RPCLASS_NAME);
    shop.isA("entity");
    shop.addRPSlot(OFFERS_SLOT_NAME, -1, Definition.HIDDEN);
    shop.addRPSlot(EARNINGS_SLOT_NAME, -1, Definition.HIDDEN);
    shop.addRPSlot(EXPIRED_OFFERS_SLOT_NAME, -1, Definition.HIDDEN);
  }

  /**
   * Creates a new Market from an RPObject
   *
   * @param object
   */
  public Market(final RPObject object) {
    super(object);
    this.setRPClass(MARKET_RPCLASS_NAME);
    hide();

    // delete the slots whose contents get wrong types
    // when loaded from the db
    if (hasSlot(OFFERS_SLOT_NAME)) {
      removeSlot(OFFERS_SLOT_NAME);
    }
    addSlot(OFFERS_SLOT_NAME);

    if (hasSlot(EARNINGS_SLOT_NAME)) {
      removeSlot(EARNINGS_SLOT_NAME);
    }
    addSlot(EARNINGS_SLOT_NAME);

    if (hasSlot(EXPIRED_OFFERS_SLOT_NAME)) {
      removeSlot(EXPIRED_OFFERS_SLOT_NAME);
    }
    addSlot(EXPIRED_OFFERS_SLOT_NAME);

    // copy the contents from the old slots
    if (object.hasSlot(OFFERS_SLOT_NAME)) {
      for (final RPObject rpo : object.getSlot(OFFERS_SLOT_NAME)) {
        Offer offer = new Offer(rpo);

        // an offer might have become obsolete, when items are removed
        if (offer.getItem() == null) {
          logger.warn("Cannot restore an offer by "
              + offer.getOfferer() + " because this item"
              + " was removed from items.xml");
          continue;
        }

        this.getSlot(OFFERS_SLOT_NAME).add(offer);
      }
    }
    if (object.hasSlot(EARNINGS_SLOT_NAME)) {
      for (final RPObject rpo : object.getSlot(EARNINGS_SLOT_NAME)) {
        final Earning earning = new Earning(rpo);
        this.getSlot(EARNINGS_SLOT_NAME).add(earning);
      }
    }
    if (object.hasSlot(EXPIRED_OFFERS_SLOT_NAME)) {
      for (final RPObject rpo : object.getSlot(EXPIRED_OFFERS_SLOT_NAME)) {
        Offer offer = new Offer(rpo);

        // an offer might have become obsolete, when items are removed
        if (offer.getItem() == null) {
          logger.warn("Cannot restore an offer by "
              + offer.getOfferer() + " because this item"
              + " was removed from items.xml");
          continue;
        }

        this.getSlot(EXPIRED_OFFERS_SLOT_NAME).add(offer);
      }
    }
    store();
  }

  /**
   * Factory method for the market
   *
   * @return a new Market
   */
  public static Market createShop() {
    Market shop = new Market();
    return shop;
  }

  private Market() {
    super();
    setRPClass(MARKET_RPCLASS_NAME);
    hide();
    if (!this.hasSlot(OFFERS_SLOT_NAME)) {
      addSlot(OFFERS_SLOT_NAME);
    }
    if (!this.hasSlot(EARNINGS_SLOT_NAME)) {
      addSlot(EARNINGS_SLOT_NAME);
    }
    if (!this.hasSlot(EXPIRED_OFFERS_SLOT_NAME)) {
      addSlot(EXPIRED_OFFERS_SLOT_NAME);
    }
    store();
  }

  /**
   * creates a new offer at the market
   *
   * @param offerer
   *            offering player
   * @param item
   *            item to sell
   * @param money
   *            price for the item
   * @param number
   *            number of items to sell
   * @return the new created offer
   */
  public Offer createOffer(final Player offerer, Item item,
      final Integer money, final Integer number) {
    if (item == null || item.isBound()) {
      return null;
    }

    if (item instanceof StackableItem) {
      if (!offerer.equals(item.getBaseContainer())) {
        return null;
      }
      item = ((StackableItem) item).splitOff(number);
      if (item == null) {
        return null;
      }
    } else if (!offerer.drop(item)) {
      return null;
    }

    Offer offer = new Offer(item, money, offerer);
    RPSlot slot = this.getSlot(OFFERS_SLOT_NAME);
    slot.add(offer);
    getZone().storeToDatabase();

    new ItemLogger().addLogItemEventCommand(new LogSimpleItemEventCommand(
        item, offerer, "slot-to-market", item.get("name"), Integer
            .toString(getQuantity(item)), "new offer",
        OFFERS_SLOT_NAME));

    return offer;
  }

  /**
   * Completes a trade of an offer by transferring item to accepting player
   * and taking the money from him
   *
   * @param offer
   * @param acceptingPlayer
   */
  public boolean acceptOffer(final Offer offer, final Player acceptingPlayer) {
    if (getSlot(OFFERS_SLOT_NAME).has(offer.getID()) && offer.hasItem()) {
      int price = offer.getPrice().intValue();
      // Take the money; free items should always succeed
      if ((price == 0) || acceptingPlayer.drop("money", price)) {
        Item item = offer.getItem();
        offer.getSlot(Offer.OFFER_ITEM_SLOT_NAME).remove(item.getID());
        acceptingPlayer.equipOrPutOnGround(item);
        // Do not give trading bonus for accepting free items
        boolean reward = offer.shouldReward(acceptingPlayer)
            && price > 0;
        final Earning earning = new Earning(offer.getPrice(),
            offer.getOfferer(), reward);
        this.getSlot(EARNINGS_SLOT_NAME).add(earning);
        this.getSlot(OFFERS_SLOT_NAME).remove(offer.getID());
        if (reward) {
          applyTradingBonus(acceptingPlayer);
        }

        // log the item movement
        String slotName = null;
        String target = "ground";
        if (item.getContainerSlot() != null) {
          slotName = item.getContainerSlot().getName();
          target = "slot";
        }
        new ItemLogger()
            .addLogItemEventCommand(new LogSimpleItemEventCommand(
                item, acceptingPlayer, "market-to-" + target,
                item.get("name"), Integer
                    .toString(getQuantity(item)),
                "accept offer", slotName));

        this.getZone().storeToDatabase();
        return true;
      }
    }

    return false;
  }

  /**
   * rewards player for a successfull trade
   *
   * @param player
   *            the player to reward
   */
  private void applyTradingBonus(Player player) {
    player.incrementTradescore();
  }

  /**
   * The earnings for complete trades are paid to the player.
   *
   * @param earner
   *            the player fetching his earnings
   * @return the fetched earnings
   */
  public Set<Earning> fetchEarnings(final Player earner) {
    Set<Earning> earningsToRemove = new HashSet<Earning>();
    for (RPObject earningRPObject : this.getSlot(EARNINGS_SLOT_NAME)) {
      Earning earning = (Earning) earningRPObject;
      if (earning.getSeller().equals(earner.getName())) {
        earningsToRemove.add(earning);
      }
    }
   
    if(!earningsToRemove.isEmpty()) {
      int summedUpEarnings = 0;
      //sum up
      for (Earning earningToSumUp : earningsToRemove) {
        summedUpEarnings = summedUpEarnings + earningToSumUp.getValue();
      }
      final StackableItem item = (StackableItem) SingletonRepository.getEntityManager().getItem("money");
      item.setQuantity(summedUpEarnings);
      if(earner.equipToInventoryOnly(item)) {
        //reward only if equipping was done
        for(Earning earningToReward : earningsToRemove) {
          if (earningToReward.shouldReward()) {
            applyTradingBonus(earner);
          }
        }
        removeEarnings(earningsToRemove);
      } else {
        //signal that no earnings were collected at all
        //a caller can only distinguish if anything was collected
        earningsToRemove.clear();
      }
    }

    return earningsToRemove;
  }

  /**
   * Remove a set of earnings.
   *
   * @param earningsToRemove
   *            The earnings to be removed
   */
  public void removeEarnings(Iterable<Earning> earningsToRemove) {
    for (Earning earning : earningsToRemove) {
      this.getSlot(EARNINGS_SLOT_NAME).remove(earning.getID());
    }
    this.getZone().storeToDatabase();
  }

  /**
   * counts the number of offers, a player has placed
   *
   * @param offerer
   * @return the number of offers
   */
  public int countOffersOfPlayer(Player offerer) {
    int count = 0;
    for (RPObject object : this.getSlot(OFFERS_SLOT_NAME)) {
      Offer offer = (Offer) object;
      if (offer.getOfferer().equals(offerer.getName())) {
        count = count + 1;
      }
    }
    return count;
  }

  /**
   * removes an offer from the market and returns the item to the user
   *
   * @param o
   *            the offer to remove
   * @param p
   *            the removing player
   */
  public void removeOffer(Offer o, Player p) {
    Item item = o.getItem();
    String itemName = item.getName();
   
    o.getSlot(Offer.OFFER_ITEM_SLOT_NAME).remove(item.getID());
    p.equipOrPutOnGround(item);

    getSlot(OFFERS_SLOT_NAME).remove(o.getID());

    getExpiredOffers().remove(o);
    getSlot(EXPIRED_OFFERS_SLOT_NAME).remove(o.getID());

    getZone().storeToDatabase();

    // log the item movement
    String slotName = null;
    String target = "ground";
    if (item.getContainerSlot() != null) {
      slotName = item.getContainerSlot().getName();
      target = "slot";
    }
    new ItemLogger().addLogItemEventCommand(new LogSimpleItemEventCommand(item, p,
            "market-to-" + target, itemName, Integer
                .toString(getQuantity(item)), "remove offer",
            slotName));
  }

  /**
   * expires an offer and removes it from the available offers
   *
   * @param o
   *            the offer to expire
   */
  public void expireOffer(Offer o) {
    this.getSlot(OFFERS_SLOT_NAME).remove(o.getID());
    this.getSlot(EXPIRED_OFFERS_SLOT_NAME).add(o);
    this.getZone().storeToDatabase();
    String itemname = "null";
    if (o.hasItem()) {
      itemname = o.getItem().getName();
    }
    new GameEvent("market", "expire-offer", o.getOfferer(), itemname, o.getPrice().toString()).raise();
  }

  /**
   * @return all currently expired offers in the market
   */
  public List<Offer> getExpiredOffers() {
    List<Offer> expiredOffers = new LinkedList<Offer>();
    for(RPObject o : getSlot(EXPIRED_OFFERS_SLOT_NAME)) {
      expiredOffers.add((Offer) o);
    }
    return expiredOffers;
  }

  /**
   * removes an expired offer permanently from the market
   *
   * @param offerToRemove
   */
  public void removeExpiredOffer(Offer offerToRemove) {
    this.getSlot(EXPIRED_OFFERS_SLOT_NAME).remove(offerToRemove.getID());

    Item item = offerToRemove.getItem();
    if (item != null) {
      new ItemLogger().destroy(null, this.getSlot(EXPIRED_OFFERS_SLOT_NAME),
          item, "timeout");
    }

    this.getZone().storeToDatabase();
  }

  /**
   * prolongs an offer in the market to make it available again
   *
   * @param offer
   *            the offer to prolong
   * @return the prolonged offer
   */
  public Offer prolongOffer(Offer offer) {
    offer.updateTimestamp();
    if (getSlot(EXPIRED_OFFERS_SLOT_NAME).has(offer.getID())) {
      // It had expired. Move to active offers slot.
      this.getSlot(EXPIRED_OFFERS_SLOT_NAME).remove(offer.getID());
      RPSlot slot = this.getSlot(OFFERS_SLOT_NAME);
      slot.add(offer);
    } else if (!getSlot(OFFERS_SLOT_NAME).has(offer.getID())) {
      // Such an offer does not exist anymore
      return null;
    }

    this.getZone().storeToDatabase();
    return offer;
  }

  /**
   * Get a list of offers whose timestamp is older than specified.
   *
   * @param seconds
   *            age of offers in seconds
   * @return list of offers that are older than the specified time
   */
  public List<Offer> getOffersOlderThan(int seconds) {
    List<Offer> offers = new LinkedList<Offer>();
    for (RPObject o : getSlot(OFFERS_SLOT_NAME)) {
      offers.add((Offer) o);
    }
    return getOlderThan(offers, seconds);
  }

  /**
   * Get a list of expired offers whose timestamp is older than specified.
   *
   * @param seconds
   *            age of offers in seconds
   * @return list of expired offers that are older than the specified time
   */
  public List<Offer> getExpiredOffersOlderThan(int seconds) {
    return getOlderThan(getExpiredOffers(), seconds);
  }

  /**
   * Get a list of earnings whose timestamp is older than specified.
   *
   * @param seconds
   *            age of offers in seconds
   * @return list of earnings that are older than the specified time
   */
  public List<Earning> getEarningsOlderThan(int seconds) {
    RPSlot slot = this.getSlot(EARNINGS_SLOT_NAME);
    List<Earning> earnings = new LinkedList<Earning>();
    for (RPObject o : slot) {
      earnings.add((Earning) o);
    }
    return getOlderThan(earnings, seconds);
  }

  /**
   * retrieves Dateable objects older than seconds from a given Iterable
   *
   * @param <T>
   * @param set
   *            the set to search in
   * @param seconds
   *            the maximum age
   * @return the filtered list
   */
  private <T extends Dateable> List<T> getOlderThan(Iterable<T> set,
      int seconds) {
    List<T> old = new LinkedList<T>();
    for (T obj : set) {
      if (System.currentTimeMillis() > obj.getTimestamp() + 1000L
          * seconds) {
        old.add(obj);
      }
    }

    return old;
  }

  /**
   * gets the quantity of an item
   *
   * @param item
   * @return the quantity
   */
  private int getQuantity(Item item) {
    int quantity = 1;
    if (item instanceof StackableItem) {
      quantity = ((StackableItem) item).getQuantity();
    }

    return quantity;
  }

  /**
   * @param o
   * @return true iff the Offer o is in this market's offers
   */
  public boolean contains(Offer o) {
    return getSlot(OFFERS_SLOT_NAME).has(o.getID());
  }

  /**
   * @param player
   * @return true iff there are earnings for this player in the market
   */
  public boolean hasEarningsFor(Player player) {
    for(RPObject o : this.getSlot(EARNINGS_SLOT_NAME)) {
      Earning e = (Earning) o;
      if(e.getSeller().equals(player.getName())) {
        return true;
      }
    }
    return false;
  }
}
TOP

Related Classes of games.stendhal.server.entity.trade.Market

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.