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.


Messages - Max

Pages: 1 ... 13 14 [15]
211
Development / Re: Creating a respawn point on map changed?
« on: March 01, 2018, 03:33:01 am »
It's a nice idea, but that behavior isn't actually default. The default, on restarting the game, teleports you to the last destination entity that saved your location. It might seem like that's the last map that has a different world, but that's actually only the case if you arrive on that world via a destination entity that has "on world changed" as its save location property.

But yeah, if you only change maps via teleports to a destination (ie, not side-of-map transitions), and set a new world for every map, that will work great, and I'd definitely recommend it, haha. I might end up going back and reworking all my maps so there's point-to-destination transition instead of scrolling ones : /

212
Development / Re: Displaying an image on pause
« on: March 01, 2018, 12:02:20 am »
Awesome! Thanks, Diarandor! I should probably get used to typing that, haha.




So for anyone interested in replicating what I've done (as it's like a baby step toward building your first pause menu, haha), here's how it goes. I'll try to do a little tutorial my past self would appreciate. My goal was, when you pause the game, a box comes up with the current step of the main quest. It's a little more complicated for me because there's a number of times when there's two main quest lines that you can do in either order, but anyway. Here's how it looks.

In my game_manager script, I've defined what to do when the game is paused. "pause_infra" is a separate script I've created that will work as a menu, which is a special way the engine can treat any table, from what I understand. So when the game is paused, I get that script using the require() syntax. Then, now that I have it, I'm going to call one of its functions, as well as start the menu via sol.menu.start()

After the game is unpaused, I want to stop the menu so it doesn't stay up. I've also got the choice to save, continue, or quit the game, which I learned from Christopho's wonderful tutorial on his youtube channel.

Code: (lua) [Select]
--Pause Menu
function game:on_paused()

  require("scripts/menus/pause_infra")
  pause_infra:get_game(game)
  sol.menu.start(game, pause_infra)


  --save dialog
  game:start_dialog("_game.pause", function(answer)
    if answer == 1 then
      game:set_paused(false)
    elseif answer == 2 then
      game:save()
      game:set_paused(false)
    elseif answer == 3 then
      sol.main.exit()
    end
  end)


end --end of on:paused function

function game:on_unpaused()
  require("scripts/menus/pause_infra")
  sol.menu.stop(pause_infra)
end


So that's all in the game_manager script. And here's the script I have that's called "pause_infra.lua"
I've added some comments to help anyone who might be able to learn from me.

Code: (lua) [Select]
pause_infra = {}    --this line means that we can refer to this a table called "pause_infra"
local current_log_A       -- these two lines are just setting up some local variables I'm going to be using
local current_log_B



--so if you remember in my game_manager script, I called pause_infra:get_game(game). I'm not 100% sure if I needed to type
--"game" into the argument on both ends, maybe someone can correct me if I'm wrong. But the purpose of that is to give this
--script the "game" userdata, just so it can call game:get_value(). This will allow us to display savegame values when we pause
--the game, such as what our stats are, or in this case, what step of the main quest we're on.

function pause_infra:get_game(game)

--so what's going to happen here is I'm going to get the current step of the main quest from game:get_value("quest_log_a")
--this value will be changed on each map where you complete a step. So for example, if you needed to defeat a monster,
--when that happens, I'd change the value of "quest_log_a" in the monster's on_dead() event.
--Then, I'm making a string variable called "current_log_A" which uses the number from "quest_log_a" to form the name of
--a png file I have saved. So if you're on step 4 of the quest, current_log_A would equal "hud/quest_logs/quest_logA4.png"
--Then this is repeated because I have two quest logs, an A and a B.

    local templog_a = game:get_value("quest_log_a")
    current_log_A = "hud/quest_logs/quest_log_A"..templog_a..".png"

    local templog_b = game:get_value("quest_log_b")
    current_log_B = "hud/quest_logs/quest_log_B"..templog_b..".png"

end



--Then this is where we actually display everything. The function pause_infra:on_draw() is automatically called by the engine
--when the pause_infra menu is started (which I did in my game_manager script). You have to display images in the on_draw function
--because otherwise, they'll only be drawn for 1 ms, which nobody will see. So now, every time the engine calles the :on_draw()
--function (which seems to be constantly when the menu is running), the image will be displayed.
--The way we do the image is to create a surface from a .PNG file, then just draw it.
--I'm personally doing this for 3 things, 2 quest logs and a box around them. Later, I'll add in things like your attack or defense
--stats, or an inventory, but this is just step one.

function pause_infra:on_draw(dst_surface)
  --draw menu architecture
  local pause_img = sol.surface.create("hud/pause_infra.png")
  pause_img:draw(dst_surface)

  --draw quest log A
  local log_a_img = sol.surface.create(current_log_A)
  log_a_img:draw(dst_surface)

  --draw quest log B
  local log_b_img = sol.surface.create(current_log_B)
  log_b_img:draw(dst_surface)

end




So yeah, thanks again, learning this has been super helpful, hopefully me documenting what I learned at a very fine level can help someone else trying to make a menu. I've still got a long way to go before I can make an item select menu, but this has been a good start!

213
Your scripts / Re: Leever-type enemy
« on: February 28, 2018, 10:53:50 pm »
I've tried your first solution already for a boss, if the above ground duration is short enough it works unless you trap him in a corner. Perhaps if the on_hurt event burrowed it underground immediately it'd work.

The problem arises because the timer for burrowing underground is started from the on_restarted or on_reset event. Since the engine calls this automatically when the enemy gets hurt, it restarts the timer. I suppose you could make the timer start from an on_created event or something like this. I'll try and test that out sometime after I work on a few other issues.

214
Development / Re: Creating a respawn point on map changed?
« on: February 28, 2018, 10:48:58 pm »
I actually tried this- still didn't reset the map. Perhaps it's because I used the same script to send the hero there and send her back. The game over function went something like:

Hero:teleport(blank_map)
Hero:teleport(respawn_map, respawn_destination)

Maybe it happens so quick the engine didn't register the change?

215
Development / Re: Creating a respawn point on map changed?
« on: February 28, 2018, 10:02:11 pm »
Thanks Diarandor! I've got it basically working. In my game_manager script, I've used the multi events script to register a function for map:on_opening_transition_finished() to set a respawn point. Map:on_started wouldn't work because the hero would be stuck halfway into the map and couldn't move.  My code looks like this for any others interested:

Code: (lua) [Select]
local map_meta = sol.main.get_metatable("map")
map_meta:register_event("on_opening_transition_finished", function()
    local map = game:get_map()
    game:set_value("respawn_map", map:get_id() )
    print(map:get_id().." respawn saved") --this is for testing
    local hero = game:get_hero()
    local x, y, layer = hero:get_position()
    game:set_value("respawn_x", x) game:set_value("respawn_y", y) game:set_value("respawn_layer", layer)
    game:set_value("respawn_direction", hero:get_direction())
end)

Then, when the game over event is called, I first teleport the hero to the saved map, then use map:create_destination() to make a respawn destination using the respawn data I've saved. Then I teleport the hero to this destination, refill her health, and do game:stop_game_over()



So, this basically works. There's a couple problems. The most easily solved is that this won't work for any maps that have map:on_opening_transition_finished() defined already until I go back and fix those to fit with the syntax for the multi events script, but that's easily solved.

More pressingly, this method of sending the hero back to the saved respawn point doesn't reset the map- for example, all enemies you've killed are still dead as if you've never left the map, any blocks you've pushed don't reset, they're still where you've pushed them. I can see this causing serious problems with puzzles, if you partially finish one, then die, you could easily trap yourself since it's basically as if you've never left the room. I haven't tested this much yet, but I'm pretty sure it'll break my game at some point- for example if you die during a boss battle and the room has been closed to trap you in, you'll then be locked outside and won't be able to get back in to fight.

I'm looking around to try to figure out if there's a way to reset the map. I've tried calling game:start(), which I thought would work because I call this after I teleport the hero to a destination that has {save_location = "yes"} as one of it's properties. Since being sent to a destination that saves your location should save your location, I'd assume that after that, calling game:start() would take you back to the saved location, but in my testing, it won't. It'll go back to the last destination that had that property.



For anyone following along and trying to replicate this, I'd definitely recommend just designing your maps so that if you want the respawn point to be saved, you have to transition to the new map using a destination. Doing it this way is complicated and could have been solved by just designing better upfront, haha.

216
Development / Re: Displaying an image on pause
« on: February 27, 2018, 08:21:01 pm »
I'm okay with doing it the simple way at first, haha. I've looked through the code for ZS:DX, which is why I think the code I wrote in the first post should work, but it's not showing me anything. Is there a problem with that code?

From looking at the pause scripts in ZS:DX and others, it seems like this should work:
Code: (lua) [Select]
function game:on_paused()
local pause_infra_img = sol.surface.create("hud/pause_infra.png")
end

But that doesn't show anything either. I can't get either of those to work, am I missing something?

217
Your scripts / Leever-type enemy
« on: 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

218
Development / Displaying an image on pause
« on: 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!

219
Development / Creating a respawn point on map changed?
« on: 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?

220
Your projects / Re: Ocean's Heart
« on: 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 : )

221
Development / Re: Activating a switch from a script?
« on: 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

222
Development / Activating a switch from a script?
« on: 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!

223
Your projects / Ocean's Heart
« on: 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:








Pages: 1 ... 13 14 [15]