feat: correct seeding

This commit is contained in:
Daniel Schmidt 2024-08-27 23:14:00 +02:00
parent 98da619522
commit 275460002e
18 changed files with 198 additions and 71 deletions

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -78,6 +78,8 @@ func initSystems(hasAudio bool) {
}
func main() {
os.Setenv("EOE_IMG_TRUECOLOR", "1")
testArgs := testargs.New()
flag.Parse()

View File

@ -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 | <ul><li>``Take it! [Gain 20 Gold]``</li> <li>``Leave!``</li></ul> | :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 | <ul><li>``Fight!``</li></ul> | :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 | <ul><li>``Fight!``</li></ul> | :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 | <ul><li>``Fight!``</li></ul> | :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 | <ul><li>``Fight!``</li></ul> | :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 | <ul><li>``Fight!``</li></ul> | :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 | <ul><li>``Fight!``</li></ul> | :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 | <ul><li>``Trade``</li> <li>``Pass``</li></ul> | :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 | <ul><li>``Fight!``</li></ul> | :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 | <ul><li>``50% [Gain Artifact & Consumeable] 50% [Take 5 damage]``</li> <li>``Leave!``</li></ul> | :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 | <ul><li>``50% [Gain Artifact & Consumeable] 50% [Take 2 damage]``</li> <li>``Leave!``</li></ul> | :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 | <ul><li>````</li> <li>``Leave...``</li></ul> | :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 | <ul><li>````</li> <li>``Leave...``</li></ul> | :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 | <ul><li>````</li> <li>``Leave...``</li></ul> | :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 | <ul><li>````</li> <li>``Leave...``</li></ul> | :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 | <ul><li>``Pay [20 Gold] [Gain 5 HP]``</li> <li>``Leave!``</li></ul> | :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 | <ul><li>``Random Artifact [Gain 1 Artifact] [Take 5 damage]``</li> <li>``Leave!``</li></ul> | :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 | <ul><li>``Random Artifact [Gain 1 Artifact] [Take 2 damage]``</li> <li>``Leave!``</li></ul> | :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 | <ul><li>``Random Artifact [Gain 1 Consumeable] [Take 2 damage]``</li> <li>``Leave!``</li></ul> | :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 | <ul><li>``Accept it! [Gain 5 Max HP]``</li> <li>``Leave!``</li></ul> | :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 | <ul><li>``Accept it! [Gain 5 Max HP]``</li> <li>``Leave!``</li></ul> | :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 | <ul><li>``Fight!``</li></ul> | :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 | <ul><li>``Upgrade a card [Upgrade a card] [Take 5 damage]``</li> <li>``Leave!``</li></ul> | :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 | <ul><li>``Upgrade a card [Upgrade a card] [Take 2 damage]``</li> <li>``Leave!``</li></ul> | :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...** | | <ul><li>``Try to find a weapon. [Find melee weapon] [Take 4 damage]``</li> <li>``Gather your strength and attack it!``</li></ul> | :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 | <ul><li>``Fight!``</li></ul> | :no_entry_sign: |

View File

@ -1,4 +1,5 @@
# End Of Eden Lua Docs
## Index
- [Game Constants](#game-constants)
@ -23,6 +24,7 @@
General game constants.
### Globals
<details> <summary><b><code>DECAY_ALL</code></b> </summary> <br/>
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
<details> <summary><b><code>PLAYER_ID</code></b> </summary> <br/>
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)`.
</details>
@ -84,6 +86,7 @@ General game constants.
None
### Functions
<details> <summary><b><code>fetch</code></b> </summary> <br/>
Fetches a value from the persistent store
@ -108,6 +111,30 @@ guid() -> guid
</details>
<details> <summary><b><code>random</code></b> </summary> <br/>
Returns a random number between 0 and 1
**Signature:**
```
random() -> number
```
</details>
<details> <summary><b><code>random_int</code></b> </summary> <br/>
Returns a random number between min and max
**Signature:**
```
random_int(min : number, max : number) -> number
```
</details>
<details> <summary><b><code>store</code></b> </summary> <br/>
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
<details> <summary><b><code>text_bg</code></b> </summary> <br/>
Makes the text background colored. Takes hex values like #ff0000.
@ -186,6 +214,7 @@ Various logging functions.
None
### Functions
<details> <summary><b><code>log_d</code></b> </summary> <br/>
Log at **danger** level to player log.
@ -255,9 +284,10 @@ Audio helper functions.
None
### Functions
<details> <summary><b><code>play_audio</code></b> </summary> <br/>
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
<details> <summary><b><code>play_music</code></b> </summary> <br/>
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
<details> <summary><b><code>get_action_points_per_round</code></b> </summary> <br/>
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
<details> <summary><b><code>actor_add_hp</code></b> </summary> <br/>
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
<details> <summary><b><code>add_actor_by_enemy</code></b> </summary> <br/>
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
<details> <summary><b><code>get_opponent_by_index</code></b> </summary> <br/>
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
<details> <summary><b><code>get_opponent_count</code></b> </summary> <br/>
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
<details> <summary><b><code>get_opponent_guids</code></b> </summary> <br/>
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[]
<details> <summary><b><code>get_player</code></b> </summary> <br/>
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
<details> <summary><b><code>get_artifact</code></b> </summary> <br/>
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
<details> <summary><b><code>add_status_effect_stacks</code></b> </summary> <br/>
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
<details> <summary><b><code>cast_card</code></b> </summary> <br/>
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
<details> <summary><b><code>deal_damage</code></b> </summary> <br/>
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
<details> <summary><b><code>finish_player_turn</code></b> </summary> <br/>
Finishes the player turn.
@ -999,6 +1036,7 @@ Functions that are related to the merchant.
None
### Functions
<details> <summary><b><code>add_merchant_artifact</code></b> </summary> <br/>
Adds another random artifact to the merchant
@ -1037,7 +1075,7 @@ get_merchant() -> merchant_state
<details> <summary><b><code>get_merchant_gold_max</code></b> </summary> <br/>
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
<details> <summary><b><code>gen_face</code></b> </summary> <br/>
Generates a random face.
@ -1101,10 +1140,11 @@ Functions that help with localization.
None
### Functions
<details> <summary><b><code>l</code></b> </summary> <br/>
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
<details> <summary><b><code>delete_base_game</code></b> </summary> <br/>
Deletes all base game content. Useful if you don't want to include base game content in your mod.
@ -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
```
</details>

View File

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

View File

@ -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

View File

@ -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()
})

4
go.mod
View File

@ -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

View File

@ -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 {

View File

@ -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},

View File

@ -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),
))),
)