diff --git a/src/ControlPanel.ts b/src/ControlPanel.ts
index eb72922..8ed02fe 100644
--- a/src/ControlPanel.ts
+++ b/src/ControlPanel.ts
@@ -1,4 +1,4 @@
-import { elClassId, getNumber, parseLocation, uniqId, waitForLoad } from './utils';
+import { parseLocation, uniqId, waitForLoad } from './utils';
import { Scheduler } from './Scheduler';
import { BuildingPageController } from './Page/BuildingPageController';
import { UpgradeBuildingTask } from './Task/UpgradeBuildingTask';
@@ -18,6 +18,7 @@ import { Resources } from './Core/Resources';
import { Village } from './Core/Village';
import { calcGatheringTimings } from './Core/GatheringTimings';
import { DataStorage } from './DataStorage';
+import { getBuildingPageAttributes, isBuildingPage } from './Page/PageDetectors';
import { debounce } from 'debounce';
interface QuickAction {
@@ -108,14 +109,8 @@ export class ControlPanel {
showBuildingSlotIds(buildingsInQueue);
}
- if (p.pathname === '/build.php') {
- const buildPage = new BuildingPageController(this.scheduler, {
- buildId: getNumber(p.query.id),
- buildTypeId: getNumber(elClassId(jQuery('#build').attr('class'), 'gid')),
- categoryId: getNumber(p.query.category, 1),
- sheetId: getNumber(p.query.s, 0),
- tabId: getNumber(p.query.t, 0),
- });
+ if (isBuildingPage()) {
+ const buildPage = new BuildingPageController(this.scheduler, getBuildingPageAttributes());
buildPage.run();
}
@@ -189,6 +184,7 @@ class VillageController {
public readonly warehouse;
public readonly granary;
public readonly buildRemainingSeconds;
+ public readonly incomingResources: Resources;
constructor(village: Village, state: VillageState, scheduler: Scheduler) {
const resources = state.getResources();
@@ -225,6 +221,7 @@ class VillageController {
this.warehouse = storage.warehouse;
this.granary = storage.granary;
this.buildRemainingSeconds = buildQueueInfo.seconds;
+ this.incomingResources = this.calcIncomingResources(state);
}
timeToRequired() {
@@ -243,4 +240,8 @@ class VillageController {
return timings.hours * 3600;
}
+
+ private calcIncomingResources(state: VillageState): Resources {
+ return state.getIncomingMerchants().reduce((m, i) => m.add(i.resources), new Resources(0, 0, 0, 0));
+ }
}
diff --git a/src/Core/Buildings.ts b/src/Core/Buildings.ts
new file mode 100644
index 0000000..64e08b4
--- /dev/null
+++ b/src/Core/Buildings.ts
@@ -0,0 +1,6 @@
+export const WAREHOUSE_ID = 10;
+export const GARNER_ID = 11;
+export const MARKET_ID = 17;
+export const QUARTERS_ID = 19;
+export const HORSE_STABLE_ID = 20;
+export const EMBASSY_ID = 25;
diff --git a/src/Core/Market.ts b/src/Core/Market.ts
new file mode 100644
index 0000000..bf80868
--- /dev/null
+++ b/src/Core/Market.ts
@@ -0,0 +1,10 @@
+import { Resources } from './Resources';
+
+export class IncomingMerchant {
+ readonly resources: Resources;
+ readonly ts: number;
+ constructor(resources: Resources, ts: number) {
+ this.resources = resources;
+ this.ts = ts;
+ }
+}
diff --git a/src/DashboardView/VillageStateList.vue b/src/DashboardView/VillageStateList.vue
index 2f627fb..2811865 100644
--- a/src/DashboardView/VillageStateList.vue
+++ b/src/DashboardView/VillageStateList.vue
@@ -85,6 +85,23 @@
|
|
+
+ Торговцы: |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+ |
+ |
+
|
@@ -97,6 +114,7 @@
>->{{ v.name }}
Казармы
+ Конюшни
|
@@ -109,6 +127,7 @@
import { path } from '../utils';
import ResourceBalance from './ResourceBalance';
import VillageResource from './VillageResource';
+import { HORSE_STABLE_ID, MARKET_ID, QUARTERS_ID, WAREHOUSE_ID } from '../Core/Buildings';
export default {
components: {
@@ -126,13 +145,22 @@ export default {
return path(name, args);
},
marketPath(fromVillage, toVillage) {
- return path('/build.php', { newdid: fromVillage.id, gid: 17, t: 5, x: toVillage.crd.x, y: toVillage.crd.y });
+ return path('/build.php', {
+ newdid: fromVillage.id,
+ gid: MARKET_ID,
+ t: 5,
+ x: toVillage.crd.x,
+ y: toVillage.crd.y,
+ });
},
warehousePath(village) {
- return path('/build.php', { newdid: village.id, gid: 10 });
+ return path('/build.php', { newdid: village.id, gid: WAREHOUSE_ID });
},
quartersPath(village) {
- return path('/build.php', { newdid: village.id, gid: 19 });
+ return path('/build.php', { newdid: village.id, gid: QUARTERS_ID });
+ },
+ horseStablePath(village) {
+ return path('/build.php', { newdid: village.id, gid: HORSE_STABLE_ID });
},
secondsToTime(value) {
if (value === 0) {
@@ -178,12 +206,9 @@ export default {
border: 1px solid #ddd;
}
-.performance-line td {
- padding: 0 4px 4px;
- font-size: 90%;
-}
-
-.required-line td {
+.performance-line td,
+.required-line td,
+.incoming-line td {
padding: 0 4px 4px;
font-size: 90%;
}
diff --git a/src/DataStorage.ts b/src/DataStorage.ts
index 068346d..180e448 100644
--- a/src/DataStorage.ts
+++ b/src/DataStorage.ts
@@ -1,4 +1,5 @@
import { ConsoleLogger, Logger, NullLogger } from './Logger';
+import { Resources } from './Core/Resources';
const NAMESPACE = 'travian:v1';
@@ -8,6 +9,36 @@ function join(...parts: Array) {
return parts.map(p => p.replace(/[:]+$/g, '').replace(/^[:]+/g, '')).join(':');
}
+interface EmptyObjectFactory {
+ (): T;
+}
+
+interface ObjectMapper {
+ (item: any): T;
+}
+
+interface ObjectMapperOptions {
+ factory?: EmptyObjectFactory;
+ mapper?: ObjectMapper;
+}
+
+function createMapper(options: ObjectMapperOptions): ObjectMapper {
+ const { mapper, factory } = options;
+
+ if (mapper) {
+ return mapper;
+ }
+
+ if (factory) {
+ return plain => {
+ let item = factory();
+ return Object.assign(item, plain) as T;
+ };
+ }
+
+ throw new Error('Factory or mapper must be specified');
+}
+
export class DataStorage {
private readonly logger: Logger;
private readonly name: string;
@@ -40,6 +71,21 @@ export class DataStorage {
}
}
+ getTyped(key: string, options: ObjectMapperOptions = {}): T {
+ let plain = this.get(key);
+ const mapper = createMapper(options);
+ return mapper(plain);
+ }
+
+ getTypedList(key: string, options: ObjectMapperOptions = {}): Array {
+ let plain = this.get(key);
+ if (!Array.isArray(plain)) {
+ return [];
+ }
+ const mapper = createMapper(options);
+ return (plain as Array).map(mapper);
+ }
+
has(key: string): boolean {
const fullKey = join(NAMESPACE, this.name, key);
return storage.getItem(fullKey) !== null;
diff --git a/src/Grabber/GrabberManager.ts b/src/Grabber/GrabberManager.ts
index e51061a..dbaceb3 100644
--- a/src/Grabber/GrabberManager.ts
+++ b/src/Grabber/GrabberManager.ts
@@ -2,6 +2,7 @@ import { Grabber } from './Grabber';
import { VillageResourceGrabber } from './VillageResourceGrabber';
import { VillageOverviewPageGrabber } from './VillageOverviewPageGrabber';
import { HeroPageGrabber } from './HeroPageGrabber';
+import { MarketPageGrabber } from './MarketPageGrabber';
export class GrabberManager {
private readonly grabbers: Array = [];
@@ -11,6 +12,7 @@ export class GrabberManager {
this.grabbers.push(new VillageResourceGrabber());
this.grabbers.push(new VillageOverviewPageGrabber());
this.grabbers.push(new HeroPageGrabber());
+ this.grabbers.push(new MarketPageGrabber());
}
grab() {
diff --git a/src/Grabber/HeroPageGrabber.ts b/src/Grabber/HeroPageGrabber.ts
index fc056f9..b7f9f9d 100644
--- a/src/Grabber/HeroPageGrabber.ts
+++ b/src/Grabber/HeroPageGrabber.ts
@@ -1,21 +1,12 @@
import { Grabber } from './Grabber';
-import {
- grabActiveVillageId,
- grabBuildingQueueInfo,
- grabResourcesPerformance,
- grabVillageList,
-} from '../Page/VillageBlock';
-import { VillageState } from '../State/VillageState';
-import { parseLocation } from '../utils';
-import { GrabError } from '../Errors';
-import { BuildingQueueInfo } from '../Game';
+import { grabVillageList } from '../Page/VillageBlock';
import { HeroState } from '../State/HeroState';
import { grabHeroAttributes, grabHeroVillage } from '../Page/HeroPage';
+import { isHeroPage } from '../Page/PageDetectors';
export class HeroPageGrabber extends Grabber {
grab(): void {
- const p = parseLocation();
- if (p.pathname !== '/hero.php') {
+ if (!isHeroPage()) {
return;
}
diff --git a/src/Grabber/MarketPageGrabber.ts b/src/Grabber/MarketPageGrabber.ts
new file mode 100644
index 0000000..4eeaa87
--- /dev/null
+++ b/src/Grabber/MarketPageGrabber.ts
@@ -0,0 +1,17 @@
+import { Grabber } from './Grabber';
+import { grabActiveVillageId } from '../Page/VillageBlock';
+import { VillageState } from '../State/VillageState';
+import { grabIncomingMerchants } from '../Page/BuildingPage';
+import { isMarketSendResourcesPage } from '../Page/PageDetectors';
+
+export class MarketPageGrabber extends Grabber {
+ grab(): void {
+ if (!isMarketSendResourcesPage()) {
+ return;
+ }
+
+ const villageId = grabActiveVillageId();
+ const state = new VillageState(villageId);
+ state.storeIncomingMerchants(grabIncomingMerchants());
+ }
+}
diff --git a/src/Page/BuildingPage.ts b/src/Page/BuildingPage.ts
index 62271f1..ccb106d 100644
--- a/src/Page/BuildingPage.ts
+++ b/src/Page/BuildingPage.ts
@@ -2,6 +2,7 @@ import { GrabError } from '../Errors';
import { elClassId, getNumber, trimPrefix, uniqId } from '../utils';
import { Resources } from '../Core/Resources';
import { Coordinates } from '../Core/Village';
+import { IncomingMerchant } from '../Core/Market';
export function clickBuildButton(typeId: number) {
const section = jQuery(`#contract_building${typeId}`);
@@ -140,3 +141,18 @@ export function fillSendResourcesForm(resources: Resources, crd: Coordinates) {
export function clickSendButton() {
jQuery('#enabledButton').trigger('click');
}
+
+export function grabIncomingMerchants(): ReadonlyArray {
+ const result: Array = [];
+ const root = jQuery('#merchantsOnTheWay .ownMerchants');
+ root.find('.traders').each((idx, el) => {
+ const $el = jQuery(el);
+ result.push(
+ new IncomingMerchant(
+ grabResourcesFromList($el.find('.resourceWrapper .resources')),
+ getNumber($el.find('.timer').attr('value'))
+ )
+ );
+ });
+ return result;
+}
diff --git a/src/Page/BuildingPageController.ts b/src/Page/BuildingPageController.ts
index 76cec3f..2e23288 100644
--- a/src/Page/BuildingPageController.ts
+++ b/src/Page/BuildingPageController.ts
@@ -9,24 +9,14 @@ import {
createSendResourcesButton,
createTrainTroopButtons,
createUpgradeButton,
+ grabIncomingMerchants,
} from './BuildingPage';
import { BuildBuildingTask } from '../Task/BuildBuildingTask';
import { Resources } from '../Core/Resources';
import { Coordinates } from '../Core/Village';
import { SendResourcesTask } from '../Task/SendResourcesTask';
-
-const MARKET_ID = 17;
-const QUARTERS_ID = 19;
-const HORSE_STABLE_ID = 20;
-const EMBASSY_ID = 25;
-
-export interface BuildingPageAttributes {
- buildId: number;
- buildTypeId: number;
- categoryId: number;
- sheetId: number;
- tabId: number;
-}
+import { EMBASSY_ID, HORSE_STABLE_ID, QUARTERS_ID } from '../Core/Buildings';
+import { BuildingPageAttributes, isMarketSendResourcesPage } from './PageDetectors';
export class BuildingPageController {
private scheduler: Scheduler;
@@ -40,7 +30,7 @@ export class BuildingPageController {
}
run() {
- const { buildTypeId, sheetId, tabId } = this.attributes;
+ const { buildTypeId, sheetId } = this.attributes;
this.logger.log('BUILD PAGE DETECTED', 'ID', this.attributes.buildId, this.attributes);
if (buildTypeId) {
@@ -61,7 +51,8 @@ export class BuildingPageController {
createTrainTroopButtons((troopId, res, count) => this.onScheduleTrainTroopers(troopId, res, count));
}
- if (buildTypeId === MARKET_ID && tabId === 5) {
+ if (isMarketSendResourcesPage()) {
+ console.log('MERCH', grabIncomingMerchants());
createSendResourcesButton((res, crd) => this.onSendResources(res, crd));
}
}
diff --git a/src/Page/PageDetectors.ts b/src/Page/PageDetectors.ts
new file mode 100644
index 0000000..cbb7120
--- /dev/null
+++ b/src/Page/PageDetectors.ts
@@ -0,0 +1,42 @@
+import { elClassId, getNumber, parseLocation } from '../utils';
+import { MARKET_ID } from '../Core/Buildings';
+
+export interface BuildingPageAttributes {
+ buildId: number;
+ buildTypeId: number;
+ categoryId: number;
+ sheetId: number;
+ tabId: number;
+}
+
+export function isBuildingPage() {
+ const p = parseLocation();
+ return p.pathname === '/build.php';
+}
+
+export function isHeroPage() {
+ const p = parseLocation();
+ return p.pathname === '/hero.php';
+}
+
+export function getBuildingPageAttributes(): BuildingPageAttributes {
+ if (!isBuildingPage()) {
+ throw Error('Not building page');
+ }
+ const p = parseLocation();
+ return {
+ buildId: getNumber(p.query.id),
+ buildTypeId: getNumber(elClassId(jQuery('#build').attr('class'), 'gid')),
+ categoryId: getNumber(p.query.category, 1),
+ sheetId: getNumber(p.query.s, 0),
+ tabId: getNumber(p.query.t, 0),
+ };
+}
+
+export function isMarketSendResourcesPage(): boolean {
+ if (!isBuildingPage()) {
+ return false;
+ }
+ const { buildTypeId, tabId } = getBuildingPageAttributes();
+ return buildTypeId === MARKET_ID && tabId === 5;
+}
diff --git a/src/State/VillageState.ts b/src/State/VillageState.ts
index 6338c59..04ae939 100644
--- a/src/State/VillageState.ts
+++ b/src/State/VillageState.ts
@@ -1,12 +1,18 @@
import { DataStorage } from '../DataStorage';
import { BuildingQueueInfo } from '../Game';
-import { Resources } from '../Core/Resources';
+import { Resources, ResourcesInterface } from '../Core/Resources';
import { ResourceStorage } from '../Core/ResourceStorage';
+import { IncomingMerchant } from '../Core/Market';
const RESOURCES_KEY = 'res';
const CAPACITY_KEY = 'cap';
const PERFORMANCE_KEY = 'perf';
const BUILDING_QUEUE_KEY = 'bq';
+const INCOMING_MERCHANTS_KEY = 'im';
+
+const ResourceOptions = {
+ factory: () => new Resources(0, 0, 0, 0),
+};
export class VillageState {
private storage: DataStorage;
@@ -19,9 +25,7 @@ export class VillageState {
}
getResources(): Resources {
- let plain = this.storage.get(RESOURCES_KEY);
- let res = new Resources(0, 0, 0, 0);
- return Object.assign(res, plain) as Resources;
+ return this.storage.getTyped(RESOURCES_KEY, ResourceOptions);
}
storeResourceStorage(storage: ResourceStorage) {
@@ -39,9 +43,7 @@ export class VillageState {
}
getResourcesPerformance(): Resources {
- let plain = this.storage.get(PERFORMANCE_KEY);
- let res = new Resources(0, 0, 0, 0);
- return Object.assign(res, plain) as Resources;
+ return this.storage.getTyped(PERFORMANCE_KEY, ResourceOptions);
}
storeBuildingQueueInfo(info: BuildingQueueInfo): void {
@@ -53,4 +55,23 @@ export class VillageState {
let res = new BuildingQueueInfo(0);
return Object.assign(res, plain) as BuildingQueueInfo;
}
+
+ storeIncomingMerchants(merchants: ReadonlyArray): void {
+ this.storage.set(
+ INCOMING_MERCHANTS_KEY,
+ merchants.map(m => ({ ...m.resources, ts: m.ts }))
+ );
+ }
+
+ getIncomingMerchants(): ReadonlyArray {
+ const objects = this.storage.getTypedList