mirror of
https://github.com/BigJk/end_of_eden.git
synced 2026-02-06 10:48:09 +00:00
feat: artifact/card carousel, rework of game flow
This commit is contained in:
parent
a77cf648b0
commit
f59d0e1b06
1
.gitignore
vendored
1
.gitignore
vendored
@ -15,3 +15,4 @@ cpu*
|
||||
heap*
|
||||
profile*
|
||||
file_index.json
|
||||
__old/
|
||||
@ -11,6 +11,48 @@ function assert_chain(tests)
|
||||
return nil
|
||||
end
|
||||
|
||||
---assert_card_present asserts that the player's first card is of a certain type, returning an error message if not
|
||||
---@param id type_id
|
||||
---@return string|nil
|
||||
function assert_card_present(id)
|
||||
local cards = get_cards(PLAYER_ID)
|
||||
|
||||
if not cards[1] then
|
||||
return "Card not in hand"
|
||||
end
|
||||
|
||||
local card = get_card_instance(cards[1])
|
||||
if card.type_id ~= id then
|
||||
return "Card has wrong type: " .. card.type_id
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
---assert_cast_damage asserts that the player's first card deals a certain amount of damage, returning an error message if not
|
||||
---@param id type_id
|
||||
---@param dmg number
|
||||
---@return string|nil
|
||||
function assert_cast_damage(id, dmg)
|
||||
local dummy = add_actor_by_enemy("DUMMY")
|
||||
local cards = get_cards(PLAYER_ID)
|
||||
|
||||
if not cards[1] then
|
||||
return "Card not in hand"
|
||||
end
|
||||
|
||||
local card = get_card_instance(cards[1])
|
||||
if card.type_id ~= id then
|
||||
return "Card has wrong type: " .. card.type_id
|
||||
end
|
||||
|
||||
cast_card(cards[1], dummy)
|
||||
|
||||
if get_actor(dummy).hp ~= 100 - dmg then
|
||||
return "Expected " .. tostring(100 - dmg) .. " health, got " .. get_actor(dummy).hp
|
||||
end
|
||||
end
|
||||
|
||||
---assert_status_effect_count asserts that the player has a certain number of status effects, returning an error message if not
|
||||
---@param count number
|
||||
---@return string|nil
|
||||
|
||||
@ -3,3 +3,131 @@
|
||||
function highlight(val)
|
||||
return text_underline(text_bold("[" .. tostring(val) .. "]"))
|
||||
end
|
||||
|
||||
---highlight_warn some value with warning colors
|
||||
---@param val any
|
||||
function highlight_warn(val)
|
||||
return text_underline(text_bold(text_red("[" .. tostring(val) .. "]")))
|
||||
end
|
||||
|
||||
---choose_weighted chooses an item from a list of choices, with a weight for each item.
|
||||
---@param choices table
|
||||
---@param weights number[]
|
||||
---@return string
|
||||
function choose_weighted(choices, weights)
|
||||
print(choices, weights)
|
||||
|
||||
local total_weight = 0
|
||||
for _, weight in ipairs(weights) do
|
||||
total_weight = total_weight + weight
|
||||
end
|
||||
|
||||
local random = math.random() * total_weight
|
||||
for i, weight in ipairs(weights) do
|
||||
random = random - weight
|
||||
if random <= 0 then
|
||||
return choices[i]
|
||||
end
|
||||
end
|
||||
|
||||
return choices[#choices]
|
||||
end
|
||||
|
||||
---table.contains check if a table contains an element.
|
||||
function table.contains(table, element)
|
||||
for _, value in pairs(table) do
|
||||
if value == element then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
---find_by_tags find all items with the given tags.
|
||||
---@param items artifact|card
|
||||
---@param tags string[]
|
||||
function find_by_tags(items, tags)
|
||||
local found = {}
|
||||
for _, item in pairs(items) do
|
||||
for _, tag in pairs(tags) do
|
||||
if item.tags == nil then
|
||||
goto continue
|
||||
end
|
||||
if not table.contains(item.tags, tag) then
|
||||
goto continue
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(found, item)
|
||||
|
||||
::continue::
|
||||
end
|
||||
return found
|
||||
end
|
||||
|
||||
---find_artifacts_by_tags find all artifacts with the given tags.
|
||||
---@param tags string[]
|
||||
---@return artifact[]
|
||||
function find_artifacts_by_tags(tags)
|
||||
return find_by_tags(registered.artifact, tags)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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()
|
||||
)
|
||||
end
|
||||
|
||||
---clear_cards_by_tag remove all cards with tag.
|
||||
---@param tag string tag to remove
|
||||
---@param excluded? table optional table of guids to exclude.
|
||||
function clear_cards_by_tag(tag, excluded)
|
||||
for _, guid in pairs(get_cards(PLAYER_ID)) do
|
||||
if excluded and table.contains(excluded, guid) then
|
||||
goto continue
|
||||
end
|
||||
|
||||
local tags = get_card(guid).tags
|
||||
if table.contains(tags, tag) then
|
||||
remove_card(guid)
|
||||
end
|
||||
|
||||
::continue::
|
||||
end
|
||||
end
|
||||
|
||||
---clear_artifacts_by_tag remove all artifacts with tag.
|
||||
---@param tag string tag to remove
|
||||
---@param excluded table optional table of guids to exclude.
|
||||
function clear_artifacts_by_tag(tag, excluded)
|
||||
for _, guid in pairs(get_artifacts(PLAYER_ID)) do
|
||||
if excluded and table.contains(excluded, guid) then
|
||||
goto continue
|
||||
end
|
||||
|
||||
local tags = get_artifact(guid).tags
|
||||
if table.contains(tags, tag) then
|
||||
remove_artifact(guid)
|
||||
end
|
||||
|
||||
::continue::
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
register_artifact("BAG_OF_HOLDING", {
|
||||
name = "Bag of Holding",
|
||||
description = "Start with a additional card at the beginning of combat.",
|
||||
price = 50,
|
||||
order = 0,
|
||||
callbacks = {
|
||||
on_player_turn = function(ctx)
|
||||
if ctx.owner == PLAYER_ID and ctx.round == 0 then
|
||||
player_draw_card(1)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
}
|
||||
});
|
||||
@ -1,21 +0,0 @@
|
||||
register_artifact("DEFLECTOR_SHIELD", {
|
||||
name = "Deflector Shield",
|
||||
description = "Gain 8 block at the start of combat.",
|
||||
price = 50,
|
||||
order = 0,
|
||||
callbacks = {
|
||||
on_player_turn = function(ctx)
|
||||
if ctx.round == 0 then
|
||||
give_status_effect("BLOCK", ctx.owner, 8)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
},
|
||||
test = function()
|
||||
add_actor_by_enemy("DUMMY")
|
||||
return assert_chain({
|
||||
function () return assert_status_effect_count(1) end,
|
||||
function () return assert_status_effect("BLOCK", 8) end
|
||||
})
|
||||
end
|
||||
});
|
||||
@ -1,26 +0,0 @@
|
||||
register_artifact("GIGANTIC_STRENGTH", {
|
||||
name = "Stone Of Gigantic Strength",
|
||||
description = "Double all damage dealt.",
|
||||
price = 250,
|
||||
order = 0,
|
||||
callbacks = {
|
||||
on_damage_calc = function(ctx)
|
||||
if ctx.source == ctx.owner then
|
||||
return ctx.damage * 2
|
||||
end
|
||||
return nil
|
||||
end
|
||||
},
|
||||
test = function()
|
||||
local dummy = add_actor_by_enemy("DUMMY")
|
||||
|
||||
local hp_before = get_actor(dummy).hp
|
||||
deal_damage(PLAYER_ID, dummy, 1)
|
||||
local hp_after = get_actor(dummy).hp
|
||||
|
||||
if hp_after == hp_before - 2 then
|
||||
return nil
|
||||
end
|
||||
return "Damage was not doubled. Before:" .. hp_before .. " After:" .. hp_after
|
||||
end
|
||||
})
|
||||
@ -1,22 +0,0 @@
|
||||
register_artifact("GOLD_CONVERTER", {
|
||||
name = "Gold Converter",
|
||||
description = "Gain 10 extra gold for each killed enemy.",
|
||||
price = 50,
|
||||
order = 0,
|
||||
callbacks = {
|
||||
on_actor_die = function(ctx)
|
||||
if ctx.owner == PLAYER_ID and ctx.owner == ctx.source then
|
||||
give_player_gold(10)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
},
|
||||
test = function()
|
||||
local dummy = add_actor_by_enemy("DUMMY")
|
||||
deal_damage(PLAYER_ID, dummy, 10000)
|
||||
if get_player().gold == 10 then
|
||||
return nil
|
||||
end
|
||||
return "Expected 10 gold, got " .. get_player().gold
|
||||
end
|
||||
});
|
||||
@ -1,14 +0,0 @@
|
||||
register_artifact("HOLY_GRAIL", {
|
||||
name = "Holy Grail",
|
||||
description = "At the start of each turn, heal for 2 HP for each card in your hand.",
|
||||
price = 150,
|
||||
order = 100, -- Evaluate late so that other draw artifacts have priority.
|
||||
callbacks = {
|
||||
on_player_turn = function(ctx)
|
||||
local num_cards = #get_cards(ctx.owner)
|
||||
local heal_amount = num_cards * 2
|
||||
heal(ctx.owner, ctx.owner, heal_amount)
|
||||
return nil
|
||||
end
|
||||
}
|
||||
});
|
||||
@ -1,12 +0,0 @@
|
||||
register_artifact("JUICY_FRUIT", {
|
||||
name = "Juicy Fruit",
|
||||
description = "Tastes good and boosts your HP.",
|
||||
price = 80,
|
||||
order = 0,
|
||||
callbacks = {
|
||||
on_pick_up = function(ctx)
|
||||
actor_add_max_hp(ctx.owner, 10)
|
||||
return nil
|
||||
end
|
||||
}
|
||||
});
|
||||
@ -1,12 +0,0 @@
|
||||
register_artifact("RADIANT_SEED", {
|
||||
name = "Radiant Seed",
|
||||
description = "A small glowing seed.",
|
||||
price = 140,
|
||||
order = 0,
|
||||
callbacks = {
|
||||
on_pick_up = function(ctx)
|
||||
give_card("RADIANT_SEED", ctx.owner)
|
||||
return nil
|
||||
end
|
||||
}
|
||||
});
|
||||
@ -1,14 +0,0 @@
|
||||
register_artifact("REPULSION_STONE", {
|
||||
name = "Repulsion Stone",
|
||||
description = "For each damage taken heal for 2",
|
||||
price = 100,
|
||||
order = 0,
|
||||
callbacks = {
|
||||
on_damage = function(ctx)
|
||||
if ctx.target == ctx.owner then
|
||||
heal(ctx.owner, ctx.owner, 2)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
}
|
||||
});
|
||||
@ -1,16 +0,0 @@
|
||||
register_artifact("SHORT_RADIANCE", {
|
||||
name = "Short Radiance",
|
||||
description = "Apply 1 vulnerable at the start of combat.",
|
||||
price = 50,
|
||||
order = 0,
|
||||
callbacks = {
|
||||
on_player_turn = function(ctx)
|
||||
if ctx.round == 0 then
|
||||
each(function(val)
|
||||
give_status_effect("VULNERABLE", val)
|
||||
end, pairs(get_opponent_guids(ctx.owner)))
|
||||
end
|
||||
return nil
|
||||
end
|
||||
}
|
||||
});
|
||||
@ -1,14 +0,0 @@
|
||||
register_artifact("SPIKED_PLANT", {
|
||||
name = "Spiked Plant",
|
||||
description = "Deal 2 damage back to enemy attacks.",
|
||||
price = 50,
|
||||
order = 0,
|
||||
callbacks = {
|
||||
on_damage = function(ctx)
|
||||
if ctx.source ~= ctx.owner and ctx.owner == ctx.target then
|
||||
deal_damage(ctx.owner, ctx.source, 2)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
}
|
||||
});
|
||||
@ -1,22 +0,0 @@
|
||||
register_card("BERSERKER_RAGE", {
|
||||
name = "Berserker Rage",
|
||||
description = "Gain " .. highlight("3 action points") .. ", but take 30% (-10% per level) of your HP as damage.",
|
||||
state = function(ctx)
|
||||
return "Gain " ..
|
||||
highlight("3 action points") .. ", but take " .. highlight(tostring(30 - ctx.level * 10) .. "%") .. " (" ..
|
||||
tostring(get_player().hp * (0.3 - ctx.level * 0.1)) .. ") of your HP as damage."
|
||||
end,
|
||||
tags = { "BUFF" },
|
||||
max_level = 0,
|
||||
color = "#d8a448",
|
||||
need_target = false,
|
||||
point_cost = 0,
|
||||
price = 100,
|
||||
callbacks = {
|
||||
on_cast = function(ctx)
|
||||
player_give_action_points(3)
|
||||
deal_damage(ctx.caster, ctx.caster, get_player().hp * (0.3 - ctx.level * 0.1), true)
|
||||
return nil
|
||||
end
|
||||
}
|
||||
})
|
||||
@ -1,19 +0,0 @@
|
||||
register_card("BLOCK", {
|
||||
name = "Block",
|
||||
description = "Shield yourself and gain 5 " .. highlight("block") .. ".",
|
||||
state = function(ctx)
|
||||
return "Shield yourself and gain " .. highlight(5 + ctx.level * 3) .. " block."
|
||||
end,
|
||||
tags = { "DEF" },
|
||||
max_level = 1,
|
||||
color = "#219ebc",
|
||||
need_target = false,
|
||||
point_cost = 1,
|
||||
price = 40,
|
||||
callbacks = {
|
||||
on_cast = function(ctx)
|
||||
give_status_effect("BLOCK", ctx.caster, 5 + ctx.level * 3)
|
||||
return nil
|
||||
end
|
||||
}
|
||||
})
|
||||
@ -1,64 +0,0 @@
|
||||
register_card("BLOCK_SPIKES", {
|
||||
name = "Block Spikes",
|
||||
description = "Transforms " .. highlight("block") .. " to damage.",
|
||||
state = function(ctx)
|
||||
-- Fetch all BLOCK instances of owner
|
||||
local blocks = fun.iter(pairs(get_actor_status_effects(ctx.owner))):map(get_status_effect_instance):filter(function(val)
|
||||
return val.type_id == "BLOCK"
|
||||
end):totable()
|
||||
|
||||
-- Sum stacks to get damage
|
||||
local damage = fun.iter(pairs(blocks)):reduce(function(acc, val)
|
||||
return acc + val.stacks
|
||||
end, 0)
|
||||
|
||||
return "Transforms block to " .. highlight(damage) .. " damage."
|
||||
end,
|
||||
max_level = 0,
|
||||
color = "#895cd6",
|
||||
need_target = true,
|
||||
point_cost = 1,
|
||||
price = 100,
|
||||
callbacks = {
|
||||
on_cast = function(ctx)
|
||||
-- Fetch all BLOCK instances of caster
|
||||
local blocks = fun.iter(pairs(get_actor_status_effects(ctx.caster))):map(get_status_effect_instance):filter(function(val)
|
||||
return val.type_id == "BLOCK"
|
||||
end):totable()
|
||||
|
||||
-- Sum stacks to get damage
|
||||
local damage = fun.iter(pairs(blocks)):reduce(function(acc, val)
|
||||
return acc + val.stacks
|
||||
end, 0)
|
||||
|
||||
if damage == 0 then
|
||||
return "No block status effect present!"
|
||||
end
|
||||
|
||||
-- Remove BLOCKs
|
||||
fun.iter(pairs(blocks)):for_each(function(val)
|
||||
remove_status_effect(val.guid)
|
||||
end)
|
||||
|
||||
-- Deal Damage
|
||||
deal_damage(ctx.caster, ctx.target, damage)
|
||||
|
||||
return nil
|
||||
end
|
||||
},
|
||||
test = function ()
|
||||
give_status_effect("BLOCK", PLAYER_ID, 10)
|
||||
return assert_chain({
|
||||
function () assert_status_effect_count(1) end,
|
||||
function () assert_status_effect("BLOCK", 10) end,
|
||||
function ()
|
||||
local dummy = add_actor_by_enemy("DUMMY")
|
||||
local cards = get_cards(PLAYER_ID)
|
||||
cast_card(cards[1], dummy)
|
||||
if get_actor(dummy).hp ~= 90 then
|
||||
return "Expected 90 health, got " .. get_actor(dummy).hp
|
||||
end
|
||||
end
|
||||
})
|
||||
end
|
||||
})
|
||||
@ -1,19 +0,0 @@
|
||||
register_card("COMBINED_SHOT", {
|
||||
name = "Combined Shot",
|
||||
description = "Deal " .. highlight(5) .. " (+5 for each level) damage for each enemy.",
|
||||
state = function(ctx)
|
||||
return "Deal " .. highlight((5 + ctx.level * 5) * #get_opponent_guids(ctx.owner)) .. " damage for each enemy."
|
||||
end,
|
||||
tags = { "ATK" },
|
||||
max_level = 1,
|
||||
color = "#d8a448",
|
||||
need_target = true,
|
||||
point_cost = 1,
|
||||
price = 150,
|
||||
callbacks = {
|
||||
on_cast = function(ctx)
|
||||
deal_damage(ctx.caster, ctx.target, (5 + ctx.level * 5) * #get_opponent_guids(ctx.owner))
|
||||
return nil
|
||||
end
|
||||
}
|
||||
})
|
||||
@ -1,19 +0,0 @@
|
||||
register_card("FEAR", {
|
||||
name = "Fear",
|
||||
description = "Inflict " .. highlight("fear") .. " on the target, causing them to miss their next turn.",
|
||||
state = function(ctx)
|
||||
return nil
|
||||
end,
|
||||
tags = { "CC" },
|
||||
max_level = 0,
|
||||
color = "#725e9c",
|
||||
need_target = true,
|
||||
point_cost = 2,
|
||||
price = 80,
|
||||
callbacks = {
|
||||
on_cast = function(ctx)
|
||||
give_status_effect("FEAR", ctx.target)
|
||||
return nil
|
||||
end
|
||||
}
|
||||
})
|
||||
@ -1,23 +0,0 @@
|
||||
register_card("RADIANT_SEED", {
|
||||
name = "Radiant Seed",
|
||||
description = "Inflict 10 (+2 for each upgrade) damage to all enemies, but also causes 5 (-2 for each upgrade) damage to the caster.",
|
||||
state = function(ctx)
|
||||
return "Inflict " .. highlight(10 + ctx.level * 2) .. " damage to all enemies, but also causes " .. highlight(5 - ctx.level * 2) ..
|
||||
" damage to the caster."
|
||||
end,
|
||||
tags = { "ATK" },
|
||||
max_level = 1,
|
||||
color = "#82c93e",
|
||||
need_target = false,
|
||||
point_cost = 2,
|
||||
price = 120,
|
||||
callbacks = {
|
||||
on_cast = function(ctx)
|
||||
-- Deal damage to caster without any modifiers applying
|
||||
deal_damage(ctx.caster, ctx.caster, 5 - ctx.level * 2, true)
|
||||
-- Deal damage to opponents
|
||||
deal_damage_multi(ctx.caster, get_opponent_guids(ctx.caster), 10 + ctx.level * 2)
|
||||
return nil
|
||||
end
|
||||
}
|
||||
})
|
||||
@ -1,19 +0,0 @@
|
||||
register_card("RUPTURE", {
|
||||
name = "Rupture",
|
||||
description = "Inflict your enemy with " .. highlight("Vulnerable") .. ".",
|
||||
state = function(ctx)
|
||||
return "Inflict your enemy with " .. highlight(tostring(1 + ctx.level) .. " Vulnerable") .. "."
|
||||
end,
|
||||
tags = { "ATK" },
|
||||
max_level = 3,
|
||||
color = "#cf532d",
|
||||
need_target = true,
|
||||
point_cost = 1,
|
||||
price = 30,
|
||||
callbacks = {
|
||||
on_cast = function(ctx)
|
||||
give_status_effect("VULNERABLE", ctx.target, 1 + ctx.level)
|
||||
return nil
|
||||
end
|
||||
}
|
||||
})
|
||||
@ -1,47 +0,0 @@
|
||||
register_card("SHIELD_BASH", {
|
||||
name = "Shield Bash",
|
||||
description = "Deal 4 (+2 for each upgrade) damage to the enemy and gain " .. highlight("block") ..
|
||||
" status effect equal to the damage dealt.",
|
||||
state = function(ctx)
|
||||
return "Deal " .. highlight(4 + ctx.level * 2) .. " damage to the enemy and gain " .. highlight("block") ..
|
||||
" status effect equal to the damage dealt."
|
||||
end,
|
||||
tags = { "ATK" },
|
||||
max_level = 1,
|
||||
color = "#ff5722",
|
||||
need_target = true,
|
||||
point_cost = 1,
|
||||
price = 40,
|
||||
callbacks = {
|
||||
on_cast = function(ctx)
|
||||
local damage = deal_damage(ctx.caster, ctx.target, 4 + ctx.level * 2)
|
||||
give_status_effect("BLOCK", ctx.caster, damage)
|
||||
return nil
|
||||
end
|
||||
},
|
||||
test = function()
|
||||
local dummy = add_actor_by_enemy("DUMMY")
|
||||
local cards = get_cards(PLAYER_ID)
|
||||
|
||||
-- Check if the card is in the player's hand
|
||||
if not cards[1] then
|
||||
return "Card not in hand"
|
||||
end
|
||||
|
||||
local card = get_card_instance(cards[1])
|
||||
if card.type_id ~= "SHIELD_BASH" then
|
||||
return "Card has wrong type: " .. card.type_id
|
||||
end
|
||||
|
||||
cast_card(cards[1], dummy)
|
||||
|
||||
if get_actor(dummy).hp ~= 96 then
|
||||
return "Expected 96 health, got " .. get_actor(dummy).hp
|
||||
end
|
||||
|
||||
return assert_chain({
|
||||
function () assert_status_effect_count(1) end,
|
||||
function () assert_status_effect("BLOCK", 4) end
|
||||
})
|
||||
end
|
||||
})
|
||||
@ -61,17 +61,16 @@ function text_bg(color, value) end
|
||||
---@return string
|
||||
function text_bold(value) end
|
||||
|
||||
--- Makes the text foreground colored. Takes hex values like #ff0000.
|
||||
---@param color string
|
||||
---@param value any
|
||||
---@return string
|
||||
function text_color(color, value) end
|
||||
|
||||
--- Makes the text italic.
|
||||
---@param value any
|
||||
---@return string
|
||||
function text_italic(value) end
|
||||
|
||||
--- Makes the text colored red.
|
||||
---@param value any
|
||||
---@return string
|
||||
function text_red(value) end
|
||||
|
||||
--- Makes the text underlined.
|
||||
---@param value any
|
||||
---@return string
|
||||
@ -125,10 +124,14 @@ function get_event_history() end
|
||||
---@return fight_state
|
||||
function get_fight() end
|
||||
|
||||
--- Gets the number of stages cleared.
|
||||
--- Gets the fight round.
|
||||
---@return number
|
||||
function get_fight_round() end
|
||||
|
||||
--- Gets the number of stages cleared.
|
||||
---@return number
|
||||
function get_stages_cleared() end
|
||||
|
||||
--- Checks if the event happened at least once.
|
||||
---@param event_id type_id
|
||||
---@return boolean
|
||||
@ -170,6 +173,16 @@ function actor_add_hp(guid, amount) end
|
||||
---@param amount number
|
||||
function actor_add_max_hp(guid, amount) end
|
||||
|
||||
--- Sets the hp value of a actor to a number. This won't trigger any on_damage callbacks
|
||||
---@param guid guid
|
||||
---@param amount number
|
||||
function actor_set_hp(guid, amount) end
|
||||
|
||||
--- Sets the max hp value of a actor to a number.
|
||||
---@param guid guid
|
||||
---@param amount number
|
||||
function actor_set_max_hp(guid, amount) end
|
||||
|
||||
--- Creates a new enemy fighting against the player. Example ``add_actor_by_enemy("RUST_MITE")``.
|
||||
---@param enemy_guid type_id
|
||||
---@return string
|
||||
@ -218,6 +231,11 @@ function get_artifact(id) end
|
||||
---@return artifact_instance
|
||||
function get_artifact_instance(guid) end
|
||||
|
||||
--- Returns all the artifacts guids from the given actor.
|
||||
---@param actor_guid string
|
||||
---@return guid[]
|
||||
function get_artifacts(actor_guid) end
|
||||
|
||||
--- Gives a actor a artifact. Returns the guid of the newly created artifact.
|
||||
---@param type_id type_id
|
||||
---@param actor guid
|
||||
@ -316,7 +334,7 @@ function upgrade_random_card(actor_guid) end
|
||||
-- Damage & Heal
|
||||
-- #####################################
|
||||
|
||||
--- Deal damage to a enemy from one source. If flat is true the damage can't be modified by status effects or artifacts. Returns the damage that was dealt.
|
||||
--- 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.
|
||||
---@param source guid
|
||||
---@param target guid
|
||||
---@param damage number
|
||||
@ -338,6 +356,14 @@ function deal_damage_multi(source, targets, damage, flat) end
|
||||
---@param amount number
|
||||
function heal(source, target, amount) end
|
||||
|
||||
--- Simulate damage from a source to a target. If flat is true the damage can't be modified by status effects or artifacts. Returns the damage that would be dealt.
|
||||
---@param source guid
|
||||
---@param target guid
|
||||
---@param damage number
|
||||
---@param flat? boolean
|
||||
---@return number
|
||||
function simulate_deal_damage(source, target, damage, flat) end
|
||||
|
||||
-- #####################################
|
||||
-- Player Operations
|
||||
-- #####################################
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
---@field id? type_id
|
||||
---@field name? string
|
||||
---@field description? string
|
||||
---@field tags? string[]
|
||||
---@field price? number
|
||||
---@field order? number
|
||||
---@field callbacks? callbacks
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
---@field level? number
|
||||
---@field tags? string[]
|
||||
---@field damage? number
|
||||
---@field simulated? boolean
|
||||
---@field heal? number
|
||||
---@field stacks? number
|
||||
---@field round? number
|
||||
|
||||
@ -20,6 +20,6 @@
|
||||
---@field description string
|
||||
---@field choices event_choice[]
|
||||
---@field on_enter? fun(ctx:event_on_enter_ctx):nil
|
||||
---@field on_end fun(ctx:event_choice_ctx):next_game_state|nil
|
||||
---@field on_end? fun(ctx:event_choice_ctx):next_game_state|nil
|
||||
---@field test? fun():nil|string
|
||||
---@field base_game? boolean
|
||||
@ -1,7 +1,7 @@
|
||||
---@meta
|
||||
|
||||
---@class story_teller
|
||||
---@field id type_id
|
||||
---@field active fun():next_game_state
|
||||
---@field decide fun():number
|
||||
---@field base_game boolean
|
||||
---@field id? type_id
|
||||
---@field active fun():number
|
||||
---@field decide fun():next_game_state
|
||||
---@field base_game? boolean
|
||||
@ -5,30 +5,30 @@ register_enemy("CLEAN_BOT", {
|
||||
(* *)
|
||||
)#(]],
|
||||
color = "#32a891",
|
||||
initial_hp = 25,
|
||||
max_hp = 25,
|
||||
initial_hp = 13,
|
||||
max_hp = 13,
|
||||
gold = 15,
|
||||
intend = function(ctx)
|
||||
local self = get_actor(ctx.guid)
|
||||
if self.hp <= 8 then
|
||||
return "Block " .. highlight(4)
|
||||
if self.hp <= 4 then
|
||||
return "Block " .. highlight(2)
|
||||
end
|
||||
|
||||
return "Deal " .. highlight(7) .. " damage"
|
||||
return "Deal " .. highlight(2) .. " damage"
|
||||
end,
|
||||
callbacks = {
|
||||
on_player_turn = function(ctx)
|
||||
local self = get_actor(ctx.guid)
|
||||
|
||||
if self.hp <= 8 then
|
||||
give_status_effect("BLOCK", ctx.guid, 4)
|
||||
if self.hp <= 4 then
|
||||
give_status_effect("BLOCK", ctx.guid, 2)
|
||||
end
|
||||
end,
|
||||
on_turn = function(ctx)
|
||||
local self = get_actor(ctx.guid)
|
||||
|
||||
if self.hp > 8 then
|
||||
deal_damage(ctx.guid, PLAYER_ID, 7)
|
||||
if self.hp > 4 then
|
||||
deal_damage(ctx.guid, PLAYER_ID, 2)
|
||||
end
|
||||
|
||||
return nil
|
||||
|
||||
@ -1,24 +1,24 @@
|
||||
register_enemy("RUST_MITE", {
|
||||
name = "Rust Mite",
|
||||
description = "Loves to eat metal.",
|
||||
name = l("enemies.RUST_MITE.name", "Rust Mite"),
|
||||
description = l("enemies.RUST_MITE.description", "A small robot that eats metal."),
|
||||
look = "/v\\",
|
||||
color = "#e6e65a",
|
||||
initial_hp = 22,
|
||||
max_hp = 22,
|
||||
initial_hp = 12,
|
||||
max_hp = 12,
|
||||
gold = 10,
|
||||
intend = function(ctx)
|
||||
if ctx.round % 4 == 0 then
|
||||
return "Gather strength"
|
||||
return "Load battery"
|
||||
end
|
||||
|
||||
return "Deal " .. highlight(6) .. " damage"
|
||||
return "Deal " .. highlight(simulate_deal_damage(ctx.guid, PLAYER_ID, 1)) .. " damage"
|
||||
end,
|
||||
callbacks = {
|
||||
on_turn = function(ctx)
|
||||
if ctx.round % 4 == 0 then
|
||||
give_status_effect("RITUAL", ctx.guid)
|
||||
give_status_effect("CHARGED", ctx.guid)
|
||||
else
|
||||
deal_damage(ctx.guid, PLAYER_ID, 6)
|
||||
deal_damage(ctx.guid, PLAYER_ID, 1)
|
||||
end
|
||||
|
||||
return nil
|
||||
@ -26,21 +26,23 @@ register_enemy("RUST_MITE", {
|
||||
}
|
||||
})
|
||||
|
||||
register_status_effect("RITUAL", {
|
||||
name = "Ritual",
|
||||
description = "Gain strength each round",
|
||||
look = "Rit",
|
||||
foreground = "#bb3e03",
|
||||
register_status_effect("CHARGED", {
|
||||
name = l("status_effects.CHARGED.name", "Charged"),
|
||||
description = l("status_effects.CHARGED.description", "Attacks will deal more damage per stack."),
|
||||
look = "CHRG",
|
||||
foreground = "#207BE7",
|
||||
state = function(ctx)
|
||||
return nil
|
||||
return string.format(l("status_effects.CHARGED.state", "Attacks deal %s more damage"), highlight(ctx.stacks * 1))
|
||||
end,
|
||||
can_stack = true,
|
||||
decay = DECAY_NONE,
|
||||
rounds = 0,
|
||||
callbacks = {
|
||||
on_player_turn = function(ctx)
|
||||
local guid = give_status_effect("STRENGTH", ctx.owner)
|
||||
set_status_effect_stacks(guid, 3 + ctx.stacks)
|
||||
on_damage_calc = function(ctx)
|
||||
if ctx.source == ctx.owner then
|
||||
return ctx.damage + 1 * ctx.stacks
|
||||
end
|
||||
return nil
|
||||
end
|
||||
}
|
||||
})
|
||||
|
||||
@ -1,29 +0,0 @@
|
||||
register_enemy("SAND_STALKER", {
|
||||
name = "Sand Stalker",
|
||||
description = "It waits for its prey to come closer.",
|
||||
look = "( ° ° )",
|
||||
color = "#8e4028",
|
||||
initial_hp = 25,
|
||||
max_hp = 25,
|
||||
gold = 20,
|
||||
intend = function(ctx)
|
||||
if ctx.round % 4 == 0 then
|
||||
return "Weaken your resolve"
|
||||
end
|
||||
|
||||
return "Deal " .. highlight(7) .. " damage"
|
||||
end,
|
||||
callbacks = {
|
||||
on_turn = function(ctx)
|
||||
if ctx.round % 4 == 0 then
|
||||
if deal_damage(ctx.guid, PLAYER_ID, 5) > 0 then
|
||||
give_status_effect("WEAKEN", PLAYER_ID, 1)
|
||||
end
|
||||
else
|
||||
deal_damage(ctx.guid, PLAYER_ID, 7)
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
}
|
||||
})
|
||||
@ -1,70 +0,0 @@
|
||||
register_enemy("SHADOW_ASSASSIN", {
|
||||
name = "Shadow Assassin",
|
||||
description = "A master of stealth and deception.",
|
||||
look = "???",
|
||||
color = "#6c5b7b",
|
||||
initial_hp = 20,
|
||||
max_hp = 20,
|
||||
gold = 30,
|
||||
intend = function(ctx)
|
||||
local bleeds = fun.iter(pairs(get_actor_status_effects(PLAYER_ID)))
|
||||
:map(get_status_effect_instance)
|
||||
:filter(function(val)
|
||||
return val.type_id == "BLEED"
|
||||
end):totable()
|
||||
|
||||
if #bleeds > 0 then
|
||||
return "Deal " .. highlight(10) .. " damage"
|
||||
elseif ctx.round % 3 == 0 then
|
||||
return "Inflict bleed"
|
||||
else
|
||||
return "Deal " .. highlight(5) .. " damage"
|
||||
end
|
||||
|
||||
return nil
|
||||
end,
|
||||
callbacks = {
|
||||
on_turn = function(ctx)
|
||||
-- Count bleed stacks
|
||||
local bleeds = fun.iter(pairs(get_actor_status_effects(PLAYER_ID)))
|
||||
:map(get_status_effect_instance)
|
||||
:filter(function(
|
||||
val)
|
||||
return val.type_id == "BLEED"
|
||||
end):totable()
|
||||
|
||||
if #bleeds > 0 then
|
||||
-- If bleeding do more damage
|
||||
deal_damage(ctx.guid, PLAYER_ID, 10)
|
||||
elseif ctx.round % 3 == 0 then
|
||||
-- Try to bleed every 2 rounds with 3 dmg
|
||||
if deal_damage(ctx.guid, PLAYER_ID, 3) > 0 then
|
||||
give_status_effect("BLEED", PLAYER_ID, 2)
|
||||
end
|
||||
else
|
||||
-- Just hit with 5 damage
|
||||
deal_damage(ctx.guid, PLAYER_ID, 5)
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
}
|
||||
})
|
||||
|
||||
register_status_effect("BLEED", {
|
||||
name = "Bleed",
|
||||
description = "Losing some red sauce.",
|
||||
look = "Bld",
|
||||
foreground = "#ff0000",
|
||||
state = function(ctx)
|
||||
return nil
|
||||
end,
|
||||
can_stack = false,
|
||||
decay = DECAY_ONE,
|
||||
rounds = 2,
|
||||
callbacks = {
|
||||
on_turn = function(ctx)
|
||||
return nil
|
||||
end
|
||||
}
|
||||
})
|
||||
3
assets/scripts/equipment/_colors.lua
Normal file
3
assets/scripts/equipment/_colors.lua
Normal file
@ -0,0 +1,3 @@
|
||||
COLOR_GRAY = "#2f3e46"
|
||||
COLOR_BLUE = "#219ebc"
|
||||
COLOR_PURPLE = "#725e9c"
|
||||
25
assets/scripts/equipment/_util.lua
Normal file
25
assets/scripts/equipment/_util.lua
Normal file
@ -0,0 +1,25 @@
|
||||
function add_found_artifact_event(id, picture, description, choice_description)
|
||||
register_event(id, {
|
||||
name = "Found: " .. registered.artifact[id].name,
|
||||
description = string.format("!!%s\n\n**You found something!** %s", picture or "artifact_chest.jpg", description),
|
||||
choices = {
|
||||
{
|
||||
description_fn = function()
|
||||
return "Take " .. registered.artifact[id].name .. "... (" .. choice_description .. ")"
|
||||
end,
|
||||
callback = function(ctx)
|
||||
give_artifact(id, PLAYER_ID)
|
||||
return nil
|
||||
end
|
||||
}, {
|
||||
description = "Leave...",
|
||||
callback = function()
|
||||
return nil
|
||||
end
|
||||
}
|
||||
},
|
||||
on_end = function()
|
||||
return GAME_STATE_RANDOM
|
||||
end
|
||||
})
|
||||
end
|
||||
39
assets/scripts/equipment/arm_mounted_gun.lua
Normal file
39
assets/scripts/equipment/arm_mounted_gun.lua
Normal file
@ -0,0 +1,39 @@
|
||||
register_artifact("ARM_MOUNTED_GUN", {
|
||||
name = "Arm Mounted Gun",
|
||||
description = "Weapon that is mounted on your arm. It is very powerful.",
|
||||
tags = { "ARM" },
|
||||
price = 190,
|
||||
order = 0,
|
||||
callbacks = {
|
||||
on_pick_up = function(ctx)
|
||||
clear_artifacts_by_tag("ARM", { ctx.guid })
|
||||
clear_cards_by_tag("ARM")
|
||||
give_card("ARM_MOUNTED_GUN", PLAYER_ID)
|
||||
return nil
|
||||
end
|
||||
}
|
||||
});
|
||||
|
||||
register_card("ARM_MOUNTED_GUN", {
|
||||
name = l("cards.ARM_MOUNTED_GUN.name", "Arm Mounted Gun"),
|
||||
description = l("cards.ARM_MOUNTED_GUN.description", "Exhaust. Use your arm mounted gun to deal 15 (+3 for each upgrade) damage."),
|
||||
state = function(ctx)
|
||||
return string.format(l("cards.ARM_MOUNTED_GUN.state", "Use your arm mounted gun to deal %s damage."), highlight(7 + ctx.level * 3))
|
||||
end,
|
||||
tags = { "ATK", "R", "T", "ARM" },
|
||||
max_level = 1,
|
||||
color = COLOR_GRAY,
|
||||
need_target = true,
|
||||
does_exhaust = true,
|
||||
point_cost = 3,
|
||||
price = -1,
|
||||
callbacks = {
|
||||
on_cast = function(ctx)
|
||||
deal_damage(ctx.caster, ctx.target, 7 + ctx.level * 3)
|
||||
return nil
|
||||
end
|
||||
},
|
||||
test = function ()
|
||||
return assert_cast_damage("ARM_MOUNTED_GUN", 7)
|
||||
end
|
||||
})
|
||||
129
assets/scripts/equipment/basic_hand_weapons.lua
Normal file
129
assets/scripts/equipment/basic_hand_weapons.lua
Normal file
@ -0,0 +1,129 @@
|
||||
local hand_warning = "**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."
|
||||
|
||||
HAND_WEAPONS = {
|
||||
{
|
||||
id = "CROWBAR",
|
||||
name = "Crowbar",
|
||||
image = "red_room.jpg",
|
||||
description = "A crowbar. It's a bit rusty, but it should still be useful!",
|
||||
base_damage = 2,
|
||||
base_cards = 3,
|
||||
tags = { "ATK", "M", "T", "HND" },
|
||||
additional_cards = { "KNOCK_OUT" },
|
||||
price = 80
|
||||
},
|
||||
{
|
||||
id = "VIBRO_KNIFE",
|
||||
name = "VIBRO Knife",
|
||||
description = "A VIBRO knife. Uses ultrasonic vibrations to cut through almost anything.",
|
||||
base_damage = 3,
|
||||
base_cards = 3,
|
||||
tags = { "ATK", "M", "T", "HND" },
|
||||
additional_cards = { "VIBRO_OVERCLOCK" },
|
||||
price = 180
|
||||
},
|
||||
{
|
||||
id = "LZR_PISTOL",
|
||||
name = "LZR Pistol",
|
||||
description = "A LZR pistol. Fires a concentrated beam of light.",
|
||||
base_damage = 4,
|
||||
base_cards = 3,
|
||||
tags = { "ATK", "R", "T", "HND" },
|
||||
additional_cards = { "LZR_OVERCHARGE" },
|
||||
price = 280
|
||||
},
|
||||
{
|
||||
id = "HAR_II",
|
||||
name = "HAR-II",
|
||||
description = "A HAR-II. A heavy assault rifle with a high rate of fire.",
|
||||
base_damage = 5,
|
||||
base_cards = 3,
|
||||
tags = { "ATK", "R", "T", "HND" },
|
||||
additional_cards = { "HAR_BURST", "TARGET_PAINTER" },
|
||||
price = 380
|
||||
}
|
||||
}
|
||||
|
||||
HAND_WEAPONS_ARTIFACT_IDS = fun.iter(HAND_WEAPONS):map(function(w) return w.id end):totable()
|
||||
|
||||
for _, weapon in pairs(HAND_WEAPONS) do
|
||||
register_card(weapon.id, {
|
||||
name = l("cards." .. weapon.id .. ".name", weapon.name),
|
||||
description = l("cards." .. weapon.id .. ".description", string.format("Use to deal %s (+3 for each upgrade) damage.", weapon.base_damage)),
|
||||
state = function(ctx)
|
||||
return string.format(l("cards." .. weapon.id .. ".state", "Use to deal %s damage."), highlight(weapon.base_damage + ctx.level * 3))
|
||||
end,
|
||||
tags = weapon.tags,
|
||||
max_level = 3,
|
||||
color = COLOR_GRAY,
|
||||
need_target = true,
|
||||
point_cost = 1,
|
||||
price = 0,
|
||||
callbacks = {
|
||||
on_cast = function(ctx)
|
||||
deal_damage(ctx.caster, ctx.target, weapon.base_damage + ctx.level * 3)
|
||||
return nil
|
||||
end
|
||||
},
|
||||
test = function()
|
||||
local dummy = add_actor_by_enemy("DUMMY")
|
||||
local cards = get_cards(PLAYER_ID)
|
||||
|
||||
-- Check if the card is in the player's hand
|
||||
if not cards[1] then
|
||||
return "Card not in hand"
|
||||
end
|
||||
|
||||
local card = get_card_instance(cards[1])
|
||||
if card.type_id ~= weapon.id then
|
||||
return "Card has wrong type: " .. card.type_id
|
||||
end
|
||||
|
||||
cast_card(cards[1], dummy)
|
||||
|
||||
if get_actor(dummy).hp ~= 100 - weapon.base_damage then
|
||||
return "Expected " .. tostring(100 - weapon.base_damage) .. " health, got " .. get_actor(dummy).hp
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
})
|
||||
|
||||
register_artifact(weapon.id, {
|
||||
name = weapon.name,
|
||||
description = weapon.description .. " Can be used in your hand.",
|
||||
tags = weapon.tags,
|
||||
price = weapon.price,
|
||||
order = 0,
|
||||
callbacks = {
|
||||
on_pick_up = function(ctx)
|
||||
clear_artifacts_by_tag("HND", { ctx.guid })
|
||||
clear_cards_by_tag("HND")
|
||||
|
||||
-- add basic cards
|
||||
for i = 1, weapon.base_cards do
|
||||
give_card(weapon.id, PLAYER_ID)
|
||||
end
|
||||
|
||||
-- add additional cards
|
||||
if weapon.additional_cards then
|
||||
for _, card in pairs(weapon.additional_cards) do
|
||||
give_card(card, PLAYER_ID)
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
}
|
||||
})
|
||||
|
||||
add_found_artifact_event(weapon.id, weapon.image, string.format("%s\n\n%s", weapon.description, hand_warning), registered.card[weapon.id].description)
|
||||
end
|
||||
|
||||
---hand_weapon_event returns a random hand weapon event weighted by price.
|
||||
---@return string
|
||||
function hand_weapon_event()
|
||||
local ids = fun.iter(HAND_WEAPONS):map(function(w) return w.id end):totable()
|
||||
local prices = fun.iter(HAND_WEAPONS):map(function(w) return 500 - w.price end):totable()
|
||||
return choose_weighted(ids, prices)
|
||||
end
|
||||
@ -1,8 +1,28 @@
|
||||
register_card("BLOCK", {
|
||||
name = "Block",
|
||||
description = "Shield yourself and gain 5 " .. highlight("block") .. ".",
|
||||
state = function(ctx)
|
||||
return "Shield yourself and gain " .. highlight(1 + ctx.level) .. " block."
|
||||
end,
|
||||
tags = { "DEF" },
|
||||
max_level = 1,
|
||||
color = COLOR_BLUE,
|
||||
need_target = false,
|
||||
point_cost = 1,
|
||||
price = 40,
|
||||
callbacks = {
|
||||
on_cast = function(ctx)
|
||||
give_status_effect("BLOCK", ctx.caster, 1 + ctx.level)
|
||||
return nil
|
||||
end
|
||||
}
|
||||
})
|
||||
|
||||
register_status_effect("BLOCK", {
|
||||
name = "Block",
|
||||
description = "Decreases incoming damage for each stack",
|
||||
look = "Blk",
|
||||
foreground = "#219ebc",
|
||||
foreground = COLOR_BLUE,
|
||||
state = function(ctx)
|
||||
return "Takes " .. highlight(ctx.stacks) .. " less damage"
|
||||
end,
|
||||
@ -12,12 +32,16 @@ register_status_effect("BLOCK", {
|
||||
order = 100,
|
||||
callbacks = {
|
||||
on_damage_calc = function(ctx)
|
||||
if ctx.simulated then
|
||||
return ctx.damage
|
||||
end
|
||||
|
||||
if ctx.target == ctx.owner then
|
||||
add_status_effect_stacks(ctx.guid, -ctx.damage)
|
||||
return ctx.damage - ctx.stacks
|
||||
end
|
||||
return ctx.damage
|
||||
end
|
||||
end,
|
||||
},
|
||||
test = function()
|
||||
return assert_chain({
|
||||
49
assets/scripts/equipment/flash_bang.lua
Normal file
49
assets/scripts/equipment/flash_bang.lua
Normal file
@ -0,0 +1,49 @@
|
||||
register_card("FLASH_BANG", {
|
||||
name = l("cards.FLASH_BANG.name", "Flash Bang"),
|
||||
description = l("cards.FLASH_BANG.description", highlight("One-Time") .. "\n\nInflicts " .. highlight("Blinded") .. " on the target, causing them to deal less damage."),
|
||||
tags = { "CC" },
|
||||
max_level = 0,
|
||||
color = COLOR_PURPLE,
|
||||
need_target = true,
|
||||
does_consume = true,
|
||||
point_cost = 1,
|
||||
price = -1,
|
||||
callbacks = {
|
||||
on_cast = function(ctx)
|
||||
give_status_effect("FLASH_BANG", ctx.target)
|
||||
return nil
|
||||
end
|
||||
}
|
||||
})
|
||||
|
||||
register_status_effect("FLASH_BANG", {
|
||||
name = l("cards.FLASH_BANG.name", "Blinded"),
|
||||
description = l("cards.FLASH_BANG.description", "Causing " .. highlight("25%") .. " less damage."),
|
||||
look = "FL",
|
||||
foreground = COLOR_PURPLE,
|
||||
state = function(ctx) return nil end,
|
||||
can_stack = true,
|
||||
decay = DECAY_ONE,
|
||||
rounds = 1,
|
||||
callbacks = {
|
||||
on_damage_calc = function(ctx)
|
||||
if ctx.source == ctx.owner then
|
||||
return ctx.damage * 0.75
|
||||
end
|
||||
return ctx.damage
|
||||
end
|
||||
},
|
||||
test = function()
|
||||
return assert_chain({
|
||||
function() return assert_status_effect_count(1) end,
|
||||
function() return assert_status_effect("FLASH_BANG", 1) end,
|
||||
function ()
|
||||
local dummy = add_actor_by_enemy("DUMMY")
|
||||
local damage = deal_damage(PLAYER_ID, dummy, 10)
|
||||
if damage ~= 7 then
|
||||
return "Expected 7 damage, got " .. damage
|
||||
end
|
||||
end
|
||||
})
|
||||
end
|
||||
})
|
||||
34
assets/scripts/equipment/knock_out.lua
Normal file
34
assets/scripts/equipment/knock_out.lua
Normal file
@ -0,0 +1,34 @@
|
||||
register_card("KNOCK_OUT", {
|
||||
name = l("cards.KNOCK_OUT.name", "Knock Out"),
|
||||
description = l("cards.KNOCK_OUT.description", "Inflicts " .. highlight("Knock Out") .. " on the target, causing them to miss their next turn."),
|
||||
tags = { "CC" },
|
||||
max_level = 0,
|
||||
color = COLOR_PURPLE,
|
||||
need_target = true,
|
||||
point_cost = 2,
|
||||
price = -1,
|
||||
callbacks = {
|
||||
on_cast = function(ctx)
|
||||
give_status_effect("KNOCK_OUT", ctx.target)
|
||||
return nil
|
||||
end
|
||||
}
|
||||
})
|
||||
|
||||
register_status_effect("KNOCK_OUT", {
|
||||
name = l("status_effects.KNOCK_OUT.name", "Knock Out"),
|
||||
description = l("status_effects.KNOCK_OUT.description", "Can't act"),
|
||||
look = "K",
|
||||
foreground = COLOR_PURPLE,
|
||||
state = function(ctx)
|
||||
return string.format(l("status_effects.KNOCK_OUT.state", "Can't act for %s turns"), highlight(ctx.stacks))
|
||||
end,
|
||||
can_stack = true,
|
||||
decay = DECAY_ONE,
|
||||
rounds = 1,
|
||||
callbacks = {
|
||||
on_turn = function(ctx)
|
||||
return true
|
||||
end
|
||||
}
|
||||
})
|
||||
@ -1,19 +1,22 @@
|
||||
register_card("MELEE_HIT", {
|
||||
name = l("cards.MELEE_HIT.name", "Melee Hit"),
|
||||
description = l("cards.MELEE_HIT.description", "Use your bare hands to deal 5 (+3 for each upgrade) damage."),
|
||||
description = l("cards.MELEE_HIT.description", "Use your bare hands to deal 1 (+1 for each upgrade) damage."),
|
||||
state = function(ctx)
|
||||
return string.format(l("cards.MELEE_HIT.state", "Use your bare hands to deal %s damage."), highlight(5 + ctx.level * 3))
|
||||
return string.format(l("cards.MELEE_HIT.state", "Use your bare hands to deal %s damage."), highlight(1 + ctx.level))
|
||||
end,
|
||||
tags = { "ATK" },
|
||||
tags = { "ATK", "M", "HND" },
|
||||
max_level = 1,
|
||||
color = "#2f3e46",
|
||||
color = COLOR_GRAY,
|
||||
need_target = true,
|
||||
point_cost = 1,
|
||||
price = 30,
|
||||
price = -1,
|
||||
callbacks = {
|
||||
on_cast = function(ctx)
|
||||
deal_damage(ctx.caster, ctx.target, 5 + ctx.level * 3)
|
||||
deal_damage(ctx.caster, ctx.target, 1 + ctx.level)
|
||||
return nil
|
||||
end
|
||||
}
|
||||
},
|
||||
test = function ()
|
||||
return assert_cast_damage("MELEE_HIT", 1)
|
||||
end
|
||||
})
|
||||
33
assets/scripts/events/act_0/enemies.lua
Normal file
33
assets/scripts/events/act_0/enemies.lua
Normal file
@ -0,0 +1,33 @@
|
||||
register_event("RUST_MITE", {
|
||||
name = "Tasty metals...",
|
||||
description = [[
|
||||
You 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.
|
||||
]],
|
||||
tags = {"ACT_0"},
|
||||
choices = {
|
||||
{
|
||||
description = "Fight!",
|
||||
callback = function()
|
||||
add_actor_by_enemy("RUST_MITE")
|
||||
return GAME_STATE_FIGHT
|
||||
end
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
register_event("CLEAN_BOT", {
|
||||
name = "Corpse. Clean. Engage.",
|
||||
description = [[
|
||||
You come across a strange robot. It seems to be cleaning up the area. It looks at you and says "Corpse. Clean. Engage.". You're not sure what it means, but it doesn't seem to be friendly.
|
||||
]],
|
||||
tags = {"ACT_0"},
|
||||
choices = {
|
||||
{
|
||||
description = "Fight!",
|
||||
callback = function()
|
||||
add_actor_by_enemy("CLEAN_BOT")
|
||||
return GAME_STATE_FIGHT
|
||||
end
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -13,43 +13,38 @@ As you struggle to gather your bearings, you notice a blinking panel on the wall
|
||||
**Shortly after you realize that you are not alone...**]],
|
||||
choices = {
|
||||
{
|
||||
description = "Try to escape the facility before it finds you...",
|
||||
description = "Try to find a weapon. " ..
|
||||
highlight('Find meele weapon') .. " " .. highlight_warn("Take 4 damage"),
|
||||
callback = function()
|
||||
-- Try to escape
|
||||
if math.random() < 0.5 then
|
||||
set_event(stage_1_init_events[math.random(#stage_1_init_events)])
|
||||
return GAME_STATE_EVENT
|
||||
end
|
||||
deal_damage(PLAYER_ID, PLAYER_ID, 4, true)
|
||||
give_artifact(
|
||||
choose_weighted_by_price(find_artifacts_by_tags({ "HND", "M" })), PLAYER_ID
|
||||
)
|
||||
|
||||
-- Let OnEnd handle the state change
|
||||
return nil
|
||||
end
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description = "Gather your strength and attack it!",
|
||||
callback = function()
|
||||
give_card("MELEE_HIT", PLAYER_ID)
|
||||
give_card("MELEE_HIT", PLAYER_ID)
|
||||
give_card("MELEE_HIT", PLAYER_ID)
|
||||
|
||||
return nil
|
||||
end
|
||||
}
|
||||
},
|
||||
on_enter = function()
|
||||
play_music("energetic_orthogonal_expansions")
|
||||
|
||||
-- Give the player it's start cards
|
||||
give_card("MELEE_HIT", PLAYER_ID)
|
||||
give_card("MELEE_HIT", PLAYER_ID)
|
||||
give_card("MELEE_HIT", PLAYER_ID)
|
||||
give_card("MELEE_HIT", PLAYER_ID)
|
||||
give_card("MELEE_HIT", PLAYER_ID)
|
||||
|
||||
give_card("RUPTURE", PLAYER_ID)
|
||||
|
||||
give_card("BLOCK", PLAYER_ID)
|
||||
give_card("BLOCK", PLAYER_ID)
|
||||
give_card("BLOCK", PLAYER_ID)
|
||||
|
||||
give_artifact(random_artifact(150), PLAYER_ID)
|
||||
end,
|
||||
on_end = function()
|
||||
actor_set_max_hp(PLAYER_ID, 10)
|
||||
actor_set_hp(PLAYER_ID, 10)
|
||||
|
||||
give_card("BLOCK", PLAYER_ID)
|
||||
give_card("BLOCK", PLAYER_ID)
|
||||
|
||||
return GAME_STATE_RANDOM
|
||||
end
|
||||
})
|
||||
@ -1,45 +0,0 @@
|
||||
register_event("RAISING_THE_BAR", {
|
||||
name = "Raising The Bar",
|
||||
description = [[!!red_room.jpg
|
||||
|
||||
...]],
|
||||
choices = {
|
||||
{
|
||||
description_fn = function()
|
||||
return "Take Crowbar... (" .. registered.card["CROWBAR"].description .. ")"
|
||||
end,
|
||||
callback = function(ctx)
|
||||
give_card("CROWBAR", PLAYER_ID)
|
||||
return nil
|
||||
end
|
||||
}, {
|
||||
description = "Leave...",
|
||||
callback = function()
|
||||
return nil
|
||||
end
|
||||
}
|
||||
},
|
||||
on_end = function()
|
||||
return GAME_STATE_RANDOM
|
||||
end
|
||||
})
|
||||
|
||||
register_card("CROWBAR", {
|
||||
name = "Crowbar",
|
||||
description = "Deal " .. highlight(22) .. " damage.",
|
||||
state = function(ctx)
|
||||
return nil
|
||||
end,
|
||||
max_level = 0,
|
||||
color = "#f37b21",
|
||||
need_target = true,
|
||||
exhaust = true,
|
||||
point_cost = 3,
|
||||
price = -1,
|
||||
callbacks = {
|
||||
on_cast = function(ctx)
|
||||
deal_damage(ctx.caster, ctx.target, 22)
|
||||
return nil
|
||||
end
|
||||
}
|
||||
})
|
||||
@ -1,53 +0,0 @@
|
||||
register_event("RECYCLE_DEVICE", {
|
||||
name = "Talking Being",
|
||||
description = [[!!artifact_chest.jpg
|
||||
|
||||
...]],
|
||||
choices = {
|
||||
{
|
||||
description_fn = function()
|
||||
return "Take Device... (" .. registered.card["RECYCLE"].description .. ")"
|
||||
end,
|
||||
callback = function(ctx)
|
||||
give_card("RECYCLE", PLAYER_ID)
|
||||
return nil
|
||||
end
|
||||
}, {
|
||||
description = "Leave...",
|
||||
callback = function()
|
||||
return nil
|
||||
end
|
||||
}
|
||||
},
|
||||
on_end = function()
|
||||
return GAME_STATE_RANDOM
|
||||
end
|
||||
})
|
||||
|
||||
register_card("RECYCLE", {
|
||||
name = "Recycle",
|
||||
description = "Deal " ..
|
||||
highlight(12) .. " damage. If " .. highlight("fatal") .. " upgrade random card. " .. highlight("Exhaust") ..
|
||||
".",
|
||||
state = function(ctx)
|
||||
return nil
|
||||
end,
|
||||
max_level = 0,
|
||||
color = "#d8a448",
|
||||
need_target = true,
|
||||
exhaust = true,
|
||||
point_cost = 2,
|
||||
price = -1,
|
||||
callbacks = {
|
||||
on_cast = function(ctx)
|
||||
local op_before = #get_opponent_guids(ctx.caster)
|
||||
deal_damage(ctx.caster, ctx.target, 12)
|
||||
|
||||
if op_before > #get_opponent_guids(ctx.caster) then
|
||||
upgrade_random_card(ctx.caster)
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
}
|
||||
})
|
||||
@ -1,59 +0,0 @@
|
||||
register_event("TALKING_BEING", {
|
||||
name = "Talking Being",
|
||||
description = [[!!alien2.jpg
|
||||
|
||||
Suddenly, a massive vine with a gaping, tooth-filled maw emerges from the shadows. It towers over you, its presence imposing and otherworldly.
|
||||
|
||||
*"Hello, little one,"* the creature speaks in a deep, rumbling voice. *"I have been watching you. I see potential in you. I offer you a gift, something that will aid you on your journey."*
|
||||
|
||||
You take a step back, unsure if you can trust this strange being.
|
||||
|
||||
*"My blood,"* the creature says. *"It is not like any substance you have encountered before. It will grant you extraordinary abilities. But it demands a price. Some of your blood, in exchange for this gift."*
|
||||
|
||||
The creature assures you that there are dangers to wielding such power and that it will change you in ways you cannot yet imagine. But the offer is tempting. Will you accept and risk the unknown, or do you refuse and potentially miss out on a powerful ally?
|
||||
|
||||
**The decision is yours...**]],
|
||||
choices = {
|
||||
{
|
||||
description_fn = function()
|
||||
return "Offer blood... " .. text_italic("(deals " .. highlight(get_player().hp * 0.2) .. " damage)")
|
||||
end,
|
||||
callback = function(ctx)
|
||||
actor_add_hp(PLAYER_ID, -get_player().hp * 0.2)
|
||||
give_card("VINE_VOLLEY", PLAYER_ID)
|
||||
give_card("VINE_VOLLEY", PLAYER_ID)
|
||||
give_card("VINE_VOLLEY", PLAYER_ID)
|
||||
return nil
|
||||
end
|
||||
}, {
|
||||
description = "Leave...",
|
||||
callback = function()
|
||||
return nil
|
||||
end
|
||||
}
|
||||
},
|
||||
on_end = function()
|
||||
return GAME_STATE_RANDOM
|
||||
end
|
||||
})
|
||||
|
||||
register_card("VINE_VOLLEY", {
|
||||
name = "Vine Volley",
|
||||
description = "Deal " .. highlight("3x" .. tostring(3)) .. " damage.",
|
||||
state = function(ctx)
|
||||
return nil
|
||||
end,
|
||||
max_level = 0,
|
||||
color = "#588157",
|
||||
need_target = true,
|
||||
point_cost = 1,
|
||||
price = 100,
|
||||
callbacks = {
|
||||
on_cast = function(ctx)
|
||||
deal_damage(ctx.caster, ctx.target, 3)
|
||||
deal_damage(ctx.caster, ctx.target, 3)
|
||||
deal_damage(ctx.caster, ctx.target, 3)
|
||||
return nil
|
||||
end
|
||||
}
|
||||
})
|
||||
@ -1,20 +0,0 @@
|
||||
register_event("BIO_KINGDOM", {
|
||||
name = "Bio Kingdom",
|
||||
description = [[!!plant_enviroment.jpg
|
||||
|
||||
You finally find a way leading to the outside, and step out of the cryo facility into a world you no longer recognize.
|
||||
|
||||
The air is thick with humidity and the sounds of the jungle are overwhelming. Strange, mutated plants tower over you, their vines twisting and tangling around each other in a macabre dance. The colors of the leaves and flowers are sickly, a greenish hue that reminds you of illness rather than life. The ruins of buildings are visible in the distance, swallowed up by the overgrowth. You can hear the chirping and buzzing of insects, but it's mixed with something else - something that sounds almost like whispers or moans. The "jungle" seems to be alive, but not in any way that you would have imagined.]],
|
||||
choices = {
|
||||
{
|
||||
description = "Go...",
|
||||
callback = function()
|
||||
set_event("MERCHANT")
|
||||
return GAME_STATE_EVENT
|
||||
end
|
||||
}
|
||||
},
|
||||
on_end = function()
|
||||
return GAME_STATE_RANDOM
|
||||
end
|
||||
})
|
||||
@ -1,20 +0,0 @@
|
||||
register_event("THE_CORE", {
|
||||
name = "The Wasteland",
|
||||
description = [[!!underground1.jpg
|
||||
|
||||
You finally find a way you thought would lead to the outside, only to discover that you're still inside the massive facility known as *"The Core."*
|
||||
|
||||
As you step out of the cryo facility, the eerie silence is broken by the sound of metal scraping against metal and distant whirring of malfunctioning machinery. The flickering lights and sparks from faulty wires cast a sickly glow on the cold metal walls. You realize that this place is not as deserted as you initially thought, and the unsettling feeling in your gut only grows stronger as you make your way through the dimly lit corridors, surrounded by the echoes of your own footsteps and the sound of flickering computer screens.]],
|
||||
choices = {
|
||||
{
|
||||
description = "Go...",
|
||||
callback = function()
|
||||
set_event("MERCHANT")
|
||||
return GAME_STATE_EVENT
|
||||
end
|
||||
}
|
||||
},
|
||||
on_end = function()
|
||||
return GAME_STATE_RANDOM
|
||||
end
|
||||
})
|
||||
@ -1,20 +0,0 @@
|
||||
register_event("THE_WASTELAND", {
|
||||
name = "The Wasteland",
|
||||
description = [[!!dark_city1.jpg
|
||||
|
||||
You finally find a way leading to the outside, and with a deep breath, you step out into the unforgiving wasteland.
|
||||
|
||||
The scorching sun beats down on you as the sand whips against your skin, a reminder of the horrors that have befallen the world. In the distance, the remains of once-great cities jut up from the ground like jagged teeth, now nothing more than crumbling ruins. The air is thick with the acrid smell of decay and the oppressive silence is only broken by the occasional howl of some mutated creature. As you take your first steps into this new world, you realize that survival will not be easy, and that the journey ahead will be fraught with danger at every turn...]],
|
||||
choices = {
|
||||
{
|
||||
description = "Go...",
|
||||
callback = function()
|
||||
set_event("MERCHANT")
|
||||
return GAME_STATE_EVENT
|
||||
end
|
||||
}
|
||||
},
|
||||
on_end = function()
|
||||
return GAME_STATE_RANDOM
|
||||
end
|
||||
})
|
||||
@ -1,18 +0,0 @@
|
||||
register_status_effect("BURN", {
|
||||
name = "Burning",
|
||||
description = "The enemy burns and receives damage.",
|
||||
look = "Brn",
|
||||
foreground = "#d00000",
|
||||
state = function(ctx)
|
||||
return "Takes " .. highlight(ctx.stacks * 4) .. " damage per turn"
|
||||
end,
|
||||
can_stack = true,
|
||||
decay = DECAY_ALL,
|
||||
rounds = 1,
|
||||
callbacks = {
|
||||
on_turn = function(ctx)
|
||||
deal_damage(ctx.guid, ctx.owner, ctx.stacks * 2, true)
|
||||
return nil
|
||||
end
|
||||
}
|
||||
})
|
||||
@ -1,17 +0,0 @@
|
||||
register_status_effect("FEAR", {
|
||||
name = "Fear",
|
||||
description = "Can't act.",
|
||||
look = "Fear",
|
||||
foreground = "#bb3e03",
|
||||
state = function(ctx)
|
||||
return "Can't act for " .. highlight(ctx.stacks) .. " turns"
|
||||
end,
|
||||
can_stack = true,
|
||||
decay = DECAY_ONE,
|
||||
rounds = 1,
|
||||
callbacks = {
|
||||
on_turn = function(ctx)
|
||||
return true
|
||||
end
|
||||
}
|
||||
})
|
||||
@ -1,20 +0,0 @@
|
||||
register_status_effect("STRENGTH", {
|
||||
name = "Strength",
|
||||
description = "Increases damage for each stack",
|
||||
look = "Str",
|
||||
foreground = "#d00000",
|
||||
state = function(ctx)
|
||||
return "Deal " .. highlight(ctx.stacks) .. " more damage"
|
||||
end,
|
||||
can_stack = true,
|
||||
decay = DECAY_ALL,
|
||||
rounds = 1,
|
||||
callbacks = {
|
||||
on_damage_calc = function(ctx)
|
||||
if ctx.source == ctx.owner then
|
||||
return ctx.damage + ctx.stacks
|
||||
end
|
||||
return ctx.damage
|
||||
end
|
||||
}
|
||||
})
|
||||
@ -1,20 +0,0 @@
|
||||
register_status_effect("VULNERABLE", {
|
||||
name = "Vulnerable",
|
||||
description = "Increases received damage for each stack",
|
||||
look = "Vur",
|
||||
foreground = "#ffba08",
|
||||
state = function(ctx)
|
||||
return "Takes " .. highlight(ctx.stacks * 25) .. "% more damage"
|
||||
end,
|
||||
can_stack = true,
|
||||
decay = DECAY_ONE,
|
||||
rounds = 1,
|
||||
callbacks = {
|
||||
on_damage_calc = function(ctx)
|
||||
if ctx.target == ctx.owner then
|
||||
return ctx.damage * (1.0 + 0.25 * ctx.stacks)
|
||||
end
|
||||
return ctx.damage
|
||||
end
|
||||
}
|
||||
})
|
||||
@ -1,20 +0,0 @@
|
||||
register_status_effect("WEAKEN", {
|
||||
name = "Weaken",
|
||||
description = "Weakens damage for each stack",
|
||||
look = "W",
|
||||
foreground = "#ed985f",
|
||||
state = function()
|
||||
return "Deals " .. highlight(ctx.stacks * 2) .. " less damage"
|
||||
end,
|
||||
can_stack = true,
|
||||
decay = DECAY_ALL,
|
||||
rounds = 1,
|
||||
callbacks = {
|
||||
on_damage_calc = function(ctx)
|
||||
if ctx.source == ctx.owner then
|
||||
return ctx.damage - ctx.stacks * 2
|
||||
end
|
||||
return ctx.damage
|
||||
end
|
||||
}
|
||||
})
|
||||
26
assets/scripts/story_teller/act_0.lua
Normal file
26
assets/scripts/story_teller/act_0.lua
Normal file
@ -0,0 +1,26 @@
|
||||
register_story_teller("ACT_0", {
|
||||
active = function()
|
||||
if #get_event_history() < 5 then
|
||||
return 1
|
||||
end
|
||||
return 0
|
||||
end,
|
||||
decide = function()
|
||||
local possible = find_events_by_tags({"ACT_0"})
|
||||
local history = get_event_history()
|
||||
|
||||
print("possible")
|
||||
print(possible)
|
||||
print("history")
|
||||
print(history)
|
||||
|
||||
-- filter out events by id that have already been played
|
||||
possible = fun.iter(possible):filter(function(event)
|
||||
return not table.contains(history, event.id)
|
||||
end):totable()
|
||||
|
||||
set_event(possible[math.random(#possible)].id)
|
||||
|
||||
return GAME_STATE_EVENT
|
||||
end
|
||||
})
|
||||
@ -1,38 +0,0 @@
|
||||
stage_1_init_events = { "THE_CORE", "BIO_KINGDOM", "THE_WASTELAND" }
|
||||
|
||||
register_story_teller("STAGE_0", {
|
||||
active = function(ctx)
|
||||
if not had_events_any(stage_1_init_events) then
|
||||
return 1
|
||||
end
|
||||
return 0
|
||||
end,
|
||||
decide = function(ctx)
|
||||
local stage = get_stages_cleared()
|
||||
|
||||
if stage >= 3 then
|
||||
-- If we didn't skip the pre-stage we get another artifact
|
||||
set_event(create_artifact_choice({ random_artifact(get_merchant_gold_max()), random_artifact(get_merchant_gold_max()) }, {
|
||||
description = [[As you explore the abandoned cryo facility, a feeling of dread washes over you. The facility is eerily quiet, with malfunctioning computers and flickering lights being the only signs of life. As you move through the winding corridors, you stumble upon a hidden door. It's almost as if the facility itself is trying to keep you from finding what lies beyond.
|
||||
|
||||
After some effort, you manage to open the door and find yourself in a small room. The room is dark, and you can barely make out a chest in the center of the room. As you approach it, the feeling of unease grows stronger. What secret artifact could be hidden inside this chest? Is it something that will aid you on your journey or something more sinister? You take a deep breath, steeling yourself for whatever you may find inside, and reach for the lid...]],
|
||||
on_end = function()
|
||||
set_event(stage_1_init_events[math.random(#stage_1_init_events)])
|
||||
return GAME_STATE_EVENT
|
||||
end
|
||||
}))
|
||||
|
||||
return GAME_STATE_EVENT
|
||||
end
|
||||
|
||||
-- Fight against rust mites or clean bots
|
||||
local d = math.random(2)
|
||||
if d == 1 then
|
||||
add_actor_by_enemy("RUST_MITE")
|
||||
elseif d == 2 then
|
||||
add_actor_by_enemy("CLEAN_BOT")
|
||||
end
|
||||
|
||||
return GAME_STATE_FIGHT
|
||||
end
|
||||
})
|
||||
@ -1,31 +0,0 @@
|
||||
stage_1_bio_kingdom = {
|
||||
fights = { { "RUST_MITE", "RUST_MITE", "RUST_MITE" }, { "SHADOW_ASSASSIN", "SHADOW_ASSASSIN" }, { "SHADOW_ASSASSIN" } }
|
||||
}
|
||||
|
||||
register_story_teller("STAGE_1_BIO_KINGDOM", {
|
||||
active = function(ctx)
|
||||
if had_event("BIO_KINGDOM") then
|
||||
return 1
|
||||
end
|
||||
return 0
|
||||
end,
|
||||
decide = function(ctx)
|
||||
local stage = get_stages_cleared()
|
||||
|
||||
if stage == 10 then
|
||||
-- BOSS
|
||||
end
|
||||
|
||||
-- 10% chance to find a random artifact
|
||||
if math.random() < 0.1 then
|
||||
set_event(create_artifact_choice({ random_artifact(get_merchant_gold_max()), random_artifact(get_merchant_gold_max()) }))
|
||||
end
|
||||
|
||||
local choice = stage_1_bio_kingdom.fights[math.random(#stage_1_bio_kingdom.fights)]
|
||||
for _, v in ipairs(choice) do
|
||||
add_actor_by_enemy(v)
|
||||
end
|
||||
|
||||
return GAME_STATE_FIGHT
|
||||
end
|
||||
})
|
||||
@ -1,29 +0,0 @@
|
||||
stage_1_the_core = { fights = { { "RUST_MITE", "RUST_MITE", "RUST_MITE" }, { "CLEAN_BOT", "CLEAN_BOT" } } }
|
||||
|
||||
register_story_teller("STAGE_1_THE_CORE", {
|
||||
active = function(ctx)
|
||||
if had_event("THE_CORE") then
|
||||
return 1
|
||||
end
|
||||
return 0
|
||||
end,
|
||||
decide = function(ctx)
|
||||
local stage = get_stages_cleared()
|
||||
|
||||
if stage == 10 then
|
||||
-- BOSS
|
||||
end
|
||||
|
||||
-- 10% chance to find a random artifact
|
||||
if math.random() < 0.1 then
|
||||
set_event(create_artifact_choice({ random_artifact(get_merchant_gold_max()), random_artifact(get_merchant_gold_max()) }))
|
||||
end
|
||||
|
||||
local choice = stage_1_the_core.fights[math.random(#stage_1_the_core.fights)]
|
||||
for _, v in ipairs(choice) do
|
||||
add_actor_by_enemy(v)
|
||||
end
|
||||
|
||||
return GAME_STATE_FIGHT
|
||||
end
|
||||
})
|
||||
@ -1,29 +0,0 @@
|
||||
stage_1_the_wasteland = { fights = { { "SAND_STALKER" }, { "SAND_STALKER", "SAND_STALKER" } } }
|
||||
|
||||
register_story_teller("STAGE_1_THE_WASTELAND", {
|
||||
active = function(ctx)
|
||||
if had_event("THE_WASTELAND") then
|
||||
return 1
|
||||
end
|
||||
return 0
|
||||
end,
|
||||
decide = function(ctx)
|
||||
local stage = get_stages_cleared()
|
||||
|
||||
if stage == 10 then
|
||||
-- BOSS
|
||||
end
|
||||
|
||||
-- 10% chance to find a random artifact
|
||||
if math.random() < 0.1 then
|
||||
set_event(create_artifact_choice({ random_artifact(get_merchant_gold_max()), random_artifact(get_merchant_gold_max()) }))
|
||||
end
|
||||
|
||||
local choice = stage_1_the_wasteland.fights[math.random(#stage_1_the_wasteland.fights)]
|
||||
for _, v in ipairs(choice) do
|
||||
add_actor_by_enemy(v)
|
||||
end
|
||||
|
||||
return GAME_STATE_FIGHT
|
||||
end
|
||||
})
|
||||
@ -1,19 +0,0 @@
|
||||
register_story_teller("STAGE_2", {
|
||||
active = function(ctx)
|
||||
if had_events_any(stage_1_init_events) and get_stages_cleared() > 10 then
|
||||
return 2
|
||||
end
|
||||
return 0
|
||||
end,
|
||||
decide = function(ctx)
|
||||
local stage = get_stages_cleared()
|
||||
|
||||
if stage == 20 then
|
||||
-- BOSS
|
||||
end
|
||||
|
||||
add_actor_by_enemy("DUMMY")
|
||||
|
||||
return GAME_STATE_FIGHT
|
||||
end
|
||||
})
|
||||
@ -1,19 +0,0 @@
|
||||
register_story_teller("STAGE_3", {
|
||||
active = function(ctx)
|
||||
if had_events_any(stage_1_init_events) and get_stages_cleared() > 20 then
|
||||
return 3
|
||||
end
|
||||
return 0
|
||||
end,
|
||||
decide = function(ctx)
|
||||
local stage = get_stages_cleared()
|
||||
|
||||
if stage == 30 then
|
||||
-- BOSS
|
||||
end
|
||||
|
||||
add_actor_by_enemy("DUMMY")
|
||||
|
||||
return GAME_STATE_FIGHT
|
||||
end
|
||||
})
|
||||
@ -153,18 +153,6 @@ text_bold(value : any) -> string
|
||||
|
||||
</details>
|
||||
|
||||
<details> <summary><b><code>text_color</code></b> </summary> <br/>
|
||||
|
||||
Makes the text foreground colored. Takes hex values like #ff0000.
|
||||
|
||||
**Signature:**
|
||||
|
||||
```
|
||||
text_color(color : string, value : any) -> string
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details> <summary><b><code>text_italic</code></b> </summary> <br/>
|
||||
|
||||
Makes the text italic.
|
||||
@ -177,6 +165,18 @@ text_italic(value : any) -> string
|
||||
|
||||
</details>
|
||||
|
||||
<details> <summary><b><code>text_red</code></b> </summary> <br/>
|
||||
|
||||
Makes the text colored red.
|
||||
|
||||
**Signature:**
|
||||
|
||||
```
|
||||
text_red(value : any) -> string
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details> <summary><b><code>text_underline</code></b> </summary> <br/>
|
||||
|
||||
Makes the text underlined.
|
||||
@ -326,7 +326,7 @@ get_fight() -> fight_state
|
||||
|
||||
<details> <summary><b><code>get_fight_round</code></b> </summary> <br/>
|
||||
|
||||
Gets the number of stages cleared.
|
||||
Gets the fight round.
|
||||
|
||||
**Signature:**
|
||||
|
||||
@ -336,6 +336,18 @@ get_fight_round() -> number
|
||||
|
||||
</details>
|
||||
|
||||
<details> <summary><b><code>get_stages_cleared</code></b> </summary> <br/>
|
||||
|
||||
Gets the number of stages cleared.
|
||||
|
||||
**Signature:**
|
||||
|
||||
```
|
||||
get_stages_cleared() -> number
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details> <summary><b><code>had_event</code></b> </summary> <br/>
|
||||
|
||||
Checks if the event happened at least once.
|
||||
@ -441,6 +453,30 @@ actor_add_max_hp(guid : guid, amount : number) -> None
|
||||
|
||||
</details>
|
||||
|
||||
<details> <summary><b><code>actor_set_hp</code></b> </summary> <br/>
|
||||
|
||||
Sets the hp value of a actor to a number. This won't trigger any on_damage callbacks
|
||||
|
||||
**Signature:**
|
||||
|
||||
```
|
||||
actor_set_hp(guid : guid, amount : number) -> None
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details> <summary><b><code>actor_set_max_hp</code></b> </summary> <br/>
|
||||
|
||||
Sets the max hp value of a actor to a number.
|
||||
|
||||
**Signature:**
|
||||
|
||||
```
|
||||
actor_set_max_hp(guid : guid, amount : number) -> None
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<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")``.
|
||||
@ -558,6 +594,18 @@ get_artifact_instance(guid : guid) -> artifact_instance
|
||||
|
||||
</details>
|
||||
|
||||
<details> <summary><b><code>get_artifacts</code></b> </summary> <br/>
|
||||
|
||||
Returns all the artifacts guids from the given actor.
|
||||
|
||||
**Signature:**
|
||||
|
||||
```
|
||||
get_artifacts(actor_guid : string) -> guid[]
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details> <summary><b><code>give_artifact</code></b> </summary> <br/>
|
||||
|
||||
Gives a actor a artifact. Returns the guid of the newly created artifact.
|
||||
@ -791,7 +839,7 @@ None
|
||||
### Functions
|
||||
<details> <summary><b><code>deal_damage</code></b> </summary> <br/>
|
||||
|
||||
Deal damage to a enemy from one source. If flat is true the damage can't be modified by status effects or artifacts. Returns the damage that was dealt.
|
||||
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.
|
||||
|
||||
**Signature:**
|
||||
|
||||
@ -825,6 +873,18 @@ heal(source : guid, target : guid, amount : number) -> None
|
||||
|
||||
</details>
|
||||
|
||||
<details> <summary><b><code>simulate_deal_damage</code></b> </summary> <br/>
|
||||
|
||||
Simulate damage from a source to a target. If flat is true the damage can't be modified by status effects or artifacts. Returns the damage that would be dealt.
|
||||
|
||||
**Signature:**
|
||||
|
||||
```
|
||||
simulate_deal_damage(source : guid, target : guid, damage : number, (optional) flat : boolean) -> number
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Player Operations
|
||||
|
||||
Functions that are related to the player.
|
||||
|
||||
@ -13,6 +13,7 @@ type Artifact struct {
|
||||
ID string
|
||||
Name string
|
||||
Description string
|
||||
Tags []string
|
||||
Order int
|
||||
Price int
|
||||
Callbacks map[string]luhelp.OwnedCallback
|
||||
|
||||
@ -20,6 +20,7 @@ type Card struct {
|
||||
PointCost int
|
||||
MaxLevel int
|
||||
DoesExhaust bool
|
||||
DoesConsume bool
|
||||
NeedTarget bool
|
||||
Price int
|
||||
Callbacks map[string]luhelp.OwnedCallback
|
||||
|
||||
@ -21,6 +21,10 @@ const (
|
||||
StateEventDamage = StateEvent("Damage")
|
||||
StateEventHeal = StateEvent("Heal")
|
||||
StateEventMoney = StateEvent("Money")
|
||||
StateEventArtifactAdded = StateEvent("ArtifactAdded")
|
||||
StateEventArtifactRemoved = StateEvent("ArtifactRemoved")
|
||||
StateEventCardAdded = StateEvent("CardAdded")
|
||||
StateEventCardRemoved = StateEvent("CardRemoved")
|
||||
)
|
||||
|
||||
type StateEventDeathData struct {
|
||||
@ -45,6 +49,30 @@ type StateEventMoneyData struct {
|
||||
Money int
|
||||
}
|
||||
|
||||
type StateEventArtifactAddedData struct {
|
||||
Owner string
|
||||
GUID string
|
||||
TypeID string
|
||||
}
|
||||
|
||||
type StateEventArtifactRemovedData struct {
|
||||
Owner string
|
||||
GUID string
|
||||
TypeID string
|
||||
}
|
||||
|
||||
type StateEventCardAddedData struct {
|
||||
Owner string
|
||||
GUID string
|
||||
TypeID string
|
||||
}
|
||||
|
||||
type StateEventCardRemovedData struct {
|
||||
Owner string
|
||||
GUID string
|
||||
TypeID string
|
||||
}
|
||||
|
||||
// StateCheckpoint saves the state of a session at a certain point. This can be used
|
||||
// to retroactively check what happened between certain actions.
|
||||
type StateCheckpoint struct {
|
||||
|
||||
@ -16,6 +16,7 @@ type Event struct {
|
||||
ID string
|
||||
Name string
|
||||
Description string
|
||||
Tags []string
|
||||
Choices []EventChoice
|
||||
OnEnter luhelp.OwnedCallback
|
||||
OnEnd luhelp.OwnedCallback
|
||||
|
||||
44
game/lua.go
44
game/lua.go
@ -127,9 +127,9 @@ fun = require "fun"
|
||||
return 1
|
||||
}))
|
||||
|
||||
d.Function("text_color", "Makes the text foreground colored. Takes hex values like #ff0000.", "string", "color : string", "value : any")
|
||||
l.SetGlobal("text_color", l.NewFunction(func(state *lua.LState) int {
|
||||
state.Push(lua.LString(removeAnsiReset(lipgloss.NewStyle().Foreground(lipgloss.Color(luhelp2.ToString(state.Get(1), mapper))).Render(luhelp2.ToString(state.Get(2), mapper)))))
|
||||
d.Function("text_red", "Makes the text colored red.", "string", "value : any")
|
||||
l.SetGlobal("text_red", l.NewFunction(func(state *lua.LState) int {
|
||||
state.Push(lua.LString("\x1b[38;5;9m" + luhelp2.ToString(state.Get(1), mapper)))
|
||||
return 1
|
||||
}))
|
||||
|
||||
@ -239,7 +239,7 @@ fun = require "fun"
|
||||
return 1
|
||||
}))
|
||||
|
||||
d.Function("get_fight_round", "Gets the number of stages cleared.", "number")
|
||||
d.Function("get_stages_cleared", "Gets the number of stages cleared.", "number")
|
||||
l.SetGlobal("get_stages_cleared", l.NewFunction(func(state *lua.LState) int {
|
||||
state.Push(lua.LNumber(session.GetStagesCleared()))
|
||||
return 1
|
||||
@ -333,12 +333,30 @@ fun = require "fun"
|
||||
return 0
|
||||
}))
|
||||
|
||||
d.Function("actor_set_max_hp", "Sets the max hp value of a actor to a number.", "", "guid : guid", "amount : number")
|
||||
l.SetGlobal("actor_set_max_hp", l.NewFunction(func(state *lua.LState) int {
|
||||
session.UpdateActor(state.ToString(1), func(actor *Actor) bool {
|
||||
actor.MaxHP = int(state.ToNumber(2))
|
||||
return true
|
||||
})
|
||||
return 0
|
||||
}))
|
||||
|
||||
d.Function("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", "", "guid : guid", "amount : number")
|
||||
l.SetGlobal("actor_add_hp", l.NewFunction(func(state *lua.LState) int {
|
||||
session.ActorAddHP(state.ToString(1), int(state.ToNumber(2)))
|
||||
return 0
|
||||
}))
|
||||
|
||||
d.Function("actor_set_hp", "Sets the hp value of a actor to a number. This won't trigger any on_damage callbacks", "", "guid : guid", "amount : number")
|
||||
l.SetGlobal("actor_set_hp", l.NewFunction(func(state *lua.LState) int {
|
||||
session.UpdateActor(state.ToString(1), func(actor *Actor) bool {
|
||||
actor.HP = int(state.ToNumber(2))
|
||||
return true
|
||||
})
|
||||
return 0
|
||||
}))
|
||||
|
||||
d.Function("add_actor_by_enemy", "Creates a new enemy fighting against the player. Example ``add_actor_by_enemy(\"RUST_MITE\")``.", "string", "enemy_guid : type_id")
|
||||
l.SetGlobal("add_actor_by_enemy", l.NewFunction(func(state *lua.LState) int {
|
||||
state.Push(lua.LString(session.AddActorFromEnemy(state.ToString(1))))
|
||||
@ -361,6 +379,12 @@ fun = require "fun"
|
||||
return 0
|
||||
}))
|
||||
|
||||
d.Function("get_artifacts", "Returns all the artifacts guids from the given actor.", "guid[]", "actor_guid : string")
|
||||
l.SetGlobal("get_artifacts", l.NewFunction(func(state *lua.LState) int {
|
||||
state.Push(luhelp2.ToLua(state, session.GetArtifacts(state.ToString(1))))
|
||||
return 1
|
||||
}))
|
||||
|
||||
d.Function("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.", "artifact", "id : string")
|
||||
l.SetGlobal("get_artifact", l.NewFunction(func(state *lua.LState) int {
|
||||
art, _ := session.GetArtifact(state.ToString(1))
|
||||
@ -483,7 +507,7 @@ fun = require "fun"
|
||||
|
||||
d.Category("Damage & Heal", "Functions that deal damage or heal.", 10)
|
||||
|
||||
d.Function("deal_damage", "Deal damage to a enemy from one source. If flat is true the damage can't be modified by status effects or artifacts. Returns the damage that was dealt.", "number", "source : guid", "target : guid", "damage : number", "(optional) flat : boolean")
|
||||
d.Function("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.", "number", "source : guid", "target : guid", "damage : number", "(optional) flat : boolean")
|
||||
l.SetGlobal("deal_damage", l.NewFunction(func(state *lua.LState) int {
|
||||
if state.GetTop() == 3 {
|
||||
state.Push(lua.LNumber(session.DealDamage(state.ToString(1), state.ToString(2), int(state.ToNumber(3)), false)))
|
||||
@ -493,6 +517,16 @@ fun = require "fun"
|
||||
return 1
|
||||
}))
|
||||
|
||||
d.Function("simulate_deal_damage", "Simulate damage from a source to a target. If flat is true the damage can't be modified by status effects or artifacts. Returns the damage that would be dealt.", "number", "source : guid", "target : guid", "damage : number", "(optional) flat : boolean")
|
||||
l.SetGlobal("simulate_deal_damage", l.NewFunction(func(state *lua.LState) int {
|
||||
if state.GetTop() == 3 {
|
||||
state.Push(lua.LNumber(session.SimulateDealDamage(state.ToString(1), state.ToString(2), int(state.ToNumber(3)), false)))
|
||||
} else {
|
||||
state.Push(lua.LNumber(session.SimulateDealDamage(state.ToString(1), state.ToString(2), int(state.ToNumber(3)), bool(state.ToBool(4)))))
|
||||
}
|
||||
return 1
|
||||
}))
|
||||
|
||||
d.Function("deal_damage_multi", "Deal damage to multiple enemies from one source. If flat is true the damage can't be modified by status effects or artifacts. Returns a array of damages for each actor hit.", "number[]", "source : guid", "targets : guid[]", "damage : number", "(optional) flat : boolean")
|
||||
l.SetGlobal("deal_damage_multi", l.NewFunction(func(state *lua.LState) int {
|
||||
var guids []string
|
||||
|
||||
@ -67,8 +67,8 @@ func NewResourcesManager(state *lua.LState, docs *ludoc.Docs, logger *log.Logger
|
||||
|
||||
// Load all local scripts
|
||||
_ = fs.Walk("./assets/scripts", func(path string, isDir bool) error {
|
||||
// Don't load libs
|
||||
if strings.Contains(path, "scripts/libs") || strings.Contains(path, "scripts/definitions") {
|
||||
// Don't load libs, definitions and paths containing two __
|
||||
if strings.Contains(path, "scripts/libs") || strings.Contains(path, "scripts/definitions") || strings.Contains(path, "__") {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -132,7 +132,10 @@ func (man *ResourcesManager) luaRegisterArtifact(l *lua.LState) int {
|
||||
man.log.Println("Registered artifact:", def.ID, def.Name)
|
||||
|
||||
man.Artifacts[def.ID] = &def
|
||||
man.registered.RawGetString("artifact").(*lua.LTable).RawSetString(def.ID, l.ToTable(2))
|
||||
|
||||
table := l.ToTable(2)
|
||||
l.SetTable(table, lua.LString("id"), lua.LString(def.ID))
|
||||
man.registered.RawGetString("artifact").(*lua.LTable).RawSetString(def.ID, table)
|
||||
return 0
|
||||
}
|
||||
|
||||
@ -151,7 +154,10 @@ func (man *ResourcesManager) luaRegisterCard(l *lua.LState) int {
|
||||
man.log.Println("Registered card:", def.ID, def.Name)
|
||||
|
||||
man.Cards[def.ID] = &def
|
||||
man.registered.RawGetString("card").(*lua.LTable).RawSetString(def.ID, l.ToTable(2))
|
||||
|
||||
table := l.ToTable(2)
|
||||
l.SetTable(table, lua.LString("id"), lua.LString(def.ID))
|
||||
man.registered.RawGetString("card").(*lua.LTable).RawSetString(def.ID, table)
|
||||
return 0
|
||||
}
|
||||
|
||||
@ -170,7 +176,10 @@ func (man *ResourcesManager) luaRegisterEnemy(l *lua.LState) int {
|
||||
man.log.Println("Registered enemy:", def.ID, def.Name)
|
||||
|
||||
man.Enemies[def.ID] = &def
|
||||
man.registered.RawGetString("enemy").(*lua.LTable).RawSetString(def.ID, l.ToTable(2))
|
||||
|
||||
table := l.ToTable(2)
|
||||
l.SetTable(table, lua.LString("id"), lua.LString(def.ID))
|
||||
man.registered.RawGetString("enemy").(*lua.LTable).RawSetString(def.ID, table)
|
||||
return 0
|
||||
}
|
||||
|
||||
@ -187,7 +196,10 @@ func (man *ResourcesManager) luaRegisterEvent(l *lua.LState) int {
|
||||
man.log.Println("Registered event:", def.ID, def.Name)
|
||||
|
||||
man.Events[def.ID] = &def
|
||||
man.registered.RawGetString("event").(*lua.LTable).RawSetString(def.ID, l.ToTable(2))
|
||||
|
||||
table := l.ToTable(2)
|
||||
l.SetTable(table, lua.LString("id"), lua.LString(def.ID))
|
||||
man.registered.RawGetString("event").(*lua.LTable).RawSetString(def.ID, table)
|
||||
return 0
|
||||
}
|
||||
|
||||
@ -206,7 +218,10 @@ func (man *ResourcesManager) luaRegisterStatusEffect(l *lua.LState) int {
|
||||
man.log.Println("Registered status_effect:", def.ID, def.Name)
|
||||
|
||||
man.StatusEffects[def.ID] = &def
|
||||
man.registered.RawGetString("status_effect").(*lua.LTable).RawSetString(def.ID, l.ToTable(2))
|
||||
|
||||
table := l.ToTable(2)
|
||||
l.SetTable(table, lua.LString("id"), lua.LString(def.ID))
|
||||
man.registered.RawGetString("status_effect").(*lua.LTable).RawSetString(def.ID, table)
|
||||
return 0
|
||||
}
|
||||
|
||||
@ -223,7 +238,10 @@ func (man *ResourcesManager) luaRegisterStoryTeller(l *lua.LState) int {
|
||||
man.log.Println("Registered story_teller:", def.ID)
|
||||
|
||||
man.StoryTeller[def.ID] = &def
|
||||
man.registered.RawGetString("story_teller").(*lua.LTable).RawSetString(def.ID, l.ToTable(2))
|
||||
|
||||
table := l.ToTable(2)
|
||||
l.SetTable(table, lua.LString("id"), lua.LString(def.ID))
|
||||
man.registered.RawGetString("story_teller").(*lua.LTable).RawSetString(def.ID, table)
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
156
game/session.go
156
game/session.go
@ -61,6 +61,12 @@ const (
|
||||
DrawSize = 3
|
||||
)
|
||||
|
||||
type Hook string
|
||||
|
||||
const (
|
||||
HookNextFightEnd = Hook("NextFightEnd")
|
||||
)
|
||||
|
||||
// FightState represents the current state of the fight in regard to the
|
||||
// deck of the player.
|
||||
type FightState struct {
|
||||
@ -107,6 +113,7 @@ type Session struct {
|
||||
eventHistory []string
|
||||
randomHistory []string
|
||||
ctxData map[string]any
|
||||
hooks map[Hook][]func()
|
||||
|
||||
loadedMods []string
|
||||
stateCheckpoints []StateCheckpoint
|
||||
@ -127,9 +134,14 @@ func NewSession(options ...func(s *Session)) *Session {
|
||||
},
|
||||
instances: map[string]any{},
|
||||
ctxData: map[string]any{},
|
||||
hooks: map[Hook][]func(){
|
||||
HookNextFightEnd: {},
|
||||
},
|
||||
stagesCleared: 0,
|
||||
onLuaError: nil,
|
||||
luaErrors: make(chan LuaError, 25),
|
||||
eventHistory: []string{},
|
||||
randomHistory: []string{},
|
||||
}
|
||||
session.SetOnLuaError(nil)
|
||||
|
||||
@ -145,7 +157,6 @@ func NewSession(options ...func(s *Session)) *Session {
|
||||
session.resources = NewResourcesManager(session.luaState, session.luaDocs, session.log)
|
||||
session.resources.MarkBaseGame()
|
||||
session.loadMods(session.loadedMods)
|
||||
session.SetEvent("START")
|
||||
|
||||
session.log.Println("Session started!")
|
||||
|
||||
@ -156,6 +167,8 @@ func NewSession(options ...func(s *Session)) *Session {
|
||||
return true
|
||||
})
|
||||
|
||||
session.SetEvent("START")
|
||||
|
||||
return session
|
||||
}
|
||||
|
||||
@ -331,12 +344,16 @@ func (s *Session) loadMods(mods []string) {
|
||||
}
|
||||
|
||||
_ = fs.Walk(filepath.Join("./mods", mods[i]), func(path string, isDir bool) error {
|
||||
if strings.Contains(path, "__") {
|
||||
return nil
|
||||
}
|
||||
|
||||
// If we find a locals folder we add it to the localization
|
||||
if isDir && filepath.Base(path) == "locals" {
|
||||
_ = localization.Global.AddFolder(path)
|
||||
}
|
||||
|
||||
if isDir && strings.HasSuffix(path, ".lua") {
|
||||
if !isDir && strings.HasSuffix(path, ".lua") {
|
||||
luaBytes, err := fs.ReadFile(path)
|
||||
if err != nil {
|
||||
// TODO: error handling
|
||||
@ -505,10 +522,10 @@ func (s *Session) FinishPlayerTurn() {
|
||||
for _, guid := range instanceKeys {
|
||||
switch instance := s.instances[guid].(type) {
|
||||
case StatusEffectInstance:
|
||||
// If it was applied this round we never remove it.
|
||||
if instance.RoundEntered == s.currentFight.Round {
|
||||
continue
|
||||
}
|
||||
// TODO: investigate why this was here
|
||||
// if instance.Owner == PlayerActorID && instance.RoundEntered == s.currentFight.Round {
|
||||
// continue
|
||||
// }
|
||||
|
||||
se := s.resources.StatusEffects[instance.TypeID]
|
||||
|
||||
@ -625,6 +642,9 @@ func (s *Session) FinishFight() bool {
|
||||
} else {
|
||||
s.SetGameState(GameStateRandom)
|
||||
}
|
||||
|
||||
// Trigger HookNextFightEnd
|
||||
s.TriggerHooks(HookNextFightEnd)
|
||||
}
|
||||
return false
|
||||
}
|
||||
@ -649,22 +669,28 @@ func (s *Session) FinishEvent(choice int) {
|
||||
if nextState != nil {
|
||||
if len(nextState.(string)) > 0 {
|
||||
s.SetGameState(GameState(nextState.(string)))
|
||||
} else {
|
||||
s.SetGameState(GameStateRandom)
|
||||
}
|
||||
_, _ = event.OnEnd(CreateContext("type_id", event.ID, "choice", choice+1))
|
||||
_, _ = event.OnEnd.Call(CreateContext("type_id", event.ID, "choice", choice+1))
|
||||
return
|
||||
}
|
||||
|
||||
// Otherwise we allow OnEnd to dictate the new state
|
||||
nextState, _ = event.OnEnd(CreateContext("type_id", event.ID, "choice", choice+1))
|
||||
nextState, _ = event.OnEnd.Call(CreateContext("type_id", event.ID, "choice", choice+1))
|
||||
if nextState != nil && len(nextState.(string)) > 0 {
|
||||
s.SetGameState(GameState(nextState.(string)))
|
||||
} else {
|
||||
s.SetGameState(GameStateRandom)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
nextState, _ := event.OnEnd(CreateContext("type_id", event.ID, "choice", nil))
|
||||
nextState, _ := event.OnEnd.Call(CreateContext("type_id", event.ID, "choice", nil))
|
||||
if nextState != nil && len(nextState.(string)) > 0 {
|
||||
s.SetGameState(GameState(nextState.(string)))
|
||||
} else {
|
||||
s.SetGameState(GameStateRandom)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1282,6 +1308,14 @@ func (s *Session) GiveArtifact(typeId string, owner string) string {
|
||||
s.logLuaError(CallbackOnPickUp, instance.TypeID, err)
|
||||
}
|
||||
|
||||
s.PushState(map[StateEvent]any{
|
||||
StateEventArtifactAdded: StateEventArtifactAddedData{
|
||||
Owner: owner,
|
||||
TypeID: typeId,
|
||||
GUID: instance.GUID,
|
||||
},
|
||||
})
|
||||
|
||||
return instance.GUID
|
||||
}
|
||||
|
||||
@ -1293,6 +1327,14 @@ func (s *Session) RemoveArtifact(guid string) {
|
||||
}
|
||||
s.actors[instance.Owner].Artifacts.Remove(instance.GUID)
|
||||
delete(s.instances, guid)
|
||||
|
||||
s.PushState(map[StateEvent]any{
|
||||
StateEventArtifactRemoved: StateEventArtifactRemovedData{
|
||||
Owner: instance.Owner,
|
||||
TypeID: instance.TypeID,
|
||||
GUID: instance.GUID,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
//
|
||||
@ -1333,6 +1375,15 @@ func (s *Session) GiveCard(typeId string, owner string) string {
|
||||
}
|
||||
s.instances[instance.GUID] = instance
|
||||
s.actors[owner].Cards.Add(instance.GUID)
|
||||
|
||||
s.PushState(map[StateEvent]any{
|
||||
StateEventCardAdded: StateEventCardAddedData{
|
||||
Owner: owner,
|
||||
TypeID: typeId,
|
||||
GUID: instance.GUID,
|
||||
},
|
||||
})
|
||||
|
||||
return instance.GUID
|
||||
}
|
||||
|
||||
@ -1341,6 +1392,14 @@ func (s *Session) RemoveCard(guid string) {
|
||||
instance := s.instances[guid].(CardInstance)
|
||||
s.actors[instance.Owner].Cards.Remove(instance.GUID)
|
||||
delete(s.instances, guid)
|
||||
|
||||
s.PushState(map[StateEvent]any{
|
||||
StateEventCardRemoved: StateEventCardRemovedData{
|
||||
Owner: instance.Owner,
|
||||
TypeID: instance.TypeID,
|
||||
GUID: instance.GUID,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// CastCard calls the OnCast callback for a card, casting it.
|
||||
@ -1351,13 +1410,15 @@ func (s *Session) CastCard(guid string, target string) bool {
|
||||
s.logLuaError(CallbackOnCast, instance.TypeID, err)
|
||||
}
|
||||
if val, ok := res.(bool); ok {
|
||||
if val {
|
||||
TriggerCallbackSimple(s, CallbackOnActorDidCast, TriggerAll, EmptyContext, CreateContext("type_id", card.ID, "guid", guid, "caster", instance.Owner, "target", target, "level", instance.Level, "tags", card.Tags))
|
||||
}
|
||||
return val
|
||||
}
|
||||
}
|
||||
if !val {
|
||||
return false
|
||||
}
|
||||
|
||||
TriggerCallbackSimple(s, CallbackOnActorDidCast, TriggerAll, EmptyContext, CreateContext("type_id", card.ID, "guid", guid, "caster", instance.Owner, "target", target, "level", instance.Level, "tags", card.Tags))
|
||||
return true
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// GetCards returns all cards owned by a actor.
|
||||
@ -1398,7 +1459,8 @@ func (s *Session) PlayerCastHand(i int, target string) error {
|
||||
cardId := s.currentFight.Hand[i]
|
||||
|
||||
// Only cast a card if castable and points are available and subtract them.
|
||||
if card, _ := s.GetCard(cardId); card != nil {
|
||||
card, _ := s.GetCard(cardId)
|
||||
if card != nil {
|
||||
if !card.Callbacks[CallbackOnCast].Present() {
|
||||
return errors.New("card is not castable")
|
||||
}
|
||||
@ -1418,12 +1480,16 @@ func (s *Session) PlayerCastHand(i int, target string) error {
|
||||
})
|
||||
|
||||
// Cast and exhaust if needed.
|
||||
exhaust := s.CastCard(cardId, target)
|
||||
if exhaust {
|
||||
didCast := s.CastCard(cardId, target)
|
||||
if didCast {
|
||||
if card.DoesExhaust {
|
||||
s.currentFight.Exhausted = append(s.currentFight.Exhausted, cardId)
|
||||
} else if card.DoesConsume {
|
||||
s.RemoveCard(cardId)
|
||||
} else {
|
||||
s.currentFight.Used = append(s.currentFight.Used, cardId)
|
||||
}
|
||||
}
|
||||
|
||||
s.FinishFight()
|
||||
|
||||
@ -1610,6 +1676,41 @@ func (s *Session) DealDamage(source string, target string, damage int, flat bool
|
||||
return damage
|
||||
}
|
||||
|
||||
// SimulateDealDamage will simulate damage to a target. If flat is true it will not trigger any callbacks which modify the damage.
|
||||
func (s *Session) SimulateDealDamage(source string, target string, damage int, flat bool) int {
|
||||
if _, ok := s.actors[source]; !ok {
|
||||
return 0
|
||||
}
|
||||
|
||||
_, ok := s.actors[target]
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
|
||||
// If not flat we will modify the damage based on the OnDamageCalc callbacks.
|
||||
if !flat {
|
||||
reducer := func(cur float64, val float64) float64 {
|
||||
return val
|
||||
}
|
||||
damage = int(TriggerCallbackReduce[float64](
|
||||
s,
|
||||
CallbackOnDamageCalc,
|
||||
TriggerAll,
|
||||
reducer,
|
||||
float64(damage),
|
||||
"damage",
|
||||
CreateContext("source", source, "target", target, "damage", damage, "simulated", true)),
|
||||
)
|
||||
}
|
||||
|
||||
// Negative damage aka heal is not allowed!
|
||||
if damage < 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
return damage
|
||||
}
|
||||
|
||||
// DealDamageMulti will deal damage to multiple targets and return the amount of damage dealt to each target.
|
||||
// If flat is true it will not trigger any OnDamageCalc callbacks which modify the damage.
|
||||
func (s *Session) DealDamageMulti(source string, targets []string, damage int, flat bool) []int {
|
||||
@ -1699,7 +1800,7 @@ func (s *Session) GetActor(id string) Actor {
|
||||
return NewActor("")
|
||||
}
|
||||
|
||||
// UpdateActor updates an actor.
|
||||
// UpdateActor updates an actor. If the update function returns true the actor will be updated.
|
||||
func (s *Session) UpdateActor(id string, update func(actor *Actor) bool) {
|
||||
actor := s.GetActor(id)
|
||||
if update(&actor) {
|
||||
@ -1893,6 +1994,23 @@ func (s *Session) GivePlayerGold(amount int) {
|
||||
})
|
||||
}
|
||||
|
||||
//
|
||||
// Hooks
|
||||
//
|
||||
|
||||
// AddHook adds a hook to the session.
|
||||
func (s *Session) AddHook(hook Hook, callback func()) {
|
||||
s.hooks[hook] = append(s.hooks[hook], callback)
|
||||
}
|
||||
|
||||
// TriggerHooks triggers all hooks of a certain type.
|
||||
func (s *Session) TriggerHooks(hook Hook) {
|
||||
for _, callback := range s.hooks[hook] {
|
||||
callback()
|
||||
}
|
||||
s.hooks[hook] = []func(){}
|
||||
}
|
||||
|
||||
//
|
||||
// Misc Functions
|
||||
//
|
||||
|
||||
@ -3,27 +3,35 @@ package components
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/BigJk/end_of_eden/game"
|
||||
"github.com/BigJk/end_of_eden/ui"
|
||||
"github.com/BigJk/end_of_eden/ui/style"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
artifactStyle = lipgloss.NewStyle().Padding(1, 2).Margin(0, 2)
|
||||
)
|
||||
|
||||
func ArtifactCard(session *game.Session, guid string, baseHeight int, maxHeight int) string {
|
||||
func ArtifactCard(session *game.Session, guid string, baseHeight int, maxHeight int, optionalWidth ...int) string {
|
||||
art, _ := session.GetArtifact(guid)
|
||||
width := 30
|
||||
if len(optionalWidth) > 0 {
|
||||
width = optionalWidth[0]
|
||||
}
|
||||
|
||||
artifactStyle := artifactStyle.Copy().
|
||||
Width(30).
|
||||
Width(width).
|
||||
Border(lipgloss.ThickBorder(), true, false, false, false).
|
||||
BorderBackground(lipgloss.Color("#495057")).
|
||||
BorderForeground(lipgloss.Color("#495057")).
|
||||
Background(lipgloss.Color("#343a40")).
|
||||
Foreground(style.BaseWhite)
|
||||
|
||||
tagsText := strings.Join(art.Tags, ", ")
|
||||
|
||||
return artifactStyle.
|
||||
Height(baseHeight).
|
||||
Render(fmt.Sprintf("%s\n\n%s\n\n%s", style.BoldStyle.Render(art.Name), art.Description, lipgloss.NewStyle().Bold(true).Foreground(style.BaseYellow).Render(fmt.Sprintf("%d$", art.Price))))
|
||||
Render(fmt.Sprintf("%s\n\n%s\n\n%s", style.BoldStyle.Render(art.Name, strings.Repeat(" ", ui.Max(width-6-lipgloss.Width(art.Name)-lipgloss.Width(tagsText), 0)), tagsText), art.Description, lipgloss.NewStyle().Bold(true).Foreground(style.BaseYellow).Render(fmt.Sprintf("%d$", art.Price))))
|
||||
|
||||
}
|
||||
|
||||
@ -13,36 +13,44 @@ import (
|
||||
|
||||
var (
|
||||
cardStyle = lipgloss.NewStyle().Padding(1, 2).Margin(0, 2)
|
||||
headerStlye = lipgloss.NewStyle().Bold(true)
|
||||
cantCastStyle = lipgloss.NewStyle().Foreground(style.BaseRed)
|
||||
)
|
||||
|
||||
func HalfCard(session *game.Session, guid string, active bool, baseHeight int, maxHeight int, minimal bool) string {
|
||||
func HalfCard(session *game.Session, guid string, active bool, baseHeight int, maxHeight int, minimal bool, optionalWidth ...int) string {
|
||||
fight := session.GetFight()
|
||||
card, _ := session.GetCard(guid)
|
||||
canCast := fight.CurrentPoints >= card.PointCost
|
||||
cardState := session.GetCardState(guid)
|
||||
|
||||
pointText := strings.Repeat("•", card.PointCost)
|
||||
if !canCast {
|
||||
pointText = cantCastStyle.Render(pointText)
|
||||
}
|
||||
tagsText := strings.Join(card.Tags, ", ")
|
||||
|
||||
cardCol, _ := colorful.Hex(card.Color)
|
||||
bgCol, _ := colorful.MakeColor(style.BaseGrayDarker)
|
||||
|
||||
width := 30
|
||||
if len(optionalWidth) > 0 {
|
||||
width = optionalWidth[0]
|
||||
}
|
||||
|
||||
cardStyle := cardStyle.Copy().
|
||||
Width(lo.Ternary(minimal && !active, 10, 30)).
|
||||
Width(lo.Ternary(minimal && !active, 10, width)).
|
||||
Border(lipgloss.NormalBorder(), true, false, false, false).
|
||||
BorderBackground(lipgloss.Color(card.Color)).
|
||||
BorderForeground(lo.Ternary(active, style.BaseGray, lipgloss.Color(card.Color))).
|
||||
Background(lipgloss.Color(cardCol.BlendRgb(bgCol, 0.6).Hex())).
|
||||
Foreground(style.BaseWhite)
|
||||
|
||||
header := headerStlye.Render(fmt.Sprintf("%s%s%s", pointText, strings.Repeat(" ", ui.Max(width-4-lipgloss.Width(pointText)-lipgloss.Width(tagsText), 0)), tagsText))
|
||||
if !canCast {
|
||||
header = cantCastStyle.Render(header)
|
||||
}
|
||||
|
||||
if active {
|
||||
return cardStyle.
|
||||
Height(ui.Min(maxHeight-1, baseHeight+5)).
|
||||
Render(fmt.Sprintf("%s%s%s\n\n%s\n\n%s", pointText, strings.Repeat(" ", 30-2-len(pointText)-len(tagsText)), tagsText, style.BoldStyle.Render(card.Name), cardState))
|
||||
Render(fmt.Sprintf("%s\n\n%s\n\n%s", header, style.BoldStyle.Render(card.Name), cardState))
|
||||
}
|
||||
|
||||
if minimal {
|
||||
@ -53,6 +61,6 @@ func HalfCard(session *game.Session, guid string, active bool, baseHeight int, m
|
||||
|
||||
return cardStyle.
|
||||
Height(baseHeight).
|
||||
Render(fmt.Sprintf("%s%s%s\n\n%s\n\n%s", pointText, strings.Repeat(" ", 30-2-len(pointText)-len(tagsText)), tagsText, style.BoldStyle.Render(card.Name), cardState))
|
||||
Render(fmt.Sprintf("%s\n\n%s\n\n%s", header, style.BoldStyle.Render(card.Name), cardState))
|
||||
|
||||
}
|
||||
|
||||
104
ui/menus/carousel/carousel.go
Normal file
104
ui/menus/carousel/carousel.go
Normal file
@ -0,0 +1,104 @@
|
||||
package carousel
|
||||
|
||||
import (
|
||||
"github.com/BigJk/end_of_eden/ui"
|
||||
"github.com/BigJk/end_of_eden/ui/style"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
zone "github.com/lrstanley/bubblezone"
|
||||
"github.com/samber/lo"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
ZoneLeftButton = "left_button"
|
||||
ZoneRightButton = "right_button"
|
||||
ZoneDoneButton = "done_button"
|
||||
)
|
||||
|
||||
type Model struct {
|
||||
ui.MenuBase
|
||||
|
||||
zones *zone.Manager
|
||||
parent tea.Model
|
||||
lastMouse tea.MouseMsg
|
||||
title string
|
||||
items []string
|
||||
selected int
|
||||
}
|
||||
|
||||
func New(parent tea.Model, zones *zone.Manager, title string, items []string) Model {
|
||||
return Model{
|
||||
zones: zones,
|
||||
parent: parent,
|
||||
title: title,
|
||||
items: items,
|
||||
}
|
||||
}
|
||||
|
||||
func (m Model) Init() tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case tea.WindowSizeMsg:
|
||||
m.Size = msg
|
||||
case tea.KeyMsg:
|
||||
if msg.Type == tea.KeyEscape {
|
||||
return m.parent, nil
|
||||
} else if msg.Type == tea.KeyEnter {
|
||||
return m.parent, nil
|
||||
} else if msg.Type == tea.KeyLeft {
|
||||
if m.selected > 0 {
|
||||
m.selected--
|
||||
}
|
||||
} else if msg.Type == tea.KeyRight {
|
||||
if m.selected < len(m.items)-1 {
|
||||
m.selected++
|
||||
}
|
||||
}
|
||||
case tea.MouseMsg:
|
||||
m.LastMouse = msg
|
||||
if msg.Type == tea.MouseLeft {
|
||||
if m.zones.Get(ZoneLeftButton).InBounds(msg) {
|
||||
if m.selected > 0 {
|
||||
m.selected--
|
||||
}
|
||||
}
|
||||
if m.zones.Get(ZoneLeftButton).InBounds(msg) {
|
||||
if m.selected < len(m.items)-1 {
|
||||
m.selected++
|
||||
}
|
||||
}
|
||||
if m.zones.Get(ZoneDoneButton).InBounds(msg) {
|
||||
return m.parent, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (m Model) View() string {
|
||||
title := style.BoldStyle.Copy().MarginBottom(4).Render(m.title)
|
||||
|
||||
leftButton := m.zones.Mark(ZoneLeftButton, style.HeaderStyle.Copy().Background(lo.Ternary(m.zones.Get(ZoneLeftButton).InBounds(m.LastMouse), style.BaseRed, style.BaseRedDarker)).Margin(0, 2).Render("<--"))
|
||||
rightButton := m.zones.Mark(ZoneRightButton, style.HeaderStyle.Copy().Background(lo.Ternary(m.zones.Get(ZoneRightButton).InBounds(m.LastMouse), style.BaseRed, style.BaseRedDarker)).Margin(0, 2).Render("-->"))
|
||||
middle := lipgloss.JoinHorizontal(lipgloss.Center,
|
||||
leftButton,
|
||||
m.items[m.selected],
|
||||
rightButton,
|
||||
)
|
||||
|
||||
dots := lipgloss.NewStyle().Margin(2, 0).Render(strings.Join(lo.Map(m.items, func(item string, index int) string {
|
||||
if index == m.selected {
|
||||
return "●"
|
||||
}
|
||||
return "○"
|
||||
}), " "))
|
||||
|
||||
doneButton := m.zones.Mark(ZoneDoneButton, style.HeaderStyle.Copy().Background(lo.Ternary(m.zones.Get(ZoneDoneButton).InBounds(m.LastMouse), style.BaseRed, style.BaseRedDarker)).MarginTop(2).Render("Continue"))
|
||||
|
||||
return lipgloss.Place(m.Size.Width, m.Size.Height, lipgloss.Center, lipgloss.Center, lipgloss.JoinVertical(lipgloss.Center, title, middle, dots, doneButton))
|
||||
}
|
||||
@ -173,7 +173,7 @@ func (m Model) eventUpdateContent() Model {
|
||||
|
||||
var chunks []string
|
||||
var mds []bool
|
||||
lines := strings.Split(m.session.GetEvent().Description, "\n")
|
||||
lines := strings.Split(strings.TrimSpace(m.session.GetEvent().Description), "\n")
|
||||
|
||||
for i := range lines {
|
||||
if strings.HasPrefix(lines[i], "!!") {
|
||||
|
||||
@ -6,6 +6,7 @@ import (
|
||||
"github.com/BigJk/end_of_eden/system/audio"
|
||||
"github.com/BigJk/end_of_eden/ui"
|
||||
"github.com/BigJk/end_of_eden/ui/components"
|
||||
"github.com/BigJk/end_of_eden/ui/menus/carousel"
|
||||
"github.com/BigJk/end_of_eden/ui/menus/eventview"
|
||||
"github.com/BigJk/end_of_eden/ui/menus/gameover"
|
||||
"github.com/BigJk/end_of_eden/ui/menus/merchant"
|
||||
@ -39,11 +40,15 @@ type Model struct {
|
||||
animations []tea.Model
|
||||
ctrlDown bool
|
||||
|
||||
lastGameState game.GameState
|
||||
lastEvent string
|
||||
|
||||
event tea.Model
|
||||
merchant tea.Model
|
||||
|
||||
Session *game.Session
|
||||
Start game.StateCheckpointMarker
|
||||
BeforeStateSwitch game.StateCheckpointMarker
|
||||
}
|
||||
|
||||
func New(parent tea.Model, zones *zone.Manager, session *game.Session) Model {
|
||||
@ -57,6 +62,7 @@ func New(parent tea.Model, zones *zone.Manager, session *game.Session) Model {
|
||||
|
||||
Session: session,
|
||||
Start: session.MarkState(),
|
||||
BeforeStateSwitch: session.MarkState(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -248,6 +254,52 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
return gameover.New(m.zones, m.Session, m.Start), nil
|
||||
}
|
||||
|
||||
if m.Session.GetGameState() != m.lastGameState || m.Session.GetEventID() != m.lastEvent {
|
||||
diff := m.BeforeStateSwitch.Diff(m.Session)
|
||||
|
||||
m.BeforeStateSwitch = m.Session.MarkState()
|
||||
m.lastGameState = m.Session.GetGameState()
|
||||
m.lastEvent = m.Session.GetEventID()
|
||||
|
||||
if len(diff) > 0 {
|
||||
fmt.Println("DIFF", len(diff))
|
||||
|
||||
artifacts := lo.Map(lo.Filter(diff, func(item game.StateCheckpoint, index int) bool {
|
||||
added, ok := item.Events[game.StateEventArtifactAdded].(game.StateEventArtifactAddedData)
|
||||
return ok && !lo.SomeBy(diff, func(item game.StateCheckpoint) bool {
|
||||
removed, ok := item.Events[game.StateEventArtifactRemoved].(game.StateEventArtifactRemovedData)
|
||||
return ok && added.GUID == removed.GUID
|
||||
})
|
||||
}), func(item game.StateCheckpoint, index int) string {
|
||||
return components.ArtifactCard(m.Session, item.Events[game.StateEventArtifactAdded].(game.StateEventArtifactAddedData).GUID, 20, 20, 45)
|
||||
})
|
||||
|
||||
cards := lo.Map(lo.Filter(diff, func(item game.StateCheckpoint, index int) bool {
|
||||
added, ok := item.Events[game.StateEventCardAdded].(game.StateEventCardAddedData)
|
||||
return ok && !lo.SomeBy(diff, func(item game.StateCheckpoint) bool {
|
||||
removed, ok := item.Events[game.StateEventCardRemoved].(game.StateEventCardRemovedData)
|
||||
return ok && added.GUID == removed.GUID
|
||||
})
|
||||
}), func(item game.StateCheckpoint, index int) string {
|
||||
return components.HalfCard(m.Session, item.Events[game.StateEventCardAdded].(game.StateEventCardAddedData).GUID, false, 20, 20, false, 45)
|
||||
})
|
||||
|
||||
if len(artifacts) > 0 {
|
||||
c := carousel.New(nil, m.zones, fmt.Sprintf("%d New Artifacts", len(artifacts)), artifacts)
|
||||
c.Size = m.Size
|
||||
cmds = append(cmds, root.Push(c))
|
||||
}
|
||||
|
||||
if len(cards) > 0 {
|
||||
c := carousel.New(nil, m.zones, fmt.Sprintf("%d New Cards", len(cards)), cards)
|
||||
c.Size = m.Size
|
||||
cmds = append(cmds, root.Push(c))
|
||||
}
|
||||
}
|
||||
|
||||
cmds = append(cmds, tea.ClearScreen)
|
||||
}
|
||||
|
||||
return m, tea.Batch(cmds...)
|
||||
}
|
||||
|
||||
|
||||
@ -99,6 +99,7 @@ func New(parent tea.Model, zones *zone.Manager, session *game.Session) MenuModel
|
||||
table.WithStyles(style.TableStyle),
|
||||
table.WithColumns([]table.Column{
|
||||
{Title: "Name", Width: 25},
|
||||
{Title: "Tags", Width: 20},
|
||||
{Title: "Level", Width: 5},
|
||||
}),
|
||||
),
|
||||
@ -106,6 +107,7 @@ func New(parent tea.Model, zones *zone.Manager, session *game.Session) MenuModel
|
||||
table.WithStyles(style.TableStyle),
|
||||
table.WithColumns([]table.Column{
|
||||
{Title: "Name", Width: 25},
|
||||
{Title: "Tags", Width: 20},
|
||||
{Title: "Price", Width: 5},
|
||||
}),
|
||||
),
|
||||
@ -141,14 +143,14 @@ func (m MenuModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
// Update card table
|
||||
m.cardTable.SetRows(lo.Map(cards, func(guid string, index int) table.Row {
|
||||
card, instance := m.Session.GetCard(guid)
|
||||
return table.Row{m.zones.Mark(ZoneCards+fmt.Sprint(index), card.Name), fmt.Sprint(instance.Level)}
|
||||
return table.Row{m.zones.Mark(ZoneCards+fmt.Sprint(index), card.Name), strings.Join(card.Tags, ", "), fmt.Sprint(instance.Level)}
|
||||
}))
|
||||
m.cardTable.SetHeight(m.Size.Height - style.HeaderStyle.GetVerticalFrameSize() - 1 - 2)
|
||||
|
||||
// Update artifact table
|
||||
m.artifactTable.SetRows(lo.Map(artifacts, func(guid string, index int) table.Row {
|
||||
art, _ := m.Session.GetArtifact(guid)
|
||||
return table.Row{m.zones.Mark(ZoneArtifacts+fmt.Sprint(index), art.Name), fmt.Sprintf("%d$", art.Price)}
|
||||
return table.Row{m.zones.Mark(ZoneArtifacts+fmt.Sprint(index), art.Name), strings.Join(art.Tags, ", "), fmt.Sprintf("%d$", art.Price)}
|
||||
}))
|
||||
m.artifactTable.SetHeight(m.Size.Height - style.HeaderStyle.GetVerticalFrameSize() - 1 - 2)
|
||||
|
||||
@ -162,15 +164,17 @@ func (m MenuModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
|
||||
m = m.updateLogViewport()
|
||||
|
||||
m.cardTable.SetWidth(m.contentWidth() - 40 - 2 - 2)
|
||||
m.cardTable.SetWidth(m.contentWidth() - 55 - 4)
|
||||
m.cardTable.SetColumns([]table.Column{
|
||||
{Title: "Name", Width: m.contentWidth() - 40 - 2 - 2 - 10},
|
||||
{Title: "Name", Width: m.contentWidth() - 55 - 4 - 10 - 20 - 4},
|
||||
{Title: "Tags", Width: 20},
|
||||
{Title: "Level", Width: 10},
|
||||
})
|
||||
|
||||
m.artifactTable.SetWidth(m.contentWidth() - 40 - 2 - 2)
|
||||
m.artifactTable.SetWidth(m.contentWidth() - 55 - 4)
|
||||
m.artifactTable.SetColumns([]table.Column{
|
||||
{Title: "Name", Width: m.contentWidth() - 40 - 2 - 2 - 10},
|
||||
{Title: "Name", Width: m.contentWidth() - 55 - 4 - 10 - 20 - 4},
|
||||
{Title: "Tags", Width: 20},
|
||||
{Title: "Price", Width: 10},
|
||||
})
|
||||
case tea.KeyMsg:
|
||||
@ -279,7 +283,7 @@ func (m MenuModel) View() string {
|
||||
case ChoiceArtifacts:
|
||||
var selected string
|
||||
if m.artifactTable.Cursor() < len(m.Session.GetArtifacts(game.PlayerActorID)) {
|
||||
selected = components.ArtifactCard(m.Session, m.Session.GetArtifacts(game.PlayerActorID)[m.artifactTable.Cursor()], 20, 40)
|
||||
selected = components.ArtifactCard(m.Session, m.Session.GetArtifacts(game.PlayerActorID)[m.artifactTable.Cursor()], 20, 40, 45)
|
||||
}
|
||||
|
||||
contentBox = contentStyle.Render(lipgloss.JoinVertical(lipgloss.Left,
|
||||
@ -292,7 +296,7 @@ func (m MenuModel) View() string {
|
||||
case ChoiceCards:
|
||||
var selected string
|
||||
if m.artifactTable.Cursor() < len(m.Session.GetCards(game.PlayerActorID)) {
|
||||
selected = components.HalfCard(m.Session, m.Session.GetCards(game.PlayerActorID)[m.cardTable.Cursor()], false, 20, 40, false)
|
||||
selected = components.HalfCard(m.Session, m.Session.GetCards(game.PlayerActorID)[m.cardTable.Cursor()], false, 20, 40, false, 45)
|
||||
}
|
||||
|
||||
contentBox = contentStyle.Render(lipgloss.JoinVertical(lipgloss.Left,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user