420 : Better potions

Balthazar:

Rather than have the potion disappear immediately when the player touches it, we'd like to have the player slowly drink from it.

Balthazar:

We need a set of potion images that all have a different amount of liquid shown.

potion-1.png

potion-2.png

potion-3.png

potion-4.png

potion-5.png

potion-6.png

potion-7.png

potion-8.png

potion-9.png

potion-10.png

potion-11.png

potion-12.png

potion-13.png

potion-14.png

potion-15.png

Copy the following files into your project:

Balthazar:

Since potions are going to be more complicated than other items, we should add a function specifically for creating them.

function init_level1() { ... var item_data = [ // [x, y, width, height, type, image] [530, 358, 18, 20, "key", "key"], [465, 270, 16, 23, "potion", "potion"], [50, 250, 20, 20, "coin", "coin"], [530, 190, 20, 20, "coin", "coin"], [230, 120, 20, 20, "coin", "coin"], ]; add_items(level, item_data); add_potion_item(level, 465, 270); ... } function init_level2() { ... var item_data = [ // [x, y, width, height, type, image] [80, 272, 18, 20, "key", "key"], [55, 230, 16, 23, "potion", "potion"], [20, 280, 20, 20, "coin", "coin"], [420, 230, 20, 20, "coin", "coin"], [320, 140, 20, 20, "coin", "coin"], ]; add_items(level, item_data); add_potion_item(level, 55, 230); ... }
Balthazar:

For the potion, we'll add a value (and a max_value) that indicates the current amount of potion remaining. All items will get this new value attribute, but it will be set to 0 for all non-potion items.

function add_items(level, item_data) { ... } function add_potion_item(level, x, y) { var val_images = [ // [image, percentage] ["potion-1", 0], ["potion-2", 3], ["potion-3", 10], ["potion-4", 20], ["potion-5", 30], ["potion-6", 40], ["potion-7", 50], ["potion-8", 60], ["potion-9", 70], ["potion-10", 79], ["potion-11", 86], ["potion-12", 91], ["potion-13", 94], ["potion-14", 97], ["potion-15", 100], ]; var item = create_item(x, y, 16, 23, "potion", val_images); item.value_max = 20; item.value = item.value_max; item.consume_rate = 0.25; item.replenish_rate = 0.01; level.items.push(item); }
function create_item(x, y, width, height, type, image) { var d = {}; d.x = x; d.y = y; d.width = width; d.height = height; d.origin_x = width / 2; d.origin_y = height; d.type = type; d.value_max = 0; d.value = 0; d.found = false; d.img = new Image(); d.img.src = _game.imagedir_items + image + ".png"; d.value_images = []; // If the image is specified as an array, then it defines a set of // images that are associated with different values for this item. if (image instanceof Array) { for (var j = 0; j < image.length; j++) { var image_name = image[j][0]; var percent = image[j][1]; var sprite = {}; sprite.img = new Image(); sprite.img.src = _game.imagedir_items + image_name + ".png"; sprite.percent = percent; d.value_images.push(sprite); } } else { // Single non-animated image. var sprite = {}; sprite.img = new Image(); sprite.img.src = _game.imagedir_items + image + ".png"; sprite.percent = 0; d.value_images.push(sprite); } return d; }
Balthazar:

Update draw items to draw the appropriate image. For potions, this will choose an image based on the percentage of potion remaining.

Balthazar:

For items that have their value and value_max equal to 0, this will select the first image (at index 0).

function draw_items(ctx) { var level = _levels[_game.current_level]; var items = level.items; for (var i = 0; i < items.length; i++) { var t = items[i]; if (!t.found) { ctx.drawImage(t.img, t.x - t.origin_x, t.y - t.origin_y); var index = 0; for (var ival = 0; ival < t.value_images.length; ival++) { var curr_percent = 100 * t.value / t.value_max; if (t.value_images[ival].percent >= curr_percent) { index = ival; break; } } set_transform(ctx, t); ctx.drawImage(t.value_images[index].img, 0, 0); reset_transform(ctx); } } }
Balthazar:

We no longer want the potions to disappear immediately when the player touches them, so we should stop setting found to true for potions.

function check_item_collisions() { var level = _levels[_game.current_level]; var items = level.items; for (var i = 0; i < items.length; i++) { var item = items[i]; if (!item.found) { if (collide(item, _player)) { if (item.type == "key") { level.player_has_key = true; item.found = true; } else if (item.type == "potion") { adjust_health(30); } else if (item.type == "coin") { _player.coins++; item.found = true; } else if (item.type == "finish") { item.found = true; _game.game_over = true; _game.game_win = true; } item.found = true; } } } }
Balthazar:

Instead of a single 30 point health boost, the potion will now adjust the player's health by its consume_rate as long as the player is touching the potion.

function check_item_collisions() { var level = _levels[_game.current_level]; var items = level.items; for (var i = 0; i < items.length; i++) { var item = items[i]; if (!item.found) { if (collide(item, _player)) { if (item.type == "key") { level.player_has_key = true; item.found = true; } else if (item.type == "potion") { adjust_health(30); if (item.value >= item.consume_rate) { adjust_health(item.consume_rate); item.value -= item.consume_rate; } } else if (item.type == "coin") { _player.coins++; item.found = true; } else if (item.type == "finish") { item.found = true; _game.game_over = true; _game.game_win = true; } } } } }
Balthazar:

And finally, having an empty potion on the screen is rather boring, so let's have the potion slowly replenish itself over time.

Balthazar:

We can do this by adding a update_items() function.

function update_monsters() { ... } function update_items() { var items = _levels[_game.current_level].items; for (var i = 0; i < items.length; i++) { var item = items[i]; if (item.type == "potion") { item.value += item.replenish_rate; if (item.value > item.value_max) { item.value = item.value_max; } } } }
Balthazar:

We'll need to call this function from our update_world() function.

function update_world() { var level = _levels[_game.current_level]; if (_game.in_transition) { draw_transition_screen(); } else if (level.type == "game") { update_monsters(); update_items(); update_player(); check_collisions(); draw(); } else if (level.type == "title") { draw_title_screen(); } requestAnimationFrame(update_world); }

Congratulations! You've earned the Treasure V - Better Potion badge!

GOTO 445