Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions src/main/java/net/tfminecraft/simplefactions/army/Military.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

import java.util.ArrayList;
import java.util.List;
import net.tfminecraft.simplefactions.guild.Guild;
import net.tfminecraft.simplefactions.loaders.RegimentLoader;
import net.tfminecraft.simplefactions.managers.FactionManager;
import net.tfminecraft.simplefactions.managers.RelationManager;
import net.tfminecraft.simplefactions.mercenary.company.MercenaryCompany;
import net.tfminecraft.simplefactions.objects.handler.GuildHandler;
import net.tfminecraft.simplefactions.objects.Faction;
import net.tfminecraft.simplefactions.utils.Formatter;
import net.tfminecraft.simplefactions.enums.FactionModifiers;
Expand Down Expand Up @@ -140,8 +143,29 @@ public ExpandResult adminAdjustSlots(String regimentId, int delta) {
return ExpandResult.ok();
}

/**
* The main guild's company, once it has finished founding and has a regiment.
* Only this company fights for the faction; companies of other guilds are
* hired by contract.
*/
public MercenaryCompany getHomeCompany() {
if(f == null) return null;
GuildHandler handler = f.getGuildHandler();
if(handler == null) return null;
Guild main = handler.getGuild(f.getId());
if(main == null) return null;
MercenaryCompany company = main.getCompany();
return company != null && company.isFormed() && company.getRegiment() != null ? company : null;
}

/** Filled slots of the home company. Empty slots add nothing. */
public int getMercenaryManpower() {
MercenaryCompany company = getHomeCompany();
return company == null ? 0 : company.getFilledSlots();
}

public int getManpower(boolean offense) {
int manpower = 0;
int manpower = getMercenaryManpower();
for(Regiment r : getRegiments()) {
if(offense && !r.isOffensive()) continue;
if(r.isLevy()) {
Expand All @@ -156,7 +180,7 @@ public int getManpower(boolean offense) {
}

public int getManpowerNoLevy(boolean offense) {
int manpower = 0;
int manpower = getMercenaryManpower();
for(Regiment r : regiments) {
if(offense && !r.isOffensive()) continue;
if(r.isLevy()) continue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import net.tfminecraft.simplefactions.army.Military;
import net.tfminecraft.simplefactions.army.MilitaryExpansion;
import net.tfminecraft.simplefactions.army.Regiment;
import net.tfminecraft.simplefactions.mercenary.company.MercenaryCompany;
import net.tfminecraft.simplefactions.objects.Faction;
import net.tfminecraft.simplefactions.utils.Formatter;
import net.tfminecraft.simplefactions.enums.FactionModifiers;
Expand Down Expand Up @@ -122,6 +123,23 @@ public ItemStack createRegimentIcon(Faction f, Regiment r) {
}
// Keep the existing legacy text representation, formatting, and exact-string comparisons.
@SuppressWarnings("deprecation")
public ItemStack createCompanyRegimentIcon(MercenaryCompany company) {
Regiment r = company.getRegiment();
ItemStack i = r.getIcon().clone();
ItemMeta meta = i.getItemMeta();
meta.setDisplayName(company.getName());
List<String> lore = new ArrayList<String>();
lore.add("搂7Soldiers: 搂e"+company.getFilledSlots()+" 搂7("+company.getSlots()+" slots)");
lore.add(StringFormatter.formatHex("#877e7cOnly filled slots count toward offense"));
lore.add(" ");
lore.add(StringFormatter.formatHex("#a89977The main guild's mercenary company."));
lore.add(StringFormatter.formatHex("#a89977Slots and upkeep are managed by the company."));
meta.setLore(lore);
i.setItemMeta(meta);
return i;
}
// Keep the existing legacy text representation, formatting, and exact-string comparisons.
@SuppressWarnings("deprecation")
public ItemStack createRegimentIncreaseButton(Faction f, Regiment r) {
ItemAPI api = TLibs.getItemAPI();
ExpandResult expand = f.getMilitary().canExpand(r);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import net.tfminecraft.simplefactions.managers.FactionManager;
import net.tfminecraft.simplefactions.managers.InventoryManager;
import net.tfminecraft.simplefactions.managers.holder.SFInventoryHolder;
import net.tfminecraft.simplefactions.mercenary.company.MercenaryCompany;
import net.tfminecraft.simplefactions.objects.Faction;
import net.tfminecraft.simplefactions.enums.SFGUI;
import net.tfminecraft.simplefactions.keys.Keys;
Expand Down Expand Up @@ -55,7 +56,13 @@ public void militaryView(Inventory i, Player player, Faction f, boolean open) {
}
}
}
for (int slot = 12 + m.getRegiments().size(); slot <= 44; slot++) {
int next = 12 + m.getRegiments().size();
MercenaryCompany company = m.getHomeCompany();
if(company != null) {
i.setItem(next, creator.createCompanyRegimentIcon(company));
next++;
}
for (int slot = next; slot <= 44; slot++) {
i.setItem(slot, new ItemStack(Material.AIR, 1));
}
for(int x = 0; x<3; x++) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,17 @@ public boolean enlist(String player) {
return true;
}

/**
* The founding slot goes to the guild leader, so a new company opens with its
* first soldier. A leader already serving in another company keeps that post
* and the slot stays open.
*/
public void enlistLeader() {
String leader = getLeader();
if (leader == null || MercenaryCompanies.findByMember(leader) != null) return;
enlist(leader);
}

/** Frees the slot, which in turn freezes expansion until it is filled again. */
public boolean kick(String player) {
for (int i = 0; i < enlisted.size(); i++) {
Expand Down Expand Up @@ -522,6 +533,7 @@ public void tick() {
formationRemaining--;
if (formationRemaining == 0 && regiment != null) {
regiment.setCurrentSlots(1);
enlistLeader();
chime(SFGUI.COMPANY_VIEW);
}
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public static MercenaryResult requestFormation(Guild guild, String actor, String
MercenaryCompany company = new MercenaryCompany(
guild, displayName(name), regiment, Cache.mercenaryFormationSeconds);
guild.setCompany(company);
if (company.isFormed()) company.enlistLeader();
return MercenaryResult.ok("Founding " + company.getName() + "搂a for " + money(cost)
+ " from the guild bank. It will be ready in "
+ hours(Cache.mercenaryFormationSeconds) + ".");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ void maxedUpgradeSaysSoAndKeepsTheWarning() {
@Test
void emptySlotLoreExplainsWhyExpansionIsFrozen() {
MercenaryCompany company = formedCompany();
company.kick(fixture.leader());

List<String> empty = creator.buildSlotLore(null);
assertTrue(empty.stream().anyMatch(line -> line.contains("Empty")));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,43 @@ void formationDebitsOnceAndOpensWithOneSlot() {
assertTrue(company.isFormed());
assertEquals(1, company.getSlots());
assertEquals(400.0, fixture.balance());
assertEquals(java.util.List.of("Ivar"), company.getEnlisted());
assertEquals(1, company.getFilledSlots());
assertFalse(company.hasFreeSlot());
}

@Test
void instantFormationEnlistsTheLeader() {
Cache.mercenaryFormationSeconds = 0;
CompanyFixture fixture = new CompanyFixture(500.0);

MercenaryCompanyService.requestFormation(fixture.guild, "Ivar", "Hired Blades");

MercenaryCompany company = fixture.company();
assertTrue(company.isFormed());
assertTrue(company.isEnlisted("Ivar"));
assertEquals(1, company.getFilledSlots());
}

@Test
void leaderServingElsewhereLeavesTheFoundingSlotOpen() {
CompanyFixture other = new CompanyFixture(500.0);
MercenaryCompanyService.requestFormation(other.guild, "Ivar", "Other Blades");
other.company().tick();
other.company().tick();
CompanyFixture fixture = new CompanyFixture(500.0);
CompanyFixture.registerGlobally(other.guild, fixture.guild);
try {
MercenaryCompanyService.requestFormation(fixture.guild, "Ivar", "Hired Blades");
fixture.company().tick();
fixture.company().tick();

assertTrue(fixture.company().isFormed());
assertTrue(fixture.company().getEnlisted().isEmpty());
assertTrue(fixture.company().hasFreeSlot());
} finally {
CompanyFixture.clearGlobalGuilds();
}
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package net.tfminecraft.simplefactions.mercenary.company;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import net.tfminecraft.simplefactions.Cache;
import net.tfminecraft.simplefactions.army.Military;
import net.tfminecraft.simplefactions.loaders.RegimentLoader;
import net.tfminecraft.simplefactions.objects.Faction;
import net.tfminecraft.simplefactions.objects.handler.GuildHandler;

class MercenaryHomeManpowerTest {
private int oldSeconds;
private Faction faction;
private GuildHandler handler;
private CompanyFixture fixture;

@BeforeEach
void setUp() {
oldSeconds = Cache.mercenaryFormationSeconds;
Cache.mercenaryFormationSeconds = 0;
RegimentLoader.oList.clear();
RegimentLoader.oList.add(CompanyFixture.regularPrototype("militia", 6, 2.0));
RegimentLoader.oList.add(CompanyFixture.prototype());

fixture = new CompanyFixture(500.0);
faction = mock(Faction.class);
handler = mock(GuildHandler.class);
when(faction.getId()).thenReturn("hired_blades");
when(faction.getGuildHandler()).thenReturn(handler);
}

@AfterEach
void tearDown() {
Cache.mercenaryFormationSeconds = oldSeconds;
CompanyFixture.clearRegiments();
}

private MercenaryCompany found() {
MercenaryCompanyService.requestFormation(fixture.guild, "Ivar", "Hired Blades");
return fixture.company();
}

@Test
void mainGuildCompanyAddsFilledSlotsOnly() {
when(handler.getGuild("hired_blades")).thenReturn(fixture.guild);
MercenaryCompany company = found();
company.adminAdjustSlots(2);
Military military = new Military(faction);

assertSame(company, military.getHomeCompany());
assertEquals(1, military.getMercenaryManpower());
assertEquals(7, military.getManpower(true));
assertEquals(7, military.getManpower(false));

company.enlist("Sigrun");

assertEquals(8, military.getManpower(true));
assertEquals(6, military.getTotalSlots());
}

@Test
void companyOfAnotherGuildDoesNotCount() {
when(handler.getGuild("hired_blades")).thenReturn(mock(net.tfminecraft.simplefactions.guild.Guild.class));
found();
Military military = new Military(faction);

assertNull(military.getHomeCompany());
assertEquals(6, military.getManpower(true));
}

@Test
void foundingCompanyDoesNotCount() {
Cache.mercenaryFormationSeconds = 10;
when(handler.getGuild("hired_blades")).thenReturn(fixture.guild);
found();
Military military = new Military(faction);

assertNull(military.getHomeCompany());
assertEquals(6, military.getManpower(true));
}

@Test
void companyWithoutARegimentDoesNotCount() {
MercenaryCompany company = new MercenaryCompany(fixture.guild, "Hired Blades", null, 0);
fixture.guild.setCompany(company);
when(handler.getGuild("hired_blades")).thenReturn(fixture.guild);
Military military = new Military(faction);

assertNull(military.getHomeCompany());
assertEquals(6, military.getManpower(true));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ public static ContractFixture formed(int slots) {
for (int i = 0; i < Cache.mercenaryFormationSeconds; i++) {
company.tick();
}
// Formation enlists the leader; release them so each test names its own roster.
company.kick(host.leader());
// Formation grants one slot; the rest are filled and grown so expansion is legal.
while (company.getSlots() < slots) {
for (int i = 0; i < company.getSlots(); i++) {
Expand Down