0ptr/nullptr/missions/base.py

327 lines
8.6 KiB
Python
Raw Normal View History

from nullptr.store import Store
from nullptr.models.base import Base
2023-06-17 18:18:14 +00:00
from nullptr.models.waypoint import Waypoint
from nullptr.models.contract import Contract
from nullptr.models.system import System
2023-06-17 18:18:14 +00:00
from nullptr.models.survey import Survey
from nullptr.models.ship import Ship
from nullptr.analyzer import Analyzer
2023-06-17 18:18:14 +00:00
from time import time
2023-06-22 12:56:51 +00:00
from functools import partial
2023-06-17 18:18:14 +00:00
import logging
from nullptr.util import *
2023-06-17 18:18:14 +00:00
class MissionError(Exception):
pass
2023-06-17 18:18:14 +00:00
class MissionParam:
def __init__(self, cls, required=True, default=None):
self.cls = cls
self.required = required
self.default = default
def parse(self, val, store):
2023-06-17 18:18:14 +00:00
if self.cls == str:
return str(val)
elif self.cls == int:
return int(val)
elif self.cls == list:
2023-07-12 20:36:29 +00:00
if type(val) == str:
return [i.strip() for i in val.split(',')]
return val
elif issubclass(self.cls, Base):
if type(val) == str:
data = store.get(self.cls, val)
else:
data = val
2023-06-17 18:18:14 +00:00
if data is None:
raise ValueError('object not found')
return data.symbol
2023-06-17 18:18:14 +00:00
else:
raise ValueError('unknown param typr')
class Mission:
@classmethod
def params(cls):
return {
}
def __init__(self, ship, store, api):
2023-06-17 18:18:14 +00:00
self.ship = ship
self.store = store
2023-06-17 18:18:14 +00:00
self.api = api
2024-01-20 19:33:50 +00:00
self.wait_for = None
self.next_step = 0
self.analyzer = Analyzer(self.store)
2024-01-20 19:33:50 +00:00
self.setup()
2023-06-17 18:18:14 +00:00
2024-01-20 19:33:50 +00:00
def setup(self):
pass
2023-06-17 18:18:14 +00:00
def sts(self, nm, v):
if issubclass(type(v), Base):
v = v.symbol
self.ship.set_mission_state(nm, v)
2023-06-17 18:18:14 +00:00
def rst(self, typ, nm):
symbol = self.st(nm)
2024-01-15 18:39:08 +00:00
if symbol is None:
return None
return self.store.get(typ, symbol)
2023-06-17 18:18:14 +00:00
def st(self, nm):
if not nm in self.ship.mission_state:
return None
return self.ship.mission_state[nm]
def status(self, nw=None):
if nw is None:
return self.ship.mission_status
else:
2024-01-20 19:33:50 +00:00
steps = self.steps()
if nw in ['init','done', 'error']:
self.ship.mission_status = nw
return
elif nw not in steps:
self.ship.log(f"Invalid mission status {nw}", 1)
self.ship.mission_status = 'error'
return
wait_for = steps[nw][2] if len(steps[nw]) > 2 else None
self.wait_for = wait_for
self.ship.mission_status = nw
2023-06-17 18:18:14 +00:00
def start_state(self):
return 'done'
def error(self, msg):
self.status('error')
print(msg)
def init_state(self):
for name, param in self.params().items():
if param.required and param.default is None:
if not name in self.ship.mission_state:
return self.error(f'Param {name} not set')
self.status(self.start_state())
def steps(self):
return {
}
def step_done(self):
2024-01-24 18:03:57 +00:00
self.ship.log(f'mission {type(self).__name__} finished with balance {self.balance()}', 3)
2023-06-17 18:18:14 +00:00
def is_waiting(self):
2024-01-20 19:33:50 +00:00
if self.next_step > time() or self.ship.cooldown > time() or self.ship.arrival > time():
return True
if self.wait_for is not None:
if self.wait_for():
self.wait_for = None
return False
else:
return True
return False
2023-06-17 18:18:14 +00:00
def is_finished(self):
return self.status() in ['done','error']
def is_ready(self):
return not self.is_waiting() and not self.is_finished()
def step(self):
steps = self.steps()
if self.status() == 'init':
self.init_state()
status = self.status()
if not status in steps:
2024-01-16 18:13:10 +00:00
self.ship.log(f"Invalid mission status {status}", 1)
2023-06-17 18:18:14 +00:00
self.status('error')
return
2024-01-20 19:33:50 +00:00
handler = steps[status][0]
next_step = steps[status][1]
2023-06-17 18:18:14 +00:00
try:
result = handler()
except Exception as e:
logging.error(e, exc_info=True)
2023-06-17 18:18:14 +00:00
self.status('error')
return
if type(next_step) == str:
self.status(next_step)
elif type(next_step) == dict:
if result not in next_step:
2024-01-16 18:13:10 +00:00
self.ship.log(f'Invalid step result {result}', 1)
2023-06-17 18:18:14 +00:00
self.status('error')
return
else:
2024-01-16 18:13:10 +00:00
if result is None: result=''
2023-06-17 18:18:14 +00:00
self.status(next_step[result])
2024-01-16 18:13:10 +00:00
self.ship.log(f'{status} {result} -> {self.status()}', 8)
2023-06-17 18:18:14 +00:00
class BaseMission(Mission):
2024-01-20 19:33:50 +00:00
def balance(self, amt=0):
if type(amt) == dict:
amt = self.api.transaction_cost(amt)
balance = self.st('balance')
if balance is None: balance = 0
balance += amt
self.sts('balance', balance)
return balance
def step_go_dest(self):
destination = self.rst(Waypoint, 'destination')
if self.ship.location() == destination:
return
self.api.navigate(self.ship, destination)
self.next_step = self.ship.arrival
def step_go_site(self):
site = self.rst(Waypoint,'site')
if self.ship.location() == site:
return
self.api.navigate(self.ship, site)
self.next_step = self.ship.arrival
2024-01-04 20:34:31 +00:00
def step_market(self):
loc = self.ship.location
self.api.marketplace(loc)
def step_unload(self):
delivery = self.st('delivery')
if delivery == 'sell':
return self.step_sell(False)
2024-01-02 05:35:26 +00:00
contract = self.rst(Contract, 'contract')
typs = self.ship.deliverable_cargo(contract)
if len(typs) == 0:
return 'done'
self.api.deliver(self.ship, typs[0], contract)
if len(typs) == 1:
return 'done'
else:
return 'more'
2023-06-17 18:18:14 +00:00
def step_sell(self, except_resource=True):
market = self.store.get('Marketplace', self.ship.location.symbol)
sellables = market.sellable_items(self.ship.cargo.keys())
if len(sellables) == 0:
return 'done'
2024-01-06 06:17:53 +00:00
resource = sellables[0]
volume = market.volume(resource)
2024-01-16 18:13:10 +00:00
amt_cargo = self.ship.get_cargo(resource)
amount = min(amt_cargo, volume)
2024-01-20 19:33:50 +00:00
res = self.api.sell(self.ship, resource, amount)
self.balance(res)
2024-01-16 18:13:10 +00:00
if len(sellables) == 1 and amt_cargo == amount:
return 'done'
else:
return 'more'
2023-06-22 12:56:51 +00:00
def step_travel(self):
traject = self.st('traject')
2023-06-22 12:56:51 +00:00
if traject is None or traject == []:
2024-01-06 06:17:53 +00:00
return
dest = traject[-1]
loc = self.ship.location
2024-01-06 06:17:53 +00:00
hop = traject.pop(0)
2023-07-12 20:26:25 +00:00
if type(hop) == Waypoint:
self.api.navigate(self.ship, hop)
self.next_step = self.ship.arrival
else:
self.api.jump(self.ship, hop)
self.next_step = self.ship.cooldown
self.sts('traject', traject)
2024-01-06 06:17:53 +00:00
def step_navigate_traject(self):
traject = self.st('traject')
loc = self.ship.location
if traject is None or traject == []:
return 'done'
dest =traject[-1]
if dest == loc:
return 'done'
return 'more'
2024-01-06 06:17:53 +00:00
2023-06-22 12:56:51 +00:00
def step_calculate_traject(self, dest):
if type(dest) == str:
dest = self.store.get(Waypoint, dest)
loc = self.ship.location
loc_sys = loc.system
2024-01-02 05:35:26 +00:00
loc_jg = self.analyzer.get_jumpgate(loc_sys)
2023-07-12 20:26:25 +00:00
loc_jg_wp = self.store.get(Waypoint, loc_jg.symbol)
dest_sys = dest.system
dest_jg = self.analyzer.get_jumpgate(dest_sys)
2023-06-26 03:48:19 +00:00
if dest_sys == loc_sys:
2024-01-06 06:17:53 +00:00
result = self.analyzer.find_nav_path(loc, dest, self.ship.range())
2023-06-26 03:48:19 +00:00
self.sts('traject', result)
2024-01-20 19:33:50 +00:00
return 'done' if len(result) == 0 else 'more'
path = self.analyzer.find_path(loc_sys, dest_sys)
result = []
if loc.symbol != loc_jg.symbol:
2023-07-12 20:26:25 +00:00
result.append(loc_jg_wp)
result += [s for s in path[1:]]
if dest_jg.symbol != dest.symbol:
result.append(dest)
2023-06-22 12:56:51 +00:00
self.sts('traject', result)
print(result)
2024-01-20 19:33:50 +00:00
return 'more'
def step_dock(self):
2024-01-06 06:17:53 +00:00
if self.ship.status == 'DOCKED':
return
self.api.dock(self.ship)
def step_refuel(self):
2023-06-26 18:50:29 +00:00
if self.ship.fuel_capacity == 0:
return
2024-01-06 06:17:53 +00:00
#if self.ship.fuel_capacity - self.ship.fuel_current > 100:
try:
self.api.refuel(self.ship)
except Exception as e:
pass
def step_orbit(self):
2024-01-06 06:17:53 +00:00
if self.ship.status != 'DOCKED':
return
self.api.orbit(self.ship)
2023-06-22 12:56:51 +00:00
def travel_steps(self, nm, destination, next_step):
destination = self.st(destination)
calc = partial(self.step_calculate_traject, destination)
2024-01-06 06:17:53 +00:00
steps = {
2024-01-20 19:33:50 +00:00
f'travel-{nm}': (calc, {
'more': f'dock-{nm}',
'done': next_step
}),
2023-06-22 12:56:51 +00:00
f'dock-{nm}': (self.step_dock, f'refuel-{nm}'),
2024-01-06 06:17:53 +00:00
f'refuel-{nm}': (self.step_refuel, f'orbit-{nm}'),
f'orbit-{nm}': (self.step_orbit, f'go-{nm}'),
f'go-{nm}': (self.step_travel, f'nav-{nm}'),
f'nav-{nm}': (self.step_navigate_traject, {
'done': next_step,
'more': f'dock-{nm}'
})
}
if self.ship.fuel_capacity == 0:
steps = {
f'travel-{nm}': (calc, f'go-{nm}'),
f'go-{nm}': (self.step_travel, f'nav-{nm}'),
f'nav-{nm}': (self.step_navigate_traject, {
'done': next_step,
'more': f'go-{nm}'
}),
2023-06-22 12:56:51 +00:00
}
2024-01-06 06:17:53 +00:00
return steps