Passive generation & next steps

Everything from the last part works — but as soon as you purchase a circuit (technically, one second after), you’ll see we still have some more work left to do. We need a way to account for changes to energy every second. We can do that in the browser with setTimeout(), but we’re actually going to roll our own version more appropriate for a game’s needs.

Remember back in src/stores/gamestate.ts when we created those two helper functions, generateNextCost and canAffordNext? I’m about to introduce a third helper function we’ll use instead of setTimeout(), which means there’s opportunity now to clean up this game template we’re writing by creating a dedicated space for helper functions. I won’t put the two we’ve already created in there so that you have something to do in case you’re out of ideas but still want to feel productive.

Let’s create a new file called src/helpers.ts, and in it, define setInterval2:

export const setInterval2 = (fn: VoidFunction, time: number) => {
  // A place to store the timeout Id (later)
  let timeout: any = null;

  // calculate the time of the next execution
  let nextAt = Date.now() + time;

  const wrapper = () => {
    // calculate the time of the next execution:
    nextAt += time;

    // set a timeout for the next execution time:
    timeout = setTimeout(wrapper, nextAt - Date.now());

    // execute the function:
    return fn();
  };

  // Set the first timeout, kicking off the recursion:
  timeout = setTimeout(wrapper, nextAt - Date.now());

  // A way to stop all future executions:
  const cancel = () => clearTimeout(timeout);

  // Return an object with a way to halt executions:
  return { cancel };
};

If you’re curious about why I wrote it this way, I go into detail in a previous blog post.

To get our circuits generating energy every second, go to App.vue, import our new helper, and use Vue’s onMounted method to handle passive energy production with our helper’s help:

<script setup lang="ts">
import Clicker from '@/components/Clicker.vue'
import StoreDisplay from '@/components/StoreDisplay.vue'
import { gameStateStore } from './stores/gamestate';
import { setInterval2 } from '@/helpers';
import { onMounted } from 'vue';

const gameState = gameStateStore();

onMounted( () => {
  setInterval2(()=>{
    gameState.energy += gameState.energyPerSecond * gameState.circuits
  }, 1000);
});

</script>


<template>
  <div id="app">
    <Clicker/>
    <StoreDisplay/>
  </div>
</template>


<style>
#app {
  display: flex;
  justify-content: center;
  align-items: top;

  height: 100vh;
}

#app > * {
  margin: 1rem;
}
</style>

So every 1000ms, add the product of gameState.energyPerSecond and gameState.circuits to gameState.energy.

Vue’s onMounted lifecycle hook docs go into more detail, but basically, when the component is ready to have things done to it, it invokes this method and our timer starts.

Run the development server again, and buy a circuit. You should see energy accumulating passively.

Congratulations! You finished the series, and in doing so, built your very own incremental game template. What feature will you add to it next? What game idea do you want to prototype and play around with?

# Where to go from here

The basic features are here, but there is so much more than just generating resources and purchasing items that makes a great incremental game. What’s the next feature to implement? Where do you go from here? Well, I have a few ideas:

  • Remember those two helper functions we created in the Pinia store file? Go put those into our dedicated helpers file, and update the imports appropriately so that the game still runs.
  • In Action functions, the logic for calculating the costs of both circuits and capacitors is hard-coded to assume that circuits cost some combination of energy and capacitors, and that capacitors only cost zero or more energy. How could you modify it so that you don’t have to hard-code these cost relationships?
  • How would you implement a way to show (or hide) unlockable items based on state conditions (e.g., unlock something when you have X circuits)?
  • How would you implement research items, with research time and in-progress updates?
  • How would you implement a new resource “Electricity” that costs X energy per second? (how would you change Cost and how it’s used?)

All source code for this project is available on GitHub.

Questions? Comments? Feedback? Please open an issue or reach out on Bluesky.

Have fun!

–Jesse

Checkpoint

Before moving on, you should be able to confidently:

  • Add passive energy generation with a game-friendly interval

    after buying a circuit, energy accumulates on its own without clicking

  • Know several concrete directions to extend the template

    you can name at least two features you’d add next and roughly how

    Stuck on a first extension? Moving the two helper functions into helpers.ts is the lowest-risk change — it touches imports only, and gives you a clean seam alongside the setInterval2 helper you just wrote.