/** * 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(); }
public String toString() { Tile tile = colony.getTile(); String eqStr = "/"; for (EquipmentType e : equipment.keySet()) { eqStr += e.toString().substring(16, 17); } String locStr = (loc == null) ? "" : (loc instanceof Building) ? Utils.lastPart(((Building) loc).getType().toString(), ".") : (loc instanceof ColonyTile) ? tile.getDirection(((ColonyTile) loc).getWorkTile()).toString() : (loc instanceof Tile) ? (loc.getId() + eqStr) : loc.getId(); Location newLoc = unit.getLocation(); String newEqStr = "/"; for (EquipmentType e : unit.getEquipment().keySet()) { newEqStr += e.toString().substring(16, 17); } String newLocStr = (newLoc == null) ? "" : (newLoc instanceof Building) ? Utils.lastPart(((Building) newLoc).getType().toString(), ".") : (newLoc instanceof ColonyTile) ? tile.getDirection(((ColonyTile) newLoc).getWorkTile()).toString() : (newLoc instanceof Tile) ? (newLoc.getId() + newEqStr) : newLoc.getId(); GoodsType newWork = unit.getWorkType(); int newWorkAmount = (newWork == null) ? 0 : getAmount(newLoc, newWork); return String.format( "%-30s %-25s -> %-25s", unit.getId() + ":" + Utils.lastPart(unit.getType().toString(), "."), locStr + ((work == null || workAmount <= 0) ? "" : "(" + Integer.toString(workAmount) + " " + Utils.lastPart(work.toString(), ".") + ")"), newLocStr + ((newWork == null || newWorkAmount <= 0) ? "" : "(" + Integer.toString(newWorkAmount) + " " + Utils.lastPart(newWork.toString(), ".") + ")")) .trim(); }
/** * Gets the amount of gold this <code>IndianSettlment</code> is willing to pay for the given * <code>Goods</code>. * * <p>It is only meaningful to call this method from the server, since the settlement's {@link * GoodsContainer} is hidden from the clients. The AI uses it though so it stays here for now. * Note that it takes no account of whether the native player actually has the gold. * * <p>FIXME: this is rancid with magic numbers. * * @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 getPriceToBuy(GoodsType type, int amount) { if (amount > GoodsContainer.CARGO_SIZE) { throw new IllegalArgumentException("Amount > " + GoodsContainer.CARGO_SIZE); } int price = 0; if (type.isMilitaryGoods()) { // Might return zero if a surplus is present price = getMilitaryGoodsPriceToBuy(type, amount); } if (price == 0) { price = getNormalGoodsPriceToBuy(type, amount); } // Apply wanted bonus final int wantedBase = 100; // Granularity for wanted bonus final int wantedBonus // Premium paid for wanted goods types = (type == wantedGoods[0]) ? 150 : (type == wantedGoods[1]) ? 125 : (type == wantedGoods[2]) ? 110 : 100; // Do not simplify with *=, we want the integer truncation. price = wantedBonus * price / wantedBase; logger.finest("Full price(" + amount + " " + type + ")" + " -> " + price); return price; }
@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; }
/** * Updates the goods wanted by this settlement. * * <p>It is only meaningful to call this method from the server, since the settlement's {@link * GoodsContainer} is hidden from the clients. */ public void updateWantedGoods() { final Specification spec = getSpecification(); final java.util.Map<GoodsType, Integer> prices = new HashMap<>(); for (GoodsType gt : spec.getGoodsTypeList()) { // The natives do not trade military or non-storable goods. if (gt.isMilitaryGoods() || !gt.isStorable()) continue; prices.put(gt, getNormalGoodsPriceToBuy(gt, GoodsContainer.CARGO_SIZE)); } int wantedIndex = 0; for (Entry<GoodsType, Integer> e : mapEntriesByValue(prices, descendingIntegerComparator)) { GoodsType goodsType = e.getKey(); if (e.getValue() <= GoodsContainer.CARGO_SIZE * TRADE_MINIMUM_PRICE || wantedIndex >= wantedGoods.length) break; wantedGoods[wantedIndex] = goodsType; wantedIndex++; } for (; wantedIndex < wantedGoods.length; wantedIndex++) { wantedGoods[wantedIndex] = null; } }
/** * 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; }
/** {@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; }
/** * Chooses a type of goods for some of the natives in a settlement to manufacture. Simple rule: * choose the refined goods that is the greatest shortage for which there is a surplus of the raw * material. * * @return A <code>GoodsType</code> to manufacture, or null if none suitable. */ private GoodsType goodsToMake() { GoodsType wantGoods = null; int diff, wantAmount = -1; for (GoodsType g : getSpecification().getGoodsTypeList()) { GoodsType produced; if (g.isRawMaterial() && (produced = g.getOutputType()) != null && !produced.isBreedable() && produced.isStorable() && getGoodsCount(g) > getWantedGoodsAmount(g) && (diff = getWantedGoodsAmount(produced) - getGoodsCount(produced)) > wantAmount) { wantGoods = produced; wantAmount = diff; } } return wantGoods; }
/** Fire any property changes resulting from actions of a unit. */ public void fireChanges() { UnitType newType = null; Unit.Role newRole = null; Location newLoc = null; GoodsType newWork = null; int newWorkAmount = 0; TypeCountMap<EquipmentType> newEquipment = null; if (!unit.isDisposed()) { newLoc = unit.getLocation(); if (colony != null) { newType = unit.getType(); newRole = unit.getRole(); newWork = unit.getWorkType(); newWorkAmount = (newWork == null) ? 0 : getAmount(newLoc, newWork); newEquipment = unit.getEquipment(); } } if (loc != newLoc) { FreeColGameObject oldFcgo = (FreeColGameObject) loc; oldFcgo.firePropertyChange(change(oldFcgo), unit, null); if (newLoc != null) { FreeColGameObject newFcgo = (FreeColGameObject) newLoc; newFcgo.firePropertyChange(change(newFcgo), null, unit); } } if (colony != null) { if (type != newType && newType != null) { String pc = ColonyChangeEvent.UNIT_TYPE_CHANGE.toString(); colony.firePropertyChange(pc, type, newType); } else if (role != newRole && newRole != null) { String pc = Tile.UNIT_CHANGE.toString(); colony.firePropertyChange(pc, role.toString(), newRole.toString()); } if (work == newWork) { if (work != null && workAmount != newWorkAmount) { colony.firePropertyChange(work.getId(), workAmount, newWorkAmount); } } else { if (work != null) { colony.firePropertyChange(work.getId(), workAmount, 0); } if (newWork != null) { colony.firePropertyChange(newWork.getId(), 0, newWorkAmount); } } } if (newEquipment != null) { Set<EquipmentType> keys = new HashSet<EquipmentType>(); keys.addAll(equipment.keySet()); keys.addAll(newEquipment.keySet()); for (EquipmentType e : keys) { int cOld = equipment.getCount(e); int cNew = newEquipment.getCount(e); if (cOld != cNew) { unit.firePropertyChange(Unit.EQUIPMENT_CHANGE, cOld, cNew); } } } if (unit.getGoodsContainer() != null) { unit.getGoodsContainer().fireChanges(); } }
/** * Will this settlement sell a type of goods. Placeholder until we have a spec-configured * blacklist. * * @param type The <code>GoodsType</code> to consider. * @return True if the settlement would sell the goods. */ public boolean willSell(GoodsType type) { return !type.isTradeGoods(); }