from nullptr.store import store from nullptr.models.waypoint import Waypoint from nullptr.models.contract import Contract from nullptr.models.survey import Survey from nullptr.models.ship import Ship from time import time import logging from util import * class MissionParam: def __init__(self, cls, required=True, default=None): self.cls = cls self.required = required self.default = default def parse(self, val): if self.cls == str: return str(val) elif self.cls == int: return int(val) elif issubclass(self.cls, StoreObject): data = store.get(self.cls, val) if data is None: raise ValueError('object not found') return data else: raise ValueError('unknown param typr') class Mission: ship: Ship next_step: int = 0 @classmethod def params(cls): return { } def __init__(self, ship, api): self.ship = ship self.api = api def sts(self, nm, v): self.ship.mission_state[nm] = v 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: self.ship.mission_status = nw 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): logging.info(f'mission finished for {self.ship}') def is_waiting(self): return self.next_step > time() 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: logging.warning(f"Invalid mission status {status}") self.status('error') return handler, next_step = steps[status] try: result = handler() except Exception as e: logging.error(e) self.status('error') return if type(next_step) == str: self.status(next_step) elif type(next_step) == dict: if result not in next_step: logging.warning(f'Invalid step result {result}') self.status('error') return else: self.status(next_step[result]) print(f'{self.ship} {status} -> {self.status()}') class MiningMission(Mission): @classmethod def params(cls): return { 'site': MissionParam(Waypoint, True), 'resource': MissionParam(str, True), 'destination': MissionParam(Waypoint, True), 'delivery': MissionParam(str, True, 'deliver'), 'contract': MissionParam(Contract, False) } def start_state(self): return 'go_site' def steps(self): return { 'extract': (self.step_extract, { 'done': 'dock', 'more': 'extract' }), 'dock': (self.step_dock, 'sell'), 'sell': (self.step_sell, { 'more': 'sell', 'done': 'orbit', }), 'orbit': (self.step_orbit, 'jettison'), 'jettison': (self.step_dispose, { 'more': 'jettison', 'done': 'extract', 'full': 'go_dest' }), 'go_dest': (self.step_go_dest, 'dock_dest'), 'dock_dest': (self.step_dock, 'unload'), 'unload': (self.step_unload, { 'done': 'refuel', 'more': 'unload' }), 'refuel': (self.step_refuel, 'orbit_dest'), 'orbit_dest': (self.step_orbit, 'go_site'), 'go_site': (self.step_go_site, 'extract') } def get_survey(self): resource = self.st('resource') site = self.st('site') # todo optimize for s in store.all(Survey): if resource in s.deposits and site == s.waypoint: return s return None def step_extract(self): survey = self.get_survey() print('using survey:', str(survey)) result = self.api.extract(self.ship, survey) symbol = sg(result,'extraction.yield.symbol') units = sg(result,'extraction.yield.units') print('extracted:', units, symbol) self.next_step = self.ship.cooldown if self.ship.cargo_units < self.ship.cargo_capacity: return 'more' else: return 'done' def step_sell(self, except_resource=True): target = self.st('resource') market = self.api.market(self.ship.location) sellables = market.sellable_items(self.ship.cargo.keys()) if target in sellables and except_resource: sellables.remove(target) if len(sellables) == 0: return 'done' self.api.sell(self.ship, sellables[0]) if len(sellables) == 1: return 'done' else: return 'more' def step_go_dest(self): destination = self.st('destination') if self.ship.location == destination: return self.api.navigate(self.ship, destination) self.next_step = self.ship.arrival def step_dock(self): self.api.dock(self.ship) def step_unload(self): contract = self.st('contract') delivery = self.st('delivery') if delivery == 'sell': return self.step_sell(False) 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' def step_refuel(self): self.api.refuel(self.ship) def step_dispose(self): contract = self.st('contract') typs = self.ship.nondeliverable_cargo(contract) if len(typs) > 0: self.api.jettison(self.ship, typs[0]) if len(typs) > 1: return 'more' elif self.ship.cargo_units > self.ship.cargo_capacity - 3: return 'full' else: return 'done' def step_orbit(self): self.api.orbit(self.ship) def step_go_site(self): site = self.st('site') if self.ship.location == site: return self.api.navigate(self.ship, site) self.next_step = self.ship.arrival class SurveyMission(Mission): def start_state(self): return 'survey' def steps(self): return { 'survey': (self.step_survey, 'survey') } def step_survey(self): result = self.api.survey(self.ship) #pprint(result, 2) self.next_step = self.ship.cooldown def create_mission(mtype, ship, api): types = { 'survey': SurveyMission, 'mine': MiningMission } if mtype not in types: logging.warning(f'invalid mission type {mtype}') return m = types[mtype](ship, api) return m