Hybrid game (Link's Awakening + Lost Vikings 2)

Started by Diarandor, August 01, 2015, 07:29:56 AM

Previous topic - Next topic
@wrightmat: Hi! Yes, of course you can use it!

I have added a previous version of the slime enemy that jumps to the sample quest. You can get it here:
https://github.com/christopho/solarus/blob/dev/sample_quest/data/enemies/slime_green.lua

I explained some things about this trajectory here (MetalZelda is using it too for his fishing rod):
http://forum.solarus-games.org/index.php/topic,572.0.html

That trajectory is the same I use for the jump of the hero (shifting the sprite).

The important part of the code is the following part (of course there are more details, like the creation of the shadow, etc, that you can check in the script of the slime enemy):
Code (Lua) Select

  -- Start shift on sprite.
  local function f(t) -- Shifting function.
    return math.floor(4 * max_height * (t / jump_duration - (t / jump_duration) ^ 2))
  end
  local t = 0
  local refreshing_time = 10
  sol.timer.start(self, refreshing_time, function() -- Update shift each 10 milliseconds.
    sprite:set_xy(0, -f(t))
    t = t + refreshing_time
    if t > jump_duration then return false
      else return true
    end
  end)
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

Thanks Diarandor! I am having a slight issue though, and I think you may have had something similar and don't recall how you fixed it. After the enemy jumps, the shadow is not being removed and the enemy remains invincible. Everything works fine up to this point, so I'm guessing it's something to do with a timer? Here's my version of the code:

Code (lua) Select

local enemy = ...

-- Tektite: a jumping enemy which moves toward the hero.

local state -- States: "stopped", "going_hero", "jumping", "prepare_jump", "finish_jump", "going_random"
local speed = 20
local detection_distance = 80
local jump_duration = 1000 -- Time in milliseconds.
local max_height = 24 -- Height for the jump, in pixels.
local jumping_speed = 100 -- Speed of the movement during the jump.

function enemy:on_created()
  self:set_life(3); self:set_damage(4)
  self:create_sprite("enemies/tektite_blue")
  self:set_size(16, 16); self:set_origin(8, 13)
  self:set_hurt_style("monster")
  self:set_pushed_back_when_hurt(true)
  self:set_push_hero_on_sword(false)

  function sprite:on_animation_finished(animation)
    if animation == "immobilized" then -- state: finish_jump
      state = "stopped"
      sprite:set_animation("stopped")
      sol.timer.start(self, 200, function()
        enemy:go_hero()
      end)
    end
  end
end

function enemy:on_restarted()
  is_pushing_enemy = false
  self:check_hero()
end

function enemy:go_hero()
  state = "going_hero"
  self:get_sprite():set_animation("walking")
  local m = sol.movement.create("target")
  m:set_speed(speed)
  m:set_target(self:get_map():get_hero())
  m:start(self)
  -- Prepare jump.
  sol.timer.start(self, 1500, function()
    self:prepare_jump()
  end)
end

function enemy:prepare_jump()
  state = "prepare_jump"
  self:stop_movement()
  self:get_sprite():set_animation("shaking")
  sol.timer.start(self, 2000, function()
    enemy:jump()
  end)
end

function enemy:finish_jump()
  state = "finish_jump"
  self:stop_movement()
  self:get_sprite():set_animation("immobilized")
  self:set_can_attack(true) -- Allow to attack the hero again.
end

function enemy:jump()
  -- Set jumping state, animation and sound.
  state = "jumping"
  local sprite = self:get_sprite()
  sprite:set_animation("walking")
  sol.audio.play_sound("jump")
  self:set_invincible(true) -- Set invincible.
  self:set_can_attack(false) -- Do not attack hero during jump.
  -- Start shift on sprite.
  local function f(t) -- Shifting function.
    return math.floor(4 * max_height * (t / jump_duration - (t / jump_duration) ^ 2))
  end
  local t = 0
  local refreshing_time = 10
  sol.timer.start(self, refreshing_time, function() -- Update shift each 10 milliseconds.
    sprite:set_xy(0, -f(t))
    t = t + refreshing_time
    if t > jump_duration then return false
      else return true
    end
  end)
  -- Add a shadow sprite.
  local shadow = self:create_sprite("entities/shadow")
  shadow:set_animation("big")
  local new_frame_delay = math.floor(jump_duration/shadow:get_num_frames())
  shadow:set_frame_delay(new_frame_delay)
  -- Add movement towards near the hero during the jump. The jump does not target the hero.
  -- The angle is partially random to avoid too many enemies overlapping.
  local m = sol.movement.create("straight")
  local angle = self:get_angle(self:get_map():get_hero())
  math.randomseed(os.time()) -- Initialize random seed.
  local d = 2*math.random() - 1 -- Random real number in [-1,1].
  angle = angle + d*math.pi/4 -- Alter jumping angle, randomly.
  m:set_speed(jumping_speed)
  m:set_angle(angle)
  m:start(self)
  -- Finish the jump.
  sol.timer.start(self, jump_duration, function()
    self:remove_sprite(shadow) -- Remove shadow sprite.
    sol.timer.start(self, 1, function() -- TODO: remove this after #868 is fixed.
      self:set_default_attack_consequences() -- Stop invincibility after jump.
      self:finish_jump()
    end)
  end)
end

function enemy:on_movement_changed(movement)
  local direction4 = movement:get_direction4()
  local sprite = self:get_sprite()
  sprite:set_direction(direction4)
end

function enemy:on_obstacle_reached(movement)
  if not going_hero then
    self:go_random()
    self:check_hero()
  end
end

function enemy:on_hurt()
  if timer ~= nil then
    timer:stop()
    timer = nil
  end
end

function enemy:check_hero()
  local hero = self:get_map():get_entity("hero")
  local _, _, layer = self:get_position()
  local _, _, hero_layer = hero:get_position()
  local near_hero = layer == hero_layer and self:get_distance(hero) < 100

  if near_hero and state ~= "going_hero" then
    self:go_hero()
  elseif not near_hero and state == "going_hero" then
    self:go_random()
  end
  timer = sol.timer.start(self, 2000, function() self:check_hero() end)
end

function enemy:go_random()
  state = "going_random"
  sol.timer.start(self, 2000, function() self:check_hero() end)
end

Hi wrightmat. Which version of Solarus are you using? There were some issues with Solarus 1.4 which are fixed in the dev version (thanks to Christopho). Make a backup and test it with the development version, so we can know if the issue comes from the engine or not. (My slime enemy works fine in the dev version, but probably not in 1.4.)
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

I'm using 1.4.5, and I thought that might be the problem. I may try the dev version or I may wait until 1.5 comes out as stable. Thanks so much!

You can use the 28th March snapshot, it's the most stable snapshot of 1.5.
If this is just for testing some enemies this is the way to go.

http://www.solarus-games.org/downloads/solarus/win32/solarus-1.5.0-snapshot-20160328-win32.zip


There is a more recent one (April 3rd), is it less stable?

I got several errors regarding sprites (illegal direction 0) and random crashes, but that might be me.
These errors didn't exist in the 28th march(the crystal fix), so i'll stuck with this version

April 07, 2016, 04:25:27 PM #37 Last Edit: April 07, 2016, 05:17:16 PM by Christopho
Ok then please report these problems :)

EDIT: Actually there is a crash when a pickable treasure dropped by a destructible or an enemy starts blinking, I fixed it two days ago. Maybe this is your random crash?  No one reported it so I did not make a new snapshot.
The errors regarding sprites are problems that could exist before but were never reported. They should be harmless. One of them was the fault of the engine (Illegal direction 3 in animation 'sword' of sprite 'hero/shield1') and I also fixed it yesterday.
So okay, I will do a new snapshot now.


Yes, I confirm, no more crash and no more errors.
Thanks for the snapshot

Greetings Solarus community! I uploaded a new garbage-video: https://www.youtube.com/watch?v=jKRV_kcr3Vw&feature=youtu.be

Improvements and added features:

1) A new (original) HUD has been added, similar to the one in Link's Awakening. It can display the current hero, three assignable items for each hero, a money counter and an action icon (it will show the action that can be done, like lifting, speaking, etc). The life counters for the three heroes are still to be done (I have not decided yet if they will be hearts, or bars, or a mixture of both).

2) As you might have noticed, the sword in the video is not the built-in one but a fully scripted one. I was able to mimic all actions of the sword, including the loading and tapping animations and the spin attack. New feature: the normal sword attack changes direction each time the sword is used. New features still to do for this custom sword: fast attack, sword jumping attacks, and running with (custom) boots attack.
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

New video! Here I show how the custom sword can be combined with the custom jump (like in Link's Awakening).
https://www.youtube.com/watch?v=REtiEtbxt_s&feature=youtu.be
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."


Thanks! Your feedback gives me motivation.

I plan to program a few more features for the custom sword in the future:
1-Custom boots compatible with sword (like in Link's Awakening). This will be easy to code.
2-Fast attack if the attack command is pressed many times and quickly (like in Terranigma). This will be easy to code too.
3-Some powerful attack to replace the super spin attack (maybe a tornado-with-electricity effect). This will be quite tedious to draw.
4-Maybe a slash-electric attack towards up, if the jump button is pressed during a normal attack (good against flying enemies). Although I am not sure of this one because maybe there is enough diversity of sword attacks.

Some sword abilities will be unblocked during the game (sword loading, spin attack, super spin attack, sword inverting direction on each attack, fast attack, etc).
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

New video showing the new weapon animations: bow, boomerang and hookshot.
https://www.youtube.com/watch?v=t02HS7kxX7c&feature=youtu.be
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."