Skip to content

Conversation

@adrian-cancio
Copy link
Contributor

@adrian-cancio adrian-cancio commented Jan 31, 2026

Summary

  • Adds a new Government Upkeep Price Calculator tool
  • Calculates upkeep costs for governors based on planet buildings
  • Includes building summary with material requirements and price estimates
  • Modular architecture with composables for maintainability

Screenshots

  1. General view
image 2. Need Type Selection
image
4. Building Summary Table image 5. Missing Prices Warning (hintwhile hovering) image 6. Material Details Table image 7. Navigation Bar Link
image

Closes #187

@netlify
Copy link

netlify bot commented Jan 31, 2026

👷 Deploy request for prunplanner-preview pending review.

Visit the deploys page to approve it

Name Link
🔨 Latest commit f52e353

@adrian-cancio
Copy link
Contributor Author

@jplacht Take your time reviewing this one - it's a complex feature. Feel free to suggest any changes, even if it means reworking the entire interface. I'm open to any feedback.

Comment on lines 8 to 186
export const UPKEEP_NEED_TYPES: UpkeepNeedType[] = [
"safety",
"health",
"comfort",
"culture",
"education",
];

export const UPKEEP_BUILDINGS: IUpkeepBuilding[] = [
{
ticker: "SST",
name: "Safety Station",
materials: [
{ ticker: "DW", qtyPerDay: 10 },
{ ticker: "OFF", qtyPerDay: 10 },
{ ticker: "SUN", qtyPerDay: 2 },
],
needs: { safety: 833.3, health: 0, comfort: 0, culture: 0, education: 0 },
},
{
ticker: "SDP",
name: "Space Defense Platform",
materials: [
{ ticker: "POW", qtyPerDay: 1 },
{ ticker: "RAD", qtyPerDay: 0.47 },
{ ticker: "CCD", qtyPerDay: 0.07 },
{ ticker: "SUD", qtyPerDay: 0.07 },
],
needs: { safety: 1250, health: 0, comfort: 0, culture: 0, education: 0 },
},
{
ticker: "EMC",
name: "Emergency Center",
materials: [
{ ticker: "PK", qtyPerDay: 2 },
{ ticker: "POW", qtyPerDay: 0.4 },
{ ticker: "BND", qtyPerDay: 4 },
{ ticker: "RED", qtyPerDay: 0.07 },
{ ticker: "BSC", qtyPerDay: 0.07 },
],
needs: { safety: 200, health: 200, comfort: 0, culture: 0, education: 0 },
},
{
ticker: "INF",
name: "Infirmary",
materials: [
{ ticker: "OFF", qtyPerDay: 10 },
{ ticker: "TUB", qtyPerDay: 6.67 },
{ ticker: "STR", qtyPerDay: 0.67 },
],
needs: { safety: 0, health: 833.33, comfort: 0, culture: 0, education: 0 },
},
{
ticker: "HOS",
name: "Hospital",
materials: [
{ ticker: "PK", qtyPerDay: 2 },
{ ticker: "SEQ", qtyPerDay: 0.4 },
{ ticker: "BND", qtyPerDay: 4 },
{ ticker: "SDR", qtyPerDay: 0.07 },
{ ticker: "RED", qtyPerDay: 0.07 },
{ ticker: "BSC", qtyPerDay: 0.13 },
],
needs: { safety: 0, health: 833.33, comfort: 0, culture: 0, education: 0 },
},
{
ticker: "WCE",
name: "Wellness Center",
materials: [
{ ticker: "KOM", qtyPerDay: 4 },
{ ticker: "OLF", qtyPerDay: 2 },
{ ticker: "DW", qtyPerDay: 6 },
{ ticker: "DEC", qtyPerDay: 0.67 },
{ ticker: "PFE", qtyPerDay: 2.67 },
{ ticker: "SOI", qtyPerDay: 6.67 },
],
needs: { safety: 0, health: 166.67, comfort: 166.7, culture: 0, education: 0 },
},
{
ticker: "PAR",
name: "Park",
materials: [
{ ticker: "DW", qtyPerDay: 10 },
{ ticker: "FOD", qtyPerDay: 6 },
{ ticker: "PFE", qtyPerDay: 2 },
{ ticker: "SOI", qtyPerDay: 3.33 },
{ ticker: "DEC", qtyPerDay: 0.33 },
],
needs: { safety: 0, health: 0, comfort: 500, culture: 0, education: 0 },
},
{
ticker: "4DA",
name: "4D Arena",
materials: [
{ ticker: "POW", qtyPerDay: 2 },
{ ticker: "MHP", qtyPerDay: 2 },
{ ticker: "OLF", qtyPerDay: 4 },
{ ticker: "BID", qtyPerDay: 0.2 },
{ ticker: "HOG", qtyPerDay: 0.2 },
{ ticker: "EDC", qtyPerDay: 0.2 },
],
needs: { safety: 0, health: 0, comfort: 833.3, culture: 0, education: 0 },
},
{
ticker: "ACA",
name: "Academy",
materials: [
{ ticker: "COF", qtyPerDay: 8 },
{ ticker: "OLF", qtyPerDay: 2 },
{ ticker: "VIT", qtyPerDay: 8 },
{ ticker: "DW", qtyPerDay: 10 },
{ ticker: "GL", qtyPerDay: 6.67 },
{ ticker: "DEC", qtyPerDay: 0.67 },
],
needs: { safety: 0, health: 0, comfort: 166.7, culture: 166.7, education: 0 },
},
{
ticker: "ART",
name: "Art Gallery",
materials: [
{ ticker: "MHP", qtyPerDay: 1 },
{ ticker: "HOG", qtyPerDay: 1 },
{ ticker: "UTS", qtyPerDay: 0.67 },
{ ticker: "DEC", qtyPerDay: 0.67 },
],
needs: { safety: 0, health: 0, comfort: 0, culture: 625, education: 0 },
},
{
ticker: "VRT",
name: "Virtual Reality Theater",
materials: [
{ ticker: "POW", qtyPerDay: 1.4 },
{ ticker: "MHP", qtyPerDay: 2 },
{ ticker: "HOG", qtyPerDay: 1.4 },
{ ticker: "OLF", qtyPerDay: 4 },
{ ticker: "BID", qtyPerDay: 0.33 },
{ ticker: "DEC", qtyPerDay: 0.67 },
],
needs: { safety: 0, health: 0, comfort: 0, culture: 833.3, education: 0 },
},
{
ticker: "PBH",
name: "Public Broadcast Hub",
materials: [
{ ticker: "OFF", qtyPerDay: 10 },
{ ticker: "MHP", qtyPerDay: 1 },
{ ticker: "SP", qtyPerDay: 1.33 },
{ ticker: "AAR", qtyPerDay: 0.67 },
{ ticker: "EDC", qtyPerDay: 0.27 },
{ ticker: "IDC", qtyPerDay: 0.13 },
],
needs: { safety: 0, health: 0, comfort: 0, culture: 166.7, education: 166.7 },
},
{
ticker: "LIB",
name: "Library",
materials: [
{ ticker: "MHP", qtyPerDay: 1 },
{ ticker: "HOG", qtyPerDay: 1 },
{ ticker: "CD", qtyPerDay: 0.33 },
{ ticker: "DIS", qtyPerDay: 0.33 },
{ ticker: "BID", qtyPerDay: 0.2 },
],
needs: { safety: 0, health: 0, comfort: 0, culture: 0, education: 500 },
},
{
ticker: "UNI",
name: "University",
materials: [
{ ticker: "COF", qtyPerDay: 10 },
{ ticker: "REA", qtyPerDay: 10 },
{ ticker: "TUB", qtyPerDay: 10 },
{ ticker: "BID", qtyPerDay: 0.33 },
{ ticker: "HD", qtyPerDay: 0.67 },
{ ticker: "IDC", qtyPerDay: 0.2 },
],
needs: { safety: 0, health: 0, comfort: 0, culture: 0, education: 833.3 },
},
];
Copy link
Contributor

Choose a reason for hiding this comment

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

@adrian-cancio Could we move this into a separate file, with "constants" or something in its name. Think this would be easier to maintain.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done! Moved constants to upkeepCalculations.constants.ts

* @param getPriceFunc Function to get material prices
* @returns Array of material calculations sorted by price per need
*/
export async function calculateMaterialsForNeed(
Copy link
Contributor

@jplacht jplacht Jan 31, 2026

Choose a reason for hiding this comment

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

Could you please also add some tests for your new functions? There should be loads you can pick from in the /tests folder, also if you need to setup some prices / materials or other things as test data (see how I test pricing for example).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done! Added tests in src/tests/features/government/upkeepCalculations.test.ts

@andykorth
Copy link

The name of some of the UPKEEP_BUILDINGS seem to be hallucinations.
Space Defense Platform -> Security Drone Post
Academy -> Art Cafe
4D Arena -> 4D Arcades
Public Broadcast Hub -> Planetary Broadcasting Hub
Virtual Reality Theater -> VR Theater

I would actually consider removing the "relative price" column. I know I included it on my governor sheet in the past, but it also shows you the "anchor material", which those prices are relative to. I think it's a little confusing though, and I have removed it from my other, newer tools. ( https://oogcapitalmanagement.com/governing-mats/ )

The intent with relative price was to help you shop prices, which was useful before we had automated price imports and good pricing models.

https://github.com/PRUNplanner/frontend/pull/339/changes/BASE..bfc4e24082e0e5c8d93197951ecf1f3ae4d428d1#diff-c19b84cea13538f8c7926d12f973bb76b714ab842cf0f6a3154c3ac4539acff0R55-R58

I also do something similar; for mixed use buildings, I double the value of the contribution since it's providing two needs. I think there's room for a better split of the value, but this is a good choice since the tool operates on one need at a time. Maybe you want to make note of the way mixed-use buildings are added up in the UI for the end-user.

@andykorth
Copy link

So thinking about the "Building Summary" section. I guess the calculated values there are based on filling all of the materials for a given building. I don't think that's useful because, No one really does that, and we shouldn't encourage it. Choosing individual materials on their $/Need from different buildings is going to result in lower prices. There's no bonus for filling a building entirely, and often they'll contain a mix of really effective and very ineffective upkeep choices. So showing the total cost of a building is meaningless and potentially misleading.

There might be value in showing the Need/day produced by a building (or by each material in a building, since the number of items in a building varies quite a bit). But that's really only valuable when you've imported the POPI building list from FIO for a given planet.

That leads me towards recommending removing the Building Summary section, at least in this tool's current form.

@adrian-cancio
Copy link
Contributor Author

@andykorth Okay, I'll get on it.

@adrian-cancio
Copy link
Contributor Author

Hey @andykorth, thanks for the feedback! I've made all the changes you suggested:

  • Fixed the building names (SDP, ACA, 4DA, PBH, VRT) - good catch on those!
  • Removed the "Relative Price" column from the table
  • Removed the "Building Summary" section entirely

I agree that showing total building costs could be misleading since mixing materials from different buildings is usually the better approach.

I can't upload screenshots right now, but if you need them to verify the changes I can add them later. Let me know if anything else needs adjusting!

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.

Government - Upkeep Material Price Calculations

3 participants