Diablo like health/magic?

Started by Starlock, September 02, 2015, 03:31:47 AM

Previous topic - Next topic
Is it possible for the hearts and magic bar used in ZSDX to be instead shown as red and blue orbs like in Diablo? I tried to simply use the code and custom graphics but I'm not exactly sure how to compile the graphics to create a HUD.

You can do whatever you want. Try inspiring you with the magic meter from ZSDX , it's more or less the same thing I think. I'm not good enough to help you with the programmation of it, but it's clearly possible (and probably more easy to do than using hearts ^^)

I finally started working on the code for this...

Code ( lua) Select
-- DIABLO STYLE HEALTH ORB

local life_bar = {}

function life_bar:new(game)
  local object = {}
  setmetatable(object, self)
  self.__index = self

  object:initialize(game)

  return object
end

function life_bar:initialize(game)
  self.game = game
  self.surface = sol.surface.create(48, 48)
  self.life_bar_img = sol.surface.create("hud/health_orb.png")
  self.container_img = sol.surface.create("hud/health_orb.png")
  self.life_displayed = game:get_life()
  self.max_life_displayed = 0

  self:check()
  self:rebuild_surface()
end

-- Checks whether the view displays the correct info
-- and updates it if necessary.
function life_bar:check()
  local need_rebuild = false
  local max_life = self.game:get_max_life()
  local life = self.game:get_life()

  -- Maximum health
  if max_life ~= self.max_life_displayed then
    need_rebuild = true
    if self.life_displayed > max_life then
      self.life_displayed = max_life
    end
    self.max_life_displayed = max_life
  end

  -- Current health
  if life ~= self.life_displayed then
    need_rebuild = true
    local increment
    if life < self.life_displayed then
      increment = -1
    elseif life > self.life_displayed then
      increment = 1
    end
    if increment ~= 0 then
      self.life_displayed = self.life_displayed + increment

      if (life - self.life_displayed) % 10 == 1 then
--sol.audio.play_sound("magic_bar")
      end
    end
  end

  -- Redraw the surface only if something has changed.
  if need_rebuild then
    self:rebuild_surface()
  end

  -- Schedule the next check.
  sol.timer.start(self.game, 20, function()
    self:check()
  end)
end

function life_bar:rebuild_surface()
  self.surface:clear()

  -- Max health
  self.container_img:draw(self.surface)

  -- Current health
  self.life_bar_img:draw_region(0, 0, 2 + self.life_displayed, 8, self.surface)
end


function life_bar:set_dst_position(x, y)
  self.dst_x = x
  self.dst_y = y
end

function life_bar:on_draw(dst_surface)
  if self.max_life_displayed > 0 then
    local x, y = self.dst_x, self.dst_y
    local width, height = dst_surface:get_size()
    if x < 0 then
      x = width + x
    end
    if y < 0 then
      y = height + y
    end

    self.surface:draw(dst_surface, x, y)
  end
end

return life_bar



It displays the graphic of the orb but I'm not sure how to make the bar go down vertically since the code was taken from the horizontal magic bar..  :-\

http://imgur.com/Rnxd22S

You just have to draw the rctangle corresponding to the life you have. That seems to be done in the function life_bar:rebuild_surface(), in the last paragraph inside that function. Of course you need to use the coordinates of the region of the picture to draw, so I cannot help you more.
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

I made the "bar" for the health on the top left corner which is why i set the region coordinates for the code at 0,0 and the orb size is about 48x48. I'm not too sure what change I should make...

The 4 first parameters of the built-in function draw_region allow to draw only a rectangle of the image, to draw only the part of the bar you need to display (which dependends on the life). That's the part you have to modify. All of this is inside the function rebuild_surface().
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

January 31, 2016, 07:00:26 PM #6 Last Edit: February 02, 2016, 11:24:38 AM by froggy77
+1 Diarandor.

Then Starlock must find a solution, for example with a cross-multiplication, to compare one life to the max life. 1 pixel is equal to 1 life if you have max_life = 39 (the height of the health orb), but if max life is different, 1 pixel will not represent 1 life.  You should reorganize the sprites of the picture in a 48x48 grid like this, it will help you to draw region and also to create animation.

The idea of this orb is very interesting for an old-school game.  ;)

EDIT: So interesting that I'm trying to fix and improve your script.  :P  I will give you the result tomorrow.

EDIT 2: Not exactly, tomorrow...

SEE ATTACHMENT.

Code (Lua) Select

    -- DIABLO STYLE HEALTH ORB
-- Script by Starlock from an another script (magic_bar.lua) written by Christopho.
-- Modified by froGgy.

-- v 1.0:
-- First version posted by Starlock. It was named 'life_bar'
    -- v 2.0:
-- Tested with SOLARUS version 1.5
-- Script renamed to 'health_orb'. So need changes in ../data/scripts/hud/hud.lua
-- Correction of problems to draw surfaces.
-- Adding get_pixel(l, m). See explanation below.
-- Adding animations (container_surface replaced by container_sprite), so need ../data/sprites/hud/health_orb.dat
-- and need this line 'sprite{ id = "hud/health_orb", description = "HUD: Health Orb" }' in ../data/project_db.dat
-- Adding calls to the sounds.
-- Sprites reorganized in the image. Need new ../data/sprites/hud/health_orb.png


    local health_orb = {}


-- Explanation of this local function named 'get_pixel(l, m)'
-- 39 is orb height. So if you change the orb size, replace 39 and 38 by your own values. Idem in rebuild_surface()
-- 1 pixel is equal to 1 life if you have max_life = 39, but if max_life is different, 1 pixel will not represent anymore 1 life,
-- that's why this function is useful.
-- Parameters: l is life and m is max_life.
local function get_pixel(l, m)

  local result = (39 * l)/m
  local e, d = math.modf(result)
  if e == 0 or d >= 0.5 and e ~= 38 then
e = math.ceil(result)
  end
  return e
end

    function health_orb:new(game)

      local object = {}
      setmetatable(object, self)
      self.__index = self
     
      object:initialize(game)
     
      return object
    end

    function health_orb:initialize(game)

      self.game = game
      self.surface = sol.surface.create(48, 48)
      self.health_orb_img = sol.surface.create("hud/health_orb.png")
      self.container_sprite = sol.sprite.create("hud/health_orb")
  self.container_sprite:set_animation("normal")
      self.life_displayed = self.game:get_life()
  self.life_pixel_displayed = 0
      self.max_life_displayed = 0
 
  self:check()
      self:rebuild_surface()
    end

function health_orb:on_started()

    -- This function is called when the HUD starts or
    -- was disabled and gets enabled again.
    -- Unlike other HUD elements, the timers were canceled because they
    -- are attached to the menu and not to the game
    -- (this is because the hearts are also used in the savegame menu).
      self.danger_sound_timer = nil
      self:check()
      self:rebuild_surface()
end

    -- Checks whether the view displays the correct info
    -- and updates it if necessary.
    function health_orb:check()

      local need_rebuild = false
      local max_life = self.game:get_max_life()
      local life = self.game:get_life()

      -- Maximum health
      if max_life ~= self.max_life_displayed then
        need_rebuild = true
        self.max_life_displayed = max_life
self.life_pixel_displayed = get_pixel(self.life_displayed, self.max_life_displayed)
      end

      -- Current health
      if life ~= self.life_displayed then
        need_rebuild = true
        local increment
        if life < self.life_displayed then
          increment = -1
        elseif life > self.life_displayed then
          increment = 1
  if self.game:is_started()
and self.life_displayed % 4 == 0 then
sol.audio.play_sound("heart")
          end
        end
        self.life_displayed = self.life_displayed + increment
for i = 1, get_pixel(1, self.max_life_displayed) do
self.life_pixel_displayed = get_pixel(self.life_displayed , self.max_life_displayed)
end
      end

  -- If we are in-game, play animations and a sound if the life is low.
      if self.game:is_started() then
  if self.game:get_life() <= self.game:get_max_life() / 4
  and not self.game:is_suspended() then
-- Show animations "danger..." of the empty container sprite if not already done.
need_rebuild = true
if self.container_sprite:get_animation() ~= "danger" then
  self.container_sprite:set_animation("danger")
end
if self.danger_sound_timer == nil then
  self.danger_sound_timer = sol.timer.start(self, 250, function()
self:repeat_danger_sound()
  end)
  self.danger_sound_timer:set_suspended_with_map(true)
end
  else
if self.container_sprite:get_animation() ~= "normal" then
  self.container_sprite:set_animation("normal")
  need_rebuild = true
end
  end
      end

      -- Redraw the surface only if something has changed.
      if need_rebuild then
        self:rebuild_surface()
      end

      -- Schedule the next check.
      sol.timer.start(self.game, 50, function()
        self:check()
      end)
    end

function health_orb:repeat_danger_sound()

  if self.game:get_life() <= self.game:get_max_life() / 4 then
sol.audio.play_sound("danger")
self.danger_sound_timer = sol.timer.start(self, 750, function()
self:repeat_danger_sound()
end)
self.danger_sound_timer:set_suspended_with_map(true)
  else
self.danger_sound_timer = nil
  end
end

    function health_orb:rebuild_surface()

      self.surface:clear()
     
      -- Empty container
  self.container_sprite:draw(self.surface)
     
      -- Current health
  self.health_orb_img:draw_region(3, (42 - self.life_pixel_displayed), 42, self.life_pixel_displayed, self.surface, 3, (42 - self.life_pixel_displayed))
end

    function health_orb:set_dst_position(x, y)

      self.dst_x = x
      self.dst_y = y
    end

    function health_orb:on_draw(dst_surface)

      if self.max_life_displayed > 0 then
        local x, y = self.dst_x, self.dst_y
        local width, height = dst_surface:get_size()
        if x < 0 then
          x = width + x
        end
        if y < 0 then
          y = height + y
        end
     
        self.surface:draw(dst_surface, x, y)
      end
    end

    return health_orb


________________________

In ../data/scripts/hud/hud.lua with other elements:

-- blablabla
  local health_orb_builder = require("scripts/hud/health_orb")
-- blablabla
-- Then, example of position!
  menu = health_orb_builder:new(game)
  menu:set_dst_position(-104, 6)
  hud.elements[#hud.elements + 1] = menu

________________________

In ../data/project_db.dat:
sprite{ id = "hud/health_orb", description = "HUD: Health Orb" }

________________________

In a ../data/sprites/hud/health_orb.dat with the health_orb.png:

animation{
  name = "danger",
  src_image = "hud/health_orb.png",
  frame_delay = 200,
  frame_to_loop_on = 0,
  directions = {
    { x = 0, y = 48, frame_width = 48, frame_height = 45, origin_x = 0, origin_y = 0, num_frames = 4 },
  },
}
animation{
  name = "normal",
  src_image = "hud/health_orb.png",
  directions = {
    { x = 48, y = 0, frame_width = 48, frame_height = 45, origin_x = 0, origin_y = 0 },
  },
}