/** * Calculates how much of the given goods type this settlement wants and should retain. * * @param type The <code>GoodsType</code>. * @return The amount of goods wanted. */ protected int getWantedGoodsAmount(GoodsType type) { if (getUnitCount() <= 0) return 0; final Specification spec = getSpecification(); final UnitType unitType = getFirstUnit().getType(); final Role militaryRole = Role.getAvailableRoles(getOwner(), unitType, spec.getMilitaryRoles()).get(0); if (type.isMilitaryGoods()) { // Retain enough goods to fully arm. int need = 0; for (Unit u : ownedUnits) { if (u.getRole() == militaryRole) continue; List<AbstractGoods> required = u.getGoodsDifference(militaryRole, 1); need += AbstractGoods.getCount(type, required); } return need; } int consumption = getConsumptionOf(type); if (type == spec.getPrimaryFoodType()) { // Food is perishable, do not try to retain that much return Math.max(40, consumption * 3); } if (type.isTradeGoods() || type.isNewWorldLuxuryType() || type.isRefined()) { // Aim for 10 years supply, resupply is doubtful return Math.max(80, consumption * 20); } // Just keep some around return 2 * getUnitCount(); }
/** * Gets the goods this settlement is willing to sell. * * @param limit The maximum number of goods required. * @param unit The <code>Unit</code> that is trading. * @return A list of goods to sell. */ public List<Goods> getSellGoods(int limit, Unit unit) { List<Goods> result = new ArrayList<>(); List<Goods> settlementGoods = getCompactGoods(); Collections.sort(settlementGoods, exportGoodsComparator); int count = 0; for (Goods goods : settlementGoods) { if (!willSell(goods.getType())) continue; int amount = goods.getAmount(); int retain = getWantedGoodsAmount(goods.getType()); if (retain >= amount) continue; amount -= retain; if (amount > GoodsContainer.CARGO_SIZE) { amount = GoodsContainer.CARGO_SIZE; } if (unit != null) { amount = Math.round( applyModifiers( (float) amount, getGame().getTurn(), unit.getModifiers(Modifier.TRADE_VOLUME_PENALTY))); } if (amount < TRADE_MINIMUM_SIZE) continue; result.add(new Goods(getGame(), this, goods.getType(), amount)); count++; if (count >= limit) break; } return result; }
@Override public int compare(Goods goods1, Goods goods2) { int cmp; GoodsType t1 = goods1.getType(); GoodsType t2 = goods2.getType(); cmp = (((t2.isNewWorldGoodsType()) ? 1 : 0) - ((t1.isNewWorldGoodsType()) ? 1 : 0)); if (cmp == 0) { int a1 = Math.min(goods2.getAmount(), GoodsContainer.CARGO_SIZE); int a2 = Math.min(goods1.getAmount(), GoodsContainer.CARGO_SIZE); cmp = getPriceToSell(t2, a2) - getPriceToSell(t1, a1); if (cmp == 0) { cmp = a2 - a1; } } return cmp; }
/** * Gets the amount of gold this <code>IndianSettlment</code> is willing to sell the given <code> * Goods</code> for. * * <p>It is only meaningful to call this method from the server, since the settlement's {@link * GoodsContainer} is hidden from the clients. * * @param type The type of <code>Goods</code> to price. * @param amount The amount of <code>Goods</code> to price. * @return The price. */ public int getPriceToSell(GoodsType type, int amount) { if (amount > GoodsContainer.CARGO_SIZE) { throw new IllegalArgumentException("Amount > " + GoodsContainer.CARGO_SIZE); } final int full = GOODS_BASE_PRICE + getType().getTradeBonus(); // Base price is purchase price plus delta. // - military goods at double value // - trade goods at +50% int price = amount + Math.max(0, 11 * getPriceToBuy(type, amount) / 10); if (type.isMilitaryGoods()) { price = Math.max(price, amount * full * 2); } else if (type.isTradeGoods()) { price = Math.max(price, 150 * amount * full / 100); } return price; }
/** {@inheritDoc} */ @Override public int getImportAmount(GoodsType goodsType, int turns) { if (goodsType.limitIgnored()) return Integer.MAX_VALUE; int present = Math.max(0, getGoodsCount(goodsType) - turns * getTotalProductionOf(goodsType)); int capacity = getWarehouseCapacity(); return capacity - present; }
/** * Add some initial goods to a newly generated settlement. After all, they have been here for some * time. * * @param random A pseudo-random number source. */ public void addRandomGoods(Random random) { HashMap<GoodsType, Integer> goodsMap = new HashMap<>(); for (Tile t : getOwnedTiles()) { for (AbstractGoods ag : t.getSortedPotential()) { GoodsType type = ag.getType().getStoredAs(); Integer i = goodsMap.get(type); int value = (i == null) ? 0 : i; goodsMap.put(type, value + ag.getAmount()); } } double d = randomInt(logger, "Goods at " + getName(), random, 10) * 0.1 + 1.0; for (Entry<GoodsType, Integer> e : goodsMap.entrySet()) { int i = e.getValue(); if (!e.getKey().isFoodType()) i = (int) Math.round(d * e.getValue()); i = Math.min(i, GoodsContainer.CARGO_SIZE); if (i > 0) addGoods(e.getKey(), i); } }
/** * Price some goods according to the amount present in the settlement. * * @param type The type of goods for sale. * @param amount The amount of goods for sale. * @return A price for the goods. */ private int getNormalGoodsPriceToBuy(GoodsType type, int amount) { final int tradeGoodsAdd = 20; // Fake additional trade goods present final int capacity = getGoodsCapacity(); int current = getGoodsCount(type); // Increase effective stock if its raw material is produced here. GoodsType rawType = type.getInputType(); if (rawType != null) { int rawProduction = getMaximumProduction(rawType); int add = (rawProduction < 5) ? 10 * rawProduction : (rawProduction < 10) ? 5 * rawProduction + 25 : (rawProduction < 20) ? 2 * rawProduction + 55 : 100; // Decrease bonus in proportion to current stock, up to capacity. add = add * Math.max(0, capacity - current) / capacity; current += add; } else if (type.isTradeGoods()) { // Small artificial increase of the trade goods stored. current += tradeGoodsAdd; } // Only interested in the amount of goods that keeps the // total under the threshold. int retain = Math.min(getWantedGoodsAmount(type), capacity); int valued = (retain <= current) ? 0 : Math.min(amount, retain - current); // Unit price then is maximum price plus the bonus for the // settlement type, reduced by the proportion of goods present. int unitPrice = (GOODS_BASE_PRICE + getType().getTradeBonus()) * Math.max(0, capacity - current) / capacity; // But farmed goods are always less interesting. // and small settlements are not interested in building. if (type.isFarmed() || type.isRawBuildingMaterial()) unitPrice /= 2; // Only pay for the portion that is valued. return (unitPrice < 0) ? 0 : valued * unitPrice; }
/** * Gets a random goods gift from this settlement. * * @param random A pseudo random number source. * @return A random goods gift, or null if none found. */ public Goods getRandomGift(Random random) { List<Goods> goodsList = new ArrayList<>(); for (GoodsType type : getSpecification().getNewWorldGoodsTypeList()) { int n = getGoodsCount(type) - KEEP_RAW_MATERIAL; if (n >= GIFT_THRESHOLD) { n -= GIFT_MINIMUM; Goods goods = new Goods( getGame(), this, type, Math.min(randomInt(logger, "Gift amount", random, n) + GIFT_MINIMUM, GIFT_MAXIMUM)); goodsList.add(goods); } } return (goodsList.isEmpty()) ? null : getRandomMember(logger, "Gift type", goodsList, random); }
/** * Price some goods that have military value to the settlement. * * @param type The type of goods for sale. * @param amount The amount of goods for sale. * @return A price for the goods. */ private int getMilitaryGoodsPriceToBuy(GoodsType type, int amount) { final int full = GOODS_BASE_PRICE + getType().getTradeBonus(); int required = getWantedGoodsAmount(type); if (required == 0) return 0; // Do not pay military price // If the settlement can use more than half of the goods on offer, // then pay top dollar for the lot. Otherwise only pay the premium // price for the part they need and refer the remaining amount to // the normal goods pricing. int valued = Math.max(0, required - getGoodsCount(type)); int price = (valued > amount / 2) ? full * amount : valued * full + getNormalGoodsPriceToBuy(type, amount - valued); logger.finest( "Military price(" + amount + " " + type + ")" + " valued=" + valued + " -> " + price); return price; }
/** {@inheritDoc} */ @Override public int getTotalProductionOf(GoodsType type) { if (type.isRefined()) { if (type != goodsToMake()) return 0; // Pretend 1/3 of the units present make the item with // basic production of 3. return getUnitCount(); } int potential = 0; int tiles = 0; for (Tile workTile : getOwnedTiles()) { if (workTile != getTile() && !workTile.isOccupied()) { // FIXME: make unitType brave potential += workTile.getPotentialProduction(type, null); tiles++; } } // When a native settlement has more tiles than units, pretend // that they produce from their entire area at reduced // efficiency. if (tiles > getUnitCount()) { potential *= (float) getUnitCount() / tiles; } // Raw production is too generous, apply a fudge factor to reduce it // a bit for the non-food cases. if (!type.isFoodType()) { potential = (int) Math.round(potential * NATIVE_PRODUCTION_EFFICIENCY); } // But always add full potential of the center tile. potential += getTile().getPotentialProduction(type, null); return potential; }
/** {@inheritDoc} */ @Override public int getExportAmount(GoodsType goodsType, int turns) { int present = Math.max(0, getGoodsCount(goodsType) + turns * getTotalProductionOf(goodsType)); int wanted = getWantedGoodsAmount(goodsType); return present - wanted; }