@shipload/sdk 1.0.0-next.20 → 1.0.0-next.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/shipload.m.js CHANGED
@@ -10722,6 +10722,69 @@ class ConstructionManager extends BaseManager {
10722
10722
  }
10723
10723
  return out.sort((a, b) => a.estimatedDuration.value - b.estimatedDuration.value);
10724
10724
  }
10725
+ inboundTransfersTo(plotId, entities, now) {
10726
+ return this.inboundTransfersByTarget(entities, now).get(plotId.toString()) ?? [];
10727
+ }
10728
+ inboundTransfersByTarget(entities, now) {
10729
+ const buckets = new Map();
10730
+ const nowMs = now.getTime();
10731
+ for (const entity of entities) {
10732
+ const schedule = entity.schedule;
10733
+ if (!schedule)
10734
+ continue;
10735
+ const entityIdStr = entity.id.toString();
10736
+ const sourceName = entity.entity_name || entityIdStr;
10737
+ const startedMs = schedule.started.toDate().getTime();
10738
+ let cumulativeSec = 0;
10739
+ for (const task of schedule.tasks) {
10740
+ cumulativeSec += task.duration.toNumber();
10741
+ if (!isTransferTask(task))
10742
+ continue;
10743
+ if (!task.entitytarget)
10744
+ continue;
10745
+ const targetIdStr = task.entitytarget.entity_id.toString();
10746
+ const etaSeconds = Math.max(0, Math.round((startedMs + cumulativeSec * 1000 - nowMs) / 1000));
10747
+ let perTarget = buckets.get(targetIdStr);
10748
+ if (!perTarget) {
10749
+ perTarget = new Map();
10750
+ buckets.set(targetIdStr, perTarget);
10751
+ }
10752
+ for (const c of task.cargo) {
10753
+ const itemId = c.item_id.toNumber();
10754
+ const quantity = c.quantity.toNumber();
10755
+ if (quantity === 0)
10756
+ continue;
10757
+ const key = `${entityIdStr}#${itemId}`;
10758
+ const existing = perTarget.get(key);
10759
+ if (existing) {
10760
+ existing.quantity += quantity;
10761
+ existing.etaSeconds = Math.min(existing.etaSeconds, etaSeconds);
10762
+ }
10763
+ else {
10764
+ perTarget.set(key, {
10765
+ sourceEntityId: entity.id,
10766
+ sourceEntityType: entity.type,
10767
+ sourceName,
10768
+ itemId,
10769
+ quantity,
10770
+ etaSeconds,
10771
+ });
10772
+ }
10773
+ }
10774
+ }
10775
+ }
10776
+ const out = new Map();
10777
+ for (const [targetId, perTarget] of buckets) {
10778
+ out.set(targetId, Array.from(perTarget.values()));
10779
+ }
10780
+ return out;
10781
+ }
10782
+ reservationsFrom(sourceEntityId, entities) {
10783
+ const source = entities.find((e) => e.id.equals(sourceEntityId));
10784
+ if (!source)
10785
+ return [];
10786
+ return reservationsOf(source);
10787
+ }
10725
10788
  estimateFinalizeDuration(target, crafterSpeed) {
10726
10789
  return calc_craft_duration(crafterSpeed, target.progress.massRequired);
10727
10790
  }
@@ -10732,26 +10795,49 @@ class ConstructionManager extends BaseManager {
10732
10795
  function coordsEqual(a, b) {
10733
10796
  return a.x.equals(b.x) && a.y.equals(b.y);
10734
10797
  }
10735
- function matchRelevantCargo(entity, target, cargo) {
10736
- const quantityByItemId = new Map();
10798
+ function moduleKey(module) {
10799
+ const installed = module.installed;
10800
+ if (!installed)
10801
+ return `${module.type.toNumber()}:empty`;
10802
+ return `${module.type.toNumber()}:${installed.item_id.toNumber()}:${installed.stats.toString()}`;
10803
+ }
10804
+ function sourceStackKey(cargo) {
10805
+ return `${cargo.item_id.toNumber()}#${cargo.stats.toString()}#${(cargo.modules ?? [])
10806
+ .map(moduleKey)
10807
+ .join(',')}`;
10808
+ }
10809
+ function matchRelevantCargo(entity, target, cargo, reservedByItem) {
10810
+ const needsByItemId = new Map(target.progress.rows.filter((row) => row.missing > 0).map((row) => [row.itemId, row]));
10811
+ const remainingReserved = new Map(reservedByItem);
10812
+ const out = [];
10737
10813
  for (const c of cargo) {
10738
10814
  if (!c.entity_id.equals(entity.id))
10739
10815
  continue;
10740
- const id = c.item_id.toNumber();
10741
- quantityByItemId.set(id, (quantityByItemId.get(id) ?? 0) + c.quantity.toNumber());
10742
- }
10743
- const out = [];
10744
- for (const row of target.progress.rows) {
10745
- if (row.missing === 0)
10816
+ const itemId = c.item_id.toNumber();
10817
+ const need = needsByItemId.get(itemId);
10818
+ if (!need)
10746
10819
  continue;
10747
- const available = quantityByItemId.get(row.itemId) ?? 0;
10820
+ const gross = c.quantity.toNumber();
10821
+ if (gross === 0)
10822
+ continue;
10823
+ const reservedRemaining = remainingReserved.get(itemId) ?? 0;
10824
+ const reserved = Math.min(gross, reservedRemaining);
10825
+ const available = gross - reserved;
10826
+ if (reserved > 0) {
10827
+ remainingReserved.set(itemId, reservedRemaining - reserved);
10828
+ }
10748
10829
  if (available === 0)
10749
10830
  continue;
10750
10831
  out.push({
10751
- itemId: row.itemId,
10752
- item: getItem(row.itemId),
10832
+ key: sourceStackKey(c),
10833
+ rowId: c.id,
10834
+ itemId,
10835
+ item: getItem(itemId),
10836
+ stats: c.stats,
10837
+ modules: c.modules ?? [],
10753
10838
  available,
10754
- plotNeeds: row.missing,
10839
+ plotNeeds: need.missing,
10840
+ reserved,
10755
10841
  });
10756
10842
  }
10757
10843
  return out;
@@ -10766,7 +10852,8 @@ function partitionSources(target, entities, cargo) {
10766
10852
  continue;
10767
10853
  if (!coordsEqual(entity.coordinates, target.coordinates))
10768
10854
  continue;
10769
- const relevant = matchRelevantCargo(entity, target, cargo);
10855
+ const reserved = reservedByItemFor(entity);
10856
+ const relevant = matchRelevantCargo(entity, target, cargo, reserved);
10770
10857
  if (relevant.length === 0)
10771
10858
  continue;
10772
10859
  const loaderCount = entity.loaders?.quantity.toNumber() ?? 0;
@@ -10786,6 +10873,50 @@ function partitionSources(target, entities, cargo) {
10786
10873
  }
10787
10874
  return { eligible, unreachable };
10788
10875
  }
10876
+ function isTransferTask(task) {
10877
+ const type = task.type.toNumber();
10878
+ return type === TaskType.LOAD || type === TaskType.UNLOAD;
10879
+ }
10880
+ function reservationsOf(source) {
10881
+ if (!source.schedule)
10882
+ return [];
10883
+ const out = new Map();
10884
+ for (const task of source.schedule.tasks) {
10885
+ if (!isTransferTask(task))
10886
+ continue;
10887
+ if (!task.entitytarget)
10888
+ continue;
10889
+ const targetType = task.entitytarget.entity_type;
10890
+ const targetId = task.entitytarget.entity_id;
10891
+ for (const c of task.cargo) {
10892
+ const itemId = c.item_id.toNumber();
10893
+ const quantity = c.quantity.toNumber();
10894
+ if (quantity === 0)
10895
+ continue;
10896
+ const key = `${targetId.toString()}#${itemId}`;
10897
+ const existing = out.get(key);
10898
+ if (existing) {
10899
+ existing.quantity += quantity;
10900
+ }
10901
+ else {
10902
+ out.set(key, {
10903
+ targetEntityId: targetId,
10904
+ targetEntityType: targetType,
10905
+ itemId,
10906
+ quantity,
10907
+ });
10908
+ }
10909
+ }
10910
+ }
10911
+ return Array.from(out.values());
10912
+ }
10913
+ function reservedByItemFor(source) {
10914
+ const out = new Map();
10915
+ for (const r of reservationsOf(source)) {
10916
+ out.set(r.itemId, (out.get(r.itemId) ?? 0) + r.quantity);
10917
+ }
10918
+ return out;
10919
+ }
10789
10920
 
10790
10921
  function totalCargoMass(cargo) {
10791
10922
  return cargo.reduce((sum, c) => {