Package horizons :: Package ai :: Package aiplayer :: Package mission :: Module internationaltrade
[hide private]
[frames] | no frames]

Source Code for Module horizons.ai.aiplayer.mission.internationaltrade

  1  # ################################################### 
  2  # Copyright (C) 2008-2017 The Unknown Horizons Team 
  3  # team@unknown-horizons.org 
  4  # This file is part of Unknown Horizons. 
  5  # 
  6  # Unknown Horizons is free software; you can redistribute it and/or modify 
  7  # it under the terms of the GNU General Public License as published by 
  8  # the Free Software Foundation; either version 2 of the License, or 
  9  # (at your option) any later version. 
 10  # 
 11  # This program is distributed in the hope that it will be useful, 
 12  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 13  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 14  # GNU General Public License for more details. 
 15  # 
 16  # You should have received a copy of the GNU General Public License 
 17  # along with this program; if not, write to the 
 18  # Free Software Foundation, Inc., 
 19  # 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA 
 20  # ################################################### 
 21   
 22  from horizons.ai.aiplayer.mission import ShipMission 
 23  from horizons.command.uioptions import BuyResource, SellResource 
 24  from horizons.component.namedcomponent import NamedComponent 
 25  from horizons.component.storagecomponent import StorageComponent 
 26  from horizons.component.tradepostcomponent import TradePostComponent 
 27  from horizons.constants import RES, TRADER 
 28  from horizons.ext.enum import Enum 
 29  from horizons.util.python.callback import Callback 
 30  from horizons.util.worldobject import WorldObject 
31 32 33 -class InternationalTrade(ShipMission):
34 """ 35 Given a ship, a settlement_manager of our settlement, a settlement of another player, 36 and either a resource to be bought or sold (or both) the ship will load/unload the 37 required resources at our settlement and do the necessary trading at the other player's one. 38 """ 39 40 missionStates = Enum('created', 'moving_to_my_settlement', 'moving_to_other_settlement', 'returning_to_my_settlement') 41
42 - def __init__(self, settlement_manager, settlement, ship, bought_resource, sold_resource, success_callback, failure_callback):
43 super().__init__(success_callback, failure_callback, ship) 44 assert sold_resource is not None or bought_resource is not None 45 self.settlement_manager = settlement_manager 46 self.settlement = settlement 47 self.bought_resource = bought_resource 48 self.sold_resource = sold_resource 49 self.state = self.missionStates.created
50
51 - def save(self, db):
52 super().save(db) 53 db("INSERT INTO ai_mission_international_trade(rowid, settlement_manager, settlement, ship, bought_resource, sold_resource, state) VALUES(?, ?, ?, ?, ?, ?, ?)", 54 self.worldid, self.settlement_manager.worldid, self.settlement.worldid, self.ship.worldid, self.bought_resource, self.sold_resource, self.state.index)
55 56 @classmethod
57 - def load(cls, db, worldid, success_callback, failure_callback):
58 self = cls.__new__(cls) 59 self._load(db, worldid, success_callback, failure_callback) 60 return self
61
62 - def _load(self, db, worldid, success_callback, failure_callback):
63 db_result = db("SELECT settlement_manager, settlement, ship, bought_resource, sold_resource, state FROM ai_mission_international_trade WHERE rowid = ?", worldid)[0] 64 self.settlement_manager = WorldObject.get_object_by_id(db_result[0]) 65 self.settlement = WorldObject.get_object_by_id(db_result[1]) 66 self.bought_resource = db_result[3] 67 self.sold_resource = db_result[4] 68 self.state = self.missionStates[db_result[5]] 69 super().load(db, worldid, success_callback, failure_callback, WorldObject.get_object_by_id(db_result[2])) 70 71 if self.state is self.missionStates.moving_to_my_settlement: 72 self.ship.add_move_callback(Callback(self._reached_my_settlement)) 73 self.ship.add_blocked_callback(Callback(self._move_to_my_settlement)) 74 elif self.state is self.missionStates.moving_to_other_settlement: 75 self.ship.add_move_callback(Callback(self._reached_other_settlement)) 76 self.ship.add_blocked_callback(Callback(self._move_to_other_settlement)) 77 elif self.state is self.missionStates.returning_to_my_settlement: 78 self.ship.add_move_callback(Callback(self._returned_to_my_settlement)) 79 self.ship.add_blocked_callback(Callback(self._return_to_my_settlement)) 80 else: 81 assert False, 'invalid state'
82
83 - def start(self):
84 if self.sold_resource is not None: 85 self.state = self.missionStates.moving_to_my_settlement 86 self._move_to_my_settlement() 87 else: 88 self.state = self.missionStates.moving_to_other_settlement 89 self._move_to_other_settlement() 90 self.log.info('%s started an international trade mission between %s and %s to sell %s and buy %s using %s', self, 91 self.settlement_manager.settlement.get_component(NamedComponent).name, self.settlement.get_component(NamedComponent).name, self.sold_resource, self.bought_resource, self.ship)
92
93 - def _move_to_my_settlement(self):
94 self._move_to_warehouse_area( 95 self.settlement_manager.settlement.warehouse.position, 96 Callback(self._reached_my_settlement), 97 Callback(self._move_to_my_settlement), 98 'Unable to move to my settlement ({})'.format( 99 self.settlement_manager.settlement.get_component(NamedComponent).name))
100
101 - def _get_max_sellable_amount(self, available_amount):
102 if self.sold_resource not in self.settlement.get_component(TradePostComponent).buy_list: 103 return 0 104 if self.settlement.get_component(TradePostComponent).buy_list[self.sold_resource] >= self.settlement.get_component(StorageComponent).inventory[self.sold_resource]: 105 return 0 106 if available_amount <= 0: 107 return 0 108 price = int(self.owner.session.db.get_res_value(self.sold_resource) * TRADER.PRICE_MODIFIER_SELL) 109 return min(self.settlement.get_component(StorageComponent).inventory[self.sold_resource] - self.settlement.get_component(TradePostComponent).buy_list[self.sold_resource], 110 self.settlement.owner.get_component(StorageComponent).inventory[RES.GOLD] // price, available_amount)
111
112 - def _reached_my_settlement(self):
113 self.log.info('%s reached my warehouse area (%s)', self, self.settlement_manager.settlement.get_component(NamedComponent).name) 114 available_amount = max(0, self.settlement_manager.settlement.get_component(StorageComponent).inventory[self.sold_resource] - self.settlement_manager.resource_manager.resource_requirements[self.sold_resource]) 115 sellable_amount = self._get_max_sellable_amount(available_amount) 116 if sellable_amount <= 0: 117 self.log.info('%s no resources can be sold', self) 118 if self.bought_resource is None: 119 self.report_failure('No resources need to be sold nor bought') 120 return 121 else: 122 self.move_resource(self.ship, self.settlement_manager.settlement, self.sold_resource, -sellable_amount) 123 self.log.info('%s loaded resources', self) 124 self.state = self.missionStates.moving_to_other_settlement 125 self._move_to_other_settlement()
126
128 self._move_to_warehouse_area( 129 self.settlement.warehouse.position, 130 Callback(self._reached_other_settlement), 131 Callback(self._move_to_other_settlement), 132 'Unable to move to the other settlement ({})'.format( 133 self.settlement.get_component(NamedComponent).name))
134
135 - def _get_max_buyable_amount(self):
136 if self.bought_resource is None: 137 return 0 138 if self.bought_resource not in self.settlement.get_component(TradePostComponent).sell_list: 139 return 0 140 if self.settlement.get_component(TradePostComponent).sell_list[self.bought_resource] >= self.settlement.get_component(StorageComponent).inventory[self.bought_resource]: 141 return 0 142 needed_amount = self.settlement_manager.resource_manager.resource_requirements[self.bought_resource] - \ 143 self.settlement_manager.settlement.get_component(StorageComponent).inventory[self.bought_resource] 144 if needed_amount <= 0: 145 return 0 146 price = int(self.owner.session.db.get_res_value(self.bought_resource) * TRADER.PRICE_MODIFIER_BUY) 147 return min(self.settlement.get_component(StorageComponent).inventory[self.bought_resource] - self.settlement.get_component(TradePostComponent).sell_list[self.bought_resource], 148 self.settlement_manager.owner.get_component(StorageComponent).inventory[RES.GOLD] // price, needed_amount)
149
151 self.log.info('%s reached the other warehouse area (%s)', self, self.settlement.get_component(NamedComponent).name) 152 if self.sold_resource is not None: 153 sellable_amount = self._get_max_sellable_amount(self.ship.get_component(StorageComponent).inventory[self.sold_resource]) 154 if sellable_amount > 0: 155 BuyResource(self.settlement.get_component(TradePostComponent), self.ship, self.sold_resource, sellable_amount).execute(self.owner.session) 156 if self.bought_resource is None: 157 self.report_success('Sold {:d} of resource {:d}'.format(sellable_amount, self.sold_resource)) 158 return 159 else: 160 self.log.info('%s sold %d of resource %d', self, sellable_amount, self.sold_resource) 161 162 buyable_amount = self._get_max_buyable_amount() 163 if buyable_amount <= 0: 164 self.report_failure('No resources can be bought') 165 return 166 167 SellResource(self.settlement.get_component(TradePostComponent), self.ship, self.bought_resource, buyable_amount).execute(self.owner.session) 168 self.log.info('%s bought %d of resource %d', self, buyable_amount, self.bought_resource) 169 self.state = self.missionStates.returning_to_my_settlement 170 self._return_to_my_settlement()
171
172 - def _return_to_my_settlement(self):
173 self._move_to_warehouse_area( 174 self.settlement_manager.settlement.warehouse.position, 175 Callback(self._returned_to_my_settlement), 176 Callback(self._return_to_my_settlement), 177 'Unable to return to {}'.format( 178 self.settlement_manager.settlement.get_component(NamedComponent).name))
179
181 self._unload_all_resources(self.settlement_manager.settlement) 182 self.report_success('Unloaded the bought resources at {}'.format( 183 self.settlement_manager.settlement.get_component(NamedComponent).name))
184