Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Messages - Max

#271
Your scripts / Leever-type enemy
February 27, 2018, 04:45:24 PM
So I've been learning how the solarus team does enemy scripts, and based on that system and their scripts, I've made one I think some people might find useful. It's enemy behavior modeled after the leevers from Zelda, but I've also found it useful for ghosts, and if you made some projectile addition, you could use it for a zora-type enemy too.

This one is made to follow the player if they're close enough to the enemy, but I've taken that part out for some more indifferent enemy types. One problem I've found with this script is that if you attack the enemy, it resets the timer for going underground. So if you keep hitting the enemy quickly, they won't have a chance to escape underground. I'm not 100% sure how to address that.


Code (lua) Select

local behavior = {}

--This enemy is like Zelda's Leever. It moves randomly, and periodically will burrow below ground to later pop up. It is invulnerable when below ground. Special properties of this enemy type are time_underground, time_aboveground, and burrow_deviation. Time below/above ground are minimums, with the deviation being the random amount (in ms) to add to that. That keeps them from popping in and out in swarms if you have multiple. You could also use this for a ghost that phases in/out.
--Required sprites are walking, burrowing, and underground (which can be an empty graphic if you don't want they player to know where they are.
--You'll also need a sound called "burrow1" so they can make a noise and they go underground.

-- The properties parameter is a table.
-- All its values are optional except the sprite.

function behavior:create(enemy, properties)

  local going_hero = false
  local underground = false
 

  -- Set default properties.
  if properties.size_x == nil then
    properties.size_x = 16
  end
  if properties.size_y == nil then
    properties.size_y = 16
  end
  if properties.life == nil then
    properties.life = 2
  end
  if properties.damage == nil then
    properties.damage = 2
  end
  if properties.normal_speed == nil then
    properties.normal_speed = 32
  end
  if properties.faster_speed == nil then
    properties.faster_speed = 48
  end
  if properties.hurt_style == nil then
    properties.hurt_style = "normal"
  end
  if properties.pushed_when_hurt == nil then
    properties.pushed_when_hurt = true
  end
  if properties.push_hero_on_sword == nil then
    properties.push_hero_on_sword = false
  end
  if properties.ignore_obstacles == nil then
    properties.ignore_obstacles = false
  end
  if properties.detection_distance == nil then
    properties.detection_distance = 80
  end
  if properties.obstacle_behavior == nil then
    properties.obstacle_behavior = "normal"
  end
  if properties.movement_create == nil then
    properties.movement_create = function()
      local m = sol.movement.create("random_path")
      return m
    end
  end
  if properties.time_underground == nil then
    properties.time_underground = 2000
  end
  if properties.time_aboveground == nil then
    properties.time_aboveground = 3000
  end
  if properties.burrow_deviation == nil then
    properties.burrow_deviation = 6000
  end
  if properties.burrow_sound == nil then
    properties.burrow_sound = "burrow1"
  end
 

--create enemy properties
  function enemy:on_created()

    self:set_life(properties.life)
    self:set_damage(properties.damage)
    self:create_sprite(properties.sprite)
    self:set_hurt_style(properties.hurt_style)
    self:set_pushed_back_when_hurt(properties.pushed_when_hurt)
    self:set_push_hero_on_sword(properties.push_hero_on_sword)
    self:set_obstacle_behavior(properties.obstacle_behavior)
    self:set_size(properties.size_x, properties.size_y)
    self:set_origin(properties.size_x / 2, properties.size_y - 3)

  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_restarted()
    self:go_random()
    self:check_hero()
   
  end

 

  function enemy:burrow_down()
    sol.timer.stop_all(self)
    self:get_sprite():set_animation("burrowing")
    --play the sound if you're close enough
    local hero = self:get_map():get_entity("hero")
    local _, _, layer = self:get_position()
    local near_hero = self:get_distance(hero) < 140
    if near_hero then sol.audio.play_sound(properties.burrow_sound) end
    sol.timer.start(self, 800, function() self:go_underground() end)
  end

  function enemy:burrow_up()
    self:get_sprite():set_animation("burrowing")
   --play the sound if you're close enough
    local hero = self:get_map():get_entity("hero")
    local _, _, layer = self:get_position()
    local near_hero = self:get_distance(hero) < 140
    if near_hero then sol.audio.play_sound(properties.burrow_sound) end
    sol.timer.start(self, 800, function() self:go_aboveground() end)
  end

  function enemy:go_underground()
    underground = true
    self:get_sprite():set_animation("underground")
--this code is so you don't get chased by an underground dude
    local m = properties.movement_create()
    if m == nil then
      -- No movement.
      self:get_sprite():set_animation("stopped")
      m = self:get_movement()
      if m ~= nil then
        -- Stop the previous movement.
        m:stop()
      end
    else
      m:set_speed(properties.normal_speed)
      m:set_ignore_obstacles(properties.ignore_obstacles)
      m:start(self)
    end
--]]
    self:set_can_attack(false)
    sol.timer.start(self, (properties.time_underground + math.random(properties.burrow_deviation)), function() self:burrow_up() end)
   
  end

  function enemy:go_aboveground()
    underground = false
    self:set_can_attack(true)
    self:get_sprite():set_animation("walking")
    self:check_hero()
  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 or enemy:has_layer_independent_collisions()) and
        self:get_distance(hero) < properties.detection_distance

    if near_hero and not going_hero then
      if underground == false then self:go_hero()
      else self:go_random()
      end
    elseif not near_hero then
      self:go_random()
    end
--and repeat this every 150ms
    sol.timer.start(self, 150, function() self:check_hero() end)

  end



  function enemy:go_random()
    going_hero = false
    sol.timer.start(self, properties.time_aboveground+math.random(properties.burrow_deviation), function() self:burrow_down() end)
    local m = properties.movement_create()
    if m == nil then
      -- No movement.
      self:get_sprite():set_animation("stopped")
      m = self:get_movement()
      if m ~= nil then
        -- Stop the previous movement.
        m:stop()
      end
    else
      m:set_speed(properties.normal_speed)
      m:set_ignore_obstacles(properties.ignore_obstacles)
      m:start(self)
    end
  end



  function enemy:go_hero()
    going_hero = true
    sol.timer.stop_all(self)
    sol.timer.start(self, (properties.time_aboveground + math.random(properties.burrow_deviation + 2000)), function() self:burrow_down() end)
    local m = sol.movement.create("target")
    m:set_speed(properties.faster_speed)
    m:set_ignore_obstacles(properties.ignore_obstacles)
    m:start(self)
    self:get_sprite():set_animation("walking")
  end



end

return behavior
#272
Development / Displaying an image on pause
February 27, 2018, 04:32:05 PM
Hey all, sorry to kind of flood the board with questions. I've been saving them up for a year trying to figure them out myself first but there's a few I need help with.

So, I've been trying to learn Solarus and lua coding for a year and a half, and I still can't figure out how to create a pause menu. I've basically given up and accepted this as a design limitation to constrain myself with, so my game only has two items. But anyway, all my playtesters have given me the feedback that with all the side quests in my game, they'd really like a main quest log kind of feature because they forget what they were supposed to be doing. I think that just displaying the current stage of the main quest on the pause screen is the way to go.

So from studying the various solarus team scripts, I think that the way to show an image would be something like this:
Code (lua) Select

function game:on_paused()
  local pause_infra_img = sol.surface.create("hud/pause_infra.png")
  local pause_surface = sol.surface.create()
  pause_infra_img:draw(pause_surface)
end


However, I'm really confused about how all this works. It seems like you can create a surface, but it won't be immediately drawn? So you have to draw it, but you need a surface to draw it on. But wouldn't the second surface need to be drawn on a surface too? Ahh!
#273
Development / Creating a respawn point on map changed?
February 27, 2018, 04:23:51 PM
Hi guys!

One thing I dislike about A Link to the Past is that you have to restart at your house or the Pyramid every time you die in the overworld. In the game I'm working on, you spend quite a lot of time in the overworld doing side-quests, and I don't want to set the player back so far if they're killed. I'd like to make it so if you die, you can be taken back whatever map you're currently on.

I know if the player is transported to a new map via a destination, you can save that location for restarting. However, if you use the side of the map scrolling feature, there is no destination to go back to, so one workaround is to design my overworld so there's never any side of the map teletransporters, but I'd like to avoid that if possible.

I also tried dynamically creating a destination using game:on_map_changed() and map:create_destination(), however it seems like the game erases this destination when a gameover happens, because the hero isn't taken back to it. I had this function in my game_manager script. This was my code that doesn't seem to work:

Code (lua) Select

--Set Respawn point whenver map changes
function game:on_map_changed()
  local map = game:get_map()
  local hero = game:get_hero()
  local x, y, layer = hero:get_position()
  map:create_destination({
    name = "respawn",
    x = x, y = y, layer = layer,
    direction = hero:get_direction(),
    save_location = "yes",
    default = true,
  })


But yeah, it seems like that destination is erased whenever a gameover happens, which makes it kind of pointless. Any ideas?
#274
Your projects / Re: Ocean's Heart
February 27, 2018, 04:14:18 PM
Oh, thanks guys! I'm glad you like the look of it. All the graphics are something I've made myself for this project. It's been a great exercise to teach myself to do pixel art better, I've come a long way since I started a year and a half ago. I've also composed all the music, and I'm in the process of creating all the sound effects. I just recorded my dog last week to turn into a monster-damage sound.

My plan is to, at some point, re-format my tileset and release many of the graphics I'm creating under a creative commons license, similar to what Diarandor is doing. I figure it seems like the Solarus team has their scripts included under the GPL v3, right? I've been looking over those and taking them apart and using them and modifying them for this game, so I figure the least I can do in return is contribute many of my graphics the same way.

With that said, the way I've been working is adding onto the tilesets as I go and they're a hot mess. I know how to use them, but they'll take some modification before they'll really be ready to release. Besides that, I know I'm still going to be creating a lot more graphics for this game's later areas. If there's any interest though, I could throw together a very basic tileset if anyone wants to use these graphics right now.

And Christopho, thanks for the game-sharing advice. I've got a couple more music tracks to write and record for some areas, and then I'll be following that to share this game. Thanks : )
#275
Development / Re: Activating a switch from a script?
February 27, 2018, 03:59:13 PM
Oh! I think I get it! Yeah, I thought events were special functions that were only called when certain requirements were met.

So I think my code should look like this, which seems to work:


Code (lua) Select
  elseif entity_type == "switch" then
    --activate the switch you hit if it's solid or arrow-type
    local switch = entity
    local sprite = switch:get_sprite()
    --check if the switch's sprite is the right type for activating
    if flying and sprite ~= nil and
    (sprite:get_animation_set() == "entities/switch_solid" or "entities/switch_lever_1" or "entities/switch_arrow") then

      --if it's off, turn it on. Or vice-versa.
      if not switch:is_activated() then
        sol.audio.play_sound("switch")
        switch:set_activated(true)
        switch:on_activated()
      else
        sol.audio.play_sound("switch")
        switch:set_activated(false)
        switch:on_inactivated()
      end

#276
Development / Activating a switch from a script?
February 27, 2018, 02:15:30 AM
Hello!

I've been looking over the Solarus Team's bow/arrows script, which is super cool, and developed a script using it that shoots fire arrows. But one problem I'm encountering is that it uses switch:set-activated() to allow arrows to interact with switches. However, this method doesn't trigger the switch:on_activated() event.

I've found a workaround with setting a timer that checks to see if the switch has been activated ever 100ms or so and acts accordingly, but is there a better way to do this? Is there a way to trigger the event from a script?

Thanks!
#277
Your projects / Ocean's Heart
February 27, 2018, 01:59:34 AM
Hello! I'm regularly updating this post to reflect progress on this game. Ocean's Heart is a game I started in about March of 2017 in order to teach myself to use the Solarus Engine and learn some coding. The most recent update was October 11th, 2018.
I've been using twitter to regularly post screenshots and news about my game, so follow me there for frequent news!:
https://twitter.com/11mraz

The game follows the journey of a girl whose father left their town six months ago to defeat pirates that attacked and kidnapped another girl. She spends most of the game following his trail and uncovering the plot that's kept him away for so long. I'm aiming for a cross between Zelda: The Wind Waker, and the Witcher 3:Wild Hunt, emphasizing exploration and interesting side-plots. I'm not trying to emulate Zelda in its entirety, so there's a few differences in my approach to dungeons, player power progression, depth of sidequests, etc. The final game is on track to be 5-8 hours long depending on how much exploration the player is interested in.

The game, rough estimate, is about 70% complete. There are currently several islands to explore, a small handful of dungeons (some larger, some smaller), items and abilities to find, many sidequests, and a main quest that I'm estimating might take a few hours to get as far as you currently can. You could reasonably sink 3-5 hours into the game right now if you wanted to exhaust every side quest and find every hidden power-up and treasure. At the moment, I'd like to release Ocean's Heart in 2019, but you know how video game releases go.

I've decided to take down the demo I had for now, since much about the game has changed and it was starting to become less than representative. It's just confusing for the both of us when people ask questions and I've already changed a lot. I'm planning another small demo sometime in the next few months that is a better vertical slice and will stand alone. If you're interested in testing or doing a let's play, send me a message or email and I might be able to hook you up with a playable build!



Here's a short, low-quality trailer of some areas:
(there's no way to embed youtube videos on this forum, is there?)

https://youtu.be/2RySTLSKjUs

And some screenshots: