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

Research triggers #350

Merged
merged 4 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion Yafc.Model/Data/DataClasses.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,16 @@ public enum RecipeFlags {
/// <summary>Set when the technology has a research trigger to craft an item</summary>
HasResearchTriggerCraft = 1 << 4,
/// <summary>Set when the technology has a research trigger to capture a spawner</summary>
HasResearchTriggerCaptureEntity = 1 << 8,
HasResearchTriggerCaptureEntity = 1 << 5,
/// <summary>Set when the technology has a research trigger to mine an entity (including a resource)</summary>
HasResearchTriggerMineEntity = 1 << 6,
/// <summary>Set when the technology has a research trigger to build an entity</summary>
HasResearchTriggerBuildEntity = 1 << 7,
/// <summary>Set when the technology has a research trigger to launch a space platform starter pack</summary>
HasResearchTriggerCreateSpacePlatform = 1 << 8,

HasResearchTriggerMask = HasResearchTriggerCraft | HasResearchTriggerCaptureEntity | HasResearchTriggerMineEntity | HasResearchTriggerBuildEntity
| HasResearchTriggerCreateSpacePlatform,
}

public abstract class RecipeOrTechnology : FactorioObject {
Expand Down Expand Up @@ -690,6 +699,7 @@ public class Technology : RecipeOrTechnology { // Technology is very similar to
public Technology[] prerequisites { get; internal set; } = [];
public List<Recipe> unlockRecipes { get; internal set; } = [];
public Dictionary<Recipe, float> changeRecipeProductivity { get; internal set; } = [];
internal bool unlocksFluidMining { get; set; }
internal override FactorioObjectSortOrder sortingOrder => FactorioObjectSortOrder.Technologies;
public override string type => "Technology";
/// <summary>
Expand All @@ -708,6 +718,17 @@ public override void GetDependencies(IDependencyCollector collector, List<Factor
if (prerequisites.Length > 0) {
collector.Add(prerequisites, DependencyList.Flags.TechnologyPrerequisites);
}
if (flags.HasFlag(RecipeFlags.HasResearchTriggerMineEntity)) {
// If we have a mining mechanic, use that as the source; otherwise just use the entity.
collector.Add([.. triggerEntities.Select(e => Database.mechanics.all.SingleOrDefault(m => m.source == e) ?? (FactorioObject)e)], DependencyList.Flags.Source);
}
if (flags.HasFlag(RecipeFlags.HasResearchTriggerBuildEntity)) {
collector.Add(triggerEntities, DependencyList.Flags.Source);
}
if (flags.HasFlag(RecipeFlags.HasResearchTriggerCreateSpacePlatform)) {
var items = Database.items.all.Where(i => i.factorioType == "space-platform-starter-pack");
collector.Add([.. items.Select(i => Database.objectsByTypeName["Mechanics.launch." + i.name])], DependencyList.Flags.Source);
}

if (hidden && !enabled) {
collector.Add(Array.Empty<FactorioId>(), DependencyList.Flags.Hidden);
Expand Down
2 changes: 1 addition & 1 deletion Yafc.Parser/Data/FactorioDataDeserializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ void readTrigger(LuaTable table) {
}
}

if (table.Get("send_to_orbit_mode", "not-sendable") != "not-sendable") {
if (table.Get("send_to_orbit_mode", "not-sendable") != "not-sendable" || item.factorioType == "space-platform-starter-pack") {
Product[] launchProducts;
if (table.Get("rocket_launch_products", out LuaTable? products)) {
launchProducts = products.ArrayElements<LuaTable>().Select(LoadProduct(item.typeDotName, item.stackSize)).ToArray();
Expand Down
5 changes: 5 additions & 0 deletions Yafc.Parser/Data/FactorioDataDeserializer_Entity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,11 @@ void parseEffect(LuaTable effect) {
if (minable.Get("required_fluid", out string? requiredFluid)) {
_ = minable.Get("fluid_amount", out float amount);
recipe.ingredients = [new Ingredient(GetObject<Fluid>(requiredFluid), amount / 10f)]; // 10x difference is correct but why?
foreach (var tech in allObjects.OfType<Technology>().Where(t => t.unlocksFluidMining)) {
// Maybe incorrect: Leave the mining recipe enabled if no technologies unlock fluid mining
recipe.enabled = false;
tech.unlockRecipes.Add(recipe);
}
}
else {
recipe.ingredients = [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,10 @@ private void LoadTechnologyData(Technology technology, LuaTable table, ErrorColl
}
break;
}
case "mining-with-fluid": {
technology.unlocksFluidMining = true;
break;
}
}
}
}
Expand Down Expand Up @@ -278,7 +282,7 @@ private void LoadResearchTrigger(LuaTable researchTriggerTable, ref Technology t
switch (type) {
case "craft-item":
if (!researchTriggerTable.Get("item", out string? craftItemName)) {
errorCollector.Error($"Research trigger craft-item of {technology.typeDotName} does not have a item field", ErrorSeverity.MinorDataLoss);
errorCollector.Error($"Research trigger {type} of {technology.typeDotName} does not have an item field", ErrorSeverity.MinorDataLoss);
break;
}
float craftCount = researchTriggerTable.Get("count", 1);
Expand All @@ -288,7 +292,7 @@ private void LoadResearchTrigger(LuaTable researchTriggerTable, ref Technology t
break;
case "capture-spawner":
technology.flags = RecipeFlags.HasResearchTriggerCaptureEntity;
if (researchTriggerTable.Get<string>("entity") is string entity) {
if (researchTriggerTable.Get("entity", out string? entity)) {
technology.getTriggerEntities = new(() => [((Entity)Database.objectsByTypeName["Entity." + entity])]);
}
else {
Expand All @@ -298,6 +302,25 @@ private void LoadResearchTrigger(LuaTable researchTriggerTable, ref Technology t
.ToList());
}
break;
case "mine-entity":
technology.flags = RecipeFlags.HasResearchTriggerMineEntity;
if (!researchTriggerTable.Get("entity", out entity)) {
errorCollector.Error($"Research trigger {type} of {technology.typeDotName} does not have an entity field", ErrorSeverity.MinorDataLoss);
break;
}
technology.getTriggerEntities = new(() => [((Entity)Database.objectsByTypeName["Entity." + entity])]);
break;
case "build-entity":
technology.flags = RecipeFlags.HasResearchTriggerBuildEntity;
if (!researchTriggerTable.Get("entity", out entity)) {
errorCollector.Error($"Research trigger {type} of {technology.typeDotName} does not have an entity field", ErrorSeverity.MinorDataLoss);
break;
}
technology.getTriggerEntities = new(() => [((Entity)Database.objectsByTypeName["Entity." + entity])]);
break;
case "create-space-platform":
technology.flags = RecipeFlags.HasResearchTriggerCreateSpacePlatform;
break;
default:
errorCollector.Error($"Research trigger of {technology.typeDotName} has an unsupported type {type}", ErrorSeverity.MinorDataLoss);
break;
Expand Down
10 changes: 10 additions & 0 deletions Yafc.Parser/LuaContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ private int Log(IntPtr lua) {
private void Pop(int popc) => lua_settop(L, lua_gettop(L) - popc);

public List<object?> ArrayElements(int refId) {
ObjectDisposedException.ThrowIf(L == IntPtr.Zero, this);
GetReg(refId); // 1
lua_pushnil(L);
List<object?> list = [];
Expand All @@ -247,6 +248,7 @@ private int Log(IntPtr lua) {
}

public Dictionary<object, object?> ObjectElements(int refId) {
ObjectDisposedException.ThrowIf(L == IntPtr.Zero, this);
GetReg(refId); // 1
lua_pushnil(L);
Dictionary<object, object?> dict = [];
Expand All @@ -265,26 +267,31 @@ private int Log(IntPtr lua) {
}

public LuaTable NewTable() {
ObjectDisposedException.ThrowIf(L == IntPtr.Zero, this);
lua_createtable(L, 0, 0);
return new LuaTable(this, luaL_ref(L, REGISTRY));
}

public object? GetGlobal(string name) {
ObjectDisposedException.ThrowIf(L == IntPtr.Zero, this);
_ = lua_getglobal(L, name); // 1
return PopManagedValue(1);
}

public void SetGlobal(string name, object value) {
ObjectDisposedException.ThrowIf(L == IntPtr.Zero, this);
PushManagedObject(value);
lua_setglobal(L, name);
}
public object? GetValue(int refId, int idx) {
ObjectDisposedException.ThrowIf(L == IntPtr.Zero, this);
GetReg(refId); // 1
lua_rawgeti(L, -1, idx); // 2
return PopManagedValue(2);
}

public object? GetValue(int refId, string idx) {
ObjectDisposedException.ThrowIf(L == IntPtr.Zero, this);
GetReg(refId); // 1
_ = lua_pushstring(L, idx); // 2
lua_rawget(L, -2); // 3
Expand Down Expand Up @@ -347,6 +354,7 @@ private void PushManagedObject(object? value) {
}

public void SetValue(int refId, string idx, object? value) {
ObjectDisposedException.ThrowIf(L == IntPtr.Zero, this);
GetReg(refId); // 1;
_ = lua_pushstring(L, idx); // 2
PushManagedObject(value); // 3;
Expand All @@ -355,6 +363,7 @@ public void SetValue(int refId, string idx, object? value) {
}

public void SetValue(int refId, int idx, object? value) {
ObjectDisposedException.ThrowIf(L == IntPtr.Zero, this);
GetReg(refId); // 1;
PushManagedObject(value); // 2;
lua_rawseti(L, -2, idx);
Expand Down Expand Up @@ -472,6 +481,7 @@ private byte[] GetData(int index) {
private string GetString(int index) => Encoding.UTF8.GetString(GetData(index));

public int Exec(ReadOnlySpan<byte> chunk, string mod, string name, int argument = 0) {
ObjectDisposedException.ThrowIf(L == IntPtr.Zero, this);
// since lua cuts file name to a few dozen symbols, add index to start of every name
fullChunkNames.Add((mod, name));
name = fullChunkNames.Count - 1 + " " + name;
Expand Down
38 changes: 25 additions & 13 deletions Yafc/Widgets/ObjectTooltip.cs
Original file line number Diff line number Diff line change
Expand Up @@ -500,8 +500,11 @@ private static void BuildRecipe(RecipeOrTechnology recipe, ImGui gui) {
private static void BuildTechnology(Technology technology, ImGui gui) {
bool isResearchTriggerCraft = technology.flags.HasFlag(RecipeFlags.HasResearchTriggerCraft);
bool isResearchTriggerCapture = technology.flags.HasFlag(RecipeFlags.HasResearchTriggerCaptureEntity);
bool isResearchTriggerMine = technology.flags.HasFlag(RecipeFlags.HasResearchTriggerMineEntity);
bool isResearchTriggerBuild = technology.flags.HasFlag(RecipeFlags.HasResearchTriggerBuildEntity);
bool isResearchTriggerPlatform = technology.flags.HasFlag(RecipeFlags.HasResearchTriggerCreateSpacePlatform);

if (!isResearchTriggerCraft && !isResearchTriggerCapture) {
if (!technology.flags.HasFlagAny(RecipeFlags.HasResearchTriggerMask)) {
BuildRecipe(technology, gui);
}

Expand All @@ -527,19 +530,28 @@ private static void BuildTechnology(Technology technology, ImGui gui) {
}
}
else if (isResearchTriggerCapture) {
BuildSubHeader(gui, "Entity capture required");
BuildSubHeader(gui, technology.triggerEntities.Count == 1 ? "Capture this entity" : "Capture any entity");
using (gui.EnterGroup(contentPadding)) {
if (technology.triggerEntities.Count == 1) {
gui.BuildText("Capture:");
gui.BuildFactorioObjectButtonWithText(technology.triggerEntities[0]);

}
else {
gui.BuildText("Capture one of:");
foreach (var entity in technology.triggerEntities) {
gui.BuildFactorioObjectButtonWithText(entity);
}
}
BuildIconRow(gui, technology.triggerEntities, 2);
}
}
else if (isResearchTriggerMine) {
BuildSubHeader(gui, technology.triggerEntities.Count == 1 ? "Mine this entity" : "Mine any entity");
using (gui.EnterGroup(contentPadding)) {
BuildIconRow(gui, technology.triggerEntities, 2);
}
}
else if (isResearchTriggerBuild) {
BuildSubHeader(gui, technology.triggerEntities.Count == 1 ? "Build this entity" : "Build any entity");
using (gui.EnterGroup(contentPadding)) {
BuildIconRow(gui, technology.triggerEntities, 2);
}
}
else if (isResearchTriggerPlatform) {
List<Item> items = Database.items.all.Where(i => i.factorioType == "space-platform-starter-pack").ToList();
BuildSubHeader(gui, items.Count == 1 ? "Launch this item" : "Launch any item");
using (gui.EnterGroup(contentPadding)) {
BuildIconRow(gui, items, 2);
}
}

Expand Down
4 changes: 4 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ Date:
Features:
- (SA) Process accessiblilty of captured spawners, which also fixes biter eggs and subsequent Gleba recipes.
- (SA) Add support for the capture-spawner technology trigger.
- Add the remaining research triggers and the mining-with-fluid research effect to the dependency/milestone
analysis.
Internal changes:
- Using the LuaContext after it is freed now produces a better error.
----------------------------------------------------------------------------------------------------------------------
Version: 2.2.0
Date: November 6th 2024
Expand Down