diff --git a/assets/scripts/_util.lua b/assets/scripts/_util.lua
index 8b7065f..ee422ad 100644
--- a/assets/scripts/_util.lua
+++ b/assets/scripts/_util.lua
@@ -12,13 +12,13 @@ end
---highlight_warn some value with warning colors
---@param val any
function highlight_warn(val)
- return text_underline(text_bold(_escape_color("38;5;161") .. "[" .. tostring(val) .. "]" .. string.char(27) .. "[0m"))
+ return text_underline(text_bold(_escape_color("38;5;161") .. "[" .. tostring(val) .. "]" .. string.char(27) .. "[0m"))
end
---highlight_success some value with success colors
---@param val any
function highlight_success(val)
- return text_underline(text_bold(_escape_color("38;5;119") .. "[" .. tostring(val) .. "]" .. string.char(27) .. "[0m"))
+ return text_underline(text_bold(_escape_color("38;5;119") .. "[" .. tostring(val) .. "]" .. string.char(27) .. "[0m"))
end
---choose_weighted chooses an item from a list of choices, with a weight for each item.
@@ -33,7 +33,7 @@ function choose_weighted(choices, weights)
total_weight = total_weight + weight
end
- local random = math.random() * total_weight
+ local random = random() * total_weight
for i, weight in ipairs(weights) do
random = random - weight
if random <= 0 then
@@ -83,27 +83,39 @@ end
---@param tags string[]
---@return artifact[]
function find_artifacts_by_tags(tags)
- return find_by_tags(registered.artifact, tags)
+ local found = find_by_tags(registered.artifact, tags)
+ table.sort(found, function(a, b) return a.id:upper() < b.id:upper() end)
+ return found
end
---find_cards_by_tags find all cards with the given tags.
---@param tags string[]
---@return card[]
function find_cards_by_tags(tags)
- return find_by_tags(registered.card, tags)
+ local found = find_by_tags(registered.card, tags)
+ table.sort(found, function(a, b) return a.id:upper() < b.id:upper() end)
+ return found
end
---find_events_by_tags find all events with the given tags.
---@param tags string[]
---@return event[]
function find_events_by_tags(tags)
- return find_by_tags(registered.event, tags)
+ local found = find_by_tags(registered.event, tags)
+ --table.sort(found, function(a, b) return a.id:upper() < b.id:upper() end)
+ return found
end
---choose_weighted_by_price choose a random item from the given list, weighted by price.
---@param items artifact|card
---@return string
function choose_weighted_by_price(items)
+ table.sort(items, function(a, b)
+ if a.id == nil then
+ return a.type_id < b.type_id
+ end
+ return a.id < b.id
+ end)
return choose_weighted(
fun.iter(items):map(function(item) return item.id or item.type_id end):totable(),
fun.iter(items):map(function(item) return item.price end):totable()
diff --git a/assets/scripts/definitions/api.lua b/assets/scripts/definitions/api.lua
index 093ef35..3088ca9 100644
--- a/assets/scripts/definitions/api.lua
+++ b/assets/scripts/definitions/api.lua
@@ -41,6 +41,16 @@ function fetch(key) end
---@return guid
function guid() end
+--- Returns a random number between 0 and 1
+---@return number
+function random() end
+
+--- Returns a random number between min and max
+---@param min number
+---@param max number
+---@return number
+function random_int(min, max) end
+
--- Stores a persistent value for this run that will be restored after a save load. Can store any lua basic value or table.
---@param key string
---@param value any
diff --git a/assets/scripts/enemies/_util.lua b/assets/scripts/enemies/_util.lua
index ac6e67b..afbe13d 100644
--- a/assets/scripts/enemies/_util.lua
+++ b/assets/scripts/enemies/_util.lua
@@ -6,7 +6,7 @@ function cast_random(guid, target)
if #cards == 0 then
print("can't cast_random with zero cards available!")
else
- cast_card(cards[math.random(#cards)], target)
+ cast_card(cards[random_int(0, #cards)], target)
end
end
diff --git a/assets/scripts/enemies/cyber_slime.lua b/assets/scripts/enemies/cyber_slime.lua
index 2f5d28a..5a3cc12 100644
--- a/assets/scripts/enemies/cyber_slime.lua
+++ b/assets/scripts/enemies/cyber_slime.lua
@@ -40,7 +40,7 @@ register_enemy("CYBER_SLIME", {
add_actor_by_enemy("CYBER_SLIME_MINION")
add_actor_by_enemy("CYBER_SLIME_MINION")
- if math.random() < 0.25 then
+ if random() < 0.25 then
add_actor_by_enemy("CYBER_SLIME_MINION")
end
return nil
diff --git a/assets/scripts/events/act_0/enemies.lua b/assets/scripts/events/act_0/enemies.lua
index 7f87293..649b0c8 100644
--- a/assets/scripts/events/act_0/enemies.lua
+++ b/assets/scripts/events/act_0/enemies.lua
@@ -13,10 +13,10 @@ It seems to be eating the metal from the walls. It looks at you and after a few
description = "Fight!",
callback = function()
add_actor_by_enemy("RUST_MITE")
- if math.random() < 0.25 then
+ if random() < 0.25 then
add_actor_by_enemy("RUST_MITE")
end
- if math.random() < 0.15 then
+ if random() < 0.15 then
add_actor_by_enemy("REPAIR_DRONE")
end
return GAME_STATE_FIGHT
@@ -41,10 +41,10 @@ It looks at you and says "Corpse. Clean. Engage.".
description = "Fight!",
callback = function()
add_actor_by_enemy("CLEAN_BOT")
- if math.random() < 0.25 then
+ if random() < 0.25 then
add_actor_by_enemy("CLEAN_BOT")
end
- if math.random() < 0.15 then
+ if random() < 0.15 then
add_actor_by_enemy("REPAIR_DRONE")
end
return GAME_STATE_FIGHT
@@ -66,10 +66,10 @@ It seems to be waiting for its prey to come closer and there is no way around it
description = "Fight!",
callback = function()
add_actor_by_enemy("CYBER_SPIDER")
- if math.random() < 0.25 then
+ if random() < 0.25 then
add_actor_by_enemy("CYBER_SPIDER")
end
- if math.random() < 0.15 then
+ if random() < 0.15 then
add_actor_by_enemy("REPAIR_DRONE")
end
return GAME_STATE_FIGHT
@@ -93,10 +93,10 @@ As you explore the facility, you hear a high-pitched whirring sound. A drone equ
description = "Fight!",
callback = function()
add_actor_by_enemy("LASER_DRONE")
- if math.random() < 0.10 then
+ if random() < 0.10 then
add_actor_by_enemy("LASER_DRONE")
end
- if math.random() < 0.15 then
+ if random() < 0.15 then
add_actor_by_enemy("REPAIR_DRONE")
end
return GAME_STATE_FIGHT
@@ -120,7 +120,7 @@ As you delve deeper into the facility, you notice a bright glow emanating from a
description = "Fight!",
callback = function()
add_actor_by_enemy("PLASMA_GOLEM")
- if math.random() < 0.05 then
+ if random() < 0.05 then
add_actor_by_enemy("REPAIR_DRONE")
end
return GAME_STATE_FIGHT
@@ -144,7 +144,7 @@ As you explore the facility, you come across a strange cybernetic slime. It seem
description = "Fight!",
callback = function()
add_actor_by_enemy("CYBER_SLIME")
- if math.random() < 0.10 then
+ if random() < 0.10 then
add_actor_by_enemy("REPAIR_DRONE")
end
return GAME_STATE_FIGHT
diff --git a/assets/scripts/events/act_0/other.lua b/assets/scripts/events/act_0/other.lua
index 70c6c5f..f8d4750 100644
--- a/assets/scripts/events/act_0/other.lua
+++ b/assets/scripts/events/act_0/other.lua
@@ -183,7 +183,7 @@ You find a room with a strange device in the middle. It seems to be some kind of
:filter(function(card)
return card.does_consume
end):totable()
- if math.random() < 0.5 then
+ if random() < 0.5 then
local choosen = choose_weighted_by_price(possible_artifacts)
if choosen then
give_artifact(choosen, PLAYER_ID)
@@ -232,7 +232,7 @@ You find a old automatic workstation. You are able to get it working again. You
return nil
end
- local choosen = cards[math.random(#cards)]
+ local choosen = cards[random_int(0, #cards)]
upgrade_card(choosen)
deal_damage(PLAYER_ID, PLAYER_ID, 2, true)
diff --git a/assets/scripts/libs/fun.lua b/assets/scripts/libs/fun.lua
index db396a2..7b63af8 100644
--- a/assets/scripts/libs/fun.lua
+++ b/assets/scripts/libs/fun.lua
@@ -82,7 +82,7 @@ local string_gen = function(param, state)
return state, r
end
-local ipairs_gen = ipairs({}) -- get the generating function from ipairs
+local ipairs_gen = ipairs({}) -- get the generating function from ipairs
local pairs_gen = pairs({ a = 0 }) -- get the generating function from pairs
local map_gen = function(tab, key)
@@ -262,11 +262,11 @@ end
exports.ones = ones
local rands_gen = function(param_x, _state_x)
- return 0, math.random(param_x[1], param_x[2])
+ return 0, random_int(param_x[1], param_x[2])
end
local rands_nil_gen = function(_param_x, _state_x)
- return 0, math.random()
+ return 0, random()
end
local rands = function(n, m)
diff --git a/assets/scripts/story_teller/act_0.lua b/assets/scripts/story_teller/act_0.lua
index 43e7a82..63e60d7 100644
--- a/assets/scripts/story_teller/act_0.lua
+++ b/assets/scripts/story_teller/act_0.lua
@@ -30,13 +30,17 @@ register_story_teller("_ACT_0", {
if #possible == 0 then
possible = find_events_by_tags({ "_ACT_0_FIGHT" })
end
- set_event(possible[math.random(#possible)].id)
+
+ local choosen = possible[random_int(0, #possible)]
+ if choosen ~= nil then
+ set_event(choosen.id)
+ end
-- if we cleared a stage, give the player a random artifact
local last_stage_count = fetch("last_stage_count")
local current_stage_count = get_stages_cleared()
if last_stage_count ~= current_stage_count then
- local gets_random_artifact = math.random() < 0.25
+ local gets_random_artifact = random() < 0.25
if gets_random_artifact then
local player_artifacts = fun.iter(get_actor(PLAYER_ID).artifacts):map(function(id)
diff --git a/cmd/game_win/main.go b/cmd/game_win/main.go
index 5a5ad27..ff33758 100644
--- a/cmd/game_win/main.go
+++ b/cmd/game_win/main.go
@@ -78,6 +78,8 @@ func initSystems(hasAudio bool) {
}
func main() {
+ os.Setenv("EOE_IMG_TRUECOLOR", "1")
+
testArgs := testargs.New()
flag.Parse()
diff --git a/docs/GAME_CONTENT_DOCS.md b/docs/GAME_CONTENT_DOCS.md
index d40311b..77a4da2 100644
--- a/docs/GAME_CONTENT_DOCS.md
+++ b/docs/GAME_CONTENT_DOCS.md
@@ -65,10 +65,10 @@ Content that is dynamically generated at runtime is not included in this documen
```mermaid
pie
title Action Points
+"3 AP" : 2
"0 AP" : 12
"2 AP" : 1
"1 AP" : 6
-"3 AP" : 2
```
@@ -107,7 +107,7 @@ title Card Types
| ID | Name | Description | Initial HP | Max HP | Color | Used Callbacks | Test Present |
|------------------------|-----------------------|-------------------------------------------------------------------|------------|--------|---------|------------------------------|-----------------|
| ``CYBER_SPIDER`` | CYBER Spider | It waits for its prey to come closer | 8 | 8 | #ff4d6d | ``OnTurn`` | :no_entry_sign: |
-| ``CLEAN_BOT`` | Cleaning Bot | It never stopped cleaning... | 13 | 13 | #32a891 | ``OnTurn``, ``OnPlayerTurn`` | :no_entry_sign: |
+| ``CLEAN_BOT`` | Cleaning Bot | It never stopped cleaning... | 13 | 13 | #32a891 | ``OnPlayerTurn``, ``OnTurn`` | :no_entry_sign: |
| ``CYBER_SLIME`` | Cyber Slime | A cybernetic slime that splits into smaller slimes when defeated. | 10 | 10 | #00ff00 | ``OnTurn``, ``OnActorDie`` | :no_entry_sign: |
| ``CYBER_SLIME_MINION`` | Cyber Slime Offspring | A smaller version of the Cyber Slime. | 4 | 4 | #00ff00 | ``OnTurn`` | :no_entry_sign: |
| ``DUMMY`` | Dummy | End me... | 100 | 100 | #deeb6a | ``OnTurn`` | :no_entry_sign: |
@@ -122,21 +122,21 @@ title Card Types
| ID | Name | Description | Tags | Choices | Test Present |
|------------------------------|---------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------|----------------------------------------------------------------------------------------------------------------------------------|-----------------|
| ``GAIN_GOLD_ACT_0`` | | ... - | _ACT_0 |
- ``Take it! [Gain 20 Gold]``
- ``Leave!``
| :no_entry_sign: |
-| ``PLASMA_GOLEM`` | A glowing figure emerges... | As you delve deeper into the facility, you notice a bright glow emanating from a nearby chamber. A massive golem made of pure plasma energy steps into view. - **It looks ready to unleash its power!** - | _ACT_0_FIGHT | | :no_entry_sign: |
-| ``LASER_DRONE`` | A menacing drone appears... | As you explore the facility, you hear a high-pitched whirring sound. A drone equipped with a powerful laser cannon appears in front of you. - **It looks ready to attack!** - | _ACT_0_FIGHT | | :no_entry_sign: |
-| ``CYBER_SLIME`` | A strange cybernetic slime appears... | As you explore the facility, you come across a strange cybernetic slime. It seems to be pulsating with energy and looks hostile. - **Prepare for a fight!** - | _ACT_0_FIGHT | | :no_entry_sign: |
+| ``PLASMA_GOLEM`` | A glowing figure emerges... | !!plasma_golem.jpg - As you delve deeper into the facility, you notice a bright glow emanating from a nearby chamber. A massive golem made of pure plasma energy steps into view. - **It looks ready to unleash its power!** - | _ACT_0_FIGHT | | :no_entry_sign: |
+| ``LASER_DRONE`` | A menacing drone appears... | !!laser_drone.jpg - As you explore the facility, you hear a high-pitched whirring sound. A drone equipped with a powerful laser cannon appears in front of you. - **It looks ready to attack!** - | _ACT_0_FIGHT | | :no_entry_sign: |
+| ``CYBER_SLIME`` | A strange cybernetic slime appears... | !!cyber_slime.jpg - As you explore the facility, you come across a strange cybernetic slime. It seems to be pulsating with energy and looks hostile. - **Prepare for a fight!** - | _ACT_0_FIGHT | | :no_entry_sign: |
| ``MERCHANT`` | A strange figure | !!merchant.jpg - - The merchant is a tall, lanky figure draped in a long, tattered coat made of plant fibers and animal hides. Their face is hidden behind a mask made of twisted roots and vines, giving them an unsettling, almost alien appearance. - Despite their strange appearance, the merchant is a shrewd negotiator and a skilled trader. They carry with them a collection of bizarre and exotic items, including plant-based weapons, animal pelts, and strange, glowing artifacts that seem to pulse with an otherworldly energy. - The merchant is always looking for a good deal, and they're not above haggling with potential customers... | _ACT_0, _ACT_1, _ACT_2, _ACT_3 | | :no_entry_sign: |
| ``CLEAN_BOT`` | Corpse. Clean. Engage. | !!clean_bot.jpg - While exploring the facility you hear a strange noise. Suddenly a strange robot appears from one of the corridors. - It seems to be cleaning up the area, but it's not working properly anymore and you can see small sparks coming out of it. - It looks at you and says "Corpse. Clean. Engage.". - **You're not sure what it means, but it doesn't seem to be friendly!** - | _ACT_0_FIGHT | | :no_entry_sign: |
-| ``GAMBLE_1_ACT_0`` | Electro Barrier | You find a room with a strange device in the middle. It seems to be some kind of electro barrier protecting a storage container. You can either try to disable the barrier or leave. - | _ACT_0 | - ``50% [Gain Artifact & Consumeable] 50% [Take 5 damage]``
- ``Leave!``
| :no_entry_sign: |
+| ``GAMBLE_1_ACT_0`` | Electro Barrier | !!electro_barrier.jpg - You find a room with a strange device in the middle. It seems to be some kind of electro barrier protecting a storage container. You can either try to disable the barrier or leave. - | _ACT_0 | - ``50% [Gain Artifact & Consumeable] 50% [Take 2 damage]``
- ``Leave!``
| :no_entry_sign: |
| ``CROWBAR`` | Found: Crowbar | !!red_room.jpg - **You found something!** A crowbar. It's a bit rusty, but it should still be useful! - **Important:** If you already carry a artifact in your hand, you will have to drop it and related cards to pick up the new one. | _ACT_0 | | :no_entry_sign: |
| ``HAR_II`` | Found: HAR-II | !!artifact_chest.jpg - **You found something!** A HAR-II. A heavy assault rifle with a high rate of fire. - **Important:** If you already carry a artifact in your hand, you will have to drop it and related cards to pick up the new one. | _ACT_1 | | :no_entry_sign: |
| ``LZR_PISTOL`` | Found: LZR Pistol | !!artifact_chest.jpg - **You found something!** A LZR pistol. Fires a concentrated beam of light. - **Important:** If you already carry a artifact in your hand, you will have to drop it and related cards to pick up the new one. | _ACT_1 | | :no_entry_sign: |
| ``VIBRO_KNIFE`` | Found: VIBRO Knife | !!artifact_chest.jpg - **You found something!** A VIBRO knife. Uses ultrasonic vibrations to cut through almost anything. - **Important:** If you already carry a artifact in your hand, you will have to drop it and related cards to pick up the new one. | _ACT_0 | | :no_entry_sign: |
| ``GOLD_TO_HP_ACT_0`` | Old Vending Machine | You find an old vending machine, it seems to be still working. You can either pay 20 Gold to get 5 HP or leave. - | _ACT_0 | - ``Pay [20 Gold] [Gain 5 HP]``
- ``Leave!``
| :no_entry_sign: |
-| ``RANDOM_ARTIFACT_ACT_0`` | Random Artifact | !!artifact_chest.jpg - You found a chest with a strange symbol on it. The chest is protected by a strange barrier. You can either open it and take some damage or leave. - | _ACT_0 | - ``Random Artifact [Gain 1 Artifact] [Take 5 damage]``
- ``Leave!``
| :no_entry_sign: |
+| ``RANDOM_ARTIFACT_ACT_0`` | Random Artifact | !!artifact_chest.jpg - You found a chest with a strange symbol on it. The chest is protected by a strange barrier. You can either open it and take some damage or leave. - | _ACT_0 | - ``Random Artifact [Gain 1 Artifact] [Take 2 damage]``
- ``Leave!``
| :no_entry_sign: |
| ``RANDOM_CONSUMEABLE_ACT_0`` | Random Consumeable | !!artifact_chest.jpg - You found a chest with a strange symbol on it. The chest is protected by a strange barrier. You can either open it and take some damage or leave. - | _ACT_0 | - ``Random Artifact [Gain 1 Consumeable] [Take 2 damage]``
- ``Leave!``
| :no_entry_sign: |
-| ``MAX_LIFE_ACT_0`` | Symbiotic Parasite | You find a strange creature, it seems to be a symbiotic parasite. It offers to increase your max HP by 5. You can either accept or leave. - | _ACT_0 | - ``Accept it! [Gain 5 Max HP]``
- ``Leave!``
| :no_entry_sign: |
+| ``MAX_LIFE_ACT_0`` | Symbiotic Parasite | !!symbiotic_parasite.jpg - You find a strange creature, it seems to be a symbiotic parasite. It offers to increase your max HP by 5. You can either accept or leave. - | _ACT_0 | - ``Accept it! [Gain 5 Max HP]``
- ``Leave!``
| :no_entry_sign: |
| ``RUST_MITE`` | Tasty metals... | !!rust_mite.jpg - You are walking through the facility hoping to find a way out. After a few turns you hear a strange noise. You look around and come across a strange being. - It seems to be eating the metal from the walls. It looks at you and after a few seconds it rushes towards you. - **It seems to be hostile!** - | _ACT_0_FIGHT | | :no_entry_sign: |
-| ``UPRAGDE_CARD_ACT_0`` | Upgrade Station | You find a old automatic workstation. You are able to get it working again. You can either upgrade a random card or leave. - | _ACT_0 | - ``Upgrade a card [Upgrade a card] [Take 5 damage]``
- ``Leave!``
| :no_entry_sign: |
+| ``UPRAGDE_CARD_ACT_0`` | Upgrade Station | !!upgrade_station.jpg - You find a old automatic workstation. You are able to get it working again. You can either upgrade a random card or leave. - | _ACT_0 | - ``Upgrade a card [Upgrade a card] [Take 2 damage]``
- ``Leave!``
| :no_entry_sign: |
| ``START`` | Waking up... | !!cryo_start.jpg - You wake up in a dimly lit room, the faint glow of a red emergency light casting an eerie hue over the surroundings. The air is musty and stale, the metallic scent of the cryo-chamber still lingering in your nostrils. You feel groggy and disoriented, your mind struggling to process what's happening. - As you try to sit up, you notice that your body is stiff and unresponsive. It takes a few moments for your muscles to warm up and regain their strength. Looking around, you see that the walls are made of a dull gray metal, covered in scratches and scuff marks. There's a faint humming sound coming from somewhere, indicating that the facility is still operational. - You try to remember how you ended up here, but your memories are hazy and fragmented. The last thing you recall is a blinding flash of light and a deafening boom. You must have been caught in one of the nuclear explosions that devastated the world. - As you struggle to gather your bearings, you notice a blinking panel on the wall, with the words *"Cryo Sleep Malfunction"* displayed in bold letters. It seems that the system has finally detected the error that caused your prolonged slumber and triggered your awakening. - **Shortly after you realize that you are not alone...** | | - ``Try to find a weapon. [Find melee weapon] [Take 4 damage]``
- ``Gather your strength and attack it!``
| :no_entry_sign: |
| ``CYBER_SPIDER`` | What is this thing at the ceiling? | !!cyber_spider.jpg - You come around a corner and see a strange creature hanging from the ceiling. It looks like a spider, but it's made out of metal. - It seems to be waiting for its prey to come closer and there is no way around it. - | _ACT_0_FIGHT | | :no_entry_sign: |
diff --git a/docs/LUA_API_DOCS.md b/docs/LUA_API_DOCS.md
index 10fc533..5a83239 100644
--- a/docs/LUA_API_DOCS.md
+++ b/docs/LUA_API_DOCS.md
@@ -1,4 +1,5 @@
# End Of Eden Lua Docs
+
## Index
- [Game Constants](#game-constants)
@@ -23,6 +24,7 @@
General game constants.
### Globals
+
DECAY_ALL
Status effect decays by all stacks per turn.
@@ -67,7 +69,7 @@ Represents the random game state in which the active story teller will decide wh
PLAYER_ID
-Player actor id for use in functions where the guid is needed, for example: ``deal_damage(PLAYER_ID, enemy_guid, 10)``.
+Player actor id for use in functions where the guid is needed, for example: `deal_damage(PLAYER_ID, enemy_guid, 10)`.
@@ -84,6 +86,7 @@ General game constants.
None
### Functions
+
fetch
Fetches a value from the persistent store
@@ -108,6 +111,30 @@ guid() -> guid
+ random
+
+Returns a random number between 0 and 1
+
+**Signature:**
+
+```
+random() -> number
+```
+
+
+
+ random_int
+
+Returns a random number between min and max
+
+**Signature:**
+
+```
+random_int(min : number, max : number) -> number
+```
+
+
+
store
Stores a persistent value for this run that will be restored after a save load. Can store any lua basic value or table.
@@ -129,6 +156,7 @@ Helper functions for text styling.
None
### Functions
+
text_bg
Makes the text background colored. Takes hex values like #ff0000.
@@ -186,6 +214,7 @@ Various logging functions.
None
### Functions
+
log_d
Log at **danger** level to player log.
@@ -255,9 +284,10 @@ Audio helper functions.
None
### Functions
+
play_audio
-Plays a sound effect. If you want to play ``button.mp3`` you call ``play_audio("button")``.
+Plays a sound effect. If you want to play `button.mp3` you call `play_audio("button")`.
**Signature:**
@@ -269,7 +299,7 @@ play_audio(sound : string) -> None
play_music
-Start a song for the background loop. If you want to play ``song.mp3`` you call ``play_music("song")``.
+Start a song for the background loop. If you want to play `song.mp3` you call `play_music("song")`.
**Signature:**
@@ -288,6 +318,7 @@ Functions that modify the general game state.
None
### Functions
+
get_action_points_per_round
get the number of action points per round.
@@ -441,6 +472,7 @@ Functions that modify or access the actors. Actors are either the player or enem
None
### Functions
+
actor_add_hp
Increases the hp value of a actor by a number. Can be negative value to decrease it. This won't trigger any on_damage callbacks
@@ -491,7 +523,7 @@ actor_set_max_hp(guid : guid, amount : number) -> None
add_actor_by_enemy
-Creates a new enemy fighting against the player. Example ``add_actor_by_enemy("RUST_MITE")``.
+Creates a new enemy fighting against the player. Example `add_actor_by_enemy("RUST_MITE")`.
**Signature:**
@@ -515,7 +547,7 @@ get_actor(guid : guid) -> actor
get_opponent_by_index
-Get opponent (actor) by index of a certain actor. ``get_opponent_by_index(PLAYER_ID, 2)`` would return the second alive opponent of the player.
+Get opponent (actor) by index of a certain actor. `get_opponent_by_index(PLAYER_ID, 2)` would return the second alive opponent of the player.
**Signature:**
@@ -527,7 +559,7 @@ get_opponent_by_index(guid : guid, index : number) -> actor
get_opponent_count
-Get the number of opponents (actors) of a certain actor. ``get_opponent_count(PLAYER_ID)`` would return 2 if the player had 2 alive enemies.
+Get the number of opponents (actors) of a certain actor. `get_opponent_count(PLAYER_ID)` would return 2 if the player had 2 alive enemies.
**Signature:**
@@ -539,7 +571,7 @@ get_opponent_count(guid : guid) -> number
get_opponent_guids
-Get the guids of opponents (actors) of a certain actor. If the player had 2 enemies, ``get_opponent_guids(PLAYER_ID)`` would return a table with 2 strings containing the guids of these actors.
+Get the guids of opponents (actors) of a certain actor. If the player had 2 enemies, `get_opponent_guids(PLAYER_ID)` would return a table with 2 strings containing the guids of these actors.
**Signature:**
@@ -551,7 +583,7 @@ get_opponent_guids(guid : guid) -> guid[]
get_player
-Get the player actor. Equivalent to ``get_actor(PLAYER_ID)``
+Get the player actor. Equivalent to `get_actor(PLAYER_ID)`
**Signature:**
@@ -582,6 +614,7 @@ Functions that modify or access the artifacts.
None
### Functions
+
get_artifact
Returns the artifact definition. Can take either a guid or a typeId. If it's a guid it will fetch the type behind the instance.
@@ -651,6 +684,7 @@ Functions that modify or access the status effects.
None
### Functions
+
add_status_effect_stacks
Adds to the stack count of a status effect. Negative values are also allowed.
@@ -744,6 +778,7 @@ Functions that modify or access the cards.
None
### Functions
+
cast_card
Tries to cast a card with a guid and optional target. If the cast isn't successful returns false.
@@ -849,6 +884,7 @@ Functions that deal damage or heal.
None
### Functions
+
deal_damage
Deal damage from one source to a target. If flat is true the damage can't be modified by status effects or artifacts. Returns the damage that was dealt.
@@ -918,6 +954,7 @@ Functions that are related to the player.
None
### Functions
+
finish_player_turn
Finishes the player turn.
@@ -999,6 +1036,7 @@ Functions that are related to the merchant.
None
### Functions
+
add_merchant_artifact
Adds another random artifact to the merchant
@@ -1037,7 +1075,7 @@ get_merchant() -> merchant_state
get_merchant_gold_max
-Returns the maximum value of artifacts and cards that the merchant will sell. Good to scale ``random_card`` and ``random_artifact``.
+Returns the maximum value of artifacts and cards that the merchant will sell. Good to scale `random_card` and `random_artifact`.
**Signature:**
@@ -1056,6 +1094,7 @@ Functions that help with random generation.
None
### Functions
+
gen_face
Generates a random face.
@@ -1101,10 +1140,11 @@ Functions that help with localization.
None
### Functions
+
l
-Returns the localized string for the given key. Examples on locals definition can be found in `/assets/locals`. Example: ``
-l('cards.MY_CARD.name', "English Default Name")``
+Returns the localized string for the given key. Examples on locals definition can be found in `/assets/locals`. Example: `
+l('cards.MY_CARD.name', "English Default Name")`
**Signature:**
@@ -1123,6 +1163,7 @@ These functions are used to define new content in the base game and in mods.
None
### Functions
+
delete_base_game
Deletes all base game content. Useful if you don't want to include base game content in your mod.
@@ -1343,7 +1384,7 @@ register_event("SOME_EVENT",
description = "Go...",
callback = function()
-- If you return nil on_end will decide the next game state
- return nil
+ return nil
end
},
{
@@ -1353,7 +1394,7 @@ register_event("SOME_EVENT",
},
on_enter = function()
play_music("energetic_orthogonal_expansions")
-
+
give_card("MELEE_HIT", PLAYER_ID)
give_card("MELEE_HIT", PLAYER_ID)
give_card("MELEE_HIT", PLAYER_ID)
@@ -1435,7 +1476,7 @@ register_story_teller("STORY_TELLER_XYZ", {
end
-- Fight against rust mites or clean bots
- local d = math.random(2)
+ local d = random_int(0, 2)
if d == 1 then
add_actor_by_enemy("RUST_MITE")
elseif d == 2 then
@@ -1454,4 +1495,3 @@ register_story_teller(id : type_id, definition : story_teller) -> None
```
-
diff --git a/game/lua.go b/game/lua.go
index 7ebdba6..6d1eef4 100644
--- a/game/lua.go
+++ b/game/lua.go
@@ -88,6 +88,20 @@ fun = require "fun"
d.Category("Utility", "General game constants.", 1)
+ d.Function("random", "Returns a random number between 0 and 1. Prefer this function over math.random(), as this is seeded for the session.", "number")
+ l.SetGlobal("random", l.NewFunction(func(state *lua.LState) int {
+ state.Push(lua.LNumber(session.rand.Float64()))
+ return 1
+ }))
+
+ d.Function("random_int", "Returns a random number between min and max. Prefer this function over math.random(), as this is seeded for the session.", "number", "min : number", "max : number")
+ l.SetGlobal("random_int", l.NewFunction(func(state *lua.LState) int {
+ min := state.ToInt(1)
+ max := state.ToInt(2)
+ state.Push(lua.LNumber(session.rand.IntN(max-min) + min))
+ return 1
+ }))
+
d.Function("guid", "returns a new random guid.", "guid")
l.SetGlobal("guid", l.NewFunction(func(state *lua.LState) int {
state.Push(lua.LString(NewGuid("LUA")))
diff --git a/game/saved_state.go b/game/saved_state.go
index 8435062..d96771a 100644
--- a/game/saved_state.go
+++ b/game/saved_state.go
@@ -1,6 +1,9 @@
package game
-import "encoding/gob"
+import (
+ "encoding/gob"
+ "math/rand/v2"
+)
func init() {
gob.Register(SavedState{})
@@ -10,6 +13,8 @@ func init() {
// runtime or other pointer.
type SavedState struct {
State GameState
+ Seed uint64
+ Rand *rand.PCG
Actors map[string]Actor
Instances map[string]any
StagesCleared int
diff --git a/game/session.go b/game/session.go
index 9e3460b..f5e5a4c 100644
--- a/game/session.go
+++ b/game/session.go
@@ -8,7 +8,7 @@ import (
"fmt"
"io"
"log"
- "math/rand"
+ "math/rand/v2"
"path/filepath"
"runtime"
"sort"
@@ -102,6 +102,9 @@ type Session struct {
log *log.Logger
luaState *lua.LState
luaDocs *ludoc.Docs
+ seed uint64
+ randSrc *rand.PCG
+ rand *rand.Rand
resources *ResourcesManager
state GameState
@@ -128,9 +131,14 @@ type Session struct {
// NewSession creates a new game session.
func NewSession(options ...func(s *Session)) *Session {
+ seed := uint64(time.Now().UnixMilli())
+ randSrc := rand.NewPCG(seed, 1337)
session := &Session{
- log: log.New(io.Discard, "", 0),
- state: GameStateEvent,
+ log: log.New(io.Discard, "", 0),
+ seed: seed,
+ randSrc: randSrc,
+ rand: rand.New(randSrc),
+ state: GameStateEvent,
actors: map[string]Actor{
PlayerActorID: NewActor(PlayerActorID),
},
@@ -163,10 +171,13 @@ func NewSession(options ...func(s *Session)) *Session {
session.log.Println("Session started!")
+ session.log.Println("Seed:", session.seed)
+ session.Log(LogTypeSuccess, fmt.Sprintf("Seed: %d", seed))
+
session.UpdatePlayer(func(actor *Actor) bool {
- actor.HP = 80
- actor.MaxHP = 80
- actor.Gold = 50 + rand.Intn(50)
+ actor.HP = 100
+ actor.MaxHP = 100
+ actor.Gold = 0
return true
})
@@ -201,6 +212,26 @@ func WithMods(mods []string) func(s *Session) {
}
}
+// WithSeed sets the seed for the random number generator.
+func WithSeed(seed uint64) func(s *Session) {
+ return func(s *Session) {
+ s.seed = seed
+ s.randSrc.Seed(seed, 1337)
+ }
+}
+
+// WithSeedString sets the seed for the random number generator based on a string.
+func WithSeedString(seed string) func(s *Session) {
+ return func(s *Session) {
+ generatedSeed := uint64(0)
+ for i, c := range seed {
+ generatedSeed += uint64(c) + uint64(i)
+ }
+ s.seed = generatedSeed
+ s.randSrc.Seed(generatedSeed, 1337)
+ }
+}
+
// WithOnLuaError sets the function that will be called when a lua error happens.
func WithOnLuaError(fn func(file string, line int, callback string, typeId string, err error)) func(s *Session) {
return func(s *Session) {
@@ -242,6 +273,8 @@ func (s *Session) LuaErrors() chan LuaError {
func (s *Session) ToSavedState() SavedState {
return SavedState{
State: s.state,
+ Seed: s.seed,
+ Rand: s.randSrc,
Actors: s.actors,
Instances: s.instances,
StagesCleared: s.stagesCleared,
@@ -261,6 +294,8 @@ func (s *Session) ToSavedState() SavedState {
// should be loaded or the state could be corrupted.
func (s *Session) LoadSavedState(save SavedState) {
s.state = save.State
+ s.seed = save.Seed
+ s.randSrc = save.Rand
s.actors = lo.MapValues(save.Actors, func(item Actor, key string) Actor {
return item.Sanitize()
})
diff --git a/go.mod b/go.mod
index c51897f..d86b292 100644
--- a/go.mod
+++ b/go.mod
@@ -1,8 +1,6 @@
module github.com/BigJk/end_of_eden
-go 1.21
-
-toolchain go1.21.6
+go 1.23.0
replace github.com/containerd/console => github.com/containerd/console v1.0.4-0.20230706203907-8f6c4e4faef5
diff --git a/system/image/image.go b/system/image/image.go
index 0598f7a..08d4e1a 100644
--- a/system/image/image.go
+++ b/system/image/image.go
@@ -50,7 +50,7 @@ func buildOption(options ...Option) (Options, []imeji.Option) {
data.tag += os.Getenv("EOE_IMG_PATTERN")
}
- if runtime.GOOS == "js" {
+ if runtime.GOOS == "js" || os.Getenv("EOE_IMG_TRUECOLOR") == "1" {
imejiOptions = append(imejiOptions, imeji.WithTrueColor())
data.tag += "truecolor"
} else {
diff --git a/ui/menus/mainmenu/choices.go b/ui/menus/mainmenu/choices.go
index 6e2e1f1..1c32b81 100644
--- a/ui/menus/mainmenu/choices.go
+++ b/ui/menus/mainmenu/choices.go
@@ -15,13 +15,14 @@ import (
type Choice string
const (
- ChoiceWaiting = Choice("WAITING")
- ChoiceContinue = Choice("CONTINUE")
- ChoiceNewGame = Choice("NEW_GAME")
- ChoiceAbout = Choice("ABOUT")
- ChoiceSettings = Choice("SETTINGS")
- ChoiceMods = Choice("MODS")
- ChoiceExit = Choice("EXIT")
+ ChoiceWaiting = Choice("WAITING")
+ ChoiceContinue = Choice("CONTINUE")
+ ChoiceNewGame = Choice("NEW_GAME")
+ ChoiceNewGameSOD = Choice("NEW_GAME_SOD")
+ ChoiceAbout = Choice("ABOUT")
+ ChoiceSettings = Choice("SETTINGS")
+ ChoiceMods = Choice("MODS")
+ ChoiceExit = Choice("EXIT")
)
type choiceItem struct {
@@ -45,6 +46,7 @@ func NewChoicesModel(zones *zone.Manager, hideSettings bool) ChoicesModel {
choices := []list.Item{
choiceItem{zones, "Continue", "Ready to continue dying?", ChoiceContinue},
choiceItem{zones, "New Game", "Start a new try.", ChoiceNewGame},
+ choiceItem{zones, "New Game: Seed of the Day", "Start a new try with the daily seed.", ChoiceNewGameSOD},
choiceItem{zones, "About", "Want to know more?", ChoiceAbout},
choiceItem{zones, "Settings", "Other settings won't let you survive...", ChoiceSettings},
choiceItem{zones, "Mods", "Make the game even more fun!", ChoiceMods},
diff --git a/ui/menus/mainmenu/mainmenu.go b/ui/menus/mainmenu/mainmenu.go
index 945a577..2752841 100644
--- a/ui/menus/mainmenu/mainmenu.go
+++ b/ui/menus/mainmenu/mainmenu.go
@@ -2,6 +2,11 @@ package mainmenu
import (
"fmt"
+ "log"
+ "os"
+ "strings"
+ "time"
+
"github.com/BigJk/end_of_eden/game"
"github.com/BigJk/end_of_eden/internal/fs"
"github.com/BigJk/end_of_eden/system/audio"
@@ -20,10 +25,6 @@ import (
"github.com/charmbracelet/lipgloss"
zone "github.com/lrstanley/bubblezone"
"github.com/samber/lo"
- "log"
- "os"
- "strings"
- "time"
)
type Model struct {
@@ -119,6 +120,8 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
}
+ case ChoiceNewGameSOD:
+ fallthrough
case ChoiceNewGame:
audio.Play("btn_menu")
@@ -133,12 +136,14 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return fmt.Sprintf("./mods/%s/images/", item)
})...)
+ isSOD := m.choices.selected == ChoiceNewGameSOD
m.choices = m.choices.Clear()
return m, tea.Sequence(
cmd,
root.Push(gameview.New(m, m.zones, game.NewSession(
game.WithLogging(log.New(f, "SESSION ", log.Ldate|log.Ltime|log.Lshortfile)),
game.WithMods(m.settings.GetStrings("mods")),
+ lo.Ternary(isSOD, game.WithSeedString(time.Now().Format(time.DateOnly)), nil),
lo.Ternary(os.Getenv("EOE_DEBUG") == "1", game.WithDebugEnabled(8272), nil),
))),
)