Compare commits

...

3 Commits

18 changed files with 115 additions and 437 deletions

View File

@@ -13,10 +13,6 @@
"type": "AddResource",
"targetResource": "Corruption",
"value": 3
},
{
"type": "UnlockMiracle",
"miraclesToUnlock": [ "unlock_age_of_industry" ]
}
]
}

View File

@@ -0,0 +1,18 @@
{
"name": "Divine Mandate",
"faithCost": 1500,
"followersRequired": 350,
"productionRequired": 100,
"unlockedByDefault": false,
"effects": [
{
"type": "ModifyStat",
"targetStat": "FollowersPerSecond",
"op": "Add",
"value": 0.5
},
{
"type": "DestroySelf"
}
]
}

View File

@@ -7,6 +7,7 @@
"effects": [
{
"type": "ApplyBuff",
"buffId": "gods_endurance",
"targetStat": "FaithPerFollower",
"multiplier": 2.0,
"duration": 60

View File

@@ -7,6 +7,7 @@
"effects": [
{
"type": "ApplyBuff",
"buffId": "harness_the_sun",
"targetStat": "CorruptionPerSecond",
"multiplier": -1.5,
"duration": 120

View File

@@ -0,0 +1,16 @@
{
"name": "Prosperity Boom",
"faithCost": 2000,
"followersRequired": 1200,
"productionRequired": 800,
"unlockedByDefault": false,
"effects": [
{
"type": "ApplyBuff",
"buffId": "prosperity_buff",
"targetStat": "FollowersPerSecond",
"multiplier": 5.0,
"duration": 60
}
]
}

View File

@@ -14,6 +14,7 @@
"globalization",
"harness_the_sun",
"tame_the_atom",
"prosperity_boom",
"unlock_space_age"
]
},

View File

@@ -6,7 +6,16 @@
"effects": [
{
"type": "UnlockMiracle",
"miraclesToUnlock": [ "erect_shrine", "communal_effort", "sustainable_practices", "ritual_of_cleansing", "gods_endurance", "geological_survey" ]
"miraclesToUnlock": [
"erect_shrine",
"communal_effort",
"sustainable_practices",
"ritual_of_cleansing",
"gods_endurance",
"geological_survey",
"divine_mandate",
"unlock_age_of_industry"
]
},
{ "type": "DestroySelf" }
],

View File

@@ -1,422 +0,0 @@
Miracles/blood_ritual.json
---
{
"name": "Blood Ritual",
"faithCost": 0,
"followersRequired": 25,
"productionRequired": 0,
"unlockedByDefault": true,
"effects": [
{
"type": "ConvertResource",
"fromResource": "Followers",
"fromAmount": 10,
"toResource": "Faith",
"toAmount": 250
},
{
"type": "AddResource",
"targetResource": "Corruption",
"value": 5
}
]
}
---
Miracles/bountiful_harvest.json
---
{
"name": "Bountiful Harvest",
"faithCost": 50,
"followersRequired": 0,
"unlockedByDefault": true,
"effects": [
{
"type": "AddResource",
"targetResource": "Followers",
"value": 10
},
{
"type": "AddResource",
"targetResource": "Corruption",
"value": 0.5
}
]
}
---
Miracles/communal_effort.json
---
{
"name": "Communal Effort",
"faithCost": 400,
"followersRequired": 250,
"unlockedByDefault": false,
"effects": [
{
"type": "AddResource",
"targetResource": "Production",
"value": 50
},
{
"type": "AddResource",
"targetResource": "Corruption",
"value": 3
},
{
"type": "UnlockMiracle",
"miraclesToUnlock": [ "unlock_age_of_industry" ]
}
]
}
---
Miracles/construct_vessel_frame.json
---
{
"name": "Project: Ark Frame",
"faithCost": 20000,
"followersRequired": 5000,
"unlockedByDefault": false,
"effects": [
{
"type": "ConvertResource",
"fromResource": "Production",
"fromAmount": 5000,
"toResource": "Faith",
"toAmount": 0
},
{ "type": "DestroySelf" }
]
}
---
Miracles/erect_shrine.json
---
{
"name": "Erect Shrine",
"faithCost": 750,
"followersRequired": 200,
"unlockedByDefault": false,
"effects": [
{
"type": "ModifyStat",
"targetStat": "FaithPerFollower",
"op": "Add",
"value": 0.2
},
{
"type": "AddResource",
"targetResource": "Corruption",
"value": 5
}
]
}
---
Miracles/exploit_earth.json
---
{
"name": "Exploit the Earth",
"faithCost": 500,
"followersRequired": 1200,
"unlockedByDefault": false,
"effects": [
{
"type": "AddResource",
"targetResource": "Production",
"value": 1000
},
{
"type": "AddResource",
"targetResource": "Corruption",
"value": 20
}
]
}
---
Miracles/fossil_fuel_frenzy.json
---
{
"name": "Fossil Fuel Frenzy",
"faithCost": 5000,
"followersRequired": 2000,
"productionRequired": 500,
"unlockedByDefault": false,
"effects": [
{
"type": "ModifyStat",
"targetStat": "ProductionPerSecond",
"op": "Add",
"value": 25
},
{
"type": "ModifyStat",
"targetStat": "CorruptionPerSecond",
"op": "Add",
"value": 2.5
},
{
"type": "DestroySelf"
}
]
}
---
Miracles/geological_survey.json
---
{
"name": "Geological Survey",
"faithCost": 800,
"followersRequired": 500,
"productionRequired": 0,
"unlockedByDefault": false,
"effects": [
{
"type": "AddResource",
"targetResource": "Production",
"value": 250
},
{
"type": "AddResource",
"targetResource": "Corruption",
"value": 8
}
]
}
---
Miracles/global_network.json
---
{
"name": "Global Network",
"faithCost": 15000,
"followersRequired": 5000,
"unlockedByDefault": false,
"effects": [
{
"type": "UnlockMiracle",
"miraclesToUnlock": [ "orbital_calculations", "construct_vessel_frame", "launch_ark" ]
},
{ "type": "DestroySelf" }
]
}
---
Miracles/globalization.json
---
{
"name": "Globalization",
"faithCost": 3000,
"followersRequired": 1500,
"productionRequired": 1000,
"unlockedByDefault": false,
"effects": [
{
"type": "ModifyStat",
"targetStat": "FaithPerFollower",
"op": "Multiply",
"value": 1.5
},
{
"type": "ModifyStat",
"targetStat": "CorruptionPerSecond",
"op": "Add",
"value": 0.2
},
{
"type": "DestroySelf"
}
]
}
---
Miracles/gods_endurance.json
---
{
"name": "God's Endurance",
"faithCost": 500,
"followersRequired": 400,
"productionRequired": 0,
"unlockedByDefault": false,
"effects": [
{
"type": "ApplyBuff",
"targetStat": "FaithPerFollower",
"multiplier": 2.0,
"duration": 60
}
]
}
---
Miracles/inspire_invention.json
---
{
"name": "Inspire Invention",
"faithCost": 2500,
"followersRequired": 1000,
"unlockedByDefault": false,
"effects": [
{
"type": "ModifyStat",
"targetStat": "ProductionPerSecond",
"op": "Add",
"value": 5
},
{
"type": "ModifyStat",
"targetStat": "CorruptionPerSecond",
"op": "Add",
"value": 0.5
}
]
}
---
Miracles/launch_ark.json
---
{
"name": "Launch Ark",
"faithCost": 100000,
"followersRequired": 5000,
"productionRequired": 10000,
"unlockedByDefault": false,
"effects": [
{
"type": "Win"
}
]
}
---
Miracles/orbital_calculations.json
---
{
"name": "Project: Trajectory",
"faithCost": 50000,
"followersRequired": 5000,
"unlockedByDefault": false,
"effects": [
{
"type": "UnlockMiracle",
"miraclesToUnlock": [ "launch_ark" ]
},
{ "type": "DestroySelf" }
]
}
---
Miracles/ritual_of_cleansing.json
---
{
"name": "Ritual of Cleansing",
"faithCost": 1000,
"followersRequired": 200,
"productionRequired": 100,
"unlockedByDefault": false,
"effects": [
{
"type": "AddResource",
"targetResource": "Corruption",
"value": -10
}
]
}
---
Miracles/sustainable_practices.json
---
{
"name": "Sustainable Practices",
"faithCost": 1200,
"followersRequired": 300,
"productionRequired": 150,
"unlockedByDefault": false,
"effects": [
{
"type": "ModifyStat",
"targetStat": "CorruptionPerSecond",
"op": "Add",
"value": -0.1
},
{
"type": "DestroySelf"
}
]
}
---
Miracles/unlock_age_of_industry.json
---
{
"name": "Age of Industry",
"faithCost": 1500,
"followersRequired": 750,
"productionRequired": 50,
"unlockedByDefault": false,
"effects": [
{
"type": "UnlockMiracle",
"miraclesToUnlock": [ "inspire_invention", "exploit_earth", "fossil_fuel_frenzy", "globalization" ]
},
{ "type": "DestroySelf" }
],
"advancesToAge": "The Industrial Age"
}
---
Miracles/unlock_settlement.json
---
{
"name": "Form Settlement",
"faithCost": 300,
"followersRequired": 150,
"unlockedByDefault": true,
"effects": [
{
"type": "UnlockMiracle",
"miraclesToUnlock": [ "erect_shrine", "communal_effort", "sustainable_practices", "ritual_of_cleansing", "gods_endurance", "geological_survey" ]
},
{ "type": "DestroySelf" }
],
"advancesToAge": "The Settlement Age"
}
---
Miracles/unlock_space_age.json
---
{
"name": "Age of Space",
"faithCost": 10000,
"followersRequired": 5000,
"unlockedByDefault": false,
"effects": [
{
"type": "UnlockMiracle",
"miraclesToUnlock": [ "global_network", "orbital_calculations" ]
},
{ "type": "DestroySelf" }
],
"advancesToAge": "The Space Age"
}
---

View File

@@ -85,6 +85,11 @@ public partial class MiracleButton : Button
{
missingRequirements.Add("Already unlocked subsequent powers.");
}
if (IsBuffAlreadyActive(state))
{
missingRequirements.Add("This buff is already active.");
}
if (missingRequirements.Any())
{
@@ -145,4 +150,16 @@ public partial class MiracleButton : Button
return unlockEffect.MiraclesToUnlock.All(state.IsMiracleUnlocked);
}
private bool IsBuffAlreadyActive(GameState state)
{
var buffEffect = _miracle.Effects.OfType<ApplyBuffEffect>().FirstOrDefault();
if (buffEffect == null || string.IsNullOrEmpty(buffEffect.BuffId))
{
return false;
}
return state.IsBuffActive(buffEffect.BuffId);
}
}

View File

@@ -34,16 +34,16 @@ public partial class ActiveBuffsManager : Node
var buffInstance = _activeBuffScene.Instantiate<ActiveBuffUi>();
AddChild(buffInstance);
buffInstance.SetBuff(buff);
_activeBuffUis.Add(buff.Id, buffInstance);
_activeBuffUis.Add(buff.InstanceId, buffInstance);
_buffAddedSfx?.Play();
}
private void OnBuffRemoved(Buff buff)
{
if (_activeBuffUis.TryGetValue(buff.Id, out var buffUi))
if (_activeBuffUis.TryGetValue(buff.InstanceId, out var buffUi))
{
buffUi.QueueFree();
_activeBuffUis.Remove(buff.Id);
_activeBuffUis.Remove(buff.InstanceId);
_buffRemovedSfx?.Play();
}
}

View File

@@ -6,20 +6,30 @@ namespace ParasiticGod.Scripts.Core.Effects;
[GlobalClass]
public partial class ApplyBuffEffect : Effect
{
[Export] public string BuffId { get; set; }
[Export] public Stat TargetStat { get; set; }
[Export] public float Multiplier { get; set; } = 2.0f;
[Export] public double Duration { get; set; } = 30.0;
public override void Execute(GameState gameState)
{
if (gameState.IsBuffActive(BuffId))
{
GD.Print($"Buff '{BuffId}' is already active. Cannot apply again.");
return;
}
var newBuff = new Buff
{
Name = $"{TargetStat} x{Multiplier}",
Multiplier = Multiplier,
Duration = Duration
Duration = Duration,
TargetStat = TargetStat,
BuffId = BuffId
};
gameState.ActiveBuffs.Add(newBuff);
gameState.AddActiveBuff(BuffId);
GameBus.Instance.NotifyBuffAdded(newBuff);
}

View File

@@ -4,8 +4,10 @@ namespace ParasiticGod.Scripts.Core.Effects;
public class Buff
{
public Guid Id { get; } = Guid.NewGuid(); // Unique identifier
public string Name { get; set; } // For display purposes
public Guid InstanceId { get; } = Guid.NewGuid();
public string BuffId { get; set; }
public string Name { get; set; }
public Stat TargetStat { get; set; }
public float Multiplier { get; set; } = 1.0f;
public double Duration { get; set; }
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Linq;
using ParasiticGod.Scripts.Singletons;
namespace ParasiticGod.Scripts.Core;
@@ -12,11 +13,29 @@ public class GameLogic
{
totalMultiplier *= buff.Multiplier;
}
var faithPerSecond = state.Get(Stat.Followers) * state.Get(Stat.FaithPerFollower) * totalMultiplier;
var faithMultiplier = state.ActiveBuffs
.Where(b => b.TargetStat == Stat.FaithPerFollower)
.Aggregate(1.0f, (acc, buff) => acc * buff.Multiplier);
var productionMultiplier = state.ActiveBuffs
.Where(b => b.TargetStat == Stat.ProductionPerSecond)
.Aggregate(1.0f, (acc, buff) => acc * buff.Multiplier);
var followerMultiplier = state.ActiveBuffs
.Where(b => b.TargetStat == Stat.FollowersPerSecond)
.Aggregate(1.0f, (acc, buff) => acc * buff.Multiplier);
var corruptionMultiplier = state.ActiveBuffs
.Where(b => b.TargetStat == Stat.CorruptionPerSecond)
.Aggregate(1.0f, (acc, buff) => acc * buff.Multiplier);
var faithPerSecond = state.Get(Stat.Followers) * state.Get(Stat.FaithPerFollower) * faithMultiplier;
var productionPerSecond = state.Get(Stat.ProductionPerSecond) * productionMultiplier;
var followersPerSecond = state.Get(Stat.FollowersPerSecond) * followerMultiplier;
var corruptionPerSecond = state.Get(Stat.CorruptionPerSecond) * corruptionMultiplier;
state.Modify(Stat.Faith, faithPerSecond * delta);
state.Modify(Stat.Production, state.Get(Stat.ProductionPerSecond) * delta);
state.Modify(Stat.Corruption, state.Get(Stat.CorruptionPerSecond) * delta);
state.Modify(Stat.Production, productionPerSecond * delta);
state.Modify(Stat.Corruption, corruptionPerSecond * delta);
state.Modify(Stat.Followers, followersPerSecond * delta);
for (var i = state.ActiveBuffs.Count - 1; i >= 0; i--)
{
@@ -26,6 +45,7 @@ public class GameLogic
{
GameBus.Instance.NotifyBuffRemoved(buff);
state.ActiveBuffs.RemoveAt(i);
state.RemoveActiveBuff(buff.BuffId);
}
}
}

View File

@@ -8,6 +8,7 @@ public class GameState
{
private readonly Dictionary<Stat, StatData> _stats = new();
private readonly HashSet<string> _unlockedMiracleIds = [];
private readonly HashSet<string> _activeBuffIds = [];
public List<Buff> ActiveBuffs { get; } = [];
@@ -23,6 +24,7 @@ public class GameState
Set(Stat.FaithPerFollower, 0.5);
Set(Stat.ProductionPerSecond, 0.0);
Set(Stat.CorruptionPerSecond, 0.01);
Set(Stat.FollowersPerSecond, 0);
}
public double Get(Stat stat) => _stats[stat].Value;
@@ -38,4 +40,8 @@ public class GameState
public bool IsMiracleUnlocked(string miracleId) => _unlockedMiracleIds.Contains(miracleId);
public void AddUnlockedMiracle(string miracleId) => _unlockedMiracleIds.Add(miracleId);
public void RemoveUnlockedMiracle(string miracleId) => _unlockedMiracleIds.Remove(miracleId);
public bool IsBuffActive(string buffId) => _activeBuffIds.Contains(buffId);
public void AddActiveBuff(string buffId) => _activeBuffIds.Add(buffId);
public void RemoveActiveBuff(string buffId) => _activeBuffIds.Remove(buffId);
}

View File

@@ -12,6 +12,7 @@ public class EffectDto
public double Value { get; set; }
// --- For "ApplyBuff" Effect ---
public string BuffId { get; set; }
public float Multiplier { get; set; }
public double Duration { get; set; }

View File

@@ -101,6 +101,7 @@ public static class MiracleLoader
applyBuffEffect.TargetStat = effectDto.TargetStat;
applyBuffEffect.Multiplier = effectDto.Multiplier;
applyBuffEffect.Duration = effectDto.Duration;
applyBuffEffect.BuffId = effectDto.BuffId;
break;
case ConvertResourceEffect convertResourceEffect:
convertResourceEffect.FromResource = effectDto.FromResource;

View File

@@ -11,6 +11,7 @@ public enum Stat
// Passive Generation Stats
ProductionPerSecond,
CorruptionPerSecond,
FollowersPerSecond,
// Modifying Stats
FaithPerFollower

View File

@@ -55,7 +55,7 @@ public partial class GameBus : Node
{
_gameLogic.UpdateGameState(_gameState, delta);
StateChanged?.Invoke(_gameState);
if (_gameState.Get(Stat.Corruption) >= 100)
{
GetTree().ChangeSceneToPacked(_gameOverScene);