Control Code

Place to get help with not working mods / modding interface.
Narinik
Burner Inserter
Burner Inserter
Posts: 8
Joined: Wed Jun 11, 2014 3:49 am
Contact:

Control Code

Post by Narinik »

After finishing a 60 hour freeplay game I wanted to try my hand at modding. I saw that windmills have been somewhat debated already on the fourm but decided to try and make one myself.

So far ive got the research, item, and placeable working. the windmill at present works like a small solar panel, increasing and decreasing power with day/night. Id like to modify this to work with the wind speed/direction.

I saw that I can get access to game.windspeed/game.windorientation but im not sure how to get the placed item to react to the changes. Ive been looking for the code that controls the solar panels and steam engines to model it after but no luck.

I imagine i need to create a control.lua but all the examples ive found are for driving around vehicles.
User avatar
FreeER
Smart Inserter
Smart Inserter
Posts: 1266
Joined: Mon Feb 18, 2013 4:26 am
Contact:

Re: Control Code

Post by FreeER »

Narinik wrote:I imagine i need to create a control.lua but all the examples ive found are for driving around vehicles.
Yes, you need a control.lua file. First, read up on the lua programming language if you haven't already (and especially if you haven't used a programming language before), I recommend PIL but you could watch some Youtube videos or whatever you prefer. Second, because of the way that Factorio is right now, very little of the energy code is viewable/usable from lua (it's mostly coded in c++). Third (following from 2), to do this you'd need an entity that can provide power (meaning, steam engine, accumulator, or solar panel) and to increase it's 'energy' (the value is actually the buffer for the entity) based on the wind speed/orientation as you desired. SO what you'd do is something like

Code: Select all

require "defines" -- added in edit, no idea why I forgot it :)
function init()
  if not glob.windmills then glob.windmills = {} end -- create a table to store built windmills, only data in the glob table will the loaded with saves
end
game.oninit(function() init() end) -- call the init function when the mod is added to a game
game.onload(function() init() end) -- call the init function when a save is loaded
game.onevent(defines.events.onbuiltentity, function() -- get notifications for player-built entities
  if event.createdentity.name == "wind_mill" then -- if the entity is the windmill
    table.insert(glob.windmills, event.createdentity) -- place the new windmill entity into the windmill table
  end
end)
game.onevent(defines.events.ontick, function()
  if event.tick%60 == 4 then -- every 60 ticks / 1 second
    for index, windmill in ipairs(glob.windmills) do
      if windmill.valid then
        GeneratePower(windmill)
      else
        table.remove(glob.windmills, index) -- remove the invalid windmill
      end
    end
  end
end)
function GeneratePower(windmill)
  windmill.energy = windmill.energy + (game.windspeed * 1000) * directionModifier(windmill)
end
function directionModifier(windmill)
  if windmill.direction/8 ~= game.windorientation or 1-windmill.direction/8 ~= game.windorientation then
    return 0.5 -- half generated power if the windmill is not facing in either the same direction or the opposite direction of the wind
  else
    return 1 -- do not reduce power if windmill IS facing in the same direction, or the opposite direction
  end
end
Note that the GeneratePower and directionModifier functions do the real work, and you could place their code directly in ontick if you wanted (however making them separate may allow you to modify them easier later on). 1000 * the windspeed was an arbitrary number/equation I made up as an example, you would probably need to play with that to get it balanced. The reason I divided windmill.direction by 8 is that the direction is an integer between 0-7 while the windorientation is a double between 0-1 (the directions, I think!, are the same, simply in 1/8 increments instead of increments of 1. Don't ask why, I don't know, but that's what the wiki showed :))

A possible issue is that the energy (buffer) is pretty much set by Factorio and there isn't much you can do to change it's max size, nor can you give it more energy than Factorio decides it's buffer size is (if you set the energy property higher than the buffer it's simply set to be the size of the buffer...). There is a buffer property in the prototypes, but the last I knew Factorio pretty much ignores it, by which I mean it starts off with that value but if Factorio thinks it should be lower based on how much the prototype says it produces it'll do so without caring that you specifically told it to be larger...

There is one other thing to note: I'm not sure how much the wind may change in a normal game...I'm not sure if it's existence is even used right now in the base game. So it's possible you'd need some extra code to make the wind actually change yourself...

I hope that was at least a little helpful :)
<I'm really not active any more so these may not be up to date>
~FreeER=Factorio Modding
- Factorio Wiki
- My Factorio Modding Guide
- Wiki Modding Guide
Feel free to pm me :)
Or drop into #factorio on irc.esper.net
Colombo
Long Handed Inserter
Long Handed Inserter
Posts: 76
Joined: Mon May 19, 2014 11:25 am
Contact:

Re: Control Code

Post by Colombo »

FreeEr: Thank you, you should post this on wiki, this is really good tutorial that explain how to do the magic lua work.
User avatar
FreeER
Smart Inserter
Smart Inserter
Posts: 1266
Joined: Mon Feb 18, 2013 4:26 am
Contact:

Re: Control Code

Post by FreeER »

Colombo wrote:FreeEr: Thank you, you should post this on wiki, this is really good tutorial that explain how to do the magic lua work.
Well a lot of it is basic lua coding (for which you need to be at least slightly familiar with the language) and simply knowing which properties and methods are available for entities, which can be seen on the wiki here, and the events which are on the wiki here.

I do have a tutorial on the wiki about making a simple bomber aircraft, here, which covers some of the essentials (though it's a bit outdated it's mostly still accurate I believe). The problem with making a fantastic tutorial is that it'd have to be very long or fairly vague to cover everything...The code I provided is somewhat exclusive to energy and a lot of people might not understand how it would apply to them without already knowing the lua language. A lot of the issues that modders (especially new modders, but also the more experienced ones) encounter are just the little quirks of Factorio's modding API (like the energy property being the buffer of the entity, and that some entities only accept electrical energy sources and not burner power sources, etc.) which can really only be learned (sometimes) by looking at the wiki, figuring it out yourself or asking someone who's already discovered the quirk ;) SO I tend to just watch the modding help sub forum and try to give good answers and commented code to at least get them on the correct path :)
I'm glad you found it helpful however :P
Colombo
Long Handed Inserter
Long Handed Inserter
Posts: 76
Joined: Mon May 19, 2014 11:25 am
Contact:

Re: Control Code

Post by Colombo »

Sure, I know your tutorial, but it covers only basic stuff, not that advanced properties that could be achieved by this.

Eg. I wanted to make "dust collectors", various tools that would lower pollution. As from 10.1, assembling machines and labs could run on burners, this will be usefull for my "burner aditions" mod, that will add those things and enable more coal-powered alternative.

But, I didn't know how to create those advanced behavior. The first, passive, dust collector would basically do:
lower pollution -> create dust -> if there is dust, stop working (forcing player to manage waste)

But how could I do it? I tried to read through Treefarm and Dytech, that have similar behavior, but those have quite complicated stuff and that behavior was a bit lost. Your example provided me with clear way how to see and work with it.
User avatar
FreeER
Smart Inserter
Smart Inserter
Posts: 1266
Joined: Mon Feb 18, 2013 4:26 am
Contact:

Re: Control Code

Post by FreeER »

Colombo wrote:Sure, I know your tutorial, but it covers only basic stuff, not that advanced properties that could be achieved by this.
True, it only directly covers the basics, but it also mentions the wiki locations where the properties can be found. It should probably also mention the Prototypes wiki page, but that was created after the tutorial was :)
Colombo wrote:Your example provided me with clear way how to see and work with it.
Mostly because it was tailored to something specific and didn't include non-relevant code, which a long tutorial (and large mods) would :)

Pollution control can be found under the "Lua/Game" object as pollute and getpollution, you should (I think) be able to give pollute a negative value to decrease pollution, and dust_collector.insert to add dust at the same time after using getinventory to see if there was any room (or caninsert) or getitemcount to see if there was any dust specifically (rather than any items), all of which are mentioned in the Lua/Inventory object, alternatively (what I believe DyTech does) you can give the dust_collector a negative emissions_per_tick value in the prototype (as mentioned in the Prototype/Entity page) to decrease pollution and simply insert some dust every second or so, but then you can't stop the pollution decreasing without destroying the collectors (and/or replacing them with a 'non-working' entity). Of course either way you'd need to know where the dust_collectors are which means adding to them to a glob table in onbuiltentity (and realizing that robot built entities do not raise the onbuiltentity event at this time, to find those you'd need player interaction through a script interface or guis...). The Treefarm hydroculture seems to use the emissions properties of the energy_source rather than emissions_per_tick which means pollution is only decreased when it's actually working, however the trees all use the emissions_per_tick (like the regular base game trees do, and I think DyTech as well). So if you went with the first you'd use code like (most numbers are arbitrary)

Code: Select all

game.onevent(defines.events.onbuiltentity, function()
  if event.createdentity.name == "dust_collector" then table.insert(glob.collectors, event.createdentity) end
end)
game.onevent(defines.events.ontick, function()
  if event.tick%60 == 20 then
    for index, collector in ipairs(glob.collectors) do
      if collector.valid then
        if collector.caninsert{name="dust", count=10} and collector.getitemcount("dust") < 20 then -- if 10 dust can be inserted and it currently has less than 20
          collector.insert{name="dust", count=10}
          game.pollute(collector.position, -10) -- game.pollute, according to the wiki, takes a position to pollute and the amount of pollution to add
        end
      else
        table.remove(glob.collectors, index)
      end
    end
  end
end)
assuming glob.collectors was added to the init function of course :) whereas with emissions_per_tick you would simply remove the game.pollute command since that is handled by the game, and just insert the dust. With the energy_source emissions you'd probably use an assembler with a "reduce pollution" recipe and use lua code to set collector.active to false when the dust is > 20 (so it stops working) and back to true if it's false and contains less than 20 dust.
Narinik
Burner Inserter
Burner Inserter
Posts: 8
Joined: Wed Jun 11, 2014 3:49 am
Contact:

Re: Control Code

Post by Narinik »

FreeER, that's pretty much exactly what I was looking for. Thanks! Sorry for the delayed reply, I was away for the weekend :P
FreeER wrote:(and especially if you haven't used a programming language before)
I actually have a degree in computer science and am working on a masters. I didn't want to be too specific in my question,(asking for entry points, event handlers ect) since im completely unfamiliar with the framework that was implemented.
User avatar
FreeER
Smart Inserter
Smart Inserter
Posts: 1266
Joined: Mon Feb 18, 2013 4:26 am
Contact:

Re: Control Code

Post by FreeER »

Narinik wrote:FreeER, that's pretty much exactly what I was looking for. Thanks!
Glad it was helpful...hm, I seem to have forgotten require "defines" at the top (that would cause an error..., it's located in data\core\lualib by the way).
Narinik wrote:Sorry for the delayed reply, I was away for the weekend :P
No problem, just left me wondering if you'd managed to see it :)
Narinik wrote:I actually have a degree in computer science and am working on a masters.
Ah, you're where I wish I was :lol: I'm actually surprised you didn't grab a few mods (F-mod, DyTech, Treefarm, etc.) and look at their code, I'd think it'd be fairly straight forward to learn from how people are implementing their features if you already understand programming quite well, though perhaps smaller mods would be better than those examples :)
Narinik
Burner Inserter
Burner Inserter
Posts: 8
Joined: Wed Jun 11, 2014 3:49 am
Contact:

Re: Control Code

Post by Narinik »

Im not sure why my post isnt showing up, so if this is a repost ill delete.

How do I define a new type? So I imagine i can access windmill.production and do something like

Code: Select all

windmill.production = (generate number) +"W"

as is, the 'windmill' is just a solar panel that reacts to the sun.

Code: Select all

type = "solar-panel",
    name = "simple-windmill",
    icon = "__windmill__/graphics/windmill_icon.png",
    flags = {"placeable-neutral", "player-creation"},
    minable = {hardness = 0.2, mining_time = 0.5, result = "simple-windmill"},
    max_health = 100,
    corpse = "big-remnants",
    collision_box = {{-0.7, -1.4}, {0.7, 1.4}},
    selection_box = {{-0.8, -1.5}, {0.8, 1.5}},
    energy_source =
    {
      type = "electric",
      usage_priority = "primary-output"
    },
    picture =
    {
      filename = "__windmill__/graphics/windmill.png",
      priority = "high",
      width = 52,
      height = 96
    },
    production = "500W"
  }
User avatar
FreeER
Smart Inserter
Smart Inserter
Posts: 1266
Joined: Mon Feb 18, 2013 4:26 am
Contact:

Re: Control Code

Post by FreeER »

Narinik wrote:Im not sure why my post isnt showing up, so if this is a repost ill delete.
Not a repost as far as I can tell.
Narinik wrote:How do I define a new type?
This is currently not possible. Neither actual types (like type="windmill") nor adding a new property to the type (Factorio simply ignores it), nor can you dynamically change the prototype once it's loaded (you can of course use regular lua functions in the prototype file, but you can't access and change those values during runtime). Which means you can not simply change the production value of a solar panel in game based on the wind nor make a new type that works like the solar panel but using wind instead of day light as a production modifier.

In this case you could probably use a type accumulator (or generator) to prevent it being affected by the sun and keep it's buffer filled in ontick (based on the wind of course). What's done to work around this with more complicated issues is to store as a variable in the control.lua any additional information that's needed (and maybe store the lua entity in a table that also includes that info, if each entity will be different), and (if necessary) use createentity to spawn another entity (usually with an invisible image and no collision) in the same position in order to use the features of other types (a train wagon with a roboport for example, see the Wagon mod). You can of course make a post in the interface requests and encourage the devs to create a type that works based on the wind, or simply being able to specify the production modifier for solar-panels, or anything else you believe would be helpful.
Narinik
Burner Inserter
Burner Inserter
Posts: 8
Joined: Wed Jun 11, 2014 3:49 am
Contact:

Re: Control Code

Post by Narinik »

So here you can see my awesome gfx. but the question being, why do i get unknown key ect?
in my data.lua

Code: Select all

{
    type = "technology",
    name = "WindPower",
Attachments
untitled.PNG
untitled.PNG (225.21 KiB) Viewed 7337 times
User avatar
FreeER
Smart Inserter
Smart Inserter
Posts: 1266
Joined: Mon Feb 18, 2013 4:26 am
Contact:

Re: Control Code

Post by FreeER »

Unkown key:"type-name.name" is the default name for prototypes that do not have a locale translation, which are added by creating a 'locale' folder and inside that a folder with the two letter language code ('en' for english), and then a cfg file for the actual locale data (there can be multiple files as in the base game or a single file), inside the cfg(s) you use the headers

Code: Select all

[autoplace-control-names]
[entity-name]
[entity-description]
[technology-name]
[technology-description]
[item-group-name]
[item-name]
[item-description]
[item-limitation] (note: only used for production-module-usable-only-on-intermeidates in the base game, not sure how this header actually works to be honest)
[equipment-name]
[modifier-description] (note: tech modifiers can not be created with lua so...until that's possible this one is practically useless to modders)
[damage-type-name]
[tile-name]
[recipe-name]
[fluid-name]
And then beneath the proper header add the translations in the format: the-code-name=Name/Description shown in game
The cfg(s) must have an encoding of "UTF-8 without BOM", I find it easiest to copy an existing locale file to avoid issues with the encoding.

Mod control.lua text (ie, game.player.print, game.showmessagedialog, etc.) can be translated by using a 'script-locale' folder with a single cfg named using the two letter code (instead of nested folders), and then using game.gettext(text-code-name-in-cfg) in the control.lua instead of hard coding the string (which will pull the proper translation based on the language settings from the locales). Mod campaigns and scenarios use a folder 'locale' like the prototype locales but the same file setup as stated for the control.lua locales.

I think that covers all of the locale information :)
edit: forgot some (copied from wiki):
game.localise is the same as game.gettext but allows accessing base game localisations as well.
game.getlocaliseditemname takes an item's code-name and returns the localised name
getlocalisedentityname and getlocalisedtechnologyname, see above but for entities and technologies.
Narinik
Burner Inserter
Burner Inserter
Posts: 8
Joined: Wed Jun 11, 2014 3:49 am
Contact:

Re: Control Code

Post by Narinik »

Sweet, got it working as a generator type. but it acts kind of funny. It only updates once a second(event.tick%60) so i get this as a power graph:
Attachments
untitled.PNG
untitled.PNG (406.24 KiB) Viewed 7336 times
User avatar
FreeER
Smart Inserter
Smart Inserter
Posts: 1266
Joined: Mon Feb 18, 2013 4:26 am
Contact:

Re: Control Code

Post by FreeER »

Narinik wrote:Sweet, got it working as a generator type. but it acts kind of funny. It only updates once a second(event.tick%60) so i get this as a power graph:
yeah...not much can be done about that, other than doing it every tick (in which case you'd probably want extra code to prevent a loop over 100+ wind mills freezing the game...). Or maybe staggering when each windmill is filled, I've never tried that to be honest. Of course, while it looks funny every second, it's fairly easy to pretend that it's filling a capacitor that gets drained every second instead of feeding the power into the grid immediately...
Narinik
Burner Inserter
Burner Inserter
Posts: 8
Joined: Wed Jun 11, 2014 3:49 am
Contact:

Re: Control Code

Post by Narinik »

I think ive got it working as a generator. set the ontick to fire every tick to smooth out the graph.

also put together a quick model, that is most likely at the wrong angle.
Attachments
untitled.PNG
untitled.PNG (12.31 KiB) Viewed 7325 times
User avatar
FreeER
Smart Inserter
Smart Inserter
Posts: 1266
Joined: Mon Feb 18, 2013 4:26 am
Contact:

Re: Control Code

Post by FreeER »

nice :)
Narinik
Burner Inserter
Burner Inserter
Posts: 8
Joined: Wed Jun 11, 2014 3:49 am
Contact:

Re: Control Code

Post by Narinik »

windmill ingame, angle is a little off. got the animation working but it only plays once for some reason. is it possible to just have it play and set the animation speed?
Attachments
untitled.PNG
untitled.PNG (191.81 KiB) Viewed 7324 times
User avatar
FreeER
Smart Inserter
Smart Inserter
Posts: 1266
Joined: Mon Feb 18, 2013 4:26 am
Contact:

Re: Control Code

Post by FreeER »

Narinik wrote:got the animation working but it only plays once for some reason. is it possible to just have it play and set the animation speed?
I don't think I've had that issue before, but then I rarely work with graphics... I know there is an animation_speed property so you can try that if you're sure you've got the other properties (frame_count, line_length, etc.) right.

edit: there's also animation_speed_coefficient, structure_animation_speed_coefficient, structure_animation_movement_cooldown, used on some entities in the base game...I can't guarantee they are used for all animations however.
edit2: From looking at the source those last three are for only certain entities not all entities, but animation_speed should work for any entity with an animation.
Narinik
Burner Inserter
Burner Inserter
Posts: 8
Joined: Wed Jun 11, 2014 3:49 am
Contact:

Re: Control Code

Post by Narinik »

Switched over to being a generator, not sure why this is happening:
Attachments
untitled.PNG
untitled.PNG (297.87 KiB) Viewed 7304 times
User avatar
FreeER
Smart Inserter
Smart Inserter
Posts: 1266
Joined: Mon Feb 18, 2013 4:26 am
Contact:

Re: Control Code

Post by FreeER »

Narinik wrote:Switched over to being a generator, not sure why this is happening:
hm, no idea unless it is somehow producing a negative amount of energy (or using power instead of generating it)...
Post Reply

Return to “Modding help”