Compare commits

...

38 Commits

Author SHA1 Message Date
f81a237f01 Update README.md; correct LICENSE file path to include .txt extension 2025-08-25 05:50:45 +02:00
47281a098d Update README.md; enhance modding instructions and improve formatting for clarity 2025-08-24 02:35:19 +02:00
5dd8e6fff9 Update README.md; enhance modding instructions and add new event creation guidelines 2025-08-24 02:32:21 +02:00
0747595f2b Update export presets and project configuration; modify export paths, application details, and debugging options 2025-08-24 02:01:17 +02:00
2bf0f4a360 Add main theme music functionality; implement enable/disable scripts and audio resources 2025-08-24 01:50:31 +02:00
bdcc0d5089 Add initial implementation of Dialogue Manager; include core scripts, scenes, and resources 2025-08-24 00:48:51 +02:00
1b3657b03a Refactor pause handling; implement GameBus for pause state management and update event popup behavior 2025-08-24 00:37:06 +02:00
6d8139fd44 Add event handling improvements; implement timeout for event choices and new event definitions 2025-08-24 00:01:09 +02:00
cdd944b9b5 Add EventManager and event handling system; implement event triggering and popup display 2025-08-23 23:38:45 +02:00
c0d18507e3 Add PauseManager functionality; implement pause menu and toggle logic 2025-08-23 23:09:07 +02:00
7e8ccb061a Add FollowersPerSecond modifications to various unlocks; adjust values for game progression 2025-08-23 22:59:22 +02:00
e7247ee715 Adjust faith costs and resource effects for various rituals and structures; optimize thresholds for follower and hut tiers 2025-08-23 22:47:16 +02:00
048688da45 Update follower and temple tier images; add new tier images and adjust paths 2025-08-23 22:32:34 +02:00
3d48d7e9d6 Fix typo in unlock_settlement.json; correct "craftmenship" to "craftsmanship" 2025-08-23 19:27:12 +02:00
33c182e623 Add Craftsmanship miracle; update production calculations and unlock logic 2025-08-23 19:20:58 +02:00
4ceb63e49c Add Organized Religion miracle and Temple tiers; update game structure with new markers and visualizations 2025-08-23 19:03:24 +02:00
0fbd49f36a Add new miracles: Divine Mandate and Prosperity Boom; update buff application logic and game state calculations 2025-08-23 18:47:42 +02:00
8ae0e7e80c Add buff management for miracles; implement checks for active buffs and update buff identifiers 2025-08-23 18:28:05 +02:00
73a89b736e clean-up 2025-08-23 18:16:52 +02:00
a5ab466df9 Add new miracles: Blood Ritual, Fossil Fuel Frenzy, Geological Survey, Globalization, God's Endurance, Harness the Sun, Tame the Atom; update existing miracles with new effects and requirements 2025-08-23 18:16:19 +02:00
ce8fd41a93 Add WinEffect class and game win functionality; implement win and game over scenes 2025-08-23 17:59:57 +02:00
283adc02c9 Add linguist-vendored attribute for .gd files in .gitattributes 2025-08-23 17:18:21 +02:00
f840b98fd0 Add linguist-vendored attribute for addons directory in .gitattributes 2025-08-23 17:17:01 +02:00
07995821f5 Add MainMenu scene and script with start and quit functionality 2025-08-23 17:12:29 +02:00
846c3a6d83 Add LimboConsole plugin with command interpreter and configuration options 2025-08-23 16:48:39 +02:00
9da3b02a46 Update resource values and unlock new miracles; refactor tier loading logic 2025-08-23 16:10:57 +02:00
cd715a24cb Refactor marker management to use child count for occupancy checks; update road management to handle Marker2D types 2025-08-23 15:48:08 +02:00
608bcc552d Refactor hut tier definitions to use images and scales instead of scene paths; add moddable visual component 2025-08-23 15:41:57 +02:00
8efbc9cc6e Remove redundant comment for stat initialization in GameState constructor 2025-08-23 15:12:08 +02:00
a6c6bde622 Implement miracle unlock management and update tooltip logic for MiracleButton 2025-08-23 14:25:35 +02:00
83b89e3099 Enhance MiracleButton functionality with state-based availability checks and tooltip updates 2025-08-23 14:20:55 +02:00
de2f24cfce Add NotificationLabel styling and manage miracles in hand 2025-08-23 14:06:52 +02:00
729a9fd1ea Add LICENSE and README files with project details and licensing information 2025-08-23 05:43:05 +02:00
3ed64b165f Add new miracles and update existing JSON configurations for resource management 2025-08-23 05:24:33 +02:00
4a3bc46081 Add tier management system with JSON loading; refactor PopulationVisualizer for category handling 2025-08-23 05:23:09 +02:00
bf272b4c2f Add sound effects for buff management and notifications; update scene configurations 2025-08-23 05:09:47 +02:00
9cf707945b Add NotificationManager and NotificationLabel for age advancement notifications; refactor scripts into Components directory 2025-08-23 04:54:45 +02:00
5719c3f920 Add Active Buffs management and UI integration 2025-08-23 04:20:42 +02:00
339 changed files with 21458 additions and 267 deletions

2
.gitattributes vendored
View File

@@ -1,2 +1,4 @@
# Normalize EOL for all files that Git considers text files. # Normalize EOL for all files that Git considers text files.
* text=auto eol=lf * text=auto eol=lf
addons/* linguist-vendored
*.gd linguist-vendored

View 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

View 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

BIN
Fonts/COMIC.TTF Normal file

Binary file not shown.

35
Fonts/COMIC.TTF.import Normal file
View File

@@ -0,0 +1,35 @@
[remap]
importer="font_data_dynamic"
type="FontFile"
uid="uid://b8tf6dbm2j0ju"
path="res://.godot/imported/COMIC.TTF-856a12f50d25f6c40b9b7475a5a3c8de.fontdata"
[deps]
source_file="res://Fonts/COMIC.TTF"
dest_files=["res://.godot/imported/COMIC.TTF-856a12f50d25f6c40b9b7475a5a3c8de.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={}

BIN
Fonts/Playful Boxes.otf Normal file

Binary file not shown.

View File

@@ -0,0 +1,35 @@
[remap]
importer="font_data_dynamic"
type="FontFile"
uid="uid://wofoiaejxgsp"
path="res://.godot/imported/Playful Boxes.otf-113f6b887ec1f2b8d73de65734580dbc.fontdata"
[deps]
source_file="res://Fonts/Playful Boxes.otf"
dest_files=["res://.godot/imported/Playful Boxes.otf-113f6b887ec1f2b8d73de65734580dbc.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={}

59
LICENSE.txt Normal file
View File

@@ -0,0 +1,59 @@
Parasitic God - License
This project uses a split licensing model. The source code is released into the public domain, while the art and audio assets are under a more restrictive license.
Code License (The Unlicense)
This applies to all .cs, .gdshader, and other source code files.
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to https://unlicense.org
Asset License (All Rights Reserved)
This license applies to all files within the following directories and their subdirectories:
/Sprites/
/Assets/
/Shaders/ (specifically the visual output, not the code)
Any other directory containing visual (.png, .svg, .jpg, etc.) or audio (.wav, .ogg, .mp3, etc.) assets.
The assets contained within this project are NOT covered by the code license above. They are the property of their respective creators.
You are granted the following permissions:
You MAY use these assets within the compiled, playable version of the game "Parasitic God".
You MAY view and modify these assets on your local machine for the purpose of learning or personal enjoyment.
You are explicitly forbidden from:
Redistributing, sharing, or selling these assets in any form, either individually or as part of a collection.
Using these assets in any other game, project, or work, commercial or non-commercial.
Creating derivative works from these assets for public distribution.
All rights to these assets are reserved by the original creators. If you wish to use them for any purpose not explicitly granted above, you must seek direct permission from the copyright holder.

View 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
}
]
}
]
}

View 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
}
]
}
]
}

View 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
View 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
}
]
}
]
}

View 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
}
]
}
]
}

View 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
}
]
}
]
}

View 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
}
]
}
]
}

View 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
}
]
}
]
}

View 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
}
]
}

View File

@@ -1,18 +1,18 @@
{ {
"name": "Bountiful Harvest", "name": "Bountiful Harvest",
"faithCost": 50, "faithCost": 10,
"followersRequired": 0, "followersRequired": 0,
"unlockedByDefault": true, "unlockedByDefault": true,
"effects": [ "effects": [
{ {
"type": "AddResource", "type": "AddResource",
"targetResource": "Followers", "targetResource": "Followers",
"value": 10 "value": 5
}, },
{ {
"type": "AddResource", "type": "AddResource",
"targetResource": "Corruption", "targetResource": "Corruption",
"value": 2 "value": 0.2
} }
] ]
} }

View File

@@ -0,0 +1,18 @@
{
"name": "Communal Effort",
"faithCost": 40,
"followersRequired": 250,
"unlockedByDefault": false,
"effects": [
{
"type": "AddResource",
"targetResource": "Production",
"value": 15
},
{
"type": "AddResource",
"targetResource": "Corruption",
"value": 1
}
]
}

View File

@@ -0,0 +1,16 @@
{
"name": "Project: Ark Frame",
"faithCost": 20000,
"followersRequired": 5000,
"unlockedByDefault": false,
"effects": [
{
"type": "ConvertResource",
"fromResource": "Production",
"fromAmount": 5000,
"toResource": "Faith",
"toAmount": 0
},
{ "type": "DestroySelf" }
]
}

View 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"
}
]
}

View File

@@ -1,14 +0,0 @@
{
"name": "Divine Frenzy",
"faithCost": 200,
"followersRequired": 50,
"unlockedByDefault": true,
"effects": [
{
"type": "ApplyBuff",
"targetBuffStat": "FaithGeneration",
"multiplier": 2.0,
"duration": 30
}
]
}

View 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"
}
]
}

View File

@@ -1,15 +0,0 @@
{
"name": "Divine Sacrifice",
"faithCost": 250,
"unlockedByDefault": false,
"followersRequired": 100,
"effects": [
{
"type": "ConvertResource",
"fromResource": "Followers",
"fromAmount": 50,
"toResource": "Faith",
"toAmount": 1000
}
]
}

View File

@@ -0,0 +1,19 @@
{
"name": "Erect Shrine",
"faithCost": 60,
"followersRequired": 200,
"unlockedByDefault": false,
"effects": [
{
"type": "ModifyStat",
"targetStat": "FaithPerFollower",
"op": "Add",
"value": 0.05
},
{
"type": "AddResource",
"targetResource": "Corruption",
"value": 1
}
]
}

View File

@@ -0,0 +1,18 @@
{
"name": "Exploit the Earth",
"faithCost": 200,
"followersRequired": 1200,
"unlockedByDefault": false,
"effects": [
{
"type": "AddResource",
"targetResource": "Production",
"value": 300
},
{
"type": "AddResource",
"targetResource": "Corruption",
"value": 5
}
]
}

View 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"
}
]
}

View 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
}
]
}

View File

@@ -0,0 +1,13 @@
{
"name": "Global Network",
"faithCost": 4000,
"followersRequired": 5000,
"unlockedByDefault": false,
"effects": [
{
"type": "UnlockMiracle",
"miraclesToUnlock": ["orbital_calculations", "construct_vessel_frame"]
},
{ "type": "DestroySelf" }
]
}

View 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"
}
]
}

View 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
}
]
}

View 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
}
]
}

View File

@@ -0,0 +1,20 @@
{
"name": "Inspire Invention",
"faithCost": 600,
"followersRequired": 1000,
"unlockedByDefault": false,
"effects": [
{
"type": "ModifyStat",
"targetStat": "ProductionPerSecond",
"op": "Add",
"value": 2
},
{
"type": "ModifyStat",
"targetStat": "CorruptionPerSecond",
"op": "Add",
"value": 0.2
}
]
}

View File

@@ -0,0 +1,12 @@
{
"name": "Launch Ark",
"faithCost": 25000,
"followersRequired": 5000,
"productionRequired": 10000,
"unlockedByDefault": false,
"effects": [
{
"type": "Win"
}
]
}

View File

@@ -0,0 +1,13 @@
{
"name": "Project: Trajectory",
"faithCost": 8000,
"followersRequired": 5000,
"unlockedByDefault": false,
"effects": [
{
"type": "UnlockMiracle",
"miraclesToUnlock": [ "launch_ark" ]
},
{ "type": "DestroySelf" }
]
}

View 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"
}
]
}

View 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
}
]
}

View File

@@ -1,15 +0,0 @@
{
"name": "Purge Sins",
"faithCost": 100,
"followersRequired": 20,
"unlockedByDefault": true,
"effects": [
{
"type": "ConvertResource",
"fromResource": "Followers",
"fromAmount": 20,
"toResource": "Corruption",
"toAmount": -5
}
]
}

View File

@@ -1,14 +0,0 @@
{
"name": "Refined Dogma",
"faithCost": 1000,
"followersRequired": 250,
"unlockedByDefault": true,
"effects": [
{
"type": "ModifyStat",
"targetStat": "FaithPerFollower",
"op": "Add",
"value": 0.1
}
]
}

View File

@@ -0,0 +1,14 @@
{
"name": "Ritual of Cleansing",
"faithCost": 200,
"followersRequired": 200,
"productionRequired": 100,
"unlockedByDefault": false,
"effects": [
{
"type": "AddResource",
"targetResource": "Corruption",
"value": -3
}
]
}

View 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"
}
]
}

View 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"
}
]
}

View File

@@ -1,13 +0,0 @@
{
"name": "Advanced Worship",
"faithCost": 500,
"unlockedByDefault": true,
"followersRequired": 100,
"effects": [
{
"type": "UnlockMiracle",
"miraclesToUnlock": [ "divine_sacrifice" ]
},
{ "type": "DestroySelf" }
]
}

View File

@@ -0,0 +1,31 @@
{
"name": "Age of Industry",
"faithCost": 300,
"followersRequired": 750,
"productionRequired": 50,
"unlockedByDefault": false,
"effects": [
{
"type": "UnlockMiracle",
"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" }
],
"advancesToAge": "The Industrial Age"
}

View File

@@ -0,0 +1,30 @@
{
"name": "Form Settlement",
"faithCost": 30,
"followersRequired": 150,
"unlockedByDefault": true,
"effects": [
{
"type": "UnlockMiracle",
"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" }
],
"advancesToAge": "The Settlement Age"
}

View File

@@ -0,0 +1,21 @@
{
"name": "Age of Space",
"faithCost": 6000,
"followersRequired": 4000,
"productionRequired": 5000,
"unlockedByDefault": false,
"effects": [
{
"type": "UnlockMiracle",
"miraclesToUnlock": ["global_network", "orbital_calculations"]
},
{
"type": "ModifyStat",
"targetStat": "FollowersPerSecond",
"op": "Add",
"value": 1.0
},
{ "type": "DestroySelf" }
],
"advancesToAge": "The Space Age"
}

View File

@@ -0,0 +1,34 @@
{
"tiers": [
{
"tierEnum": "Tier1",
"threshold": 0,
"imagePath": "res://Sprites/Follower.png",
"scale": { "x": 0.1, "y": 0.1 }
},
{
"tierEnum": "Tier2",
"threshold": 30,
"imagePath": "res://Sprites/follower_tier_2.png",
"scale": { "x": 0.1, "y": 0.1 }
},
{
"tierEnum": "Tier3",
"threshold": 80,
"imagePath": "res://Sprites/follower_tier_3.png",
"scale": { "x": 0.1, "y": 0.1 }
},
{
"tierEnum": "Tier4",
"threshold": 200,
"imagePath": "res://Sprites/follower_tier_4.png",
"scale": { "x": 0.1, "y": 0.1 }
},
{
"tierEnum": "Tier5",
"threshold": 500,
"imagePath": "res://Sprites/follower_tier_5.png",
"scale": { "x": 0.1, "y": 0.1 }
}
]
}

64
Mods/Tiers/hut_tiers.json Normal file
View File

@@ -0,0 +1,64 @@
{
"tiers": [
{
"tierEnum": "Tier1",
"threshold": 20,
"imagePath": "res://Sprites/Hut.png",
"scale": { "x": 0.05, "y": 0.05 }
},
{
"tierEnum": "Tier2",
"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 }
}
]
}

View 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 }
}
]
}

View File

@@ -5,6 +5,7 @@
<RootNamespace>parasiticgod</RootNamespace> <RootNamespace>parasiticgod</RootNamespace>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="LimboConsole.Sharp" Version="0.0.1-beta-008" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4-beta1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.4-beta1" />
</ItemGroup> </ItemGroup>
</Project> </Project>

182
README.md Normal file
View File

@@ -0,0 +1,182 @@
# Parasitic God
![Made With Godot](https://img.shields.io/badge/Made%20With-Godot-478CBF?style=for-the-badge&logo=godot-engine&logoColor=white)
![Lines of Code](https://tokei.rs/b1/github/GKaszewski/parasitic-god?style=for-the-badge&category=code)
![Last Commit](https://img.shields.io/github/last-commit/GKaszewski/parasitic-god?style=for-the-badge)
![Stars](https://img.shields.io/github/stars/GKaszewski/parasitic-god?style=for-the-badge)
---
## 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**
Watch a handful of followers grow into a massive civilization with huts, cities, and procedural road networks.
- **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.
- **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, create random events, and even define new visual tiers for your civilization.
### Finding the Mods Folder
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/`
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, create a JSON file in `Mods/Miracles`.
The filename becomes its unique **ID** (e.g., `my_cool_miracle.json`):
```json
{
"name": "My Cool Miracle",
"faithCost": 100,
"followersRequired": 50,
"productionRequired": 0,
"unlockedByDefault": true,
"advancesToAge": "The Cool Age",
"effects": [
{
"type": "AddResource",
"targetResource": "Faith",
"value": 200
}
]
}
````
---
### 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
}
]
}
]
}
```
---
### Modifying Visual Tiers
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
{
"tiers": [
{
"tierEnum": "Tier1",
"threshold": 0,
"imagePath": "user://Mods/Tiers/Huts/my_custom_hut.png",
"scale": { "x": 1.0, "y": 1.0 }
}
]
}
```
* **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.
---
### 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:**
![Lines of code](https://tokei.rs/b1/github/GKaszewski/parasitic-god)
**Repo Activity:**
![Commit activity](https://img.shields.io/github/commit-activity/m/GKaszewski/parasitic-god)
---
## License
This project is open source. See the [LICENSE](./LICENSE.txt) file for details.
---
## Contributing
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!

View File

@@ -1,9 +0,0 @@
[gd_resource type="Resource" script_class="AddResourceEffect" load_steps=2 format=3 uid="uid://bs5iwc5lsdu0r"]
[ext_resource type="Script" uid="uid://flyhl4i86han" path="res://Scripts/Core/Effects/AddResourceEffect.cs" id="1_2o4it"]
[resource]
script = ExtResource("1_2o4it")
TargetResource = 1
Value = 10000.0
metadata/_custom_type_script = "uid://flyhl4i86han"

View File

@@ -1,9 +0,0 @@
[gd_resource type="Resource" script_class="AddResourceEffect" load_steps=2 format=3 uid="uid://dxiaxhggfqcyb"]
[ext_resource type="Script" uid="uid://flyhl4i86han" path="res://Scripts/Core/Effects/AddResourceEffect.cs" id="1_2xs1x"]
[resource]
script = ExtResource("1_2xs1x")
TargetResource = 1
Value = 100.0
metadata/_custom_type_script = "uid://flyhl4i86han"

View File

@@ -1,9 +0,0 @@
[gd_resource type="Resource" script_class="AddResourceEffect" load_steps=2 format=3 uid="uid://dd17hc8jju5ek"]
[ext_resource type="Script" uid="uid://flyhl4i86han" path="res://Scripts/Core/Effects/AddResourceEffect.cs" id="1_pce0a"]
[resource]
script = ExtResource("1_pce0a")
TargetResource = 2
Value = 5.0
metadata/_custom_type_script = "uid://flyhl4i86han"

View File

@@ -1,13 +0,0 @@
[gd_resource type="Resource" script_class="MiracleDefinition" load_steps=4 format=3 uid="uid://df3cq0eb82x0i"]
[ext_resource type="Resource" uid="uid://dd17hc8jju5ek" path="res://Resources/Effects/Add_5_Corruption.tres" id="1_jmmaa"]
[ext_resource type="Script" uid="uid://cfn3mx12xism5" path="res://Scripts/Core/MiracleDefinition.cs" id="1_oh2pa"]
[ext_resource type="Resource" uid="uid://dxiaxhggfqcyb" path="res://Resources/Effects/Add_10_Followers.tres" id="2_lg4lk"]
[resource]
script = ExtResource("1_oh2pa")
Name = "Fertility Blessing"
FaithCost = 100.0
FollowersRequired = 0
Effects = Array[Resource]([ExtResource("2_lg4lk"), ExtResource("1_jmmaa")])
metadata/_custom_type_script = "uid://cfn3mx12xism5"

View File

@@ -1,12 +0,0 @@
[gd_resource type="Resource" script_class="MiracleDefinition" load_steps=3 format=3 uid="uid://ckdc32ptfjhx6"]
[ext_resource type="Resource" uid="uid://bs5iwc5lsdu0r" path="res://Resources/Effects/Add_1000_Followers.tres" id="1_bkpio"]
[ext_resource type="Script" uid="uid://cfn3mx12xism5" path="res://Scripts/Core/MiracleDefinition.cs" id="3_oqu5j"]
[resource]
script = ExtResource("3_oqu5j")
Name = "GOD POWER"
FaithCost = 1.0
FollowersRequired = 0
Effects = Array[Resource]([ExtResource("1_bkpio")])
metadata/_custom_type_script = "uid://cfn3mx12xism5"

View File

@@ -0,0 +1,7 @@
[gd_scene load_steps=2 format=3 uid="uid://c2baysanqtp8g"]
[ext_resource type="Texture2D" uid="uid://c46t8kj1gb5qd" path="res://Sprites/castle.png" id="1_42k1r"]
[node name="CastleTier1" type="Sprite2D"]
scale = Vector2(0.03, 0.03)
texture = ExtResource("1_42k1r")

View File

@@ -0,0 +1,7 @@
[gd_scene load_steps=2 format=3 uid="uid://cg5mdwkf7fxqn"]
[ext_resource type="Texture2D" uid="uid://c61h7a8byuyeu" path="res://Sprites/house.png" id="1_ub3ef"]
[node name="HouseTier1" type="Sprite2D"]
scale = Vector2(0.02, 0.02)
texture = ExtResource("1_ub3ef")

View File

@@ -0,0 +1,11 @@
[gd_scene load_steps=2 format=3 uid="uid://bi6053qjm60vt"]
[ext_resource type="Texture2D" uid="uid://c61h7a8byuyeu" path="res://Sprites/house.png" id="1_8opcd"]
[node name="HouseTier2" type="Sprite2D"]
scale = Vector2(0.02, 0.02)
texture = ExtResource("1_8opcd")
[node name="Sprite2D" type="Sprite2D" parent="."]
position = Vector2(450, 0)
texture = ExtResource("1_8opcd")

View File

@@ -0,0 +1,15 @@
[gd_scene load_steps=2 format=3 uid="uid://ctqgx6alvxci0"]
[ext_resource type="Texture2D" uid="uid://c61h7a8byuyeu" path="res://Sprites/house.png" id="1_rp41l"]
[node name="HouseTier2" type="Sprite2D"]
scale = Vector2(0.02, 0.02)
texture = ExtResource("1_rp41l")
[node name="Sprite2D2" type="Sprite2D" parent="."]
position = Vector2(-550, 0)
texture = ExtResource("1_rp41l")
[node name="Sprite2D" type="Sprite2D" parent="."]
position = Vector2(450, 0)
texture = ExtResource("1_rp41l")

View File

@@ -1,24 +1,32 @@
[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://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"] [ext_resource type="Script" uid="uid://b77vh831r1e3c" path="res://Scenes/Main/MiraclePanel.cs" id="2_hcu3t"]
[ext_resource type="PackedScene" uid="uid://rj1fsdlhju5y" path="res://Scenes/Main/miracle_button.tscn" id="3_qdkat"] [ext_resource type="PackedScene" uid="uid://rj1fsdlhju5y" path="res://Scenes/UI/miracle_button.tscn" id="3_qdkat"]
[ext_resource type="Texture2D" uid="uid://dg6ac3jb1366r" path="res://Sprites/globe.svg" id="4_i3fi7"] [ext_resource type="Texture2D" uid="uid://dg6ac3jb1366r" path="res://Sprites/globe.svg" id="4_i3fi7"]
[ext_resource type="Script" uid="uid://ddshg236tlltt" path="res://Scripts/Components/ActiveBuffsManager.cs" id="4_xggvw"]
[ext_resource type="PackedScene" uid="uid://b417dl07c13uc" path="res://Scenes/UI/buff_button.tscn" id="5_xd21n"]
[ext_resource type="AudioStream" uid="uid://4cgkxo3qconx" path="res://Sfx/UI_SFX_Pack_61_50.wav" id="6_32vk6"]
[ext_resource type="PackedScene" uid="uid://be5d0d3aweg0l" path="res://Scenes/Huts/HutMarker.tscn" id="6_cv8e0"] [ext_resource type="PackedScene" uid="uid://be5d0d3aweg0l" path="res://Scenes/Huts/HutMarker.tscn" id="6_cv8e0"]
[ext_resource type="Script" uid="uid://dj2wyrq07gfp2" path="res://Scripts/PopulationVisualizer.cs" id="8_cv8e0"] [ext_resource type="Script" uid="uid://c6uh5h3sdlg7n" path="res://Scripts/Components/NotificationManager.cs" id="6_iwp64"]
[ext_resource type="PackedScene" uid="uid://crpf0llofg0sc" path="res://Scenes/UI/notification_label.tscn" id="7_4etfk"]
[ext_resource type="AudioStream" uid="uid://vuf57vdueq3b" path="res://Sfx/UI_SFX_Pack_61_49.wav" id="7_crdpj"]
[ext_resource type="AudioStream" uid="uid://dv0i7xw8o5ac0" path="res://Sfx/UI_SFX_Pack_61_5.wav" id="8_4etfk"]
[ext_resource type="Script" uid="uid://dj2wyrq07gfp2" path="res://Scripts/Components/PopulationVisualizer.cs" id="8_cv8e0"]
[ext_resource type="PackedScene" uid="uid://8w7tvsgkev1y" path="res://Scenes/tree.tscn" id="8_hcu3t"] [ext_resource type="PackedScene" uid="uid://8w7tvsgkev1y" path="res://Scenes/tree.tscn" id="8_hcu3t"]
[ext_resource type="Resource" uid="uid://8ooxfo2wdbhu" path="res://Resources/Tiers/Followers/follower_tier_1.tres" id="9_hkvnm"]
[ext_resource type="Shader" uid="uid://bf8nk145fjkgh" path="res://Shaders/corruption_shader.gdshader" id="9_wgovn"] [ext_resource type="Shader" uid="uid://bf8nk145fjkgh" path="res://Shaders/corruption_shader.gdshader" id="9_wgovn"]
[ext_resource type="Resource" uid="uid://cejeb3467iiyl" path="res://Resources/Tiers/Followers/follower_tier_2.tres" id="10_5ci8a"]
[ext_resource type="PackedScene" uid="uid://cqkye7yykakns" path="res://Scenes/Followers/FollowerMarker.tscn" id="11_5ci8a"] [ext_resource type="PackedScene" uid="uid://cqkye7yykakns" path="res://Scenes/Followers/FollowerMarker.tscn" id="11_5ci8a"]
[ext_resource type="Resource" uid="uid://q0rha23lx4wl" path="res://Resources/Tiers/Followers/follower_tier_3.tres" id="11_18xdc"] [ext_resource type="Script" uid="uid://djmtle2h3yd2e" path="res://Scripts/Components/PauseManager.cs" id="11_xggvw"]
[ext_resource type="Resource" uid="uid://i1oo0q84q8ps" path="res://Resources/Tiers/Followers/follower_tier_4.tres" id="12_epx8f"] [ext_resource type="PackedScene" uid="uid://wysxqe44rxhf" path="res://Scenes/TempleMarker.tscn" id="14_udh0u"]
[ext_resource type="Resource" uid="uid://bwu8k7cyjhf8c" path="res://Resources/Tiers/Followers/follower_tier_5.tres" id="13_hcu3t"] [ext_resource type="PackedScene" uid="uid://xk2xirjd1sma" path="res://Scenes/moddable_visual.tscn" id="17_qdkat"]
[ext_resource type="Resource" uid="uid://bbkbssvptkyvh" path="res://Resources/Tiers/Huts/hut_tier_1.tres" id="14_18xdc"] [ext_resource type="Script" uid="uid://furbvcmw31bx" path="res://Scripts/Components/ForestVisualizer.cs" id="18_qdkat"]
[ext_resource type="Resource" uid="uid://co2sdpwpajjqi" path="res://Resources/Tiers/Huts/hut_tier_2.tres" id="15_epx8f"] [ext_resource type="Script" uid="uid://cw8gpeaq3yfjn" path="res://Scripts/Components/RoadManager.cs" id="19_qdkat"]
[ext_resource type="Resource" uid="uid://b8k30qsd434dp" path="res://Resources/Tiers/Huts/hut_tier_3.tres" id="16_hcu3t"] [ext_resource type="Script" uid="uid://2ipbgwlx1ld1" path="res://Scripts/Components/EventManager.cs" id="22_iwp64"]
[ext_resource type="Script" uid="uid://furbvcmw31bx" path="res://Scripts/ForestVisualizer.cs" id="18_qdkat"] [ext_resource type="PackedScene" uid="uid://gdejd44km3co" path="res://Scenes/UI/event_popup.tscn" id="23_4etfk"]
[ext_resource type="Script" uid="uid://cw8gpeaq3yfjn" path="res://Scripts/RoadManager.cs" id="19_qdkat"] [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"] [sub_resource type="ShaderMaterial" id="ShaderMaterial_cv8e0"]
shader = ExtResource("9_wgovn") shader = ExtResource("9_wgovn")
@@ -36,7 +44,10 @@ _productionLabel = NodePath("UiLayer/Control/Main/Top/ProductionLabel")
_miraclePanel = NodePath("UiLayer/Control/Main/ScrollContainer/Bottom") _miraclePanel = NodePath("UiLayer/Control/Main/ScrollContainer/Bottom")
_worldSprite = NodePath("World Sprite") _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"] [node name="Control" type="Control" parent="UiLayer"]
layout_mode = 3 layout_mode = 3
@@ -47,6 +58,7 @@ grow_horizontal = 2
grow_vertical = 2 grow_vertical = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
size_flags_vertical = 3 size_flags_vertical = 3
mouse_filter = 2
[node name="Main" type="MarginContainer" parent="UiLayer/Control"] [node name="Main" type="MarginContainer" parent="UiLayer/Control"]
layout_mode = 1 layout_mode = 1
@@ -57,6 +69,7 @@ grow_horizontal = 2
grow_vertical = 2 grow_vertical = 2
size_flags_horizontal = 4 size_flags_horizontal = 4
size_flags_vertical = 0 size_flags_vertical = 0
mouse_filter = 2
theme_override_constants/margin_left = 8 theme_override_constants/margin_left = 8
theme_override_constants/margin_top = 8 theme_override_constants/margin_top = 8
theme_override_constants/margin_right = 8 theme_override_constants/margin_right = 8
@@ -66,6 +79,7 @@ theme_override_constants/margin_bottom = 8
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 4 size_flags_horizontal = 4
size_flags_vertical = 0 size_flags_vertical = 0
mouse_filter = 2
alignment = 1 alignment = 1
[node name="FaithLabel" type="Label" parent="UiLayer/Control/Main/Top"] [node name="FaithLabel" type="Label" parent="UiLayer/Control/Main/Top"]
@@ -108,60 +122,75 @@ size_flags_horizontal = 0
size_flags_vertical = 10 size_flags_vertical = 10
vertical_scroll_mode = 0 vertical_scroll_mode = 0
[node name="HBoxContainer" type="HBoxContainer" parent="UiLayer/Control/Main/ScrollContainer2"] [node name="HBoxContainer" type="HBoxContainer" parent="UiLayer/Control/Main/ScrollContainer2" node_paths=PackedStringArray("_buffRemovedSfx", "_buffAddedSfx")]
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
size_flags_vertical = 3 size_flags_vertical = 3
script = ExtResource("4_xggvw")
_activeBuffScene = ExtResource("5_xd21n")
_buffRemovedSfx = NodePath("BuffRemoved")
_buffAddedSfx = NodePath("BuffAdded")
[node name="Button" type="Button" parent="UiLayer/Control/Main/ScrollContainer2/HBoxContainer"] [node name="BuffRemoved" type="AudioStreamPlayer" parent="UiLayer/Control/Main/ScrollContainer2/HBoxContainer"]
layout_mode = 2 stream = ExtResource("6_32vk6")
disabled = true
text = "Effect"
[node name="Button2" type="Button" parent="UiLayer/Control/Main/ScrollContainer2/HBoxContainer"] [node name="BuffAdded" type="AudioStreamPlayer" parent="UiLayer/Control/Main/ScrollContainer2/HBoxContainer"]
layout_mode = 2 stream = ExtResource("7_crdpj")
disabled = true
text = "Effect"
[node name="Button3" type="Button" parent="UiLayer/Control/Main/ScrollContainer2/HBoxContainer"] [node name="MarginContainer" type="MarginContainer" parent="UiLayer"]
layout_mode = 2 anchors_preset = 15
disabled = true anchor_right = 1.0
text = "Effect" 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="Button4" type="Button" parent="UiLayer/Control/Main/ScrollContainer2/HBoxContainer"] [node name="Button" type="Button" parent="UiLayer/MarginContainer"]
layout_mode = 2 layout_mode = 2
disabled = true size_flags_horizontal = 8
text = "Effect" size_flags_vertical = 0
focus_mode = 0
text = "Pause"
[node name="Button5" type="Button" parent="UiLayer/Control/Main/ScrollContainer2/HBoxContainer"] [node name="PanelContainer" type="PanelContainer" parent="UiLayer"]
layout_mode = 2 anchors_preset = 15
disabled = true anchor_right = 1.0
text = "Effect" 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="Button6" type="Button" parent="UiLayer/Control/Main/ScrollContainer2/HBoxContainer"] [node name="Label" type="Label" parent="UiLayer/PanelContainer"]
layout_mode = 2 layout_mode = 2
disabled = true size_flags_vertical = 1
text = "Effect" text = "Paused"
horizontal_alignment = 1
vertical_alignment = 1
uppercase = true
[node name="Button7" type="Button" parent="UiLayer/Control/Main/ScrollContainer2/HBoxContainer"] [node name="Notification Layer" type="CanvasLayer" parent="." node_paths=PackedStringArray("_sfx")]
layout_mode = 2 layer = 3
disabled = true script = ExtResource("6_iwp64")
text = "Effect" _notificationLabelScene = ExtResource("7_4etfk")
_sfx = NodePath("Advance Age")
[node name="Button8" type="Button" parent="UiLayer/Control/Main/ScrollContainer2/HBoxContainer"] [node name="Advance Age" type="AudioStreamPlayer" parent="Notification Layer"]
layout_mode = 2 stream = ExtResource("8_4etfk")
disabled = true
text = "Effect"
[node name="Button9" type="Button" parent="UiLayer/Control/Main/ScrollContainer2/HBoxContainer"] [node name="CenterContainer" type="CenterContainer" parent="Notification Layer"]
layout_mode = 2 anchors_preset = 15
disabled = true anchor_right = 1.0
text = "Effect" anchor_bottom = 1.0
grow_horizontal = 2
[node name="Button10" type="Button" parent="UiLayer/Control/Main/ScrollContainer2/HBoxContainer"] grow_vertical = 2
layout_mode = 2 mouse_filter = 2
disabled = true
text = "Effect"
[node name="Camera2D" type="Camera2D" parent="."] [node name="Camera2D" type="Camera2D" parent="."]
@@ -172,7 +201,14 @@ scale = Vector2(2.41247, 2.41247)
texture = ExtResource("4_i3fi7") texture = ExtResource("4_i3fi7")
metadata/_edit_lock_ = true 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="."] [node name="Hut Markers" type="Node2D" parent="."]
metadata/_edit_lock_ = true
[node name="HutMarker" parent="Hut Markers" instance=ExtResource("6_cv8e0")] [node name="HutMarker" parent="Hut Markers" instance=ExtResource("6_cv8e0")]
position = Vector2(-1, -29) position = Vector2(-1, -29)
@@ -237,7 +273,52 @@ position = Vector2(65, 472)
[node name="HutMarker21" parent="Hut Markers" instance=ExtResource("6_cv8e0")] [node name="HutMarker21" parent="Hut Markers" instance=ExtResource("6_cv8e0")]
position = Vector2(-209, 28) 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="."] [node name="Followers Markers" type="Node2D" parent="."]
metadata/_edit_lock_ = true
[node name="FollowerMarker" parent="Followers Markers" instance=ExtResource("11_5ci8a")] [node name="FollowerMarker" parent="Followers Markers" instance=ExtResource("11_5ci8a")]
@@ -296,6 +377,7 @@ position = Vector2(-88, -32)
position = Vector2(82, -234) position = Vector2(82, -234)
[node name="ForestContainer" type="Node2D" parent="."] [node name="ForestContainer" type="Node2D" parent="."]
metadata/_edit_lock_ = true
[node name="Tree" parent="ForestContainer" instance=ExtResource("8_hcu3t")] [node name="Tree" parent="ForestContainer" instance=ExtResource("8_hcu3t")]
position = Vector2(214, 38) position = Vector2(214, 38)
@@ -5580,14 +5662,16 @@ position = Vector2(243.12, -124.88)
[node name="FollowerPopulationVisualizer" type="Node" parent="." node_paths=PackedStringArray("_markersContainer")] [node name="FollowerPopulationVisualizer" type="Node" parent="." node_paths=PackedStringArray("_markersContainer")]
script = ExtResource("8_cv8e0") script = ExtResource("8_cv8e0")
_markersContainer = NodePath("../Followers Markers") _markersContainer = NodePath("../Followers Markers")
_tiers = Array[Object]([ExtResource("9_hkvnm"), ExtResource("10_5ci8a"), ExtResource("11_18xdc"), ExtResource("12_epx8f"), ExtResource("13_hcu3t")]) _unitsPerMarker = 1
_moddableVisualScene = ExtResource("17_qdkat")
metadata/_custom_type_script = "uid://dj2wyrq07gfp2" metadata/_custom_type_script = "uid://dj2wyrq07gfp2"
[node name="HutPopulationVisualizer" type="Node" parent="." node_paths=PackedStringArray("_markersContainer")] [node name="HutPopulationVisualizer" type="Node" parent="." node_paths=PackedStringArray("_markersContainer")]
script = ExtResource("8_cv8e0") script = ExtResource("8_cv8e0")
_markersContainer = NodePath("../Hut Markers") _markersContainer = NodePath("../Hut Markers")
_unitsPerMarker = 1 _unitsPerMarker = 1
_tiers = Array[Object]([ExtResource("14_18xdc"), ExtResource("15_epx8f"), ExtResource("16_hcu3t")]) Category = 1
_moddableVisualScene = ExtResource("17_qdkat")
metadata/_custom_type_script = "uid://dj2wyrq07gfp2" metadata/_custom_type_script = "uid://dj2wyrq07gfp2"
[node name="ForestVisualizer" type="Node" parent="." node_paths=PackedStringArray("_treesContainer")] [node name="ForestVisualizer" type="Node" parent="." node_paths=PackedStringArray("_treesContainer")]
@@ -5595,7 +5679,19 @@ script = ExtResource("18_qdkat")
_treesContainer = NodePath("../ForestContainer") _treesContainer = NodePath("../ForestContainer")
metadata/_custom_type_script = "uid://furbvcmw31bx" metadata/_custom_type_script = "uid://furbvcmw31bx"
[node name="RoadManager" type="Node2D" parent="." node_paths=PackedStringArray("_markersContainer")] [node name="TemplesVisualizer" type="Node" parent="." node_paths=PackedStringArray("_markersContainer")]
script = ExtResource("19_qdkat") script = ExtResource("8_cv8e0")
_markersContainer = NodePath("../Hut Markers") _markersContainer = NodePath("../TemplesContainer")
metadata/_custom_type_script = "uid://cw8gpeaq3yfjn" _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")

View File

@@ -1,5 +1,8 @@
using System.Collections.Generic;
using System.Linq;
using Godot; using Godot;
using ParasiticGod.Scripts.Core; using ParasiticGod.Scripts.Core;
using ParasiticGod.Scripts.Core.Effects;
using ParasiticGod.Scripts.Singletons; using ParasiticGod.Scripts.Singletons;
namespace ParasiticGod.Scenes.Main; namespace ParasiticGod.Scenes.Main;
@@ -7,6 +10,7 @@ namespace ParasiticGod.Scenes.Main;
public partial class MiracleButton : Button public partial class MiracleButton : Button
{ {
private MiracleDefinition _miracle; private MiracleDefinition _miracle;
[Export] private AudioStreamPlayer _sfx;
public override void _Ready() public override void _Ready()
{ {
@@ -20,41 +24,142 @@ public partial class MiracleButton : Button
Text = BuildText(); Text = BuildText();
TooltipText = BuildTooltipText(); TooltipText = BuildTooltipText();
if (_sfx == null)
{
_sfx = GetNodeOrNull<AudioStreamPlayer>("SFX");
}
Pressed += OnPressed; Pressed += OnPressed;
GameBus.Instance.StateChanged += UpdateAvailability;
UpdateAvailability(GameBus.Instance.CurrentState);
} }
public override void _ExitTree() public override void _ExitTree()
{ {
Pressed -= OnPressed; Pressed -= OnPressed;
if (GameBus.Instance != null)
{
GameBus.Instance.StateChanged -= UpdateAvailability;
}
} }
private void OnPressed() private void OnPressed()
{ {
_sfx?.Play();
GameBus.Instance.PerformMiracle(_miracle); GameBus.Instance.PerformMiracle(_miracle);
} }
public void SetMiracle(MiracleDefinition miracle) public void SetMiracle(MiracleDefinition miracle)
{ {
_miracle = miracle; _miracle = miracle;
Text = BuildText();
TooltipText = BuildTooltipText();
} }
public MiracleDefinition GetMiracle() { return _miracle; } public MiracleDefinition GetMiracle() { return _miracle; }
/// <summary>
/// Checks the miracle's requirements against the current game state
/// and updates the button's disabled status and tooltip.
/// </summary>
private void UpdateAvailability(GameState state)
{
if (_miracle == null) return;
var missingRequirements = new List<string>();
if (state.Get(Stat.Faith) < _miracle.FaithCost)
{
missingRequirements.Add($"Not enough Faith ({state.Get(Stat.Faith):F0} / {_miracle.FaithCost})");
}
if (state.Get(Stat.Followers) < _miracle.FollowersRequired)
{
missingRequirements.Add($"Not enough Followers ({(long)state.Get(Stat.Followers)} / {_miracle.FollowersRequired})");
}
if (state.Get(Stat.Production) < _miracle.ProductionRequired)
{
missingRequirements.Add($"Not enough Production ({state.Get(Stat.Production):F0} / {_miracle.ProductionRequired})");
}
if (AreAllUnlocksAlreadyPresent(state))
{
missingRequirements.Add("Already unlocked subsequent powers.");
}
if (IsBuffAlreadyActive(state))
{
missingRequirements.Add("This buff is already active.");
}
if (missingRequirements.Any())
{
Disabled = true;
TooltipText = string.Join("\n", missingRequirements);
}
else
{
Disabled = false;
TooltipText = BuildTooltipText();
}
}
private string BuildText() private string BuildText()
{ {
return $"{_miracle.Name}\nCost: {_miracle.FaithCost} Faith"; string costText;
if (_miracle.ProductionRequired > 0 && _miracle.FaithCost <= 0)
{
costText = $"Cost: {_miracle.ProductionRequired:F0} Prod";
}
else
{
costText = $"Cost: {_miracle.FaithCost:F0} Faith";
}
return $"{_miracle.Name}\n{costText}";
} }
private string BuildTooltipText() private string BuildTooltipText()
{ {
var tooltip = $"Cost: {_miracle.FaithCost} Faith\nRequires: {_miracle.FollowersRequired} Followers\nEffects:\n"; var tooltip = "";
if (_miracle.FaithCost > 0)
tooltip += $"Cost: {_miracle.FaithCost:F0} Faith\n";
if (_miracle.ProductionRequired > 0)
tooltip += $"Cost: {_miracle.ProductionRequired:F0} Production\n";
if (_miracle.FollowersRequired > 0)
tooltip += $"Requires: {_miracle.FollowersRequired} Followers\n";
tooltip += "\nEffects:\n";
foreach (var effect in _miracle.Effects) foreach (var effect in _miracle.Effects)
{ {
if (effect.ToString() == string.Empty) continue; if (string.IsNullOrEmpty(effect.ToString())) continue;
tooltip += $"- {effect}\n"; tooltip += $"- {effect}\n";
} }
return tooltip.TrimEnd(); return tooltip.TrimEnd();
} }
private bool AreAllUnlocksAlreadyPresent(GameState state)
{
var unlockEffect = _miracle.Effects.OfType<UnlockMiracleEffect>().FirstOrDefault();
if (unlockEffect == null || unlockEffect.MiraclesToUnlock.Count == 0)
{
return false;
}
return unlockEffect.MiraclesToUnlock.All(state.IsMiracleUnlocked);
}
private bool IsBuffAlreadyActive(GameState state)
{
var buffEffect = _miracle.Effects.OfType<ApplyBuffEffect>().FirstOrDefault();
if (buffEffect == null || string.IsNullOrEmpty(buffEffect.BuffId))
{
return false;
}
return state.IsBuffActive(buffEffect.BuffId);
}
} }

View File

@@ -27,6 +27,7 @@ public partial class MiraclePanel : GridContainer
{ {
if (miracle.UnlockedByDefault) if (miracle.UnlockedByDefault)
{ {
GameBus.Instance.CurrentState.AddUnlockedMiracle(miracle.Id);
AddButtonForMiracle(miracle); AddButtonForMiracle(miracle);
} }
} }

14
Scenes/TempleMarker.tscn Normal file
View 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")

View File

@@ -0,0 +1,10 @@
[gd_scene load_steps=2 format=3 uid="uid://b417dl07c13uc"]
[ext_resource type="Script" uid="uid://chnf0t5xdosys" path="res://Scripts/ActiveBuffUi.cs" id="1_7ujuu"]
[node name="BuffButton" type="Button"]
focus_mode = 0
disabled = true
button_mask = 0
text = "Effect"
script = ExtResource("1_7ujuu")

View 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

View 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

View File

@@ -1,9 +1,13 @@
[gd_scene load_steps=2 format=3 uid="uid://rj1fsdlhju5y"] [gd_scene load_steps=3 format=3 uid="uid://rj1fsdlhju5y"]
[ext_resource type="Script" uid="uid://ctjmwgucwh3le" path="res://Scenes/Main/MiracleButton.cs" id="1_sxcu0"] [ext_resource type="Script" uid="uid://ctjmwgucwh3le" path="res://Scenes/Main/MiracleButton.cs" id="1_sxcu0"]
[ext_resource type="AudioStream" uid="uid://c2kkfl2nv5rlh" path="res://Sfx/UI_SFX_Pack_61_22.wav" id="2_bjlki"]
[node name="MiracleButton" type="Button"] [node name="MiracleButton" type="Button"]
custom_minimum_size = Vector2(128, 64) custom_minimum_size = Vector2(128, 64)
offset_right = 8.0 offset_right = 8.0
offset_bottom = 8.0 offset_bottom = 8.0
script = ExtResource("1_sxcu0") script = ExtResource("1_sxcu0")
[node name="SFX" type="AudioStreamPlayer" parent="."]
stream = ExtResource("2_bjlki")

View File

@@ -0,0 +1,42 @@
[gd_scene load_steps=3 format=3 uid="uid://crpf0llofg0sc"]
[ext_resource type="Script" uid="uid://drx8lqcjq5khj" path="res://Scripts/NotificationLabel.cs" id="1_x4sn4"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_4bx8n"]
bg_color = Color(0.187176, 0.187176, 0.187176, 1)
border_width_left = 1
border_width_top = 1
border_width_right = 1
border_width_bottom = 1
border_color = Color(1, 0.759215, 0.00113704, 1)
corner_radius_top_left = 2
corner_radius_top_right = 2
corner_radius_bottom_right = 2
corner_radius_bottom_left = 2
[node name="NotificationLabel" type="Label"]
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -20.0
offset_top = -11.5
offset_right = 20.0
offset_bottom = 11.5
grow_horizontal = 2
grow_vertical = 2
horizontal_alignment = 1
vertical_alignment = 1
script = ExtResource("1_x4sn4")
[node name="Panel" type="Panel" parent="."]
z_index = -1
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_4bx8n")
metadata/_edit_group_ = true

57
Scenes/game_over.tscn Normal file
View 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
View 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")

View 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

View File

@@ -0,0 +1,9 @@
[gd_scene load_steps=2 format=3 uid="uid://xk2xirjd1sma"]
[ext_resource type="Script" uid="uid://dpfcbaqgq3l6d" path="res://Scripts/Components/ModdableVisual.cs" id="1_5b1dg"]
[node name="ModdableVisual" type="Node2D" node_paths=PackedStringArray("_sprite")]
script = ExtResource("1_5b1dg")
_sprite = NodePath("Sprite")
[node name="Sprite" type="Sprite2D" parent="."]

View 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
View 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")

31
Scripts/ActiveBuffUi.cs Normal file
View File

@@ -0,0 +1,31 @@
using Godot;
using ParasiticGod.Scripts.Core.Effects;
namespace ParasiticGod.Scripts;
[GlobalClass]
public partial class ActiveBuffUi : Button
{
private Buff _buff;
public void SetBuff(Buff buff)
{
_buff = buff;
Disabled = true;
UpdateDisplay();
}
public override void _Process(double delta)
{
if (_buff != null)
{
UpdateDisplay();
}
}
private void UpdateDisplay()
{
Text = _buff.Name;
TooltipText = $"x{_buff.Multiplier:F1} to {_buff.Name.Split(' ')[0]}\n{_buff.Duration:F0}s remaining";
}
}

View File

@@ -0,0 +1 @@
uid://chnf0t5xdosys

View File

@@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using Godot;
using ParasiticGod.Scripts.Core.Effects;
using ParasiticGod.Scripts.Singletons;
namespace ParasiticGod.Scripts.Components;
[GlobalClass]
public partial class ActiveBuffsManager : Node
{
[Export] private PackedScene _activeBuffScene;
[Export] private AudioStreamPlayer _buffRemovedSfx;
[Export] private AudioStreamPlayer _buffAddedSfx;
private readonly Dictionary<Guid, ActiveBuffUi> _activeBuffUis = new();
public override void _Ready()
{
GameBus.Instance.BuffAdded += OnBuffAdded;
GameBus.Instance.BuffRemoved += OnBuffRemoved;
}
public override void _ExitTree()
{
if (GameBus.Instance == null) return;
GameBus.Instance.BuffAdded -= OnBuffAdded;
GameBus.Instance.BuffRemoved -= OnBuffRemoved;
}
private void OnBuffAdded(Buff buff)
{
var buffInstance = _activeBuffScene.Instantiate<ActiveBuffUi>();
AddChild(buffInstance);
buffInstance.SetBuff(buff);
_activeBuffUis.Add(buff.InstanceId, buffInstance);
_buffAddedSfx?.Play();
}
private void OnBuffRemoved(Buff buff)
{
if (_activeBuffUis.TryGetValue(buff.InstanceId, out var buffUi))
{
buffUi.QueueFree();
_activeBuffUis.Remove(buff.InstanceId);
_buffRemovedSfx?.Play();
}
}
}

View File

@@ -0,0 +1 @@
uid://ddshg236tlltt

View 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}");
}
}
}

View File

@@ -0,0 +1 @@
uid://2ipbgwlx1ld1

View File

@@ -5,7 +5,7 @@ using Godot;
using ParasiticGod.Scripts.Core; using ParasiticGod.Scripts.Core;
using ParasiticGod.Scripts.Singletons; using ParasiticGod.Scripts.Singletons;
namespace ParasiticGod.Scripts; namespace ParasiticGod.Scripts.Components;
[GlobalClass] [GlobalClass]
public partial class ForestVisualizer : Node public partial class ForestVisualizer : Node

View File

@@ -0,0 +1,28 @@
using Godot;
namespace ParasiticGod.Scripts.Components;
[GlobalClass]
public partial class ModdableVisual : Node2D
{
[Export] private Sprite2D _sprite;
public Follower.FollowerTier Tier { get; private set; }
public override void _ExitTree()
{
if (_sprite != null)
{
_sprite.Texture = null;
}
}
public void Initialize(Follower.FollowerTier tier, Texture2D texture, Vector2 scale)
{
Tier = tier;
if (_sprite != null && texture != null)
{
_sprite.Texture = texture;
_sprite.Scale = scale;
}
}
}

View File

@@ -0,0 +1 @@
uid://dpfcbaqgq3l6d

View File

@@ -0,0 +1,33 @@
using Godot;
using ParasiticGod.Scripts;
using ParasiticGod.Scripts.Singletons;
namespace ParasiticGod.Scripts.Components;
[GlobalClass]
public partial class NotificationManager : CanvasLayer
{
[Export] private PackedScene _notificationLabelScene;
[Export] private AudioStreamPlayer _sfx;
public override void _Ready()
{
GameBus.Instance.AgeAdvanced += OnAgeAdvanced;
}
public override void _ExitTree()
{
if (GameBus.Instance != null)
{
GameBus.Instance.AgeAdvanced -= OnAgeAdvanced;
}
}
private void OnAgeAdvanced(string ageName)
{
var notification = _notificationLabelScene.Instantiate<NotificationLabel>();
AddChild(notification);
_sfx?.Play();
notification.ShowNotification($"You have entered\n{ageName}!");
}
}

View File

@@ -0,0 +1 @@
uid://c6uh5h3sdlg7n

View 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;
}
}

View File

@@ -0,0 +1 @@
uid://djmtle2h3yd2e

View File

@@ -1,18 +1,21 @@
using System.Collections.Generic; using System.Collections.Generic;
using Godot; using Godot;
using Godot.Collections;
using ParasiticGod.Scripts.Core; using ParasiticGod.Scripts.Core;
using ParasiticGod.Scripts.Singletons; using ParasiticGod.Scripts.Singletons;
namespace ParasiticGod.Scripts; namespace ParasiticGod.Scripts.Components;
[GlobalClass] [GlobalClass]
public partial class PopulationVisualizer : Node public partial class PopulationVisualizer : Node
{ {
public enum VisualCategory { Followers, Huts, Temples }
[Export] private Node2D _markersContainer; [Export] private Node2D _markersContainer;
[Export] private int _unitsPerMarker = 5; [Export] private int _unitsPerMarker = 5;
[Export] private Array<TierDefinition> _tiers; [Export] public VisualCategory Category { get; private set; }
[Export] private PackedScene _moddableVisualScene;
private List<TierDefinition> _tiers;
private readonly List<FollowerMarker> _markers = []; private readonly List<FollowerMarker> _markers = [];
private long _lastKnownUnitCount = -1; private long _lastKnownUnitCount = -1;
private int _lastKnownTierIndex = -1; private int _lastKnownTierIndex = -1;
@@ -20,6 +23,22 @@ public partial class PopulationVisualizer : Node
public override void _Ready() public override void _Ready()
{ {
switch (Category)
{
case VisualCategory.Followers:
_tiers = GameBus.Instance.FollowerTiers;
break;
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;
}
foreach (var child in _markersContainer.GetChildren()) foreach (var child in _markersContainer.GetChildren())
{ {
if (child is FollowerMarker marker) if (child is FollowerMarker marker)
@@ -40,7 +59,13 @@ public partial class PopulationVisualizer : Node
{ {
if (_isUpdating) return; if (_isUpdating) return;
var currentUnitCount = (long)newState.Get(Stat.Followers); long currentUnitCount = Category switch
{
VisualCategory.Followers => (long)newState.Get(Stat.Followers),
VisualCategory.Huts => (long)newState.Get(Stat.Followers),
VisualCategory.Temples => (long)newState.Get(Stat.Followers),
_ => 0
};
var currentMarkersToShow = (int)currentUnitCount / _unitsPerMarker; var currentMarkersToShow = (int)currentUnitCount / _unitsPerMarker;
var lastMarkersToShow = (int)_lastKnownUnitCount / _unitsPerMarker; var lastMarkersToShow = (int)_lastKnownUnitCount / _unitsPerMarker;
@@ -84,19 +109,23 @@ public partial class PopulationVisualizer : Node
if (i < followersToShow) if (i < followersToShow)
{ {
if (!marker.IsOccupied || _lastKnownTierIndex != newTierIndex) var currentVisual = marker.GetChildOrNull<ModdableVisual>(0);
if (currentVisual == null || currentVisual.Tier != currentTier.TierEnum)
{ {
if (marker.IsOccupied) marker.RemoveFollower(); if (marker.GetChildCount() > 0) marker.GetChild(0).QueueFree();
var followerInstance = currentTier.Scene.Instantiate<Follower>();
marker.PlaceFollower(followerInstance); var visualInstance = _moddableVisualScene.Instantiate<ModdableVisual>();
visualInstance.Initialize(currentTier.TierEnum, currentTier.Texture, currentTier.Scale);
marker.AddChild(visualInstance);
needsChange = true; needsChange = true;
} }
} }
else else
{ {
if (marker.IsOccupied) if (marker.GetChildCount() > 0)
{ {
marker.RemoveFollower(); marker.GetChild(0).QueueFree();
needsChange = true; needsChange = true;
} }
} }

View File

@@ -0,0 +1 @@
uid://m3qqshwpk16h

View File

@@ -1,11 +1,9 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Godot; using Godot;
using ParasiticGod.Scripts.Core;
using ParasiticGod.Scripts.Singletons; using ParasiticGod.Scripts.Singletons;
namespace ParasiticGod.Scripts; namespace ParasiticGod.Scripts.Components;
[GlobalClass] [GlobalClass]
public partial class RoadManager : Node2D public partial class RoadManager : Node2D
@@ -47,15 +45,16 @@ public partial class RoadManager : Node2D
_roadNetwork.ClearPoints(); _roadNetwork.ClearPoints();
var activeMarkers = _markersContainer.GetChildren() var activeMarkers = _markersContainer.GetChildren()
.OfType<FollowerMarker>() .OfType<Marker2D>() // We can just look for any Marker2D
.Where(m => m.IsOccupied && m.FollowerInstance != null && .Select(m => new { Marker = m, Visual = m.GetChildOrNull<ModdableVisual>(0) })
m.FollowerInstance.Tier >= _minimumTierForRoads) .Where(mv => mv.Visual != null && mv.Visual.Tier >= _minimumTierForRoads)
.Select(mv => mv.Marker)
.ToList(); .ToList();
if (activeMarkers.Count < 2) return; if (activeMarkers.Count < 2) return;
var treeNodes = new HashSet<FollowerMarker>(); var treeNodes = new HashSet<Node2D>();
var remainingNodes = new List<FollowerMarker>(activeMarkers); var remainingNodes = new List<Node2D>(activeMarkers);
var edges = new List<(Vector2, Vector2)>(); var edges = new List<(Vector2, Vector2)>();
var startNode = remainingNodes[0]; var startNode = remainingNodes[0];
@@ -64,8 +63,8 @@ public partial class RoadManager : Node2D
while (remainingNodes.Any()) while (remainingNodes.Any())
{ {
FollowerMarker bestSource = null; Node2D bestSource = null;
FollowerMarker bestDest = null; Node2D bestDest = null;
var minDistanceSq = float.MaxValue; var minDistanceSq = float.MaxValue;
foreach (var source in treeNodes) foreach (var source in treeNodes)

View File

@@ -1,23 +1,36 @@
using Godot; using Godot;
using ParasiticGod.Scripts.Singletons;
namespace ParasiticGod.Scripts.Core.Effects; namespace ParasiticGod.Scripts.Core.Effects;
[GlobalClass] [GlobalClass]
public partial class ApplyBuffEffect : Effect public partial class ApplyBuffEffect : Effect
{ {
[Export] public string BuffId { get; set; }
[Export] public Stat TargetStat { get; set; } [Export] public Stat TargetStat { get; set; }
[Export] public float Multiplier { get; set; } = 2.0f; [Export] public float Multiplier { get; set; } = 2.0f;
[Export] public double Duration { get; set; } = 30.0; [Export] public double Duration { get; set; } = 30.0;
public override void Execute(GameState gameState) 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 var newBuff = new Buff
{ {
Name = $"{TargetStat} x{Multiplier}",
Multiplier = Multiplier, Multiplier = Multiplier,
Duration = Duration Duration = Duration,
TargetStat = TargetStat,
BuffId = BuffId
}; };
gameState.ActiveBuffs.Add(newBuff); gameState.ActiveBuffs.Add(newBuff);
gameState.AddActiveBuff(BuffId);
GameBus.Instance.NotifyBuffAdded(newBuff);
} }
public override string ToString() public override string ToString()

View File

@@ -1,7 +1,13 @@
using System;
namespace ParasiticGod.Scripts.Core.Effects; namespace ParasiticGod.Scripts.Core.Effects;
public class Buff public class Buff
{ {
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 float Multiplier { get; set; } = 1.0f;
public double Duration { get; set; } public double Duration { get; set; }
} }

View 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();
}
}

View File

@@ -0,0 +1 @@
uid://77fa2htfghwy

View 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; }
}

View File

@@ -0,0 +1 @@
uid://ilyr01u70ciw

Some files were not shown because too many files have changed in this diff Show More