Skip to content

Commit 0e629f5

Browse files
committed
Added support to scout all villages of a player
1 parent 49d9361 commit 0e629f5

File tree

12 files changed

+473
-0
lines changed

12 files changed

+473
-0
lines changed

TbsCore/Helpers/AttackHelper.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using TbsCore.Parsers;
5+
6+
namespace TbsCore.Helpers
7+
{
8+
public static class AttackHelper
9+
{
10+
public static Classificator.ReportType GetReportType(string iReport)
11+
{
12+
int num = (int)Parser.RemoveNonNumeric(iReport);
13+
return (Classificator.ReportType)num;
14+
}
15+
}
16+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using TbsCore.Helpers;
5+
using TbsCore.Models.ResourceModels;
6+
7+
namespace TbsCore.Models.AttackModels
8+
{
9+
/// <summary>
10+
/// Report of an attack
11+
/// </summary>
12+
public class AttackReport
13+
{
14+
// TODO: Attacker (nickname, id), Attacker Village (id, coords, name), Deffender (nickname, id), Deffending village (id, coords, name)
15+
// Attacker troops, killed, tribe, additional information (bounty / scouting report / ram&cata report)
16+
// Deffender List<troops, tribe, killed>
17+
18+
/// <summary>
19+
/// Resources raided / scouted
20+
/// </summary>
21+
public Resources Resources { get; set; }
22+
/// <summary>
23+
/// When scouting for resources, you see cranny size as well
24+
/// </summary>
25+
public int CrannySize { get; set; }
26+
/// <summary>
27+
/// When scouting for deffense, you see residence/palace and wall level as well
28+
/// </summary>
29+
///
30+
31+
public int ResidenceLevel {get;set;}
32+
public int WallLevel { get; set; }
33+
34+
/// <summary>
35+
/// Calculates resources that can be raided
36+
/// </summary>
37+
public Resources GetRaidableRes()
38+
{
39+
long[] res = new long[4];
40+
var arr = this.Resources.ToArray();
41+
for (int i = 0; i < arr.Length; i++)
42+
{
43+
res[i] = arr[i] - this.CrannySize;
44+
if (res[i] < 0) res[i] = 0;
45+
}
46+
return ResourcesHelper.ArrayToResources(res);
47+
}
48+
}
49+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using TbsCore.Models.MapModels;
2+
3+
namespace TbsCore.Models
4+
{
5+
public class InactiveFarm
6+
{
7+
public Coordinates coord { get; set; }
8+
public int distance { get; set; }
9+
public string namePlayer { get; set; }
10+
public string nameAlly { get; set; }
11+
public string nameVill { get; set; }
12+
public int population { get; set; }
13+
}
14+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using TbsCore.Helpers;
5+
6+
namespace TbsCore.Models.AttackModels
7+
{
8+
/// <summary>
9+
/// Last 5 attacks on the village can be viewed at position_details.php
10+
/// </summary>
11+
public class VillageReport
12+
{
13+
/// <summary>
14+
/// reports.php?id={Id}
15+
/// </summary>
16+
public string Id { get; set; }
17+
/// <summary>
18+
/// Type of the report
19+
/// </summary>
20+
public Classificator.ReportType Type { get; set; }
21+
/// <summary>
22+
/// Eg. "Today 08:22"
23+
/// </summary>
24+
public string ReportTime { get; set; }
25+
}
26+
}

TbsCore/Models/World/AccServerData.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using TbsCore.Models.AttackModels;
5+
using TbsCore.Models.MapModels;
6+
7+
namespace TbsCore.Models.World
8+
{
9+
/// <summary>
10+
/// Currently each acc has it's own Server data. This should be shared accross accounts
11+
/// and should have it's own entry in db.sqlite (DbServer). We could also save map.sql
12+
/// to query inactive players
13+
/// </summary>
14+
public class AccServerData
15+
{
16+
public void Init()
17+
{
18+
FarmScoutReport = new List<AttackReport>();
19+
}
20+
21+
/// <summary>
22+
/// Latest scout report of farms
23+
/// </summary>
24+
public List<AttackReport> FarmScoutReport { get; set; }
25+
26+
}
27+
}

TbsCore/Models/World/TravianUser.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace TbsCore.Models.AttackModels
5+
{
6+
public class TravianUser
7+
{
8+
// Username
9+
// Rank
10+
// Tribe
11+
// Ally
12+
// Hero!! For checking if anything changed, to recognize fake attacks
13+
14+
public List<TravianVillage> Villages { get; set; }
15+
}
16+
17+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using TbsCore.Models.MapModels;
5+
6+
namespace TbsCore.Models.AttackModels
7+
{
8+
public class TravianVillage
9+
{
10+
// TODO Artifacts, Oasis
11+
public int Id { get; set; }
12+
public string Name { get; set; }
13+
public bool Capital { get; set; }
14+
public int Population { get; set; }
15+
public Coordinates Coordinates { get; set; }
16+
}
17+
}

TbsCore/Parsers/AttackParser.cs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using TbsCore.Helpers;
6+
using TbsCore.Models.AttackModels;
7+
using TbsCore.Models.ResourceModels;
8+
9+
namespace TbsCore.Parsers
10+
{
11+
public static class AttackParser
12+
{
13+
//map_details
14+
public static List<VillageReport> ParseVillageReports(HtmlAgilityPack.HtmlDocument html)
15+
{
16+
var ret = new List<VillageReport>();
17+
18+
var reports = html.GetElementbyId("troop_info").Descendants("tr");
19+
foreach(var report in reports)
20+
{
21+
22+
string reportType = report.Descendants("img")
23+
.First(x => x.HasClass("iReport"))
24+
.GetClasses()
25+
.First(x => x != "iReport");
26+
var a = report.Descendants("a").First();
27+
28+
ret.Add(new VillageReport
29+
{
30+
Type = AttackHelper.GetReportType(reportType),
31+
Id = a.GetAttributeValue("href", ""),
32+
ReportTime = a.InnerText
33+
});
34+
}
35+
return ret;
36+
}
37+
38+
internal static AttackReport ParseAttack(HtmlAgilityPack.HtmlDocument html)
39+
{
40+
var report = new AttackReport();
41+
var content = html.GetElementbyId("content");
42+
43+
// TODO parse: type of attack, attacker (nickname, id), Attacker Village (id, coords, name), Deffender (nickname, id), Deffending village (id, coords, name)
44+
// Attacker troops, killed, tribe, additional information (ram&cata report)
45+
// Deffender List<troops, tribe, killed>
46+
47+
var infos = content.Descendants("table").Where(x => x.HasClass("additionalInformation")).ToList();
48+
foreach (var info in infos)
49+
{
50+
var res = info.Descendants("div").FirstOrDefault(x => x.HasClass("res"));
51+
if (res != null && res.ChildNodes.Count == 4) report.Resources = ParseBountyRes(res);
52+
53+
var cranny = info.Descendants("i").FirstOrDefault(x => x.HasClass("g23Icon"));
54+
if (cranny != null) report.CrannySize = (int)Parser.RemoveNonNumeric(cranny.ParentNode.InnerText);
55+
}
56+
return report;
57+
}
58+
59+
private static Resources ParseBountyRes(HtmlAgilityPack.HtmlNode node)
60+
{
61+
var resNodes = node.ChildNodes;
62+
return new Resources
63+
{
64+
Wood = Parser.RemoveNonNumeric(resNodes[0].InnerText),
65+
Clay = Parser.RemoveNonNumeric(resNodes[1].InnerText),
66+
Iron = Parser.RemoveNonNumeric(resNodes[2].InnerText),
67+
Crop = Parser.RemoveNonNumeric(resNodes[3].InnerText)
68+
};
69+
}
70+
}
71+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using System.Threading.Tasks;
4+
using TbsCore.Models.AccModels;
5+
using TbsCore.Models.AttackModels;
6+
using TbsCore.Parsers;
7+
8+
namespace TbsCore.Tasks.LowLevel
9+
{
10+
public class CheckProfile : BotTask
11+
{
12+
public int UserId { get; set; }
13+
public TravianUser Profile { get; } = new TravianUser() {
14+
Villages = new List<TravianVillage>()
15+
};
16+
17+
/// <summary>
18+
/// Only used by higher level tasks
19+
/// </summary>
20+
public override async Task<TaskRes> Execute(Account acc)
21+
{
22+
await acc.Wb.Navigate($"{acc.AccInfo.ServerUrl}/spieler.php?uid={UserId}");
23+
24+
var vills = acc.Wb.Html.GetElementbyId("villages")
25+
.ChildNodes
26+
.First(x => x.Name == "tbody")
27+
.Descendants("tr");
28+
29+
foreach (var vill in vills)
30+
{
31+
var villProfile = new TravianVillage();
32+
var nameNode = vill.ChildNodes.First(x => x.HasClass("name"));
33+
34+
villProfile.Capital = nameNode.Descendants("span").Any(x => x.HasClass("mainVillage"));
35+
villProfile.Id = MapParser.GetKarteHref(nameNode) ?? 0;
36+
villProfile.Name = nameNode.InnerText;
37+
villProfile.Population = (int)Parser.RemoveNonNumeric(vill.Descendants("td").First(x => x.HasClass("inhabitants")).InnerHtml);
38+
villProfile.Coordinates = MapParser.GetCoordinates(vill);
39+
40+
Profile.Villages.Add(villProfile);
41+
}
42+
43+
return TaskRes.Executed;
44+
}
45+
}
46+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using System;
2+
using System.Linq;
3+
using System.Threading.Tasks;
4+
using TbsCore.Models.AccModels;
5+
using TbsCore.Models.MapModels;
6+
using TbsCore.Parsers;
7+
using static TbsCore.Helpers.Classificator;
8+
9+
namespace TbsCore.Tasks.LowLevel
10+
{
11+
/// <summary>
12+
/// Use in combination with ScoutPlayer.cs. It will first scout villages, read the reports and then potentially send raid attack
13+
/// </summary>
14+
public class ReadFarmScoutReport : BotTask
15+
{
16+
/// <summary>
17+
/// Coordinates to read the scout report
18+
/// </summary>
19+
public Coordinates Coordinates { get; set; }
20+
/// <summary>
21+
/// Minimal resources that have to be available to send raid
22+
/// </summary>
23+
public long MinResRaid { get; set; } = 30000;
24+
25+
public override async Task<TaskRes> Execute(Account acc)
26+
{
27+
await acc.Wb.Navigate($"{acc.AccInfo.ServerUrl}/position_details.php?x={Coordinates.x}&y={Coordinates.y}");
28+
29+
var report = AttackParser.ParseVillageReports(acc.Wb.Html)
30+
.FirstOrDefault(x => x.Type == ReportType.ScoutNoLosses || x.Type == ReportType.ScoutSomeLosses);
31+
32+
if (report == null)
33+
{
34+
acc.Logger.Warning("Read scout report failed - no scouting report found!");
35+
return TaskRes.Executed;
36+
}
37+
38+
await acc.Wb.Navigate($"{acc.AccInfo.ServerUrl}/{report.Id}");
39+
40+
var scoutReport = AttackParser.ParseAttack(acc.Wb.Html);
41+
42+
// Save report to acc server data
43+
acc.Server.FarmScoutReport.Add(this.Coordinates, scoutReport);
44+
45+
if (this.MinResRaid < scoutReport.GetRaidableRes().Sum())
46+
{
47+
// Send raid
48+
acc.Tasks.Add(new SendRaid()
49+
{
50+
ExecuteAt = DateTime.Now,
51+
TargetVillage = this.Coordinates,
52+
ResourcesAvailable = scoutReport.GetRaidableRes().Sum()
53+
});
54+
}
55+
56+
return TaskRes.Executed;
57+
}
58+
}
59+
}

0 commit comments

Comments
 (0)