Can I create a custom missable achievement?

Place to get help with not working mods / modding interface.
Solinya
Long Handed Inserter
Long Handed Inserter
Posts: 79
Joined: Sun Mar 17, 2019 10:39 pm
Contact:

Can I create a custom missable achievement?

Post by Solinya »

I'm trying to add an achievement to a mod, specifically to reward players that don't die before launching a rocket in a multiplayer game. I've figured out how to add custom script-based achievements and I could simply not award the achievement if the 'failure condition' was triggered, but I'd like to use the missable/ineligible display factorio has for achievements like don't build solar.

My first thought was to make my achievement of type "dont-build-entity-achievement" based off a special hidden entity (type "simple-entity-with-owner") and then spawn that entity on player death. I believe the hidden entity spawning is working, but spawning through LuaSurface.create_entity doesn't appear to count as "building" for purposes of disabling the achievement.

My spawning code:

Code: Select all

  ...
    game.surfaces.nauvis.create_entity({
        name = "hidden-grave-marker",
        amount = 1,
        position = {player.position.x, player.position.y},
        force = game.forces.player
        }) 
(though this also probably would break when using mods that generate additional surfaces.)

Is there another way I can create an achievement that players can "fail" while reflecting the failed state in the achievement UI?
User avatar
ZeroKnight
Manual Inserter
Manual Inserter
Posts: 4
Joined: Sun Sep 29, 2019 3:30 am
Contact:

Re: Can I create a custom missable achievement?

Post by ZeroKnight »

There doesn't appear to be any existing achievement types applicable to what you're trying to accomplish, as I'm sure you've already noticed, so aside from a workaround I don't think it's possible.
Solinya wrote: Sat Sep 28, 2019 8:31 am I believe the hidden entity spawning is working, but spawning through LuaSurface.create_entity doesn't appear to count as "building" for purposes of disabling the achievement.
You could try LuaPlayer.build_from_cursor, as it has the following note:
lua-api.factorio.com wrote:Note: Anything built will fire normal player-built events.
I haven't tested this personally, but I assume this would trigger the achievement miss. Also, since the function uses the current surface, you won't have to worry about non-Nauvis surfaces.

As an aside, I feel it would be worthwhile for Wube to add an achievement type that fails on death, something like "DontDieAchievement" or similar. It's an interesting enough criterion as it could be useful for any "no death" challenges; I could see this facilitating no-death speedruns, or launching a rocket on a death world without dying, just to name a couple examples.

Might be worth making a post in the suggestions board?
Solinya
Long Handed Inserter
Long Handed Inserter
Posts: 79
Joined: Sun Mar 17, 2019 10:39 pm
Contact:

Re: Can I create a custom missable achievement?

Post by Solinya »

ZeroKnight wrote: Sun Sep 29, 2019 3:59 am You could try LuaPlayer.build_from_cursor, as it has the following note:
lua-api.factorio.com wrote:Note: Anything built will fire normal player-built events.
I haven't tested this personally, but I assume this would trigger the achievement miss. Also, since the function uses the current surface, you won't have to worry about non-Nauvis surfaces.
That's an interesting approach and I missed that function before. The only problem is...I don't see a way to load my invalidate achievement item into the cursor, only clear it.
As an aside, I feel it would be worthwhile for Wube to add an achievement type that fails on death, something like "DontDieAchievement" or similar. It's an interesting enough criterion as it could be useful for any "no death" challenges; I could see this facilitating no-death speedruns, or launching a rocket on a death world without dying, just to name a couple examples.

Might be worth making a post in the suggestions board?
For most players I think they could trivially earn it by just reloading from save (as in single player the game just ends when you die). I can get away with it in my case because the players on the server this is for have been asking for ways to make death more punishing so people are more careful on biter base raids.

That being said, I do wish there was a simple way to flag an achievement as unobtainable in a game from a script, perhaps some kind of eligibility property.

Since DontBuildEntityAchievement didn't work out, I think my next workaround attempt is going to be to generate a hidden power source, power pole, and power sink and use Prototype/DontUseEntityInEnergyProductionAchievement. I did test that create_entity does invalidate the vanilla no solar power achievement when I do this, so it seems technically possible. But this is more hacky because I don't want my invalidation items showing up on the electric grid, so I need to make sure they remain separate (or remove them somehow after they fire the fail condition) so that someone building a substation on top won't see weird stuff on the electric network display.
User avatar
ZeroKnight
Manual Inserter
Manual Inserter
Posts: 4
Joined: Sun Sep 29, 2019 3:30 am
Contact:

Re: Can I create a custom missable achievement?

Post by ZeroKnight »

Solinya wrote: Sun Sep 29, 2019 6:13 am
ZeroKnight wrote: Sun Sep 29, 2019 3:59 am You could try LuaPlayer.build_from_cursor, as it has the following note:
lua-api.factorio.com wrote:Note: Anything built will fire normal player-built events.
I haven't tested this personally, but I assume this would trigger the achievement miss. Also, since the function uses the current surface, you won't have to worry about non-Nauvis surfaces.
That's an interesting approach and I missed that function before. The only problem is...I don't see a way to load my invalidate achievement item into the cursor, only clear it.
It took some digging, but I managed to find out how to set the cursor item; you want LuaItemStack.set_stack. LuaControl has a cursor_stack attribute that you can call set_stack on. For example, I was able to get a stack of Iron Plates on my cursor with:

Code: Select all

game.player.cursor_stack.set_stack('iron-plate')
I opted for a SimpleItemStack concept simply because I was testing in the game console, so use whichever you prefer as an argument.
Solinya
Long Handed Inserter
Long Handed Inserter
Posts: 79
Joined: Sun Mar 17, 2019 10:39 pm
Contact:

Re: Can I create a custom missable achievement?

Post by Solinya »

That worked!

I had to take a bit of a detour and define an item prototype first that corresponded to my hidden placeable entity. Then I used the following sequence to place my item in the event handler for on_player_died:

Code: Select all

    player.cursor_stack.clear()
    player.cursor_stack.set_stack("grave-marker")
    player.build_from_cursor({
        position = {player.position.x, player.position.y},
    })
I'm not entirely sure if the clear is required, but since this specific case is based on a character death, it doesn't seem like it'd be harmful.

I also had to override the build_sound on my entity because it sounded weird having your standard entity placed (think placing inserters/power poles) noise going off when you died.

Thanks for the help!
User avatar
ZeroKnight
Manual Inserter
Manual Inserter
Posts: 4
Joined: Sun Sep 29, 2019 3:30 am
Contact:

Re: Can I create a custom missable achievement?

Post by ZeroKnight »

Awesome, glad to be of some help! :D
Post Reply

Return to “Modding help”