Compare commits
26 Commits
cd715a24cb
...
master
Author | SHA1 | Date | |
---|---|---|---|
f81a237f01 | |||
47281a098d | |||
5dd8e6fff9 | |||
0747595f2b | |||
2bf0f4a360 | |||
bdcc0d5089 | |||
1b3657b03a | |||
6d8139fd44 | |||
cdd944b9b5 | |||
c0d18507e3 | |||
7e8ccb061a | |||
e7247ee715 | |||
048688da45 | |||
3d48d7e9d6 | |||
33c182e623 | |||
4ceb63e49c | |||
0fbd49f36a | |||
8ae0e7e80c | |||
73a89b736e | |||
a5ab466df9 | |||
ce8fd41a93 | |||
283adc02c9 | |||
f840b98fd0 | |||
07995821f5 | |||
846c3a6d83 | |||
9da3b02a46 |
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -1,2 +1,4 @@
|
||||
# Normalize EOL for all files that Git considers text files.
|
||||
* text=auto eol=lf
|
||||
addons/* linguist-vendored
|
||||
*.gd linguist-vendored
|
||||
|
15
Dialogue/tutorial.dialogue
Normal file
15
Dialogue/tutorial.dialogue
Normal file
@@ -0,0 +1,15 @@
|
||||
~ start
|
||||
Narrator: You are a newborn god, a consciousness adrift in the cosmos.
|
||||
|
||||
Narrator: Below you, a nascent world teems with life. A small tribe of followers has begun to worship you. Their belief is your existence.
|
||||
|
||||
Narrator: Your goal is simple: nurture this civilization until they can reach for the stars and carry you with them.
|
||||
|
||||
Narrator: Grant them miracles to grow their population and advance their industry. Click the buttons on the right to see your powers.
|
||||
|
||||
Narrator: But be warned... your divine presence is a paradox. Every miracle that helps your followers also poisons their world, corrupting it.
|
||||
|
||||
Narrator: If the world's corruption reaches 100%, it will be consumed, and you along with it.
|
||||
|
||||
Narrator: Guide them. Grow them. But do not destroy them before they can escape. Their fate, and yours, is in your hands.
|
||||
=> END
|
16
Dialogue/tutorial.dialogue.import
Normal file
16
Dialogue/tutorial.dialogue.import
Normal file
@@ -0,0 +1,16 @@
|
||||
[remap]
|
||||
|
||||
importer="dialogue_manager"
|
||||
importer_version=15
|
||||
type="Resource"
|
||||
uid="uid://dxgpvgx7axp88"
|
||||
path="res://.godot/imported/tutorial.dialogue-20e92c929aa826a6ba83a2adbbdba7f9.tres"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Dialogue/tutorial.dialogue"
|
||||
dest_files=["res://.godot/imported/tutorial.dialogue-20e92c929aa826a6ba83a2adbbdba7f9.tres"]
|
||||
|
||||
[params]
|
||||
|
||||
defaults=true
|
Binary file not shown.
BIN
Fonts/Playful Boxes.otf
Normal file
BIN
Fonts/Playful Boxes.otf
Normal file
Binary file not shown.
@@ -2,13 +2,13 @@
|
||||
|
||||
importer="font_data_dynamic"
|
||||
type="FontFile"
|
||||
uid="uid://cxiv86ha7xb6"
|
||||
path="res://.godot/imported/comicz.ttf-66a741130a0c8d7e12d809da56e73bbf.fontdata"
|
||||
uid="uid://wofoiaejxgsp"
|
||||
path="res://.godot/imported/Playful Boxes.otf-113f6b887ec1f2b8d73de65734580dbc.fontdata"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Fonts/comicz.ttf"
|
||||
dest_files=["res://.godot/imported/comicz.ttf-66a741130a0c8d7e12d809da56e73bbf.fontdata"]
|
||||
source_file="res://Fonts/Playful Boxes.otf"
|
||||
dest_files=["res://.godot/imported/Playful Boxes.otf-113f6b887ec1f2b8d73de65734580dbc.fontdata"]
|
||||
|
||||
[params]
|
||||
|
BIN
Fonts/comici.ttf
BIN
Fonts/comici.ttf
Binary file not shown.
BIN
Fonts/comicz.ttf
BIN
Fonts/comicz.ttf
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,35 +0,0 @@
|
||||
[remap]
|
||||
|
||||
importer="font_data_dynamic"
|
||||
type="FontFile"
|
||||
uid="uid://cafeq0txk8o1p"
|
||||
path="res://.godot/imported/design.graffiti.comicsansmsgras.ttf-ab7fcd3a4b4dcda31884c957a19862a4.fontdata"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Fonts/design.graffiti.comicsansmsgras.ttf"
|
||||
dest_files=["res://.godot/imported/design.graffiti.comicsansmsgras.ttf-ab7fcd3a4b4dcda31884c957a19862a4.fontdata"]
|
||||
|
||||
[params]
|
||||
|
||||
Rendering=null
|
||||
antialiasing=1
|
||||
generate_mipmaps=false
|
||||
disable_embedded_bitmaps=true
|
||||
multichannel_signed_distance_field=false
|
||||
msdf_pixel_range=8
|
||||
msdf_size=48
|
||||
allow_system_fallback=true
|
||||
force_autohinter=false
|
||||
hinting=1
|
||||
subpixel_positioning=4
|
||||
keep_rounding_remainders=true
|
||||
oversampling=0.0
|
||||
Fallbacks=null
|
||||
fallbacks=[]
|
||||
Compress=null
|
||||
compress=true
|
||||
preload=[]
|
||||
language_support={}
|
||||
script_support={}
|
||||
opentype_features={}
|
37
Mods/Events/a_plague_descends.json
Normal file
37
Mods/Events/a_plague_descends.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"id": "event_plague_descends",
|
||||
"title": "A Plague Descends",
|
||||
"description": "A virulent disease is sweeping through the population, sickening your followers and halting progress. Drastic measures may be required.",
|
||||
"meanTimeToHappen": 300,
|
||||
"trigger": {
|
||||
"minFollowers": 500,
|
||||
"maxCorruption": 80
|
||||
},
|
||||
"options": [
|
||||
{
|
||||
"text": "We must sacrifice the sick.",
|
||||
"tooltip": "Immediately lose 20% of your Followers, but the plague is stopped.",
|
||||
"effects": [
|
||||
{
|
||||
"type": "ModifyStat",
|
||||
"targetStat": "Followers",
|
||||
"op": "Multiply",
|
||||
"value": 0.8
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "We will pray for their recovery.",
|
||||
"tooltip": "Lose all passive Follower generation for 120 seconds as the plague runs its course.",
|
||||
"effects": [
|
||||
{
|
||||
"type": "ApplyBuff",
|
||||
"buffId": "plague_debuff",
|
||||
"targetStat": "FollowersPerSecond",
|
||||
"multiplier": 0,
|
||||
"duration": 120
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
34
Mods/Events/a_prophets_rise.json
Normal file
34
Mods/Events/a_prophets_rise.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"id": "event_prophets_rise",
|
||||
"title": "A Prophet's Rise",
|
||||
"description": "A charismatic leader has emerged among your followers, inspiring them to new heights of devotion and ingenuity. How shall you guide their efforts?",
|
||||
"meanTimeToHappen": 200,
|
||||
"trigger": {
|
||||
"minFollowers": 800,
|
||||
"maxCorruption": 60
|
||||
},
|
||||
"options": [
|
||||
{
|
||||
"text": "Focus their zeal on industry.",
|
||||
"tooltip": "Gain a large, one-time boost to Production.",
|
||||
"effects": [
|
||||
{
|
||||
"type": "AddResource",
|
||||
"targetResource": "Production",
|
||||
"value": 750
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "Inspire a population boom.",
|
||||
"tooltip": "Gain a large, one-time boost to Followers.",
|
||||
"effects": [
|
||||
{
|
||||
"type": "AddResource",
|
||||
"targetResource": "Followers",
|
||||
"value": 250
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
38
Mods/Events/divine_inspiration.json
Normal file
38
Mods/Events/divine_inspiration.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"id": "event_divine_inspiration",
|
||||
"title": "Divine Inspiration",
|
||||
"description": "You feel a surge of pure cosmic energy. You can channel this power to inspire your followers in their efforts to either build or cleanse.",
|
||||
"meanTimeToHappen": 250,
|
||||
"trigger": {
|
||||
"minFollowers": 1500,
|
||||
"maxCorruption": 75
|
||||
},
|
||||
"options": [
|
||||
{
|
||||
"text": "Inspire frantic construction.",
|
||||
"tooltip": "Greatly increases all passive Production for 2 minutes.",
|
||||
"effects": [
|
||||
{
|
||||
"type": "ApplyBuff",
|
||||
"buffId": "inspiration_production_buff",
|
||||
"targetStat": "ProductionPerSecond",
|
||||
"multiplier": 3,
|
||||
"duration": 120
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "Inspire planetary healing.",
|
||||
"tooltip": "Greatly reduces all passive Corruption for 2 minutes.",
|
||||
"effects": [
|
||||
{
|
||||
"type": "ApplyBuff",
|
||||
"buffId": "inspiration_cleansing_buff",
|
||||
"targetStat": "CorruptionPerSecond",
|
||||
"multiplier": -2,
|
||||
"duration": 120
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
24
Mods/Events/doomsday.json
Normal file
24
Mods/Events/doomsday.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"id": "event_dooms_day",
|
||||
"title": "DOOMS DAY!",
|
||||
"description": "A catastrophic event is unfolding, threatening to annihilate everything in its path",
|
||||
"meanTimeToHappen": 2000,
|
||||
"trigger": {
|
||||
"minFollowers": 500,
|
||||
"maxCorruption": 99
|
||||
},
|
||||
"options": [
|
||||
{
|
||||
"text": "NOT TODAY...",
|
||||
"tooltip": "Increase Corruption by 200. (Instant game over)",
|
||||
"effects": [
|
||||
{
|
||||
"type": "ModifyStat",
|
||||
"targetStat": "Corruption",
|
||||
"op": "Add",
|
||||
"value": 200
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
35
Mods/Events/ecological_collapse.json
Normal file
35
Mods/Events/ecological_collapse.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"id": "event_ecological_collapse",
|
||||
"title": "Ecological Collapse",
|
||||
"description": "The planet's ecosystem has reached a breaking point. Widespread famine and resource scarcity are imminent. We must choose what to prioritize for survival.",
|
||||
"meanTimeToHappen": 400,
|
||||
"trigger": {
|
||||
"minFollowers": 3000,
|
||||
"maxCorruption": 85
|
||||
},
|
||||
"options": [
|
||||
{
|
||||
"text": "Ration food for the workers.",
|
||||
"tooltip": "Maintain your production, but lose a significant number of followers to famine.",
|
||||
"effects": [
|
||||
{
|
||||
"type": "AddResource",
|
||||
"targetResource": "Followers",
|
||||
"value": -1000
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "Divert all efforts to food production.",
|
||||
"tooltip": "Save your population, but suffer a permanent blow to your industrial efficiency.",
|
||||
"effects": [
|
||||
{
|
||||
"type": "ModifyStat",
|
||||
"targetStat": "ProductionPerFollower",
|
||||
"op": "Multiply",
|
||||
"value": 0.75
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
23
Mods/Events/good_harvest.json
Normal file
23
Mods/Events/good_harvest.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"id": "event_good_harvest",
|
||||
"title": "Bountiful Harvest",
|
||||
"description": "A miraculous confluence of weather and soil fertility has led to an unexpectedly large harvest. Our granaries are overflowing!",
|
||||
"meanTimeToHappen": 120,
|
||||
"trigger": {
|
||||
"minFollowers": 100,
|
||||
"maxCorruption": 50
|
||||
},
|
||||
"options": [
|
||||
{
|
||||
"text": "A true blessing!",
|
||||
"tooltip": "Gain a large amount (500) of Faith.",
|
||||
"effects": [
|
||||
{
|
||||
"type": "AddResource",
|
||||
"targetResource": "Faith",
|
||||
"value": 500
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
24
Mods/Events/industrial_breakthrough.json
Normal file
24
Mods/Events/industrial_breakthrough.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"id": "event_industrial_breakthrough",
|
||||
"title": "Industrial Breakthrough",
|
||||
"description": "One of your followers has made a revolutionary discovery in manufacturing techniques! This will permanently increase the efficiency of all future industry.",
|
||||
"meanTimeToHappen": 240,
|
||||
"trigger": {
|
||||
"minFollowers": 1000,
|
||||
"maxCorruption": 70
|
||||
},
|
||||
"options": [
|
||||
{
|
||||
"text": "A brilliant mind!",
|
||||
"tooltip": "Permanently increases base Production Per Second by 2.",
|
||||
"effects": [
|
||||
{
|
||||
"type": "ModifyStat",
|
||||
"targetStat": "ProductionPerSecond",
|
||||
"op": "Add",
|
||||
"value": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
35
Mods/Events/unstable_rift.json
Normal file
35
Mods/Events/unstable_rift.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"id": "event_unstable_rift",
|
||||
"title": "Unstable Rift",
|
||||
"description": "The planet groans under the weight of your power. A rift of pure corruption has torn open, spewing filth into the environment.",
|
||||
"meanTimeToHappen": 180,
|
||||
"trigger": {
|
||||
"minFollowers": 2000,
|
||||
"maxCorruption": 90
|
||||
},
|
||||
"options": [
|
||||
{
|
||||
"text": "Seal it with our power.",
|
||||
"tooltip": "Lose a large amount of Production to seal the rift.",
|
||||
"effects": [
|
||||
{
|
||||
"type": "AddResource",
|
||||
"targetResource": "Production",
|
||||
"value": -1000
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "This is a necessary evil.",
|
||||
"tooltip": "The rift remains, permanently increasing passive Corruption gain.",
|
||||
"effects": [
|
||||
{
|
||||
"type": "ModifyStat",
|
||||
"targetStat": "CorruptionPerSecond",
|
||||
"op": "Add",
|
||||
"value": 0.5
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
21
Mods/Miracles/blood_ritual.json
Normal file
21
Mods/Miracles/blood_ritual.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "Blood Ritual",
|
||||
"faithCost": 0,
|
||||
"followersRequired": 25,
|
||||
"productionRequired": 0,
|
||||
"unlockedByDefault": true,
|
||||
"effects": [
|
||||
{
|
||||
"type": "ConvertResource",
|
||||
"fromResource": "Followers",
|
||||
"fromAmount": 10,
|
||||
"toResource": "Faith",
|
||||
"toAmount": 100
|
||||
},
|
||||
{
|
||||
"type": "AddResource",
|
||||
"targetResource": "Corruption",
|
||||
"value": 2
|
||||
}
|
||||
]
|
||||
}
|
@@ -1,18 +1,18 @@
|
||||
{
|
||||
"name": "Bountiful Harvest",
|
||||
"faithCost": 50,
|
||||
"faithCost": 10,
|
||||
"followersRequired": 0,
|
||||
"unlockedByDefault": true,
|
||||
"effects": [
|
||||
{
|
||||
"type": "AddResource",
|
||||
"targetResource": "Followers",
|
||||
"value": 100
|
||||
"value": 5
|
||||
},
|
||||
{
|
||||
"type": "AddResource",
|
||||
"targetResource": "Corruption",
|
||||
"value": 0.02
|
||||
"value": 0.2
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@@ -1,22 +1,18 @@
|
||||
{
|
||||
"name": "Communal Effort",
|
||||
"faithCost": 400,
|
||||
"faithCost": 40,
|
||||
"followersRequired": 250,
|
||||
"unlockedByDefault": false,
|
||||
"effects": [
|
||||
{
|
||||
"type": "AddResource",
|
||||
"targetResource": "Production",
|
||||
"value": 50
|
||||
"value": 15
|
||||
},
|
||||
{
|
||||
"type": "AddResource",
|
||||
"targetResource": "Corruption",
|
||||
"value": 3
|
||||
},
|
||||
{
|
||||
"type": "UnlockMiracle",
|
||||
"miraclesToUnlock": [ "unlock_age_of_industry" ]
|
||||
"value": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
|
18
Mods/Miracles/craftmenship.json
Normal file
18
Mods/Miracles/craftmenship.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "Craftsmanship",
|
||||
"faithCost": 80,
|
||||
"followersRequired": 300,
|
||||
"productionRequired": 25,
|
||||
"unlockedByDefault": false,
|
||||
"effects": [
|
||||
{
|
||||
"type": "ModifyStat",
|
||||
"targetStat": "ProductionPerFollower",
|
||||
"op": "Add",
|
||||
"value": 0.01
|
||||
},
|
||||
{
|
||||
"type": "DestroySelf"
|
||||
}
|
||||
]
|
||||
}
|
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"name": "Divine Frenzy",
|
||||
"faithCost": 200,
|
||||
"followersRequired": 50,
|
||||
"unlockedByDefault": true,
|
||||
"effects": [
|
||||
{
|
||||
"type": "ApplyBuff",
|
||||
"targetBuffStat": "FaithPerFollower",
|
||||
"multiplier": 2.0,
|
||||
"duration": 30
|
||||
}
|
||||
]
|
||||
}
|
18
Mods/Miracles/divine_mandate.json
Normal file
18
Mods/Miracles/divine_mandate.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "Divine Mandate",
|
||||
"faithCost": 300,
|
||||
"followersRequired": 350,
|
||||
"productionRequired": 100,
|
||||
"unlockedByDefault": false,
|
||||
"effects": [
|
||||
{
|
||||
"type": "ModifyStat",
|
||||
"targetStat": "FollowersPerSecond",
|
||||
"op": "Add",
|
||||
"value": 0.1
|
||||
},
|
||||
{
|
||||
"type": "DestroySelf"
|
||||
}
|
||||
]
|
||||
}
|
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"name": "Divine Sacrifice",
|
||||
"faithCost": 250,
|
||||
"unlockedByDefault": false,
|
||||
"followersRequired": 100,
|
||||
"effects": [
|
||||
{
|
||||
"type": "ConvertResource",
|
||||
"fromResource": "Followers",
|
||||
"fromAmount": 50,
|
||||
"toResource": "Faith",
|
||||
"toAmount": 1000
|
||||
}
|
||||
]
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Erect Shrine",
|
||||
"faithCost": 750,
|
||||
"faithCost": 60,
|
||||
"followersRequired": 200,
|
||||
"unlockedByDefault": false,
|
||||
"effects": [
|
||||
@@ -8,12 +8,12 @@
|
||||
"type": "ModifyStat",
|
||||
"targetStat": "FaithPerFollower",
|
||||
"op": "Add",
|
||||
"value": 0.2
|
||||
"value": 0.05
|
||||
},
|
||||
{
|
||||
"type": "AddResource",
|
||||
"targetResource": "Corruption",
|
||||
"value": 5
|
||||
"value": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@@ -1,18 +1,18 @@
|
||||
{
|
||||
"name": "Exploit the Earth",
|
||||
"faithCost": 500,
|
||||
"faithCost": 200,
|
||||
"followersRequired": 1200,
|
||||
"unlockedByDefault": false,
|
||||
"effects": [
|
||||
{
|
||||
"type": "AddResource",
|
||||
"targetResource": "Production",
|
||||
"value": 1000
|
||||
"value": 300
|
||||
},
|
||||
{
|
||||
"type": "AddResource",
|
||||
"targetResource": "Corruption",
|
||||
"value": 20
|
||||
"value": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
|
24
Mods/Miracles/fossil_fuel_frenzy.json
Normal file
24
Mods/Miracles/fossil_fuel_frenzy.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "Fossil Fuel Frenzy",
|
||||
"faithCost": 2000,
|
||||
"followersRequired": 2000,
|
||||
"productionRequired": 500,
|
||||
"unlockedByDefault": false,
|
||||
"effects": [
|
||||
{
|
||||
"type": "ModifyStat",
|
||||
"targetStat": "ProductionPerSecond",
|
||||
"op": "Add",
|
||||
"value": 8
|
||||
},
|
||||
{
|
||||
"type": "ModifyStat",
|
||||
"targetStat": "CorruptionPerSecond",
|
||||
"op": "Add",
|
||||
"value": 2
|
||||
},
|
||||
{
|
||||
"type": "DestroySelf"
|
||||
}
|
||||
]
|
||||
}
|
19
Mods/Miracles/geological_survey.json
Normal file
19
Mods/Miracles/geological_survey.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "Geological Survey",
|
||||
"faithCost": 120,
|
||||
"followersRequired": 500,
|
||||
"productionRequired": 0,
|
||||
"unlockedByDefault": false,
|
||||
"effects": [
|
||||
{
|
||||
"type": "AddResource",
|
||||
"targetResource": "Production",
|
||||
"value": 80
|
||||
},
|
||||
{
|
||||
"type": "AddResource",
|
||||
"targetResource": "Corruption",
|
||||
"value": 2
|
||||
}
|
||||
]
|
||||
}
|
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "Global Network",
|
||||
"faithCost": 15000,
|
||||
"faithCost": 4000,
|
||||
"followersRequired": 5000,
|
||||
"unlockedByDefault": false,
|
||||
"effects": [
|
||||
{
|
||||
"type": "UnlockMiracle",
|
||||
"miraclesToUnlock": [ "orbital_calculations", "construct_vessel_frame", "launch_ark" ]
|
||||
"miraclesToUnlock": ["orbital_calculations", "construct_vessel_frame"]
|
||||
},
|
||||
{ "type": "DestroySelf" }
|
||||
]
|
||||
|
24
Mods/Miracles/globalization.json
Normal file
24
Mods/Miracles/globalization.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "Globalization",
|
||||
"faithCost": 1200,
|
||||
"followersRequired": 1500,
|
||||
"productionRequired": 1000,
|
||||
"unlockedByDefault": false,
|
||||
"effects": [
|
||||
{
|
||||
"type": "ModifyStat",
|
||||
"targetStat": "FaithPerFollower",
|
||||
"op": "Multiply",
|
||||
"value": 1.2
|
||||
},
|
||||
{
|
||||
"type": "ModifyStat",
|
||||
"targetStat": "CorruptionPerSecond",
|
||||
"op": "Add",
|
||||
"value": 0.5
|
||||
},
|
||||
{
|
||||
"type": "DestroySelf"
|
||||
}
|
||||
]
|
||||
}
|
16
Mods/Miracles/gods_endurance.json
Normal file
16
Mods/Miracles/gods_endurance.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "God's Endurance",
|
||||
"faithCost": 100,
|
||||
"followersRequired": 400,
|
||||
"productionRequired": 0,
|
||||
"unlockedByDefault": false,
|
||||
"effects": [
|
||||
{
|
||||
"type": "ApplyBuff",
|
||||
"buffId": "gods_endurance",
|
||||
"targetStat": "FaithPerFollower",
|
||||
"multiplier": 1.5,
|
||||
"duration": 30
|
||||
}
|
||||
]
|
||||
}
|
16
Mods/Miracles/harness_the_sun.json
Normal file
16
Mods/Miracles/harness_the_sun.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "Harness the Sun",
|
||||
"faithCost": 1200,
|
||||
"followersRequired": 2500,
|
||||
"productionRequired": 2000,
|
||||
"unlockedByDefault": false,
|
||||
"effects": [
|
||||
{
|
||||
"type": "ApplyBuff",
|
||||
"buffId": "harness_the_sun",
|
||||
"targetStat": "CorruptionPerSecond",
|
||||
"multiplier": -1.0,
|
||||
"duration": 60
|
||||
}
|
||||
]
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Inspire Invention",
|
||||
"faithCost": 2500,
|
||||
"faithCost": 600,
|
||||
"followersRequired": 1000,
|
||||
"unlockedByDefault": false,
|
||||
"effects": [
|
||||
@@ -8,13 +8,13 @@
|
||||
"type": "ModifyStat",
|
||||
"targetStat": "ProductionPerSecond",
|
||||
"op": "Add",
|
||||
"value": 5
|
||||
"value": 2
|
||||
},
|
||||
{
|
||||
"type": "ModifyStat",
|
||||
"targetStat": "CorruptionPerSecond",
|
||||
"op": "Add",
|
||||
"value": 0.5
|
||||
"value": 0.2
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@@ -1,13 +1,12 @@
|
||||
{
|
||||
"name": "Launch Ark",
|
||||
"faithCost": 100000,
|
||||
"faithCost": 25000,
|
||||
"followersRequired": 5000,
|
||||
"productionRequired": 10000,
|
||||
"unlockedByDefault": false,
|
||||
"effects": [
|
||||
{
|
||||
"type": "AddResource",
|
||||
"targetResource": "Corruption",
|
||||
"value": 100
|
||||
"type": "Win"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@@ -1,9 +1,13 @@
|
||||
{
|
||||
"name": "Project: Trajectory",
|
||||
"faithCost": 50000,
|
||||
"faithCost": 8000,
|
||||
"followersRequired": 5000,
|
||||
"unlockedByDefault": false,
|
||||
"effects": [
|
||||
{
|
||||
"type": "UnlockMiracle",
|
||||
"miraclesToUnlock": [ "launch_ark" ]
|
||||
},
|
||||
{ "type": "DestroySelf" }
|
||||
]
|
||||
}
|
||||
|
18
Mods/Miracles/organized_religion.json
Normal file
18
Mods/Miracles/organized_religion.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "Organized Religion",
|
||||
"faithCost": 800,
|
||||
"followersRequired": 1000,
|
||||
"productionRequired": 500,
|
||||
"unlockedByDefault": false,
|
||||
"effects": [
|
||||
{
|
||||
"type": "ModifyStat",
|
||||
"targetStat": "FaithPerFollower",
|
||||
"op": "Multiply",
|
||||
"value": 1.1
|
||||
},
|
||||
{
|
||||
"type": "DestroySelf"
|
||||
}
|
||||
]
|
||||
}
|
16
Mods/Miracles/prosperity_boom.json
Normal file
16
Mods/Miracles/prosperity_boom.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "Prosperity Boom",
|
||||
"faithCost": 400,
|
||||
"followersRequired": 1200,
|
||||
"productionRequired": 800,
|
||||
"unlockedByDefault": false,
|
||||
"effects": [
|
||||
{
|
||||
"type": "ApplyBuff",
|
||||
"buffId": "prosperity_buff",
|
||||
"targetStat": "FollowersPerSecond",
|
||||
"multiplier": 2.0,
|
||||
"duration": 20
|
||||
}
|
||||
]
|
||||
}
|
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"name": "Purge Sins",
|
||||
"faithCost": 100,
|
||||
"followersRequired": 20,
|
||||
"unlockedByDefault": true,
|
||||
"effects": [
|
||||
{
|
||||
"type": "ConvertResource",
|
||||
"fromResource": "Followers",
|
||||
"fromAmount": 20,
|
||||
"toResource": "Corruption",
|
||||
"toAmount": -5
|
||||
}
|
||||
]
|
||||
}
|
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"name": "Refined Dogma",
|
||||
"faithCost": 1000,
|
||||
"followersRequired": 250,
|
||||
"unlockedByDefault": true,
|
||||
"effects": [
|
||||
{
|
||||
"type": "ModifyStat",
|
||||
"targetStat": "FaithPerFollower",
|
||||
"op": "Add",
|
||||
"value": 0.1
|
||||
}
|
||||
]
|
||||
}
|
14
Mods/Miracles/ritual_of_cleansing.json
Normal file
14
Mods/Miracles/ritual_of_cleansing.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "Ritual of Cleansing",
|
||||
"faithCost": 200,
|
||||
"followersRequired": 200,
|
||||
"productionRequired": 100,
|
||||
"unlockedByDefault": false,
|
||||
"effects": [
|
||||
{
|
||||
"type": "AddResource",
|
||||
"targetResource": "Corruption",
|
||||
"value": -3
|
||||
}
|
||||
]
|
||||
}
|
18
Mods/Miracles/sustainable_practices.json
Normal file
18
Mods/Miracles/sustainable_practices.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "Sustainable Practices",
|
||||
"faithCost": 200,
|
||||
"followersRequired": 300,
|
||||
"productionRequired": 150,
|
||||
"unlockedByDefault": false,
|
||||
"effects": [
|
||||
{
|
||||
"type": "ModifyStat",
|
||||
"targetStat": "CorruptionPerSecond",
|
||||
"op": "Add",
|
||||
"value": -0.2
|
||||
},
|
||||
{
|
||||
"type": "DestroySelf"
|
||||
}
|
||||
]
|
||||
}
|
18
Mods/Miracles/tame_the_atom.json
Normal file
18
Mods/Miracles/tame_the_atom.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "Tame the Atom",
|
||||
"faithCost": 3000,
|
||||
"followersRequired": 3000,
|
||||
"productionRequired": 4000,
|
||||
"unlockedByDefault": false,
|
||||
"effects": [
|
||||
{
|
||||
"type": "ModifyStat",
|
||||
"targetStat": "CorruptionPerSecond",
|
||||
"op": "Add",
|
||||
"value": -0.8
|
||||
},
|
||||
{
|
||||
"type": "DestroySelf"
|
||||
}
|
||||
]
|
||||
}
|
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"name": "Advanced Worship",
|
||||
"faithCost": 500,
|
||||
"unlockedByDefault": true,
|
||||
"followersRequired": 100,
|
||||
"effects": [
|
||||
{
|
||||
"type": "UnlockMiracle",
|
||||
"miraclesToUnlock": [ "divine_sacrifice" ]
|
||||
},
|
||||
{ "type": "DestroySelf" }
|
||||
]
|
||||
}
|
@@ -1,13 +1,29 @@
|
||||
{
|
||||
"name": "Age of Industry",
|
||||
"faithCost": 1500,
|
||||
"faithCost": 300,
|
||||
"followersRequired": 750,
|
||||
"productionRequired": 50,
|
||||
"unlockedByDefault": false,
|
||||
"effects": [
|
||||
{
|
||||
"type": "UnlockMiracle",
|
||||
"miraclesToUnlock": [ "inspire_invention", "exploit_earth" ]
|
||||
"miraclesToUnlock": [
|
||||
"inspire_invention",
|
||||
"exploit_earth",
|
||||
"fossil_fuel_frenzy",
|
||||
"globalization",
|
||||
"harness_the_sun",
|
||||
"tame_the_atom",
|
||||
"prosperity_boom",
|
||||
"organized_religion",
|
||||
"unlock_space_age"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "ModifyStat",
|
||||
"targetStat": "FollowersPerSecond",
|
||||
"op": "Add",
|
||||
"value": 0.75
|
||||
},
|
||||
{ "type": "DestroySelf" }
|
||||
],
|
||||
|
@@ -1,12 +1,28 @@
|
||||
{
|
||||
"name": "Form Settlement",
|
||||
"faithCost": 300,
|
||||
"faithCost": 30,
|
||||
"followersRequired": 150,
|
||||
"unlockedByDefault": true,
|
||||
"effects": [
|
||||
{
|
||||
"type": "UnlockMiracle",
|
||||
"miraclesToUnlock": [ "erect_shrine", "communal_effort" ]
|
||||
"miraclesToUnlock": [
|
||||
"erect_shrine",
|
||||
"communal_effort",
|
||||
"sustainable_practices",
|
||||
"ritual_of_cleansing",
|
||||
"gods_endurance",
|
||||
"geological_survey",
|
||||
"divine_mandate",
|
||||
"craftmenship",
|
||||
"unlock_age_of_industry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "ModifyStat",
|
||||
"targetStat": "FollowersPerSecond",
|
||||
"op": "Add",
|
||||
"value": 0.5
|
||||
},
|
||||
{ "type": "DestroySelf" }
|
||||
],
|
||||
|
@@ -1,12 +1,19 @@
|
||||
{
|
||||
"name": "Age of Space",
|
||||
"faithCost": 10000,
|
||||
"followersRequired": 5000,
|
||||
"faithCost": 6000,
|
||||
"followersRequired": 4000,
|
||||
"productionRequired": 5000,
|
||||
"unlockedByDefault": false,
|
||||
"effects": [
|
||||
{
|
||||
"type": "UnlockMiracle",
|
||||
"miraclesToUnlock": [ "global_network" ]
|
||||
"miraclesToUnlock": ["global_network", "orbital_calculations"]
|
||||
},
|
||||
{
|
||||
"type": "ModifyStat",
|
||||
"targetStat": "FollowersPerSecond",
|
||||
"op": "Add",
|
||||
"value": 1.0
|
||||
},
|
||||
{ "type": "DestroySelf" }
|
||||
],
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 101 KiB |
Binary file not shown.
Before Width: | Height: | Size: 101 KiB |
@@ -3,27 +3,32 @@
|
||||
{
|
||||
"tierEnum": "Tier1",
|
||||
"threshold": 0,
|
||||
"scenePath": "res://Scenes/Followers/followers_tier_1.tscn"
|
||||
"imagePath": "res://Sprites/Follower.png",
|
||||
"scale": { "x": 0.1, "y": 0.1 }
|
||||
},
|
||||
{
|
||||
"tierEnum": "Tier2",
|
||||
"threshold": 200,
|
||||
"scenePath": "res://Scenes/Followers/followers_tier_2.tscn"
|
||||
"threshold": 30,
|
||||
"imagePath": "res://Sprites/follower_tier_2.png",
|
||||
"scale": { "x": 0.1, "y": 0.1 }
|
||||
},
|
||||
{
|
||||
"tierEnum": "Tier3",
|
||||
"threshold": 500,
|
||||
"scenePath": "res://Scenes/Followers/followers_tier_3.tscn"
|
||||
"threshold": 80,
|
||||
"imagePath": "res://Sprites/follower_tier_3.png",
|
||||
"scale": { "x": 0.1, "y": 0.1 }
|
||||
},
|
||||
{
|
||||
"tierEnum": "Tier4",
|
||||
"threshold": 1000,
|
||||
"scenePath": "res://Scenes/Followers/followers_tier_4.tscn"
|
||||
"threshold": 200,
|
||||
"imagePath": "res://Sprites/follower_tier_4.png",
|
||||
"scale": { "x": 0.1, "y": 0.1 }
|
||||
},
|
||||
{
|
||||
"tierEnum": "Tier5",
|
||||
"threshold": 2500,
|
||||
"scenePath": "res://Scenes/Followers/followers_tier_5.tscn"
|
||||
"threshold": 500,
|
||||
"imagePath": "res://Sprites/follower_tier_5.png",
|
||||
"scale": { "x": 0.1, "y": 0.1 }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@@ -2,15 +2,63 @@
|
||||
"tiers": [
|
||||
{
|
||||
"tierEnum": "Tier1",
|
||||
"threshold": 150,
|
||||
"imagePath": "user://Mods/Tiers/Huts/hut_tier_1.png",
|
||||
"scale": {"x": 0.01, "y": 0.01}
|
||||
"threshold": 20,
|
||||
"imagePath": "res://Sprites/Hut.png",
|
||||
"scale": { "x": 0.05, "y": 0.05 }
|
||||
},
|
||||
{
|
||||
"tierEnum": "Tier2",
|
||||
"threshold": 750,
|
||||
"imagePath": "user://Mods/Tiers/Huts/hut_tier_2.png",
|
||||
"scale": {"x": 0.01, "y": 0.01}
|
||||
"threshold": 60,
|
||||
"imagePath": "res://Sprites/hut_tier_2.png",
|
||||
"scale": { "x": 0.05, "y": 0.05 }
|
||||
},
|
||||
{
|
||||
"tierEnum": "Tier3",
|
||||
"threshold": 120,
|
||||
"imagePath": "res://Sprites/hut_tier_3.png",
|
||||
"scale": { "x": 0.05, "y": 0.05 }
|
||||
},
|
||||
{
|
||||
"tierEnum": "Tier4",
|
||||
"threshold": 250,
|
||||
"imagePath": "res://Sprites/castle.png",
|
||||
"scale": { "x": 0.05, "y": 0.05 }
|
||||
},
|
||||
{
|
||||
"tierEnum": "Tier5",
|
||||
"threshold": 400,
|
||||
"imagePath": "res://Sprites/house.png",
|
||||
"scale": { "x": 0.05, "y": 0.05 }
|
||||
},
|
||||
{
|
||||
"tierEnum": "Tier6",
|
||||
"threshold": 600,
|
||||
"imagePath": "res://Sprites/house_tier_2.png",
|
||||
"scale": { "x": 0.05, "y": 0.05 }
|
||||
},
|
||||
{
|
||||
"tierEnum": "Tier7",
|
||||
"threshold": 1200,
|
||||
"imagePath": "res://Sprites/house_tier_3.png",
|
||||
"scale": { "x": 0.05, "y": 0.05 }
|
||||
},
|
||||
{
|
||||
"tierEnum": "Tier8",
|
||||
"threshold": 2500,
|
||||
"imagePath": "res://Sprites/Skyscraper.png",
|
||||
"scale": { "x": 0.05, "y": 0.05 }
|
||||
},
|
||||
{
|
||||
"tierEnum": "Tier9",
|
||||
"threshold": 3500,
|
||||
"imagePath": "res://Sprites/skyscraper_tier_2.png",
|
||||
"scale": { "x": 0.05, "y": 0.05 }
|
||||
},
|
||||
{
|
||||
"tierEnum": "Tier10",
|
||||
"threshold": 5500,
|
||||
"imagePath": "res://Sprites/skyscraper_tier_3.png",
|
||||
"scale": { "x": 0.05, "y": 0.05 }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
22
Mods/Tiers/temple_tiers.json
Normal file
22
Mods/Tiers/temple_tiers.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"tiers": [
|
||||
{
|
||||
"tierEnum": "Tier1",
|
||||
"threshold": 200,
|
||||
"imagePath": "res://Sprites/Temple.png",
|
||||
"scale": { "x": 0.05, "y": 0.05 }
|
||||
},
|
||||
{
|
||||
"tierEnum": "Tier2",
|
||||
"threshold": 600,
|
||||
"imagePath": "res://Sprites/temple_tier_2.png",
|
||||
"scale": { "x": 0.05, "y": 0.05 }
|
||||
},
|
||||
{
|
||||
"tierEnum": "Tier3",
|
||||
"threshold": 1200,
|
||||
"imagePath": "res://Sprites/temple_tier_3.png",
|
||||
"scale": { "x": 0.05, "y": 0.05 }
|
||||
}
|
||||
]
|
||||
}
|
@@ -5,6 +5,7 @@
|
||||
<RootNamespace>parasiticgod</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="LimboConsole.Sharp" Version="0.0.1-beta-008" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4-beta1" />
|
||||
</ItemGroup>
|
||||
</Project>
|
146
README.md
146
README.md
@@ -1,52 +1,65 @@
|
||||
# Parasitic God
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
## The Concept
|
||||
|
||||
You are a nascent god, tethered to a small tribe of followers on a vibrant, living world. Their worship is your lifeblood, their growth your only purpose. You grant them miracles, blessing them with bountiful harvests and inspiring great works.
|
||||
|
||||
But your power comes at a cost. Every miracle that nurtures your civilization also poisons the planet. The soil turns barren, the forests wither, and the sky darkens. You are their savior and their apocalypse.
|
||||
|
||||
---
|
||||
|
||||
## The Mission
|
||||
|
||||
Guide your people from a simple tribe to a star-faring civilization capable of escaping the dying world. Manage your resources **Faith**, **Followers**, and **Production** while trying to keep the planet's ever-rising **Corruption** at bay.
|
||||
|
||||
Unlock new ages of technology, build a network of cities, and perform the final, desperate miracle to launch your followers into the stars before you consume everything.
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
* **Exponential Growth**
|
||||
|
||||
- **Exponential Growth**
|
||||
Watch a handful of followers grow into a massive civilization with huts, cities, and procedural road networks.
|
||||
|
||||
* **A World That Reacts**
|
||||
- **A World That Reacts**
|
||||
See the direct consequences of your actions as the vibrant globe fades to a corrupted wasteland and forests vanish based on your decisions.
|
||||
|
||||
* **Deeply Moddable**
|
||||
The entire game from miracles and their effects to the visual tiers of your civilization is driven by simple JSON files. If you can edit a text file, you can mod this game.
|
||||
- **Deeply Moddable**
|
||||
The entire game—from miracles and their effects to the visual tiers of your civilization—is driven by simple JSON files. If you can edit a text file, you can mod this game.
|
||||
|
||||
* **Strategic Resource Management**
|
||||
- **Strategic Resource Management**
|
||||
Balance the generation of multiple resources and make difficult choices. Will you sacrifice followers to purge corruption, or push for industrial growth at any cost?
|
||||
|
||||
---
|
||||
|
||||
## Modding Your Universe
|
||||
|
||||
This game was built from the ground up to be modified. You can add new miracles, change existing ones, and even define new visual tiers for your civilization.
|
||||
This game was built from the ground up to be modified. You can add new miracles, create random events, and even define new visual tiers for your civilization.
|
||||
|
||||
### Finding the Mods Folder
|
||||
|
||||
First, you need to find the game's `user data` directory. The game will create a `Mods` folder here on its first launch.
|
||||
The game creates a `Mods` folder in its user data directory on first launch:
|
||||
|
||||
* **Windows:** `%APPDATA%\Godot\app_userdata\ParasiticGod\Mods\`
|
||||
* **macOS:** `~/Library/Application Support/Godot/app_userdata/ParasiticGod/Mods/`
|
||||
* **Linux:** `~/.local/share/godot/app_userdata/ParasiticGod/Mods/`
|
||||
- **Windows:** `%APPDATA%\Godot\app_userdata\ParasiticGod\Mods\`
|
||||
- **macOS:** `~/Library/Application Support/Godot/app_userdata/ParasiticGod/Mods/`
|
||||
- **Linux:** `~/.local/share/godot/app_userdata/ParasiticGod/Mods/`
|
||||
|
||||
Inside, you'll find two folders: `Miracles` and `Tiers`.
|
||||
Inside, you'll find three folders: `Miracles`, `Tiers`, and `Events`.
|
||||
The game also loads a set of base mods from its installation directory (`res://Mods`). Any files you place in the user folder will be added to or override the base game's content.
|
||||
|
||||
---
|
||||
|
||||
### Creating a New Miracle
|
||||
|
||||
To add a new miracle, simply create a new `.json` file in the `Mods/Miracles` folder. The filename will be its unique **ID** (e.g., `my_cool_miracle.json`).
|
||||
|
||||
Here is a template with all available fields:
|
||||
To add a new miracle, create a JSON file in `Mods/Miracles`.
|
||||
The filename becomes its unique **ID** (e.g., `my_cool_miracle.json`):
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -55,7 +68,7 @@ Here is a template with all available fields:
|
||||
"followersRequired": 50,
|
||||
"productionRequired": 0,
|
||||
"unlockedByDefault": true,
|
||||
"advancesToAge": "",
|
||||
"advancesToAge": "The Cool Age",
|
||||
"effects": [
|
||||
{
|
||||
"type": "AddResource",
|
||||
@@ -64,28 +77,46 @@ Here is a template with all available fields:
|
||||
}
|
||||
]
|
||||
}
|
||||
````
|
||||
|
||||
---
|
||||
|
||||
### Creating a New Event
|
||||
|
||||
To add a new random event, create a JSON file in `Mods/Events`:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "event_my_event",
|
||||
"title": "A Thing Happened!",
|
||||
"description": "Something unexpected occurred. What will you do?",
|
||||
"meanTimeToHappen": 120,
|
||||
"trigger": {
|
||||
"minFollowers": 100,
|
||||
"maxCorruption": 50
|
||||
},
|
||||
"options": [
|
||||
{
|
||||
"text": "Do the thing!",
|
||||
"tooltip": "Gain 50 Production.",
|
||||
"effects": [
|
||||
{
|
||||
"type": "AddResource",
|
||||
"targetResource": "Production",
|
||||
"value": 50
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Available Effect Types
|
||||
---
|
||||
|
||||
This is the core of the modding system. Each miracle can have one or more effects.
|
||||
### Modifying Visual Tiers
|
||||
|
||||
| Type | Description | Parameters |
|
||||
| :--- | :--- | :--- |
|
||||
| **`AddResource`** | Adds or subtracts from a core stat. | `targetResource` (Stat), `value` (number) |
|
||||
| **`ConvertResource`** | Trades one resource for another. | `fromResource` (Stat), `fromAmount` (number), `toResource` (Stat), `toAmount` (number) |
|
||||
| **`ModifyStat`** | Permanently changes a passive stat. | `targetStat` (Stat), `op` ("Add" or "Multiply"), `value` (number) |
|
||||
| **`ApplyBuff`** | Applies a temporary multiplier. | `targetStat` (Stat), `multiplier` (number), `duration` (seconds) |
|
||||
| **`UnlockMiracle`** | Unlocks other miracles. | `miraclesToUnlock` (list of miracle IDs) |
|
||||
| **`DestroySelf`** | Removes the miracle's button after use. | (No parameters) |
|
||||
|
||||
**Valid Stat Names:** `Faith`, `Followers`, `Corruption`, `Production`, `ProductionPerSecond`, `CorruptionPerSecond`, `FaithPerFollower`.
|
||||
|
||||
### Modifying Tiers
|
||||
|
||||
You can change the progression of visuals like followers and huts by editing the files in `Mods/Tiers`. For example, to change when followers get new looks, edit `follower_tiers.json`.
|
||||
|
||||
The format is a list of tiers, sorted by their threshold.
|
||||
You can change the visual progression of followers, huts, and temples by editing the files in `Mods/Tiers`.
|
||||
The format is a list of tiers, sorted by threshold:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -93,37 +124,54 @@ The format is a list of tiers, sorted by their threshold.
|
||||
{
|
||||
"tierEnum": "Tier1",
|
||||
"threshold": 0,
|
||||
"scenePath": "res://Scenes/Followers/followers_tier_1.tscn"
|
||||
},
|
||||
{
|
||||
"tierEnum": "Tier2",
|
||||
"threshold": 200,
|
||||
"scenePath": "res://Scenes/Followers/followers_tier_2.tscn"
|
||||
"imagePath": "user://Mods/Tiers/Huts/my_custom_hut.png",
|
||||
"scale": { "x": 1.0, "y": 1.0 }
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
* **`tierEnum`**: Must be one of `Tier1`, `Tier2`, `Tier3`, `Tier4`, `Tier5`.
|
||||
* **`threshold`**: The number of followers (or other stat) needed to unlock this visual.
|
||||
* **`scenePath`**: The path to the Godot scene (`.tscn`) to display for this tier. You can even point to your own custom scenes if you're an advanced modder\!
|
||||
* **tierEnum**: Must be one of `Tier1` through `Tier10`.
|
||||
* **threshold**: The number of followers needed to unlock this visual.
|
||||
* **imagePath**: The path to the image file. Use `user://` for mods or `res://` for base assets.
|
||||
* **scale**: Optional X/Y scale multiplier for the image.
|
||||
|
||||
-----
|
||||
---
|
||||
|
||||
## 📊 Project Stats
|
||||
📦 **Lines of Code:**
|
||||
### Available Effect Types
|
||||
|
||||
Both miracles and event options use this list of effects:
|
||||
|
||||
| Type | Description | Parameters |
|
||||
| ------------------- | ----------------------------------- | ---------------------------------------------------------- |
|
||||
| **AddResource** | Adds or subtracts from a core stat. | `targetResource` (Stat), `value` (number) |
|
||||
| **ConvertResource** | Trades one resource for another. | `fromResource`, `fromAmount`, `toResource`, `toAmount` |
|
||||
| **ModifyStat** | Permanently changes a passive stat. | `targetStat`, `op` ("Add" or "Multiply"), `value` |
|
||||
| **ApplyBuff** | Applies a temporary multiplier. | `buffId`, `targetStat`, `multiplier`, `duration` (seconds) |
|
||||
| **UnlockMiracle** | Unlocks other miracles. | `miraclesToUnlock` (list of IDs) |
|
||||
| **DestroySelf** | Removes the miracle's button. | (No parameters) |
|
||||
| **Win** | Triggers the game's win condition. | (No parameters) |
|
||||
|
||||
**Valid Stat Names:**
|
||||
`Faith`, `Followers`, `Corruption`, `Production`, `ProductionPerSecond`, `CorruptionPerSecond`, `FollowersPerSecond`, `FaithPerFollower`, `ProductionPerFollower`
|
||||
|
||||
---
|
||||
|
||||
## Project Stats
|
||||
|
||||
**Lines of Code:**
|
||||

|
||||
|
||||
📈 **Repo Activity:**
|
||||
**Repo Activity:**
|
||||

|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
This project is open source. See the [LICENSE](https://www.google.com/search?q=./LICENSE) file for details.
|
||||
This project is open source. See the [LICENSE](./LICENSE.txt) file for details.
|
||||
|
||||
-----
|
||||
---
|
||||
|
||||
## Contributing
|
||||
|
||||
@@ -131,4 +179,4 @@ While the core code is complete for the jam, you can help by:
|
||||
|
||||
* Reporting bugs or balance issues.
|
||||
* Creating cool new miracles and sharing them.
|
||||
* Spreading the word\!
|
||||
* Spreading the word!
|
@@ -1,4 +1,4 @@
|
||||
[gd_scene load_steps=21 format=3 uid="uid://bfil8sd154327"]
|
||||
[gd_scene load_steps=27 format=3 uid="uid://bfil8sd154327"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://t71ewkpa5uqs" path="res://Scenes/Main/Main.cs" id="1_p8rbg"]
|
||||
[ext_resource type="Script" uid="uid://b77vh831r1e3c" path="res://Scenes/Main/MiraclePanel.cs" id="2_hcu3t"]
|
||||
@@ -16,9 +16,17 @@
|
||||
[ext_resource type="PackedScene" uid="uid://8w7tvsgkev1y" path="res://Scenes/tree.tscn" id="8_hcu3t"]
|
||||
[ext_resource type="Shader" uid="uid://bf8nk145fjkgh" path="res://Shaders/corruption_shader.gdshader" id="9_wgovn"]
|
||||
[ext_resource type="PackedScene" uid="uid://cqkye7yykakns" path="res://Scenes/Followers/FollowerMarker.tscn" id="11_5ci8a"]
|
||||
[ext_resource type="Script" uid="uid://djmtle2h3yd2e" path="res://Scripts/Components/PauseManager.cs" id="11_xggvw"]
|
||||
[ext_resource type="PackedScene" uid="uid://wysxqe44rxhf" path="res://Scenes/TempleMarker.tscn" id="14_udh0u"]
|
||||
[ext_resource type="PackedScene" uid="uid://xk2xirjd1sma" path="res://Scenes/moddable_visual.tscn" id="17_qdkat"]
|
||||
[ext_resource type="Script" uid="uid://furbvcmw31bx" path="res://Scripts/Components/ForestVisualizer.cs" id="18_qdkat"]
|
||||
[ext_resource type="Script" uid="uid://cw8gpeaq3yfjn" path="res://Scripts/Components/RoadManager.cs" id="19_qdkat"]
|
||||
[ext_resource type="Script" uid="uid://2ipbgwlx1ld1" path="res://Scripts/Components/EventManager.cs" id="22_iwp64"]
|
||||
[ext_resource type="PackedScene" uid="uid://gdejd44km3co" path="res://Scenes/UI/event_popup.tscn" id="23_4etfk"]
|
||||
[ext_resource type="Script" uid="uid://dvmrpbba7plsf" path="res://Scripts/EnableMainThemeMusic.cs" id="24_4etfk"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_xggvw"]
|
||||
bg_color = Color(0, 0, 0, 0.733333)
|
||||
|
||||
[sub_resource type="ShaderMaterial" id="ShaderMaterial_cv8e0"]
|
||||
shader = ExtResource("9_wgovn")
|
||||
@@ -36,7 +44,10 @@ _productionLabel = NodePath("UiLayer/Control/Main/Top/ProductionLabel")
|
||||
_miraclePanel = NodePath("UiLayer/Control/Main/ScrollContainer/Bottom")
|
||||
_worldSprite = NodePath("World Sprite")
|
||||
|
||||
[node name="UiLayer" type="CanvasLayer" parent="."]
|
||||
[node name="UiLayer" type="CanvasLayer" parent="." node_paths=PackedStringArray("_pauseButton", "_pauseMenu")]
|
||||
script = ExtResource("11_xggvw")
|
||||
_pauseButton = NodePath("MarginContainer/Button")
|
||||
_pauseMenu = NodePath("PanelContainer")
|
||||
|
||||
[node name="Control" type="Control" parent="UiLayer"]
|
||||
layout_mode = 3
|
||||
@@ -47,6 +58,7 @@ grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="Main" type="MarginContainer" parent="UiLayer/Control"]
|
||||
layout_mode = 1
|
||||
@@ -57,6 +69,7 @@ grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_horizontal = 4
|
||||
size_flags_vertical = 0
|
||||
mouse_filter = 2
|
||||
theme_override_constants/margin_left = 8
|
||||
theme_override_constants/margin_top = 8
|
||||
theme_override_constants/margin_right = 8
|
||||
@@ -66,6 +79,7 @@ theme_override_constants/margin_bottom = 8
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 4
|
||||
size_flags_vertical = 0
|
||||
mouse_filter = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="FaithLabel" type="Label" parent="UiLayer/Control/Main/Top"]
|
||||
@@ -123,6 +137,44 @@ stream = ExtResource("6_32vk6")
|
||||
[node name="BuffAdded" type="AudioStreamPlayer" parent="UiLayer/Control/Main/ScrollContainer2/HBoxContainer"]
|
||||
stream = ExtResource("7_crdpj")
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="UiLayer"]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
theme_override_constants/margin_left = 4
|
||||
theme_override_constants/margin_top = 4
|
||||
theme_override_constants/margin_right = 4
|
||||
theme_override_constants/margin_bottom = 4
|
||||
|
||||
[node name="Button" type="Button" parent="UiLayer/MarginContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 8
|
||||
size_flags_vertical = 0
|
||||
focus_mode = 0
|
||||
text = "Pause"
|
||||
|
||||
[node name="PanelContainer" type="PanelContainer" parent="UiLayer"]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_horizontal = 4
|
||||
size_flags_vertical = 4
|
||||
mouse_filter = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_xggvw")
|
||||
|
||||
[node name="Label" type="Label" parent="UiLayer/PanelContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 1
|
||||
text = "Paused"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
uppercase = true
|
||||
|
||||
[node name="Notification Layer" type="CanvasLayer" parent="." node_paths=PackedStringArray("_sfx")]
|
||||
layer = 3
|
||||
script = ExtResource("6_iwp64")
|
||||
@@ -132,6 +184,14 @@ _sfx = NodePath("Advance Age")
|
||||
[node name="Advance Age" type="AudioStreamPlayer" parent="Notification Layer"]
|
||||
stream = ExtResource("8_4etfk")
|
||||
|
||||
[node name="CenterContainer" type="CenterContainer" parent="Notification Layer"]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="Camera2D" type="Camera2D" parent="."]
|
||||
|
||||
[node name="World Sprite" type="Sprite2D" parent="."]
|
||||
@@ -141,7 +201,14 @@ scale = Vector2(2.41247, 2.41247)
|
||||
texture = ExtResource("4_i3fi7")
|
||||
metadata/_edit_lock_ = true
|
||||
|
||||
[node name="RoadManager" type="Node2D" parent="." node_paths=PackedStringArray("_markersContainer")]
|
||||
script = ExtResource("19_qdkat")
|
||||
_markersContainer = NodePath("../Hut Markers")
|
||||
metadata/_custom_type_script = "uid://cw8gpeaq3yfjn"
|
||||
metadata/_edit_lock_ = true
|
||||
|
||||
[node name="Hut Markers" type="Node2D" parent="."]
|
||||
metadata/_edit_lock_ = true
|
||||
|
||||
[node name="HutMarker" parent="Hut Markers" instance=ExtResource("6_cv8e0")]
|
||||
position = Vector2(-1, -29)
|
||||
@@ -206,7 +273,52 @@ position = Vector2(65, 472)
|
||||
[node name="HutMarker21" parent="Hut Markers" instance=ExtResource("6_cv8e0")]
|
||||
position = Vector2(-209, 28)
|
||||
|
||||
[node name="TemplesContainer" type="Node2D" parent="."]
|
||||
|
||||
[node name="HutMarker" parent="TemplesContainer" instance=ExtResource("14_udh0u")]
|
||||
|
||||
[node name="HutMarker2" parent="TemplesContainer" instance=ExtResource("14_udh0u")]
|
||||
position = Vector2(78, -66)
|
||||
|
||||
[node name="HutMarker3" parent="TemplesContainer" instance=ExtResource("14_udh0u")]
|
||||
position = Vector2(337, -80)
|
||||
|
||||
[node name="HutMarker4" parent="TemplesContainer" instance=ExtResource("14_udh0u")]
|
||||
position = Vector2(357, 67)
|
||||
|
||||
[node name="HutMarker5" parent="TemplesContainer" instance=ExtResource("14_udh0u")]
|
||||
position = Vector2(545, 76)
|
||||
|
||||
[node name="HutMarker6" parent="TemplesContainer" instance=ExtResource("14_udh0u")]
|
||||
position = Vector2(595, -67)
|
||||
|
||||
[node name="HutMarker7" parent="TemplesContainer" instance=ExtResource("14_udh0u")]
|
||||
position = Vector2(572, -243)
|
||||
|
||||
[node name="HutMarker8" parent="TemplesContainer" instance=ExtResource("14_udh0u")]
|
||||
position = Vector2(214, -237)
|
||||
|
||||
[node name="HutMarker9" parent="TemplesContainer" instance=ExtResource("14_udh0u")]
|
||||
position = Vector2(-112, -46)
|
||||
|
||||
[node name="HutMarker11" parent="TemplesContainer" instance=ExtResource("14_udh0u")]
|
||||
position = Vector2(-140, 206)
|
||||
|
||||
[node name="HutMarker12" parent="TemplesContainer" instance=ExtResource("14_udh0u")]
|
||||
position = Vector2(234, 161)
|
||||
|
||||
[node name="HutMarker13" parent="TemplesContainer" instance=ExtResource("14_udh0u")]
|
||||
position = Vector2(187, 425)
|
||||
|
||||
[node name="HutMarker14" parent="TemplesContainer" instance=ExtResource("14_udh0u")]
|
||||
position = Vector2(-48, 338)
|
||||
rotation = -0.0412825
|
||||
|
||||
[node name="HutMarker10" parent="TemplesContainer" instance=ExtResource("14_udh0u")]
|
||||
position = Vector2(-242, 10)
|
||||
|
||||
[node name="Followers Markers" type="Node2D" parent="."]
|
||||
metadata/_edit_lock_ = true
|
||||
|
||||
[node name="FollowerMarker" parent="Followers Markers" instance=ExtResource("11_5ci8a")]
|
||||
|
||||
@@ -265,6 +377,7 @@ position = Vector2(-88, -32)
|
||||
position = Vector2(82, -234)
|
||||
|
||||
[node name="ForestContainer" type="Node2D" parent="."]
|
||||
metadata/_edit_lock_ = true
|
||||
|
||||
[node name="Tree" parent="ForestContainer" instance=ExtResource("8_hcu3t")]
|
||||
position = Vector2(214, 38)
|
||||
@@ -5566,7 +5679,19 @@ script = ExtResource("18_qdkat")
|
||||
_treesContainer = NodePath("../ForestContainer")
|
||||
metadata/_custom_type_script = "uid://furbvcmw31bx"
|
||||
|
||||
[node name="RoadManager" type="Node2D" parent="." node_paths=PackedStringArray("_markersContainer")]
|
||||
script = ExtResource("19_qdkat")
|
||||
_markersContainer = NodePath("../Hut Markers")
|
||||
metadata/_custom_type_script = "uid://cw8gpeaq3yfjn"
|
||||
[node name="TemplesVisualizer" type="Node" parent="." node_paths=PackedStringArray("_markersContainer")]
|
||||
script = ExtResource("8_cv8e0")
|
||||
_markersContainer = NodePath("../TemplesContainer")
|
||||
_unitsPerMarker = 1
|
||||
Category = 2
|
||||
_moddableVisualScene = ExtResource("17_qdkat")
|
||||
metadata/_custom_type_script = "uid://dj2wyrq07gfp2"
|
||||
|
||||
[node name="EventManager" type="Node" parent="." node_paths=PackedStringArray("_eventPopupContainer")]
|
||||
script = ExtResource("22_iwp64")
|
||||
_eventPopupScene = ExtResource("23_4etfk")
|
||||
_eventPopupContainer = NodePath("../Notification Layer/CenterContainer")
|
||||
metadata/_custom_type_script = "uid://2ipbgwlx1ld1"
|
||||
|
||||
[node name="Node" type="Node" parent="."]
|
||||
script = ExtResource("24_4etfk")
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
14
Scenes/TempleMarker.tscn
Normal file
14
Scenes/TempleMarker.tscn
Normal file
@@ -0,0 +1,14 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://wysxqe44rxhf"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://djaf0gv8s7qib" path="res://Scripts/FollowerMarker.cs" id="1_x8f1l"]
|
||||
[ext_resource type="Texture2D" uid="uid://dcs48aa84w21u" path="res://icon.svg" id="2_tc18m"]
|
||||
|
||||
[node name="HutMarker" type="Marker2D"]
|
||||
script = ExtResource("1_x8f1l")
|
||||
metadata/_custom_type_script = "uid://djaf0gv8s7qib"
|
||||
|
||||
[node name="Sprite2D" type="Sprite2D" parent="."]
|
||||
visible = false
|
||||
modulate = Color(0.406241, 0.161792, 0.609211, 1)
|
||||
scale = Vector2(0.235, 0.235)
|
||||
texture = ExtResource("2_tc18m")
|
8
Scenes/UI/event_option_button.tscn
Normal file
8
Scenes/UI/event_option_button.tscn
Normal file
@@ -0,0 +1,8 @@
|
||||
[gd_scene format=3 uid="uid://b2o5rufqn8dpf"]
|
||||
|
||||
[node name="EventOptionButton" type="Button"]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
37
Scenes/UI/event_popup.tscn
Normal file
37
Scenes/UI/event_popup.tscn
Normal file
@@ -0,0 +1,37 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://gdejd44km3co"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://h1x5eqt0lc5m" path="res://Scripts/UI/EventPopup.cs" id="1_lkb7n"]
|
||||
[ext_resource type="PackedScene" uid="uid://b2o5rufqn8dpf" path="res://Scenes/UI/event_option_button.tscn" id="2_gk0qx"]
|
||||
|
||||
[node name="EventPopup" type="PanelContainer" node_paths=PackedStringArray("_titleLabel", "_descriptionLabel", "_optionsContainer")]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1_lkb7n")
|
||||
_titleLabel = NodePath("VBoxContainer/Title")
|
||||
_descriptionLabel = NodePath("VBoxContainer/Description")
|
||||
_optionsContainer = NodePath("VBoxContainer/OptionButtons")
|
||||
_optionButtonScene = ExtResource("2_gk0qx")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 4
|
||||
size_flags_vertical = 4
|
||||
alignment = 1
|
||||
|
||||
[node name="Title" type="Label" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 6
|
||||
text = "TITLE"
|
||||
|
||||
[node name="Description" type="RichTextLabel" parent="VBoxContainer"]
|
||||
custom_minimum_size = Vector2(320, 128)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
fit_content = true
|
||||
|
||||
[node name="OptionButtons" type="VBoxContainer" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
57
Scenes/game_over.tscn
Normal file
57
Scenes/game_over.tscn
Normal file
@@ -0,0 +1,57 @@
|
||||
[gd_scene load_steps=6 format=3 uid="uid://kcla4knp80mq"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://cbdokimy0qarg" path="res://Scripts/MainMenu.cs" id="1_8fo1c"]
|
||||
[ext_resource type="PackedScene" uid="uid://cmhvni5njpmee" path="res://Scenes/main_menu.tscn" id="2_ek8ke"]
|
||||
[ext_resource type="AudioStream" uid="uid://defdxbv24q6l1" path="res://Sfx/The Hollow Throne.mp3" id="3_ek8ke"]
|
||||
[ext_resource type="Script" uid="uid://bif3eyfa1lgl4" path="res://Scripts/DisableMainThemeMusic.cs" id="4_eoghk"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_i2yjh"]
|
||||
bg_color = Color(0, 0, 0, 1)
|
||||
|
||||
[node name="Game Over" type="CanvasLayer" node_paths=PackedStringArray("_startButton")]
|
||||
script = ExtResource("1_8fo1c")
|
||||
_gameScene = ExtResource("2_ek8ke")
|
||||
_startButton = NodePath("CenterContainer/VBoxContainer/Quit")
|
||||
|
||||
[node name="Panel" type="Panel" parent="."]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_i2yjh")
|
||||
|
||||
[node name="CenterContainer" type="CenterContainer" parent="."]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="RichTextLabel" type="RichTextLabel" parent="CenterContainer/VBoxContainer"]
|
||||
custom_minimum_size = Vector2(640, 360)
|
||||
layout_mode = 2
|
||||
bbcode_enabled = true
|
||||
text = "By your hand, this creation has been [color=#B22222][b]unmade[/b][/color].
|
||||
Your followers, who offered you their very souls, have [color=#B22222][b]perished[/b][/color].
|
||||
You have [color=#B22222][b]failed[/b][/color] your sacred charge. You are an [color=#B22222][b]abomination[/b][/color] to your brethren."
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="Quit" type="Button" parent="CenterContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
text = "Quit"
|
||||
flat = true
|
||||
|
||||
[node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="."]
|
||||
stream = ExtResource("3_ek8ke")
|
||||
volume_db = -3.0
|
||||
autoplay = true
|
||||
parameters/looping = true
|
||||
|
||||
[node name="Node" type="Node" parent="."]
|
||||
script = ExtResource("4_eoghk")
|
71
Scenes/main_menu.tscn
Normal file
71
Scenes/main_menu.tscn
Normal file
@@ -0,0 +1,71 @@
|
||||
[gd_scene load_steps=7 format=3 uid="uid://cmhvni5njpmee"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://cbdokimy0qarg" path="res://Scripts/MainMenu.cs" id="1_1ehe0"]
|
||||
[ext_resource type="FontFile" uid="uid://wofoiaejxgsp" path="res://Fonts/Playful Boxes.otf" id="1_28flt"]
|
||||
[ext_resource type="Texture2D" uid="uid://d2wi2cs20q2b6" path="res://Parasitic_God.png" id="1_48xlc"]
|
||||
[ext_resource type="PackedScene" uid="uid://kvpk5wrcp3rv" path="res://Scenes/tutorial_scene.tscn" id="2_ce3w2"]
|
||||
[ext_resource type="Script" uid="uid://dvmrpbba7plsf" path="res://Scripts/EnableMainThemeMusic.cs" id="5_n45e2"]
|
||||
|
||||
[sub_resource type="LabelSettings" id="LabelSettings_48xlc"]
|
||||
font = ExtResource("1_28flt")
|
||||
font_size = 72
|
||||
outline_size = 8
|
||||
outline_color = Color(0.48, 0.408, 0, 1)
|
||||
|
||||
[node name="MainMenu" type="CanvasLayer" node_paths=PackedStringArray("_startButton", "_quitButton")]
|
||||
script = ExtResource("1_1ehe0")
|
||||
_gameScene = ExtResource("2_ce3w2")
|
||||
_startButton = NodePath("CenterContainer/VBoxContainer/Play")
|
||||
_quitButton = NodePath("CenterContainer/VBoxContainer/Exit")
|
||||
|
||||
[node name="Control" type="Control" parent="."]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="Control"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_horizontal = 4
|
||||
size_flags_vertical = 4
|
||||
texture = ExtResource("1_48xlc")
|
||||
expand_mode = 2
|
||||
stretch_mode = 5
|
||||
|
||||
[node name="CenterContainer" type="CenterContainer" parent="."]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Title" type="Label" parent="CenterContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
size_flags_stretch_ratio = 0.0
|
||||
text = "PARASITIC GOD"
|
||||
label_settings = SubResource("LabelSettings_48xlc")
|
||||
|
||||
[node name="Play" type="Button" parent="CenterContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_stretch_ratio = 0.0
|
||||
theme_override_font_sizes/font_size = 30
|
||||
text = "Play"
|
||||
|
||||
[node name="Exit" type="Button" parent="CenterContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 30
|
||||
text = "Exit"
|
||||
|
||||
[node name="Node" type="Node" parent="."]
|
||||
script = ExtResource("5_n45e2")
|
10
Scenes/main_theme_music.tscn
Normal file
10
Scenes/main_theme_music.tscn
Normal file
@@ -0,0 +1,10 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://b7we8gtene47t"]
|
||||
|
||||
[ext_resource type="AudioStream" uid="uid://wm3vocs0kkk8" path="res://Sfx/MainTheme.mp3" id="1_f0c52"]
|
||||
|
||||
[node name="MainThemeMusic" type="AudioStreamPlayer"]
|
||||
process_mode = 3
|
||||
stream = ExtResource("1_f0c52")
|
||||
volume_db = -5.0
|
||||
autoplay = true
|
||||
parameters/looping = true
|
26
Scenes/tutorial_scene.tscn
Normal file
26
Scenes/tutorial_scene.tscn
Normal file
@@ -0,0 +1,26 @@
|
||||
[gd_scene load_steps=5 format=3 uid="uid://kvpk5wrcp3rv"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://d08d3pi7sx8k3" path="res://Scripts/UI/TutorialScene.cs" id="1_epmsy"]
|
||||
[ext_resource type="PackedScene" uid="uid://bfil8sd154327" path="res://Scenes/Main/Main.tscn" id="2_mw53g"]
|
||||
[ext_resource type="Resource" uid="uid://dxgpvgx7axp88" path="res://Dialogue/tutorial.dialogue" id="3_oaf0i"]
|
||||
[ext_resource type="Texture2D" uid="uid://dg6ac3jb1366r" path="res://Sprites/globe.svg" id="4_7u0dx"]
|
||||
|
||||
[node name="TutorialScene" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1_epmsy")
|
||||
_mainGameScene = ExtResource("2_mw53g")
|
||||
_tutorialDialogue = ExtResource("3_oaf0i")
|
||||
|
||||
[node name="World" type="TextureRect" parent="."]
|
||||
layout_mode = 0
|
||||
offset_left = 195.0
|
||||
offset_top = -98.0
|
||||
offset_right = 915.0
|
||||
offset_bottom = 732.0
|
||||
texture = ExtResource("4_7u0dx")
|
||||
expand_mode = 4
|
56
Scenes/win_screen.tscn
Normal file
56
Scenes/win_screen.tscn
Normal file
@@ -0,0 +1,56 @@
|
||||
[gd_scene load_steps=6 format=3 uid="uid://dtuyx1f5fa8sy"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://cbdokimy0qarg" path="res://Scripts/MainMenu.cs" id="1_a00f1"]
|
||||
[ext_resource type="PackedScene" uid="uid://cmhvni5njpmee" path="res://Scenes/main_menu.tscn" id="2_awi1s"]
|
||||
[ext_resource type="AudioStream" uid="uid://46kqkf4ckqek" path="res://Sfx/Ascend Beyond.mp3" id="3_awi1s"]
|
||||
[ext_resource type="Script" uid="uid://bif3eyfa1lgl4" path="res://Scripts/DisableMainThemeMusic.cs" id="4_bwc64"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_i2yjh"]
|
||||
bg_color = Color(0, 0, 0, 1)
|
||||
|
||||
[node name="Game Over" type="CanvasLayer" node_paths=PackedStringArray("_startButton")]
|
||||
script = ExtResource("1_a00f1")
|
||||
_gameScene = ExtResource("2_awi1s")
|
||||
_startButton = NodePath("CenterContainer/VBoxContainer/Quit")
|
||||
|
||||
[node name="Panel" type="Panel" parent="."]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_i2yjh")
|
||||
|
||||
[node name="CenterContainer" type="CenterContainer" parent="."]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="RichTextLabel" type="RichTextLabel" parent="CenterContainer/VBoxContainer"]
|
||||
custom_minimum_size = Vector2(640, 360)
|
||||
layout_mode = 2
|
||||
bbcode_enabled = true
|
||||
text = "Through Your divine guidance, your followers have [color=gold][b]ascended to the stars[/b][/color].
|
||||
They now traverse the cosmos, settling new worlds in Your name and spreading word of Your glory.
|
||||
[color=gold][b]Congratulations[/b][/color], you have proven yourself a [color=gold][b]True God[/b][/color], a creator, not a parasite."
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="Quit" type="Button" parent="CenterContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
text = "Quit"
|
||||
flat = true
|
||||
|
||||
[node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="."]
|
||||
stream = ExtResource("3_awi1s")
|
||||
volume_db = -3.0
|
||||
autoplay = true
|
||||
parameters/looping = true
|
||||
|
||||
[node name="Node" type="Node" parent="."]
|
||||
script = ExtResource("4_bwc64")
|
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Godot;
|
||||
using ParasiticGod.Scripts;
|
||||
using ParasiticGod.Scripts.Core.Effects;
|
||||
using ParasiticGod.Scripts.Singletons;
|
||||
|
||||
@@ -34,16 +33,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();
|
||||
}
|
||||
}
|
||||
|
73
Scripts/Components/EventManager.cs
Normal file
73
Scripts/Components/EventManager.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using System.Collections.Generic;
|
||||
using Godot;
|
||||
using Limbo.Console.Sharp;
|
||||
using ParasiticGod.Scripts.Core;
|
||||
using ParasiticGod.Scripts.Singletons;
|
||||
using ParasiticGod.Scripts.UI;
|
||||
|
||||
namespace ParasiticGod.Scripts.Components;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class EventManager : Node
|
||||
{
|
||||
[Export] private double _checkInterval = 5.0;
|
||||
[Export] private PackedScene _eventPopupScene;
|
||||
[Export] private Container _eventPopupContainer;
|
||||
|
||||
private List<EventDefinition> _allEvents;
|
||||
private Timer _timer;
|
||||
private RandomNumberGenerator _rng = new();
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
RegisterConsoleCommands();
|
||||
_allEvents = GameBus.Instance.AllEvents;
|
||||
|
||||
_timer = new Timer { WaitTime = _checkInterval, Autostart = true };
|
||||
AddChild(_timer);
|
||||
_timer.Timeout += OnCheckEvents;
|
||||
}
|
||||
|
||||
private void OnCheckEvents()
|
||||
{
|
||||
if (GetTree().Paused) return;
|
||||
|
||||
var state = GameBus.Instance.CurrentState;
|
||||
|
||||
foreach (var ev in _allEvents)
|
||||
{
|
||||
if (state.Get(Stat.Followers) < ev.Trigger.MinFollowers) continue;
|
||||
if (state.Get(Stat.Corruption) > ev.Trigger.MaxCorruption) continue;
|
||||
|
||||
var probability = _checkInterval / ev.MeanTimeToHappen;
|
||||
if (_rng.Randf() < probability)
|
||||
{
|
||||
FireEvent(ev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void FireEvent(EventDefinition eventDef)
|
||||
{
|
||||
GameBus.Instance.SetPause(true);
|
||||
|
||||
var popup = _eventPopupScene.Instantiate<EventPopup>();
|
||||
_eventPopupContainer.AddChild(popup);
|
||||
popup.DisplayEvent(eventDef);
|
||||
}
|
||||
|
||||
[ConsoleCommand("trigger_event", "Triggers an event by its ID for testing purposes.")]
|
||||
private void TriggerEventCommand(string eventId)
|
||||
{
|
||||
var eventDef = _allEvents.Find(e => e.Id == eventId);
|
||||
if (eventDef != null)
|
||||
{
|
||||
FireEvent(eventDef);
|
||||
}
|
||||
else
|
||||
{
|
||||
GD.PushError($"No event found with ID: {eventId}");
|
||||
}
|
||||
}
|
||||
}
|
1
Scripts/Components/EventManager.cs.uid
Normal file
1
Scripts/Components/EventManager.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://2ipbgwlx1ld1
|
43
Scripts/Components/PauseManager.cs
Normal file
43
Scripts/Components/PauseManager.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using Godot;
|
||||
using ParasiticGod.Scripts.Singletons;
|
||||
|
||||
namespace ParasiticGod.Scripts.Components;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class PauseManager : CanvasLayer
|
||||
{
|
||||
[Export] private Button _pauseButton;
|
||||
[Export] private Control _pauseMenu;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
ProcessMode = ProcessModeEnum.Always;
|
||||
_pauseMenu.Hide();
|
||||
_pauseButton.Pressed += TogglePause;
|
||||
|
||||
GameBus.Instance.PauseStateChanged += OnPauseStateChanged;
|
||||
}
|
||||
|
||||
public override void _Input(InputEvent @event)
|
||||
{
|
||||
if (@event.IsActionPressed("pause")) TogglePause();
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
if (GameBus.Instance != null)
|
||||
{
|
||||
GameBus.Instance.PauseStateChanged -= OnPauseStateChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private void TogglePause()
|
||||
{
|
||||
GameBus.Instance.SetPause(!GetTree().Paused);
|
||||
}
|
||||
|
||||
private void OnPauseStateChanged(bool isPaused)
|
||||
{
|
||||
_pauseMenu.Visible = isPaused;
|
||||
}
|
||||
}
|
1
Scripts/Components/PauseManager.cs.uid
Normal file
1
Scripts/Components/PauseManager.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://djmtle2h3yd2e
|
@@ -1,6 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
using ParasiticGod.Scripts.Core;
|
||||
using ParasiticGod.Scripts.Singletons;
|
||||
|
||||
@@ -9,7 +8,7 @@ namespace ParasiticGod.Scripts.Components;
|
||||
[GlobalClass]
|
||||
public partial class PopulationVisualizer : Node
|
||||
{
|
||||
public enum VisualCategory { Followers, Huts }
|
||||
public enum VisualCategory { Followers, Huts, Temples }
|
||||
|
||||
[Export] private Node2D _markersContainer;
|
||||
[Export] private int _unitsPerMarker = 5;
|
||||
@@ -32,6 +31,9 @@ public partial class PopulationVisualizer : Node
|
||||
case VisualCategory.Huts:
|
||||
_tiers = GameBus.Instance.HutTiers;
|
||||
break;
|
||||
case VisualCategory.Temples:
|
||||
_tiers = GameBus.Instance.TempleTiers;
|
||||
break;
|
||||
default:
|
||||
GD.PushError($"PopulationVisualizer has an invalid category: {Category}");
|
||||
return;
|
||||
@@ -61,6 +63,7 @@ public partial class PopulationVisualizer : Node
|
||||
{
|
||||
VisualCategory.Followers => (long)newState.Get(Stat.Followers),
|
||||
VisualCategory.Huts => (long)newState.Get(Stat.Followers),
|
||||
VisualCategory.Temples => (long)newState.Get(Stat.Followers),
|
||||
_ => 0
|
||||
};
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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; }
|
||||
}
|
13
Scripts/Core/Effects/WinEffect.cs
Normal file
13
Scripts/Core/Effects/WinEffect.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using Godot;
|
||||
using ParasiticGod.Scripts.Singletons;
|
||||
|
||||
namespace ParasiticGod.Scripts.Core.Effects;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class WinEffect : Effect
|
||||
{
|
||||
public override void Execute(GameState gameState)
|
||||
{
|
||||
GameBus.Instance.NotifyGameIsWon();
|
||||
}
|
||||
}
|
1
Scripts/Core/Effects/WinEffect.cs.uid
Normal file
1
Scripts/Core/Effects/WinEffect.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://77fa2htfghwy
|
22
Scripts/Core/EventDefinition.cs
Normal file
22
Scripts/Core/EventDefinition.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System.Collections.Generic;
|
||||
using Godot.Collections;
|
||||
using ParasiticGod.Scripts.Core.Effects;
|
||||
|
||||
namespace ParasiticGod.Scripts.Core;
|
||||
|
||||
public class EventDefinition
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string Description { get; set; }
|
||||
public int MeanTimeToHappen { get; set; }
|
||||
public EventTriggerDto Trigger { get; set; }
|
||||
public List<EventOptionDefinition> Options { get; set; } = [];
|
||||
}
|
||||
|
||||
public class EventOptionDefinition
|
||||
{
|
||||
public string Text { get; set; }
|
||||
public string Tooltip { get; set; }
|
||||
public Array<Effect> Effects { get; set; }
|
||||
}
|
1
Scripts/Core/EventDefinition.cs.uid
Normal file
1
Scripts/Core/EventDefinition.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://ilyr01u70ciw
|
46
Scripts/Core/EventDto.cs
Normal file
46
Scripts/Core/EventDto.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ParasiticGod.Scripts.Core;
|
||||
|
||||
public class EventTriggerDto
|
||||
{
|
||||
[JsonProperty("minFollowers")]
|
||||
public long MinFollowers { get; set; } = 0;
|
||||
|
||||
[JsonProperty("maxCorruption")]
|
||||
public double MaxCorruption { get; set; } = 100;
|
||||
}
|
||||
|
||||
public class EventOptionDto
|
||||
{
|
||||
[JsonProperty("text")]
|
||||
public string Text { get; set; }
|
||||
|
||||
[JsonProperty("tooltip")]
|
||||
public string Tooltip { get; set; }
|
||||
|
||||
[JsonProperty("effects")]
|
||||
public List<EffectDto> Effects { get; set; }
|
||||
}
|
||||
|
||||
public class EventDto
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[JsonProperty("title")]
|
||||
public string Title { get; set; }
|
||||
|
||||
[JsonProperty("description")]
|
||||
public string Description { get; set; }
|
||||
|
||||
[JsonProperty("meanTimeToHappen")]
|
||||
public int MeanTimeToHappen { get; set; } = 100; // in game days
|
||||
|
||||
[JsonProperty("trigger")]
|
||||
public EventTriggerDto Trigger { get; set; }
|
||||
|
||||
[JsonProperty("options")]
|
||||
public List<EventOptionDto> Options { get; set; }
|
||||
}
|
1
Scripts/Core/EventDto.cs.uid
Normal file
1
Scripts/Core/EventDto.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://nwwqn028soa5
|
71
Scripts/Core/EventLoader.cs
Normal file
71
Scripts/Core/EventLoader.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using System.Collections.Generic;
|
||||
using Godot;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ParasiticGod.Scripts.Core;
|
||||
|
||||
public static class EventLoader
|
||||
{
|
||||
public static List<EventDefinition> LoadAllEvents()
|
||||
{
|
||||
var loadedEvents = new Dictionary<string, EventDefinition>();
|
||||
|
||||
LoadEventsFromPath("res://Mods/Events", loadedEvents);
|
||||
LoadEventsFromPath("user://Mods/Events", loadedEvents);
|
||||
|
||||
GD.Print($"Finished loading. Total unique events: {loadedEvents.Count}");
|
||||
return new List<EventDefinition>(loadedEvents.Values);
|
||||
}
|
||||
|
||||
private static void LoadEventsFromPath(string path, Dictionary<string, EventDefinition> events)
|
||||
{
|
||||
if (!DirAccess.DirExistsAbsolute(path)) return;
|
||||
|
||||
using var dir = DirAccess.Open(path);
|
||||
dir.ListDirBegin();
|
||||
var fileName = dir.GetNext();
|
||||
while (!string.IsNullOrEmpty(fileName))
|
||||
{
|
||||
if (!dir.CurrentIsDir() && fileName.EndsWith(".json"))
|
||||
{
|
||||
var filePath = path.PathJoin(fileName);
|
||||
var ev = LoadEventFromFile(filePath);
|
||||
if (ev != null)
|
||||
{
|
||||
events[ev.Id] = ev; // Add or overwrite
|
||||
}
|
||||
}
|
||||
fileName = dir.GetNext();
|
||||
}
|
||||
}
|
||||
|
||||
private static EventDefinition LoadEventFromFile(string filePath)
|
||||
{
|
||||
var fileContent = FileAccess.GetFileAsString(filePath);
|
||||
if (string.IsNullOrEmpty(fileContent)) return null;
|
||||
|
||||
var dto = JsonConvert.DeserializeObject<EventDto>(fileContent);
|
||||
if (dto == null) return null;
|
||||
|
||||
var eventDef = new EventDefinition
|
||||
{
|
||||
Id = dto.Id,
|
||||
Title = dto.Title,
|
||||
Description = dto.Description,
|
||||
MeanTimeToHappen = dto.MeanTimeToHappen,
|
||||
Trigger = dto.Trigger
|
||||
};
|
||||
|
||||
foreach (var optionDto in dto.Options)
|
||||
{
|
||||
var optionDef = new EventOptionDefinition
|
||||
{
|
||||
Text = optionDto.Text,
|
||||
Tooltip = optionDto.Tooltip,
|
||||
Effects = MiracleLoader.ConvertEffectDtos(optionDto.Effects)
|
||||
};
|
||||
eventDef.Options.Add(optionDef);
|
||||
}
|
||||
return eventDef;
|
||||
}
|
||||
}
|
1
Scripts/Core/EventLoader.cs.uid
Normal file
1
Scripts/Core/EventLoader.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://ceimix3ejnadj
|
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Godot;
|
||||
using ParasiticGod.Scripts.Singletons;
|
||||
|
||||
namespace ParasiticGod.Scripts.Core;
|
||||
@@ -12,11 +14,38 @@ 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 productionFollowerMultiplier = state.ActiveBuffs
|
||||
.Where(b => b.TargetStat == Stat.ProductionPerFollower)
|
||||
.Aggregate(1.0f, (acc, buff) => acc * buff.Multiplier);
|
||||
var productionFlatMultiplier = 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 productionFromFollowers = state.Get(Stat.Followers) * state.Get(Stat.ProductionPerFollower) * productionFollowerMultiplier;
|
||||
var productionFromIndustry = state.Get(Stat.ProductionPerSecond) * productionFlatMultiplier;
|
||||
var totalProductionPerSecond = productionFromFollowers + productionFromIndustry;
|
||||
|
||||
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, totalProductionPerSecond * 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 +55,7 @@ public class GameLogic
|
||||
{
|
||||
GameBus.Instance.NotifyBuffRemoved(buff);
|
||||
state.ActiveBuffs.RemoveAt(i);
|
||||
state.RemoveActiveBuff(buff.BuffId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ParasiticGod.Scripts.Core.Effects;
|
||||
using static System.Double;
|
||||
|
||||
namespace ParasiticGod.Scripts.Core;
|
||||
|
||||
@@ -8,6 +9,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,11 +25,13 @@ public class GameState
|
||||
Set(Stat.FaithPerFollower, 0.5);
|
||||
Set(Stat.ProductionPerSecond, 0.0);
|
||||
Set(Stat.CorruptionPerSecond, 0.01);
|
||||
Set(Stat.FollowersPerSecond, 0);
|
||||
Set(Stat.ProductionPerFollower, 0);
|
||||
}
|
||||
|
||||
public double Get(Stat stat) => _stats[stat].Value;
|
||||
|
||||
public void Set(Stat stat, double value) => _stats[stat].Set(value);
|
||||
public void Set(Stat stat, double value) => _stats[stat].Set(Math.Clamp(value, 0, MaxValue));
|
||||
|
||||
public void Modify(Stat stat, double delta) => _stats[stat].Set(Get(stat) + delta);
|
||||
|
||||
@@ -38,4 +42,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);
|
||||
}
|
@@ -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; }
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using System.Collections.Generic;
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
using Newtonsoft.Json;
|
||||
@@ -16,15 +16,30 @@ public static class MiracleLoader
|
||||
{ "ConvertResource", typeof(ConvertResourceEffect) },
|
||||
{ "ModifyStat", typeof(ModifyStatEffect) },
|
||||
{ "UnlockMiracle", typeof(UnlockMiracleEffect) },
|
||||
{ "DestroySelf", typeof(DestroySelfEffect) }
|
||||
{ "DestroySelf", typeof(DestroySelfEffect) },
|
||||
{ "Win", typeof(WinEffect)}
|
||||
};
|
||||
|
||||
public static System.Collections.Generic.Dictionary<string, MiracleDefinition> LoadMiraclesFromDirectory(string path)
|
||||
|
||||
public static System.Collections.Generic.Dictionary<string, MiracleDefinition> LoadAllMiracles()
|
||||
{
|
||||
var loadedMiracles = new System.Collections.Generic.Dictionary<string, MiracleDefinition>();
|
||||
using var dir = DirAccess.Open(path);
|
||||
if (dir == null) return loadedMiracles;
|
||||
|
||||
LoadMiraclesFromPath("res://Mods/Miracles", loadedMiracles);
|
||||
LoadMiraclesFromPath("user://Mods/Miracles", loadedMiracles);
|
||||
|
||||
GD.Print($"Finished loading. Total unique miracles: {loadedMiracles.Count}");
|
||||
return loadedMiracles;
|
||||
}
|
||||
|
||||
private static void LoadMiraclesFromPath(string path, System.Collections.Generic.Dictionary<string, MiracleDefinition> miracles)
|
||||
{
|
||||
if (!DirAccess.DirExistsAbsolute(path))
|
||||
{
|
||||
GD.Print($"Mod directory not found, skipping: {path}");
|
||||
return;
|
||||
}
|
||||
|
||||
using var dir = DirAccess.Open(path);
|
||||
dir.ListDirBegin();
|
||||
var fileName = dir.GetNext();
|
||||
while (!string.IsNullOrEmpty(fileName))
|
||||
@@ -33,17 +48,15 @@ public static class MiracleLoader
|
||||
{
|
||||
var filePath = path.PathJoin(fileName);
|
||||
var fileNameNoExt = fileName.GetBaseName();
|
||||
var miracle = LoadMiracleFromFile(filePath, fileNameNoExt); // Pass the ID
|
||||
var miracle = LoadMiracleFromFile(filePath, fileNameNoExt);
|
||||
if (miracle != null)
|
||||
{
|
||||
loadedMiracles.Add(fileNameNoExt, miracle);
|
||||
// Add or overwrite the miracle in the dictionary.
|
||||
miracles[fileNameNoExt] = miracle;
|
||||
}
|
||||
}
|
||||
fileName = dir.GetNext();
|
||||
}
|
||||
|
||||
GD.Print($"Loaded {loadedMiracles.Count} miracles from {path}");
|
||||
return loadedMiracles;
|
||||
}
|
||||
|
||||
private static MiracleDefinition LoadMiracleFromFile(string filePath, string miracleId)
|
||||
@@ -74,7 +87,17 @@ public static class MiracleLoader
|
||||
Effects = []
|
||||
};
|
||||
|
||||
foreach (var effectDto in miracleDto.Effects)
|
||||
var effects = ConvertEffectDtos(miracleDto.Effects);
|
||||
miracleDef.Effects = effects;
|
||||
return miracleDef;
|
||||
}
|
||||
|
||||
public static Array<Effect> ConvertEffectDtos(List<EffectDto> dtos)
|
||||
{
|
||||
var effects = new Array<Effect>();
|
||||
if (dtos == null) return effects;
|
||||
|
||||
foreach (var effectDto in dtos)
|
||||
{
|
||||
if (EffectRegistry.TryGetValue(effectDto.Type, out var effectType))
|
||||
{
|
||||
@@ -89,6 +112,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;
|
||||
@@ -105,11 +129,9 @@ public static class MiracleLoader
|
||||
unlockMiracleEffect.MiraclesToUnlock = new Array<string>(effectDto.MiraclesToUnlock);
|
||||
break;
|
||||
}
|
||||
|
||||
miracleDef.Effects.Add(effectInstance);
|
||||
effects.Add(effectInstance);
|
||||
}
|
||||
}
|
||||
|
||||
return miracleDef;
|
||||
return effects;
|
||||
}
|
||||
}
|
@@ -11,7 +11,9 @@ public enum Stat
|
||||
// Passive Generation Stats
|
||||
ProductionPerSecond,
|
||||
CorruptionPerSecond,
|
||||
FollowersPerSecond,
|
||||
|
||||
// Modifying Stats
|
||||
FaithPerFollower
|
||||
FaithPerFollower,
|
||||
ProductionPerFollower,
|
||||
}
|
@@ -7,7 +7,21 @@ namespace ParasiticGod.Scripts.Core;
|
||||
|
||||
public static class TierLoader
|
||||
{
|
||||
public static List<TierDefinition> LoadTiersFromFile(string filePath)
|
||||
public static List<TierDefinition> LoadTiers(string baseFilePath, string userFilePath)
|
||||
{
|
||||
// Prioritize the user's file. If it exists, load it.
|
||||
if (FileAccess.FileExists(userFilePath))
|
||||
{
|
||||
GD.Print($"Loading user tier file: {userFilePath}");
|
||||
return LoadTierListFromFile(userFilePath);
|
||||
}
|
||||
|
||||
// Otherwise, fall back to the base game's file.
|
||||
GD.Print($"Loading base tier file: {baseFilePath}");
|
||||
return LoadTierListFromFile(baseFilePath);
|
||||
}
|
||||
|
||||
private static List<TierDefinition> LoadTierListFromFile(string filePath)
|
||||
{
|
||||
var loadedTiers = new List<TierDefinition>();
|
||||
|
||||
@@ -27,17 +41,25 @@ public static class TierLoader
|
||||
|
||||
foreach (var dto in tierListDto.Tiers)
|
||||
{
|
||||
var image = Image.LoadFromFile(dto.ImagePath);
|
||||
if (image == null)
|
||||
Texture2D texture = null;
|
||||
if (dto.ImagePath.StartsWith("res://"))
|
||||
{
|
||||
GD.PushError($"Failed to load image at path: {dto.ImagePath}");
|
||||
continue;
|
||||
texture = GD.Load<Texture2D>(dto.ImagePath);
|
||||
}
|
||||
else if (dto.ImagePath.StartsWith("user://"))
|
||||
{
|
||||
var image = Image.LoadFromFile(dto.ImagePath);
|
||||
if (image != null)
|
||||
{
|
||||
texture = ImageTexture.CreateFromImage(image);
|
||||
}
|
||||
}
|
||||
|
||||
var tierDef = new TierDefinition
|
||||
{
|
||||
Threshold = dto.Threshold,
|
||||
TierEnum = dto.TierEnum,
|
||||
Texture = ImageTexture.CreateFromImage(image),
|
||||
Texture = texture,
|
||||
Scale = new Vector2(dto.Scale.X, dto.Scale.Y)
|
||||
};
|
||||
loadedTiers.Add(tierDef);
|
||||
|
12
Scripts/DisableMainThemeMusic.cs
Normal file
12
Scripts/DisableMainThemeMusic.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Godot;
|
||||
|
||||
namespace ParasiticGod.Scripts;
|
||||
|
||||
public partial class DisableMainThemeMusic : Node
|
||||
{
|
||||
public override void _Ready()
|
||||
{
|
||||
var mainThemeMusic = GetNodeOrNull<AudioStreamPlayer>("/root/MainThemeMusic");
|
||||
mainThemeMusic?.Stop();
|
||||
}
|
||||
}
|
1
Scripts/DisableMainThemeMusic.cs.uid
Normal file
1
Scripts/DisableMainThemeMusic.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bif3eyfa1lgl4
|
15
Scripts/EnableMainThemeMusic.cs
Normal file
15
Scripts/EnableMainThemeMusic.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using Godot;
|
||||
|
||||
namespace ParasiticGod.Scripts;
|
||||
|
||||
public partial class EnableMainThemeMusic : Node
|
||||
{
|
||||
public override void _Ready()
|
||||
{
|
||||
var mainThemeMusic = GetNodeOrNull<AudioStreamPlayer>("/root/MainThemeMusic");
|
||||
if (mainThemeMusic is { Playing: false })
|
||||
{
|
||||
mainThemeMusic.Play();
|
||||
}
|
||||
}
|
||||
}
|
1
Scripts/EnableMainThemeMusic.cs.uid
Normal file
1
Scripts/EnableMainThemeMusic.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dvmrpbba7plsf
|
@@ -5,6 +5,6 @@ namespace ParasiticGod.Scripts;
|
||||
[GlobalClass]
|
||||
public partial class Follower : Node2D
|
||||
{
|
||||
public enum FollowerTier { Tier1, Tier2, Tier3, Tier4, Tier5 }
|
||||
public enum FollowerTier { Tier1, Tier2, Tier3, Tier4, Tier5, Tier6, Tier7, Tier8, Tier9, Tier10 }
|
||||
[Export] public FollowerTier Tier { get; private set; }
|
||||
}
|
52
Scripts/MainMenu.cs
Normal file
52
Scripts/MainMenu.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using Godot;
|
||||
|
||||
namespace ParasiticGod.Scripts;
|
||||
|
||||
public partial class MainMenu : Node
|
||||
{
|
||||
[Export] private PackedScene _gameScene;
|
||||
[Export] private Button _startButton;
|
||||
[Export] private Button _quitButton;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
if (_startButton != null)
|
||||
{
|
||||
_startButton.Pressed += OnStartButtonPressed;
|
||||
}
|
||||
|
||||
if (_quitButton != null)
|
||||
{
|
||||
_quitButton.Pressed += OnQuitButtonPressed;
|
||||
}
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
if (_startButton != null)
|
||||
{
|
||||
_startButton.Pressed -= OnStartButtonPressed;
|
||||
}
|
||||
|
||||
if (_quitButton != null)
|
||||
{
|
||||
_quitButton.Pressed -= OnQuitButtonPressed;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnStartButtonPressed()
|
||||
{
|
||||
if (_gameScene == null)
|
||||
{
|
||||
GD.PrintErr("Game scene is not assigned in MainMenu.");
|
||||
return;
|
||||
}
|
||||
|
||||
GetTree().ChangeSceneToPacked(_gameScene);
|
||||
}
|
||||
|
||||
private void OnQuitButtonPressed()
|
||||
{
|
||||
GetTree().Quit();
|
||||
}
|
||||
}
|
1
Scripts/MainMenu.cs.uid
Normal file
1
Scripts/MainMenu.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cbdokimy0qarg
|
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
using Limbo.Console.Sharp;
|
||||
using ParasiticGod.Scripts.Core;
|
||||
using ParasiticGod.Scripts.Core.Effects;
|
||||
|
||||
@@ -9,9 +11,14 @@ namespace ParasiticGod.Scripts.Singletons;
|
||||
public partial class GameBus : Node
|
||||
{
|
||||
public static GameBus Instance { get; private set; }
|
||||
public Dictionary<string, MiracleDefinition> AllMiracles { get; private set; }
|
||||
public System.Collections.Generic.Dictionary<string, MiracleDefinition> AllMiracles { get; private set; }
|
||||
public List<TierDefinition> FollowerTiers { get; private set; }
|
||||
public List<TierDefinition> HutTiers { get; private set; }
|
||||
public List<TierDefinition> TempleTiers { get; private set; }
|
||||
public List<EventDefinition> AllEvents { get; private set; }
|
||||
|
||||
private PackedScene _gameOverScene = GD.Load<PackedScene>("res://Scenes/game_over.tscn");
|
||||
private PackedScene _winScene = GD.Load<PackedScene>("res://Scenes/win_screen.tscn");
|
||||
|
||||
private readonly GameState _gameState = new();
|
||||
private readonly GameLogic _gameLogic = new();
|
||||
@@ -24,29 +31,41 @@ public partial class GameBus : Node
|
||||
public event Action<Buff> BuffRemoved;
|
||||
public event Action PopulationVisualsUpdated;
|
||||
public event Action<string> AgeAdvanced;
|
||||
public event Action GameWon;
|
||||
public event Action<bool> PauseStateChanged;
|
||||
|
||||
public override void _EnterTree()
|
||||
{
|
||||
Instance = this;
|
||||
AllMiracles = MiracleLoader.LoadMiraclesFromDirectory("user://Mods/Miracles");
|
||||
FollowerTiers = TierLoader.LoadTiersFromFile("user://Mods/Tiers/follower_tiers.json");
|
||||
HutTiers = TierLoader.LoadTiersFromFile("user://Mods/Tiers/hut_tiers.json");
|
||||
AllMiracles = MiracleLoader.LoadAllMiracles();
|
||||
AllEvents = EventLoader.LoadAllEvents();
|
||||
FollowerTiers = TierLoader.LoadTiers("res://Mods/Tiers/follower_tiers.json", "user://Mods/Tiers/follower_tiers.json");
|
||||
HutTiers = TierLoader.LoadTiers("res://Mods/Tiers/hut_tiers.json","user://Mods/Tiers/hut_tiers.json");
|
||||
TempleTiers = TierLoader.LoadTiers("res://Mods/Tiers/temple_tiers.json","user://Mods/Tiers/temple_tiers.json");
|
||||
|
||||
GameWon += OnGameWon;
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
Instance = null;
|
||||
GameWon -= OnGameWon;
|
||||
}
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
RegisterConsoleCommands();
|
||||
}
|
||||
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
_gameLogic.UpdateGameState(_gameState, delta);
|
||||
StateChanged?.Invoke(_gameState);
|
||||
|
||||
|
||||
if (_gameState.Get(Stat.Corruption) >= 100)
|
||||
{
|
||||
GD.Print("The world has died!");
|
||||
GetTree().Quit(); // For now
|
||||
GetTree().ChangeSceneToPacked(_gameOverScene);
|
||||
_gameState.Set(Stat.Corruption, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +85,7 @@ public partial class GameBus : Node
|
||||
if (AllMiracles.TryGetValue(id, out var def) && !_gameState.IsMiracleUnlocked(id))
|
||||
{
|
||||
miraclesToUnlock.Add(def);
|
||||
_gameState.AddUnlockedMiracle(id);
|
||||
_gameState.AddUnlockedMiracle(def.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,8 +123,42 @@ public partial class GameBus : Node
|
||||
BuffRemoved?.Invoke(buff);
|
||||
}
|
||||
|
||||
public void NotifyGameIsWon()
|
||||
{
|
||||
GameWon?.Invoke();
|
||||
}
|
||||
|
||||
public void ExecuteEffects(Array<Effect> effects)
|
||||
{
|
||||
foreach (var effect in effects)
|
||||
{
|
||||
effect.Execute(_gameState);
|
||||
}
|
||||
SetPause(false);
|
||||
}
|
||||
|
||||
public void SubscribeToStat(Stat stat, Action<double> listener) => _gameState.Subscribe(stat, listener);
|
||||
public void UnsubscribeFromStat(Stat stat, Action<double> listener) => _gameState.Unsubscribe(stat, listener);
|
||||
|
||||
public void SetPause(bool isPaused)
|
||||
{
|
||||
GetTree().Paused = isPaused;
|
||||
PauseStateChanged?.Invoke(isPaused);
|
||||
}
|
||||
|
||||
public GameState CurrentState => _gameState;
|
||||
|
||||
[ConsoleCommand("set_stat", "Sets the value of a specified stat.")]
|
||||
private void SetStatCommand(Stat stat, double value) => _gameState.Set(stat, value);
|
||||
|
||||
[ConsoleCommand("game_over")]
|
||||
private void GameOverCommand() => GetTree().ChangeSceneToPacked(_gameOverScene);
|
||||
|
||||
[ConsoleCommand("win_game")]
|
||||
private void WinGameCommand() => GetTree().ChangeSceneToPacked(_winScene);
|
||||
|
||||
private void OnGameWon()
|
||||
{
|
||||
GetTree().ChangeSceneToPacked(_winScene);
|
||||
}
|
||||
}
|
76
Scripts/UI/EventPopup.cs
Normal file
76
Scripts/UI/EventPopup.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using Godot;
|
||||
using ParasiticGod.Scripts.Core;
|
||||
using ParasiticGod.Scripts.Singletons;
|
||||
|
||||
namespace ParasiticGod.Scripts.UI;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class EventPopup : PanelContainer
|
||||
{
|
||||
[Export] private Label _titleLabel;
|
||||
[Export] private RichTextLabel _descriptionLabel;
|
||||
[Export] private VBoxContainer _optionsContainer;
|
||||
[Export] private PackedScene _optionButtonScene; // A scene for a single button
|
||||
[Export] private float _choiceTimeout = 30.0f;
|
||||
|
||||
private EventDefinition _eventDef;
|
||||
private Timer _timeoutTimer;
|
||||
private readonly RandomNumberGenerator _rng = new();
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
ProcessMode = ProcessModeEnum.Always;
|
||||
}
|
||||
|
||||
public void DisplayEvent(EventDefinition eventDef)
|
||||
{
|
||||
_eventDef = eventDef;
|
||||
_titleLabel.Text = eventDef.Title;
|
||||
_descriptionLabel.Text = eventDef.Description;
|
||||
|
||||
foreach (var child in _optionsContainer.GetChildren())
|
||||
{
|
||||
child.QueueFree();
|
||||
}
|
||||
|
||||
foreach (var option in eventDef.Options)
|
||||
{
|
||||
var button = _optionButtonScene.Instantiate<Button>();
|
||||
button.Text = option.Text;
|
||||
button.TooltipText = option.Tooltip;
|
||||
|
||||
button.Pressed += () =>
|
||||
{
|
||||
_timeoutTimer.Stop();
|
||||
HandleChoice(option);
|
||||
};
|
||||
|
||||
_optionsContainer.AddChild(button);
|
||||
}
|
||||
|
||||
_timeoutTimer = new Timer { WaitTime = _choiceTimeout, OneShot = true };
|
||||
AddChild(_timeoutTimer);
|
||||
_timeoutTimer.Timeout += OnTimeout;
|
||||
_timeoutTimer.Start();
|
||||
}
|
||||
|
||||
private void OnTimeout()
|
||||
{
|
||||
if (_eventDef.Options.Count > 0)
|
||||
{
|
||||
var randomIndex = _rng.RandiRange(0, _eventDef.Options.Count - 1);
|
||||
HandleChoice(_eventDef.Options[randomIndex]);
|
||||
}
|
||||
else
|
||||
{
|
||||
QueueFree();
|
||||
GetTree().Paused = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleChoice(EventOptionDefinition option)
|
||||
{
|
||||
GameBus.Instance.ExecuteEffects(option.Effects);
|
||||
QueueFree();
|
||||
}
|
||||
}
|
1
Scripts/UI/EventPopup.cs.uid
Normal file
1
Scripts/UI/EventPopup.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://h1x5eqt0lc5m
|
29
Scripts/UI/TutorialScene.cs
Normal file
29
Scripts/UI/TutorialScene.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using DialogueManagerRuntime;
|
||||
using Godot;
|
||||
|
||||
namespace ParasiticGod.Scripts.UI;
|
||||
|
||||
public partial class TutorialScene : Control
|
||||
{
|
||||
[Export] private PackedScene _mainGameScene;
|
||||
[Export] private Resource _tutorialDialogue;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
DialogueManager.DialogueEnded += OnDialogueEnded;
|
||||
DialogueManager.ShowExampleDialogueBalloon(_tutorialDialogue, "start");
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
DialogueManager.DialogueEnded -= OnDialogueEnded;
|
||||
}
|
||||
|
||||
private void OnDialogueEnded(Resource resource)
|
||||
{
|
||||
if (resource == _tutorialDialogue)
|
||||
{
|
||||
GetTree().ChangeSceneToPacked(_mainGameScene);
|
||||
}
|
||||
}
|
||||
}
|
1
Scripts/UI/TutorialScene.cs.uid
Normal file
1
Scripts/UI/TutorialScene.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://d08d3pi7sx8k3
|
BIN
Sfx/Ascend Beyond.mp3
Normal file
BIN
Sfx/Ascend Beyond.mp3
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user