Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for campaign status #1681

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

kubaau
Copy link
Contributor

@kubaau kubaau commented Jul 23, 2024

Adds support for Roman-style (video) campaigns and the world campaign (video).

Fixed world campaign scripts to unlock the same continents as it was in S2.

Related to issues #1639 and #1177

@Spikeone
Copy link
Member

Either I'm blind or the implementation is somewhere else? What is rttr:SetCampaignChapterCompleted good for?

@kubaau
Copy link
Contributor Author

kubaau commented Jul 26, 2024

Either I'm blind or the implementation is somewhere else? What is rttr:SetCampaignChapterCompleted good for?

It doesn't make a difference in Roman-style campaigns, but it marks a continent as conquered in the world campaign. It could also be used for other campaign types as well if we wanted to show what was completed, especially if someone designs a campaign whose progression is not linear.

Edit: For roman-style campaigns it shows the "You have successfully completed chapter X." screen after ending the game.

Copy link
Member

@Flamefire Flamefire left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome, the last bit for campaigns! Review inline.

General hint: Try to make best use of the type system. E.g. "unsigned char" is/should be a (text-)character (although only since C++11 introduced uint8_t) And using this narrow width number-type as parameters likely introduces overhead (registers are wider so often need to be masked) so it is even faster to just use unsigned there.
Similar for string which should be text. It can be stored in the config as a string for brevity but converted during load/save to a more appropriate type, especially when (scoped) enums can be used.

In general I like the changes, thanks! :)

constexpr auto chapterDisabled = '0';
constexpr auto chapterEnabled = '1';
constexpr auto chapterCompleted = '2';
constexpr auto defaultChaptersEnabled = "1100000000";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What exactly does this mean? Can you add some description?

@@ -23,6 +24,7 @@ struct CampaignDescription
std::string image;
unsigned maxHumanPlayers = 0;
std::string difficulty;
std::string defaultChaptersEnabled;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a docstring what this is and how it should be interpreted? Maybe a string isn't the right type as it isn't exactly text. I guess it is a variable sized array of enum type represented as a string. So maybe an own type and corresponding lua loader is better.

Comment on lines +36 to +43
std::vector<MissionStatus> ret(saveData.length());
for(auto i = 0u; i < saveData.length(); ++i)
{
ret[i].playable = saveData[i] != CampaignSaveCodes::chapterDisabled;
ret[i].conquered = saveData[i] == CampaignSaveCodes::chapterCompleted;
}

ret.resize(campaignDesc.getNumMaps());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say it would be better: Either a range-based for and push_back or use numMaps as the initial size and loop over rttr::Range(min(saveData.length(), ret.length())


struct CampaignDescription;

bool isChapterEnabled(const CampaignDescription& campaignDesc, unsigned char chapter);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The argument isn't a char but a number:

Suggested change
bool isChapterEnabled(const CampaignDescription& campaignDesc, unsigned char chapter);
bool isChapterEnabled(const CampaignDescription& campaignDesc, unsigned chapter);

Or uint8_t although that is likely even slower so isn't really useful, is it?

Comment on lines +10 to +20
namespace {
auto getCampaignSaveData(const CampaignDescription& campaignDesc)
{
auto& saveData = SETTINGS.campaigns.saveData;

if(!saveData.count(campaignDesc.uid))
saveData[campaignDesc.uid] = campaignDesc.defaultChaptersEnabled;

return saveData[campaignDesc.uid];
}
} // namespace
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For a single function (or even just functions) static is less clutter:

Suggested change
namespace {
auto getCampaignSaveData(const CampaignDescription& campaignDesc)
{
auto& saveData = SETTINGS.campaigns.saveData;
if(!saveData.count(campaignDesc.uid))
saveData[campaignDesc.uid] = campaignDesc.defaultChaptersEnabled;
return saveData[campaignDesc.uid];
}
} // namespace
static auto getCampaignSaveData(const CampaignDescription& campaignDesc)
{
auto& saveData = SETTINGS.campaigns.saveData;
if(!saveData.count(campaignDesc.uid))
saveData[campaignDesc.uid] = campaignDesc.defaultChaptersEnabled;
return saveData[campaignDesc.uid];
}

Also do you intend to return a copy? It looks like it is supposed to be either a reference or a const reference


BOOST_FIXTURE_TEST_CASE(Chapters0and1AreEnabledByDefault, CampaignSaveDataFixture)
{
saveData[uid] = CampaignSaveCodes::defaultChaptersEnabled;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the default is to enable the first 2 chapters? Why that? Wouldn't (only) the first one be a more sensible default?
I'm also a bit confused as you seem to be using indices and 1-based values for chapters

Maybe stick to indices and use an optional instead of zero

BOOST_TEST_REQUIRE(isChapterEnabled(desc, 0) == true);
BOOST_TEST_REQUIRE(isChapterEnabled(desc, 1) == false);
BOOST_TEST_REQUIRE(isChapterEnabled(desc, 2) == false);
BOOST_TEST_REQUIRE(isChapterEnabled(desc, 9) == false);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should also include a change to saveData to check that the default really is only a default and overwritten by an explicit value.

}

CampaignDescription dsc;
CampaignDataLoader loader{dsc, tmp};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't this just use desc from the fixture?

executeLua("rttr:SetCampaignChapterCompleted('campaign_id', 0)"); // noop - chapters start from 1
executeLua("rttr:EnableCampaignChapter('campaign_id', 0)"); // noop - chapters start from 1
executeLua("rttr:EnableCampaignChapter('campaign_id', 2)"); // noop - already completed
game.executeAICommands();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this line for?

@@ -12,6 +13,7 @@
CampaignDescription::CampaignDescription(const kaguya::LuaRef& table)
{
CheckedLuaTable luaData(table);
luaData.getOrThrow(uid, "uid");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this you need to Increase CampaignDataLoader::GetVersion()

I'd try to make this backwards compatible: and make the uid optional. Or check the version here (again) and make sure the uid is empty for older versions and non-empty otherwise.

Then you can revert the changes to existing tests

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants