diff --git a/lucksmith.lua b/lucksmith.lua index f32cd0f..72b95e4 100644 --- a/lucksmith.lua +++ b/lucksmith.lua @@ -7,35 +7,50 @@ player = { hp = 10, roll_mod = 0, mult = 1, - counter = false + energy = 0 } enemies = {{ + name = "Goblin", + hp = 4, + roll_mod = 0, + mult = 1 +}, { + name = "Orc", + hp = 6, + roll_mod = 0, + mult = 1 +}, { + name = "Troll", hp = 8, roll_mod = 0, - mult = 1, - counter = false -}, -{ + mult = 1 +}, { + name = "Ogre", hp = 10, roll_mod = 0, - mult = 1, - counter = false + mult = 1 }, { + name = "Dragon", hp = 12, roll_mod = 0, - mult = 1, - counter = false + mult = 1 +}, { + name = "Demon", + hp = 14, + roll_mod = 0, + mult = 1 }} level = 1 function get_enemy(level) return { - hp = enemies[level].hp, - roll_mod = enemies[level].roll_mod, - mult = enemies[level].mult, - counter = enemies[level].counter -} + name = enemies[level].name, + hp = enemies[level].hp, + max_hp = enemies[level].hp, -- store original max HP + roll_mod = enemies[level].roll_mod, + mult = enemies[level].mult + } end -enemy = get_enemy(level) +enemy = get_enemy(level) msg = "" -- State @@ -43,20 +58,39 @@ phase = "choose" phase_time = 0 roll_text = "" damage_applied = false +current_damage = {player = 0, enemy = 0} -- store damage for display pending_effects = { player = {}, - enemy = {} + enemy = {}, + previous_player = {}, + previous_enemy = {} } +-- Card tracking for two-round strategy +previous_card = nil +previous_enemy_card = nil -- animation + shake player.display_roll = 0 enemy.display_roll = 0 shake = 0 d = 2 -- shake intensity +flash = 0 + +original_palette = {} +for i = 0, 15 do + original_palette[i] = peek(0x3FC0 + i) +end + +-- Deck system +draw_pile = {} +discard_pile = {} +unlocked_cards = {} -- tracks which cards are in your deck + -- basic card pool cards = {{ - name = "Str", + name = "Bless", desc = "+2 to roll", timing = "pre_roll", + energy = -1, -- costs 1 energy use = function(p, e) p.roll_mod = p.roll_mod + 2 end @@ -64,68 +98,80 @@ cards = {{ name = "Hex", desc = "-2 enemy roll", timing = "pre_roll", + energy = -1, -- costs 1 energy use = function(p, e) e.roll_mod = e.roll_mod - 2 end }, { - name = "2x", + name = "Fury", desc = "win/lose double dmg", timing = "pre_roll", + energy = -1, -- reduced from -2 use = function(p, e) - p.mult = 2 + p.mult = p.mult * 2 + e.mult = e.mult * 2 end }, { - name = "0.5x", + name = "Caution", desc = "win/lose half dmg", timing = "pre_roll", + energy = 0, -- reduced from +1 since we get +1 per turn use = function(p, e) - p.mult = 0.5 + p.mult = p.mult * 0.5 + e.mult = e.mult * 0.5 end }, { name = "Heal", desc = "+2 hp", timing = "immediate", + energy = -1, -- costs 1 energy use = function(p, e) p.hp = math.min(10, p.hp + 2) + current_damage.player = current_damage.player - 2 -- show damage taken end }, { name = "Counter", desc = "hit enemy for 1 if you lose", timing = "post_result", + energy = 0, -- reduced from -1 use = function(p, e, player_won) - -- player_won is from player's perspective when called on player cards - if player_won == false then -- player lost + if player_won == false then -- fixed: should trigger when player loses e.hp = e.hp - 1 + current_damage.enemy = current_damage.enemy + 1 -- show damage taken end end }, { name = "Weaken", desc = "-1 enemy dmg", timing = "pre_roll", + energy = 0, -- reduced from -1 use = function(p, e) - e.mult = math.max(0.5, e.mult - 0.5) + e.weaken = (e.weaken or 0) + 1 -- enemy is weakened (does less damage) end }, { name = "Rage", desc = "+1 your dmg", - timing = "pre_roll", + timing = "pre_roll", -- changed from post_result for clarity + energy = -1, -- reduced from -1 use = function(p, e) - p.mult = p.mult + 0.5 + p.rage = (p.rage or 0) + 1 -- add rage bonus end }, { - name = "Tier", - desc = "if tie, you win", + name = "Break", + desc = "if tied, you win", timing = "post_result", + energy = -1, -- reduced from -2 use = function(p, e, player_won) if player_won == nil then -- tie occurred - -- Player wins the tie, so enemy takes damage e.hp = e.hp - (2 * p.mult) + current_damage.enemy = current_damage.enemy + (2 * p.mult) -- show damage taken end end }, { name = "Shield", - desc = "take 1 less dmg this turn", - timing = "pre_roll", + desc = "take 1 less dmg until damaged", + timing = "immediate", + energy = -1, -- increased from 0 use = function(p, e) p.shield = (p.shield or 0) + 1 end @@ -133,25 +179,31 @@ cards = {{ name = "Poison", desc = "enemy loses 1 hp at turn end", timing = "post_result", + energy = 0, -- reduced from -1 use = function(p, e, player_won) e.hp = e.hp - 1 + current_damage.enemy = current_damage.enemy + 1 -- show damage taken end }, { name = "Spike", desc = "deal 1 dmg to whoever wins", - timing = "post_result", + timing = "post_result", + energy = 1, -- now gives energy since it's risky use = function(p, e, player_won) if player_won == true then p.hp = p.hp - 1 -- hurt yourself if you win + current_damage.player = current_damage.player + 1 -- show damage taken elseif player_won == false then e.hp = e.hp - 1 -- hurt enemy if they win + current_damage.enemy = current_damage.enemy + 1 -- show damage taken end -- ties do nothing end }, { name = "Lucky", desc = "reroll if you roll 1-3", - timing = "post_roll", -- New timing needed + timing = "post_roll", + energy = -1, -- reduced from -2 use = function(p, e) if p.roll <= 3 then p.roll = math.random(1, 6) @@ -162,6 +214,7 @@ cards = {{ name = "Curse", desc = "enemy rerolls if they roll 4-6", timing = "post_roll", + energy = -1, -- reduced from -2 use = function(p, e) if e.roll >= 4 then e.roll = math.random(1, 6) @@ -169,10 +222,14 @@ cards = {{ end end }, { - name = "Swap", - desc = "switch your roll with enemy's", + name = "Intuition", + desc = "Pick the higher roll", timing = "post_roll", + energy = -3, -- reduced from -3 use = function(p, e) + if p.roll >= e.roll then + return -- no change needed + end local temp = p.roll p.roll = e.roll e.roll = temp @@ -183,22 +240,34 @@ cards = {{ name = "Mirror", desc = "copy enemy's roll modifier", timing = "pre_roll", + energy = -1, -- reduced from -1 use = function(p, e) p.roll_mod = p.roll_mod + e.roll_mod end +}, { + name = "Double Vision", + desc = "copy enemy's multiplier", + timing = "post_roll", + energy = -1, -- reduced from -2 + use = function(p, e) + p.mult = e.mult + end }, { name = "Drain", desc = "heal 1 if you win", timing = "post_result", + energy = 0, -- reduced from -1 use = function(p, e, player_won) if player_won == true then p.hp = math.min(10, p.hp + 1) + current_damage.player = current_damage.player - 1 -- show healing end end }, { name = "Gamble", desc = "50% chance: +3 roll or -1 roll", timing = "pre_roll", + energy = 1, -- now gives energy since it's risky use = function(p, e) if math.random() < 0.5 then p.roll_mod = p.roll_mod + 3 @@ -207,17 +276,19 @@ cards = {{ end end }, { - name = "Fury", - desc = "+1 dmg per hp you're missing", + name = "Impulse", + desc = "+0.5 dmg per hp missing", timing = "pre_roll", + energy = -1, -- reduced from -1 use = function(p, e) local missing_hp = 10 - p.hp - p.mult = p.mult + (missing_hp * 0.1) + p.mult = p.mult + (missing_hp * 0.5) -- increased effect end }, { name = "Turtle", desc = "set your roll to 3", timing = "post_roll", + energy = 2, -- increased from 1 use = function(p, e) p.roll = 3 p.total = p.roll + p.roll_mod @@ -226,16 +297,36 @@ cards = {{ name = "Berserk", desc = "2x dmg but take 1 dmg", timing = "immediate", + energy = -1, -- reduced from -2 use = function(p, e) p.mult = p.mult * 2 p.hp = p.hp - 1 + current_damage.player = current_damage.player + 1 -- show damage taken + end +}, { + name = "Focus", + desc = "+2 energy", + timing = "immediate", + energy = 2, -- gives 2 energy (net +3 with the +1 per turn) + use = function(p, e) + -- Energy gain handled automatically + end +}, { + name = "Rest", + desc = "+1 energy, +1 hp", + timing = "immediate", + energy = 1, -- gives 1 energy (net +2 with the +1 per turn) + use = function(p, e) + current_damage.player = current_damage.player - 1 -- show healing + p.hp = math.min(10, p.hp + 1) end }} enemy_cards = {{ - name = "Str", + name = "Bless", desc = "+2 to roll", timing = "pre_roll", + energy = -1, use = function(p, e) e.roll_mod = e.roll_mod + 2 end @@ -243,78 +334,240 @@ enemy_cards = {{ name = "Hex", desc = "-2 enemy roll", timing = "pre_roll", + energy = -2, use = function(p, e) p.roll_mod = p.roll_mod - 2 end }, { - name = "2x", + name = "Fury", desc = "win/lose double dmg", timing = "pre_roll", + energy = -2, use = function(p, e) - e.mult = 2 + e.mult = e.mult * 2 + p.mult = p.mult * 2 end }, { - name = "0.5x", + name = "Caution", desc = "win/lose half dmg", timing = "pre_roll", + energy = 1, use = function(p, e) - e.mult = 0.5 + e.mult = e.mult * 0.5 + p.mult = p.mult * 0.5 end }, { name = "Heal", desc = "+2 hp", timing = "immediate", + energy = -1, use = function(p, e) - e.hp = math.min(10, e.hp + 2) + e.hp = math.min(e.max_hp, e.hp + 2) + current_damage.enemy = current_damage.enemy - 2 -- show damage taken end }, { name = "Counter", desc = "hit enemy for 1 if you lose", timing = "post_result", + energy = -1, use = function(p, e, player_won) -- player_won is from player's perspective, so enemy perspective is flipped if player_won == true then -- enemy lost (player won) p.hp = p.hp - 1 + current_damage.player = current_damage.player + 1 -- show damage taken end end }, { name = "Weaken", desc = "-1 enemy dmg", timing = "pre_roll", + energy = -1, use = function(p, e) - p.mult = math.max(0.5, p.mult - 0.5) + p.weaken = (p.weaken or 0) + 1 -- player is weakened (does less damage) end }, { name = "Rage", desc = "+1 your dmg", timing = "pre_roll", + energy = -1, use = function(p, e) - e.mult = e.mult + 0.5 + e.rage = (e.rage or 0) + 1 -- add rage bonus for enemy end }, { name = "Tier", desc = "if tie, you win", timing = "post_result", + energy = -2, use = function(p, e, player_won) if player_won == nil then -- tie occurred -- Enemy wins the tie, so player takes damage p.hp = p.hp - (2 * e.mult) + current_damage.player = current_damage.player + (2 * e.mult) -- show damage taken end end }} -- player's starting hand (random subset) hand = {} -function draw_hand() - hand = {} - for i = 1, 3 do - table.insert(hand, cards[math.random(#cards)]) +card_choice_options = {} -- for level up card selection + +function shuffle_deck(deck) + for i = #deck, 2, -1 do + local j = math.random(i) + deck[i], deck[j] = deck[j], deck[i] end end + +function initialize_deck() + draw_pile = {} + discard_pile = {} + + -- Start with first 8 cards for level 1, or use unlocked_cards for higher levels + local cards_to_add = {} + if level == 1 then + unlocked_cards = {} + for i = 1, 8 do + unlocked_cards[i] = true + cards_to_add = {1, 2, 3, 4, 5, 6, 7, 8} + end + end + + -- Add 2 copies of each unlocked card to deck + for i, card in ipairs(cards) do + if unlocked_cards[i] then + table.insert(draw_pile, card) + table.insert(draw_pile, card) + end + end + + shuffle_deck(draw_pile) +end + +function generate_card_choices() + card_choice_options = {} + local available_cards = {} + + -- Find cards not yet unlocked + for i, card in ipairs(cards) do + if not unlocked_cards[i] then + table.insert(available_cards, i) + end + end + + -- If we have available cards, pick 3 random ones + if #available_cards > 0 then + shuffle_deck(available_cards) + for i = 1, math.min(3, #available_cards) do + table.insert(card_choice_options, available_cards[i]) + end + end +end + +function add_card_to_deck(card_index) + unlocked_cards[card_index] = true + local card = cards[card_index] + -- Add 2 copies to discard pile (will be shuffled in next game) + table.insert(discard_pile, card) + table.insert(discard_pile, card) +end + +-- player's starting hand (random subset) +hand = {} + +function shuffle_deck(deck) + for i = #deck, 2, -1 do + local j = math.random(i) + deck[i], deck[j] = deck[j], deck[i] + end +end + +function reshuffle_if_needed() + if #draw_pile < 3 and #discard_pile > 0 then + -- Move discard pile to draw pile and shuffle + for _, card in ipairs(discard_pile) do + table.insert(draw_pile, card) + end + discard_pile = {} + shuffle_deck(draw_pile) + end +end + +function draw_hand() + hand = {} + reshuffle_if_needed() + + -- If player has no energy, ensure at least one energy-positive card + if player.energy == 0 then + local energy_positive_found = false + local temp_hand = {} + + -- Try to draw 3 cards, checking for energy-positive ones + for i = 1, 3 do + if #draw_pile > 0 then + local card = table.remove(draw_pile, 1) + table.insert(temp_hand, card) + if card.energy >= 0 then + energy_positive_found = true + end + end + end + + -- If no energy-positive card found, replace one with an energy-positive card + if not energy_positive_found and #temp_hand > 0 then + -- Find an energy-positive card in remaining deck or discard + local energy_card = nil + + -- Look in draw pile first + for i, card in ipairs(draw_pile) do + if card.energy >= 0 then + energy_card = table.remove(draw_pile, i) + break + end + end + + -- If not found, look in discard pile + if not energy_card then + for i, card in ipairs(discard_pile) do + if card.energy >= 0 then + energy_card = table.remove(discard_pile, i) + break + end + end + end + + -- Replace first card with energy-positive card + if energy_card then + table.insert(draw_pile, temp_hand[1]) -- Put replaced card back + temp_hand[1] = energy_card + end + end + + hand = temp_hand + else + -- Normal draw when player has energy + for i = 1, 3 do + if #draw_pile > 0 then + table.insert(hand, table.remove(draw_pile, 1)) + end + end + end + + -- Fill remaining slots if deck was too small + while #hand < 3 and (#draw_pile > 0 or #discard_pile > 0) do + reshuffle_if_needed() + if #draw_pile > 0 then + table.insert(hand, table.remove(draw_pile, 1)) + end + end +end + +-- Initialize deck at start +initialize_deck() draw_hand() -- === CORE TURN LOGIC === function apply_effects(timing, player_won) + -- Apply current card effects for _, effect in ipairs(pending_effects.player) do if effect.timing == timing then if timing == "post_result" then @@ -324,6 +577,19 @@ function apply_effects(timing, player_won) end end end + + -- Apply previous card effects if they exist + for _, effect in ipairs(pending_effects.previous_player) do + if effect.timing == timing then + if timing == "post_result" then + effect.use(player, enemy, player_won) + else + effect.use(player, enemy) + end + end + end + + -- Apply current enemy card effects for _, effect in ipairs(pending_effects.enemy) do if effect.timing == timing then if timing == "post_result" then @@ -333,14 +599,38 @@ function apply_effects(timing, player_won) end end end + + -- Apply previous enemy card effects if they exist + for _, effect in ipairs(pending_effects.previous_enemy) do + if effect.timing == timing then + if timing == "post_result" then + effect.use(player, enemy, player_won) + else + effect.use(player, enemy) + end + end + end end + function start_turn(card) + -- Don't store previous cards here anymore - they're already set when returning to choose phase current_card = card enemy_card = enemy_cards[math.random(#enemy_cards)] - -- Store effects for proper timing + -- Apply card energy cost/gain + player.energy = math.max(0, math.min(5, player.energy + card.energy)) + + -- Discard all cards in hand + for _, hand_card in ipairs(hand) do + table.insert(discard_pile, hand_card) + end + hand = {} + + -- Store effects for proper timing - both current and previous cards pending_effects.player = {card} pending_effects.enemy = {enemy_card} + pending_effects.previous_player = previous_card and {previous_card} or {} + pending_effects.previous_enemy = previous_enemy_card and {previous_enemy_card} or {} -- Apply immediate effects apply_effects("immediate") @@ -363,9 +653,119 @@ function start_turn(card) end picked = 1 +card_pick_selection = 1 + +-- === DRAWING FUNCTIONS === +function draw_hud() + -- Draw level + spr(0, 110, 2, 0) + print(level, 115, 2, 7, false, 1, true) + + -- Draw energy sprites + for i = 1, player.energy do + local x = -4 + (8 * i) + spr(17, x, 10, 0) + end + + for i = player.energy + 1, 5 do + local x = -4 + (8 * i) + spr(18, x, 10, 0) + end + + -- Draw deck info + spr(4, 220, 115, 0) + print(#draw_pile, 230, 116, 10, false, 1, true) + spr(5, 220, 125, 0) + print(#discard_pile, 230, 126, 14, false, 1, true) + + -- Draw card play boxes at top + -- Player card boxes (left side) + rectb(4, 20, 40, 16, phase == "choose" and 8 or 2) -- Current player card (or empty slot) + if phase == "choose" then + print("Next", 16, 25, 8, false, 1, true) + elseif current_card then + print(current_card.name, 6, 25, 2, false, 1, true) + end + + rectb(45, 20, 40, 16, 8) -- Previous player card + if previous_card then + print(previous_card.name, 47, 25, 8, false, 1, true) + end + + -- Enemy card boxes (right side) + rectb(155, 20, 40, 16, 8) -- Previous enemy card + if previous_enemy_card then + print(previous_enemy_card.name, 157, 25, 8, false, 1, true) + end + + rectb(196, 20, 40, 16, phase == "choose" and 8 or 6) -- Current enemy card (or empty slot) + if phase == "choose" then + print("Next", 208, 25, 8, false, 1, true) + elseif enemy_card then + print(enemy_card.name, 198, 25, 6, false, 1, true) + end +end + +function draw_health_bars() + -- Draw player health bar + for i = 1, 10 do + local x = 4 + (i - 1) * 8 + if i <= player.hp then + spr(1, x, 2, 0) -- healthy HP + else + spr(3, x, 2, 0) -- damaged HP + end + end + + -- Show player damage + if current_damage.player ~= 0 then + print((current_damage.player < 0 and "+" or "") .. -math.floor(current_damage.player), 88, 3, current_damage.player > 0 and 2 or 6, false, 1, true) + end + + -- Draw enemy health bar + for i = 1, enemy.max_hp do + local x = 240 - (enemy.max_hp * 8) + (i - 1) * 8 + if i <= (enemy.max_hp - enemy.hp) then + spr(3, x, 2, 0) -- damaged HP on the left + else + spr(2, x, 2, 0) -- healthy HP on the right + end + end + + -- Show enemy damage + if current_damage.enemy > 0 then + local enemy_bar_start = 240 - (enemy.max_hp * 8) - 8 + print((current_damage.enemy < 0 and "+" or "") .. -math.floor(current_damage.enemy), enemy_bar_start, 3, current_damage.enemy > 0 and 2 or 6, false, 1, true) + end + + -- Draw enemy name under health bar + local enemy_name_x = 240 - (#enemy.name * 4) + print(enemy.name, enemy_name_x, 12, 7, false, 1, true) + + -- Show shield sprite if player has shield + if player.shield and player.shield > 0 then + for i = 1, player.shield do + spr(16, 44 + (i - 1) * 8, 12, 0) + end + end +end + +function draw_card(card, x, is_selected, can_afford) + local border_color = can_afford and (is_selected and 11 or 9) or (is_selected and 13 or 14) + rectb(x, 40, 52, 64, border_color) + if is_selected then rect(x+1, 41, 50, 62, 8) end + local text_color = can_afford and (is_selected and 11 or 9) or (is_selected and 0 or 14) + print(card.name, x + 3, 70, text_color, false, 1, true) + + -- Show energy cost/gain + local energy_text = card.energy >= 0 and ("+" .. card.energy) or tostring(card.energy) + spr(17, x + 30, 42, 0) + print(energy_text, x + 40, 44, card.energy >= 0 and 6 or 2, false, 1, true) +end + -- === INPUT === function TIC() - cls(1) + cls(0) if shake > 0 then poke(0x3FF9, math.random(-d, d)) @@ -376,11 +776,25 @@ function TIC() end end - -- camera(dx, dy) - -- Draw HUD - print("Level: " .. level, 100, 4, 12) - print("PLAYER HP: " .. player.hp, 4, 4, 12) - print("ENEMY HP: " .. enemy.hp, 4, 14, 8) + -- Handle flash effect + if flash > 0 then + -- Tint screen red on alternating frames + if flash % 4 < 2 then -- Flash on/off every 2 frames + poke(0x3FC0, 255) -- Red tint + else + poke(0x3FC0, original_palette[0]) + end + flash = flash - 1 + + -- When effect ends, ensure palette is restored + if flash == 0 then + poke(0x3FC0, original_palette[0]) + end + end + + -- Draw HUD and health bars + draw_hud() + draw_health_bars() if phase == "choose" then -- Card Picker @@ -392,19 +806,76 @@ function TIC() picked = picked + 1 end picked = math.min(3, math.max(1, picked)) + -- Show cards for i, card in ipairs(hand) do local x = 10 + (i - 1) * 64 - rectb(x, 80, 62, 24, i == picked and 1 or 12) - print(card.name, x + 3, 90, i == picked and 11 or 10, false,1,true) + local can_afford = (card.energy >= 0) or (player.energy >= -card.energy) + local is_selected = i == picked + draw_card(card, x, is_selected, can_afford) + end + + local selected_card = hand[picked] + local can_afford = (selected_card.energy >= 0) or (player.energy >= -selected_card.energy) + print(selected_card.desc, 4, 120, can_afford and 6 or 14) + if not can_afford then + print("Not enough energy!", 4, 130, 3) end - print(hand[picked].desc, 4, 120, 15) print(msg, 4, 40, 15) - if btnp(4) then + if btnp(4) and can_afford then start_turn(hand[picked]) end + elseif phase == "card_reward" then + -- Card selection after level victory + print("Choose a card to add to your deck:", 4, 30, 12) + + if btnp(2) then + card_pick_selection = card_pick_selection - 1 + end + if btnp(3) then + card_pick_selection = card_pick_selection + 1 + end + card_pick_selection = math.min(#card_choice_options, math.max(1, card_pick_selection)) + + -- Show card options + for i, card_index in ipairs(card_choice_options) do + local card = cards[card_index] + local x = 10 + (i - 1) * 64 + local is_selected = i == card_pick_selection + draw_card(card, x, is_selected, true) -- always affordable in reward phase + end + + -- Show selected card description + if #card_choice_options > 0 then + local selected_card = cards[card_choice_options[card_pick_selection]] + print(selected_card.desc, 4, 110, 15) + end + + if btnp(4) and #card_choice_options > 0 then + add_card_to_deck(card_choice_options[card_pick_selection]) + -- Advance to next level + level = math.min(level + 1, #enemies) + player = { + hp = 10, + roll_mod = 0, + mult = 1, + energy = 0 + } + enemy = get_enemy(level) + -- Clear card displays for new level + current_card = nil + enemy_card = nil + previous_card = nil + previous_enemy_card = nil + initialize_deck() + draw_hand() + phase = "choose" + phase_time = 0 + damage_applied = false + end + elseif phase == "roll" then phase_time = phase_time + 1 @@ -422,62 +893,100 @@ function TIC() phase_time = 0 end - card_text = "You played " .. current_card.name - enemy_card_text = "Enemy played " .. enemy_card.name - print(card_text, 4, 30, 14) - print(enemy_card_text, 4, 20, 8) - -- show rolls - roll_text = "You roll " .. player.display_roll .. " / Enemy rolls " .. enemy.display_roll - print(roll_text, 4, 40, 12) + card_text = ""--"You played " .. current_card.name + enemy_card_text = ""--enemy.name .. " played " .. enemy_card.name + print(card_text, 4, 40, 2) + print(enemy_card_text, 4, 50, 6) + -- show dice rolls as sprites + local player_dice_sprite = 256 + (player.display_roll - 1) * 2 + local enemy_dice_sprite = 288 + (enemy.display_roll - 1) * 2 + spr(player_dice_sprite, 50, 50, 0, 1, 0, 0, 2, 2) -- player dice on left, 16x16 + spr(enemy_dice_sprite, 150, 50, 0, 1, 0, 0, 2, 2) -- enemy dice on right, 16x16 elseif phase == "mod" then - print(card_text, 4, 30, 14) - print(enemy_card_text, 4, 20, 8) - print(roll_text, 4, 40, 12) + print(card_text, 4, 40, 2) + print(enemy_card_text, 4, 50, 6) + -- show dice rolls as sprites + local player_dice_sprite = 256 + (player.display_roll - 1) * 2 + local enemy_dice_sprite = 288 + (enemy.display_roll - 1) * 2 + spr(player_dice_sprite, 50, 50, 0, 1, 0, 0, 2, 2) -- player dice on left, 16x16 + spr(enemy_dice_sprite, 150, 50, 0, 1, 0, 0, 2, 2) -- enemy dice on right, 16x16 mod_text = "mods applied: +" .. player.roll_mod .. " / " .. enemy.roll_mod - print(mod_text, 4, 50, 10) + print(mod_text, 4, 80, 12) phase_time = phase_time + 1 if phase_time > 60 then phase = "total" phase_time = 0 end elseif phase == "total" then - print(card_text, 4, 30, 14) - print(enemy_card_text, 4, 20, 8) - print(roll_text, 4, 40, 12) - print(mod_text, 4, 50, 12) - print("Total " .. player.total .. " / " .. enemy.total, 4, 60, 10) + current_damage.player, current_damage.enemy = 0, 0 -- reset damage display + print(card_text, 4, 40, 2) + print(enemy_card_text, 4, 50, 6) + -- show dice rolls as sprites + local player_dice_sprite = 256 + (player.display_roll - 1) * 2 + local enemy_dice_sprite = 288 + (enemy.display_roll - 1) * 2 + spr(player_dice_sprite, 50, 50, 0, 1, 0, 0, 2, 2) -- player dice on left, 16x16 + spr(enemy_dice_sprite, 150, 50, 0, 1, 0, 0, 2, 2) -- enemy dice on right, 16x16 + print(mod_text, 4, 80, 14) + print("Total " .. player.total .. " / " .. enemy.total, 4, 90, 12) phase_time = phase_time + 1 if phase_time > 120 then phase = "result" phase_time = 0 end elseif phase == "result" then - print(card_text, 4, 30, 14) - print(enemy_card_text, 4, 20, 8) - print(roll_text, 4, 40, 12) - print(mod_text, 4, 50, 12) - print("Total " .. player.total .. " / " .. enemy.total, 4, 60, 10) + print(card_text, 4, 40, 2) + print(enemy_card_text, 4, 50, 6) + -- show dice rolls as sprites + local player_dice_sprite = 256 + (player.display_roll - 1) * 2 + local enemy_dice_sprite = 288 + (enemy.display_roll - 1) * 2 + spr(player_dice_sprite, 50, 50, 0, 1, 0, 0, 2, 2) -- player dice on left, 16x16 + spr(enemy_dice_sprite, 150, 50, 0, 1, 0, 0, 2, 2) -- enemy dice on right, 16x16 + print(mod_text, 4, 80, 14) + print("Total " .. player.total .. " / " .. enemy.total, 4, 90, 14) local msgtxt = "" local player_won = nil if player.total > enemy.total then player_won = true local dmg = 2 * player.mult + -- Apply rage bonus to increase damage + local rage_bonus = player.rage or 0 + dmg = dmg + rage_bonus + -- Apply weaken debuff (player is weakened, so their attacks do less damage) + local weaken_reduction = player.weaken or 0 + dmg = math.max(0, dmg - weaken_reduction) + if not damage_applied then + enemy.hp = enemy.hp - dmg + current_damage.enemy = current_damage.enemy + dmg + flash = 8 end - msgtxt = "You win! Enemy -" .. dmg + msgtxt = "You win!" elseif enemy.total > player.total then player_won = false local dmg = 2 * enemy.mult + -- Apply rage bonus to increase damage + local rage_bonus = enemy.rage or 0 + dmg = dmg + rage_bonus + -- Apply weaken debuff (enemy is weakened, so their attacks do less damage) + local weaken_reduction = enemy.weaken or 0 + dmg = math.max(0, dmg - weaken_reduction) + if not damage_applied then + -- Apply shield reduction local shield_reduction = player.shield or 0 dmg = math.max(0, dmg - shield_reduction) player.hp = player.hp - dmg + current_damage.player = current_damage.player + dmg + -- Remove shield after taking damage + if shield_reduction > 0 and dmg < (2 * enemy.mult) then + player.shield = math.max(0, (player.shield or 0) - 1) + end shake = 10 end - msgtxt = "Enemy wins! You -" .. dmg + msgtxt = enemy.name .. " wins!" else msgtxt = "Tie!" end @@ -488,62 +997,171 @@ function TIC() end damage_applied = true - print(msgtxt, 4, 70, 14) + local color = player_won == true and 2 or (player_won == false and 6 or 9) + print(msgtxt, 4, 100, color) if btnp(4) then -- reset mods & back to choose player.roll_mod, enemy.roll_mod = 0, 0 player.mult, enemy.mult = 1, 1 - player.counter, enemy.counter = false, false - player.shield, enemy.shield = 0, 0 - draw_hand() + player.weaken, enemy.weaken = 0, 0 -- reset weaken debuffs + player.rage, enemy.rage = 0, 0 -- reset rage bonuses + -- Don't reset shield - it persists until damage taken + -- Add 1 energy at end of turn + player.energy = math.min(5, player.energy + 1) + -- Reset damage display + current_damage.player = 0 + current_damage.enemy = 0 + + -- Move current cards to previous when returning to choose phase + previous_card = current_card + previous_enemy_card = enemy_card + current_card = nil + enemy_card = nil + + draw_hand() -- Draw new hand from deck phase = (enemy.hp <= 0 or player.hp <= 0) and "game_over" or "choose" phase_time = 0 damage_applied = false end elseif phase == "game_over" then - if enemy.hp < player.hp then - print("You Win", 4, 60, 12) + if enemy.hp <= 0 and player.hp > 0 then + local victory_text = "You defeated " .. enemy.name .. "!" + local victory_x = 120 - (#victory_text * 3) -- center text (6 pixel font, so 3 pixels per char from center) + print(victory_text, victory_x, 60, 6) + + local continue_text = "Press Z to choose new card" + local continue_x = 120 - (#continue_text * 3) + print(continue_text, continue_x, 100, 9) else - print("You Lose", 4, 60, 14) + local defeat_text = enemy.name .. " defeated you!" + local defeat_x = 120 - (#defeat_text * 3) + print(defeat_text, defeat_x, 60, 2) + + local restart_text = "Press X to restart" + local restart_x = 120 - (#restart_text * 3) + print(restart_text, restart_x, 100, 14) end - if btnp(5) then - if enemy.hp < player.hp then - level = math.min(level + 1, #enemies) + if btnp(4) and enemy.hp <= 0 and player.hp > 0 then + -- Victory - go to card selection + generate_card_choices() + if #card_choice_options > 0 then + phase = "card_reward" + card_pick_selection = 1 else - level = 1 + -- No more cards to unlock, just advance level + level = math.min(level + 1, #enemies) + player = { + hp = 10, + roll_mod = 0, + mult = 1, + energy = 0 + } + enemy = get_enemy(level) + -- Clear card displays for new level + current_card = nil + enemy_card = nil + previous_card = nil + previous_enemy_card = nil + initialize_deck() + draw_hand() + phase = "choose" end + phase_time = 0 + damage_applied = false + elseif btnp(5) and (enemy.hp > 0 or player.hp <= 0) then + -- Loss - restart from level 1 + level = 1 player = { hp = 10, roll_mod = 0, mult = 1, - counter = false + energy = 0 } - enemy = get_enemy(level) + -- Clear card displays on restart + current_card = nil + enemy_card = nil + previous_card = nil + previous_enemy_card = nil msg = "" - - -- State + initialize_deck() + draw_hand() phase = "choose" phase_time = 0 roll_text = "" damage_applied = false end end - -- camera(0,0) end -- --- 001:eccccccccc888888caaaaaaaca888888cacccccccacc0ccccacc0ccccacc0ccc --- 002:ccccceee8888cceeaaaa0cee888a0ceeccca0ccc0cca0c0c0cca0c0c0cca0c0c --- 003:eccccccccc888888caaaaaaaca888888cacccccccacccccccacc0ccccacc0ccc --- 004:ccccceee8888cceeaaaa0cee888a0ceeccca0cccccca0c0c0cca0c0c0cca0c0c --- 017:cacccccccaaaaaaacaaacaaacaaaaccccaaaaaaac8888888cc000cccecccccec --- 018:ccca00ccaaaa0ccecaaa0ceeaaaa0ceeaaaa0cee8888ccee000cceeecccceeee --- 019:cacccccccaaaaaaacaaacaaacaaaaccccaaaaaaac8888888cc000cccecccccec --- 020:ccca00ccaaaa0ccecaaa0ceeaaaa0ceeaaaa0cee8888ccee000cceeecccceeee +-- 000:3200000032000000322200003332000033322200333332003333322200000000 +-- 001:0ff00ff0f22ff33ff22f223ff222223f0f2222f000f22f00000ff00000000000 +-- 002:0ff00ff0f66ff55ff66f665ff666665f0f6666f000f66f00000ff00000000000 +-- 003:0ff00ff0feeffddffeefeedffeeeeedf0feeeef000feef00000ff00000000000 +-- 004:0aaaaaa00aa99aa00a9999a00a9999a00a9999a00a9999a00aa99aa00aaaaaa0 +-- 005:0eeeeee00eeffee00effffe00effffe00effffe00effffe00eeffee00eeeeee0 +-- 016:0011110001333310133223311333233113323331013323100013310000011000 +-- 017:0000000000333300033443300344c43003444430033443300033330000000000 +-- 018:0000000000eeee000eeddee00eddcde00edddde00eeddee000eeee0000000000 +-- 019:0006600000666600066666600666666000066000000660000006600000066000 +-- 020:0002200000022000000220000002200002222220022222200022220000022000 +-- 021:3300000034300000034300000034300100034110000012100000112100010011 +-- 022:ee000000ede000000ede000000ede00f000edff00000f2f00000ff2f000f00ff -- +-- +-- 000:0000000000022222002222220222222202222222022222220222222f022222ff +-- 001:000000002222200022222200222222202222222022222220f2222220ff222220 +-- 002:000000000002222200222222022f222202fff22202fff222022f222202222222 +-- 003:0000000022222000222222002222222022222220222222202222222022222220 +-- 004:000000000002222200222222022f222202fff22202fff222022f222f022222ff +-- 005:000000002222200022222200222222202222222022222220f2222220ff222220 +-- 006:000000000002222200222222022f222202fff22202fff222022f222202222222 +-- 007:0000000022222000222222002222f220222fff20222fff202222f22022222220 +-- 008:000000000002222200222222022f222202fff22202fff222022f222f022222ff +-- 009:0000000022222000222222002222f220222fff20222fff20f222f220ff222220 +-- 010:000000000002222200222222022f222f02fff2ff02fff2ff022f222202222222 +-- 011:0000000022222000222222002222f220ff2fff20ff2fff20f222f22022222220 +-- 016:022222ff0222222f022222220222222202222222002222220002222200000000 +-- 017:ff222220f2222220222222202222222022222220222222002222200000000000 +-- 018:0222222202222222022222220222222202222222002222220002222200000000 +-- 019:2222222022222220222f222022fff22022fff220222f22002222200000000000 +-- 020:022222ff0222222f022222220222222202222222002222220002222200000000 +-- 021:ff222220f222f220222fff20222fff202222f220222222002222200000000000 +-- 022:02222222022f222202fff22202fff222022f2222002222220002222200000000 +-- 023:222222202222f220222fff20222fff202222f220222222002222200000000000 +-- 024:022222ff022f222f02fff22202fff222022f2222002222220002222200000000 +-- 025:ff222220f222f220222fff20222fff202222f220222222002222200000000000 +-- 026:02222222022f222202fff2ff02fff2ff022f222f002222220002222200000000 +-- 027:22222220f222f220ff2fff20ff2fff202222f220222222002222200000000000 +-- 032:0000000000066666006666660666666606666666066666660666666f066666ff +-- 033:000000006666600066666600666666606666666066666660f6666660ff666660 +-- 034:000000000006666600666666066f666606fff66606fff666066f666606666666 +-- 035:0000000066666000666666006666666066666660666666606666666066666660 +-- 036:000000000006666600666666066f666606fff66606fff666066f666f066666ff +-- 037:000000006666600066666600666666606666666066666660f6666660ff666660 +-- 038:000000000006666600666666066f666606fff66606fff666066f666606666666 +-- 039:0000000066666000666666006666f660666fff60666fff606666f66066666660 +-- 040:000000000006666600666666066f666606fff66606fff666066f666f066666ff +-- 041:0000000066666000666666006666f660666fff60666fff60f666f660ff666660 +-- 042:000000000006666600666666066f666f06fff6ff06fff6ff066f666606666666 +-- 043:0000000066666000666666006666f660ff6fff60ff6fff60f666f66066666660 +-- 048:066666ff0666666f066666660666666606666666006666660006666600000000 +-- 049:ff666660f6666660666666606666666066666660666666006666600000000000 +-- 050:0666666606666666066666660666666606666666006666660006666600000000 +-- 051:6666666066666660666f666066fff66066fff660666f66006666600000000000 +-- 052:066666ff0666666f066666660666666606666666006666660006666600000000 +-- 053:ff666660f666f660666fff60666fff606666f660666666006666600000000000 +-- 054:06666666066f666606fff66606fff666066f6666006666660006666600000000 +-- 055:666666606666f660666fff60666fff606666f660666666006666600000000000 +-- 056:066666ff066f666f06fff66606fff666066f6666006666660006666600000000 +-- 057:ff666660f666f660666fff60666fff606666f660666666006666600000000000 +-- 058:06666666066f666606fff6ff06fff6ff066f666f006666660006666600000000 +-- 059:66666660f666f660ff6fff60ff6fff606666f660666666006666600000000000 +-- + -- -- 000:00000000ffffffff00000000ffffffff -- 001:0123456789abcdeffedcba9876543210