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 - lefthandedhero

In my code, I wrote the "game:on_command_pressed(command)" event to override the built-in behaviour of the sword command. However, when I run my game, the "game:on_command_pressed(command)" event is never called and the built-in behaviour is used.

I put the event in its own metatable called "game_meta.lua" (which is in a file called "meta") and, in features.lua, I wrote, "require("scripts/meta/game_meta")".

Why is the engine not calling the event, and how do I get the engine to call the event?

EDIT: It turns out that I accidentally wrote the wrong variable name in my code; I accidentally wrote "if action == "attack"" instead of "if command == "attack"".
I recently started making an item and one thing about this item is that, when the hero obtains the item, an entity appears near the hero. I saw that all item scripts have "local map = item:get_map()" near the top of the script, so, in my item:on_obtained() function, I wrote, "local hero = map:get_hero()" so that I could then obtain the hero's position for knowing where to place the entity. However, when I tested my code by having the hero obtain the item, I got this error message:

"attempt to index upvalue 'map' (a nil value)"

What does this error mean?

EDIT: I fixed the problem by adding, "local map = item:get_map()" to the function.
Development / Question About Using 8-Bit Graphics
January 14, 2024, 06:33:06 PM
If I understand correctly, the default art assets in the game's engine are 16-bit. I'm planning on using 8-bit NES-like art made using Pro Motion (the pixel art tool that the Shovel Knight team used), and I was wondering if there's anything I need to keep in mind in regards to using 8-bit sprites, tiles, etc. in this engine.
Development / Re: How to Receive an Item From an NPC
December 19, 2023, 11:10:41 PM
Quote from: PeterB on December 18, 2023, 07:51:48 AM
You have to do that with the map lua script.

npc:on_interaction() then do the dialog and finally give the item to the hero with hero:start_treasure(treasure_name, [treasure_variant, [treasure_savegame_variable, [callback]]])

You can have a look in the Manual for further help on these :)

Thank you; that helps a lot.

What are you referring to by the manual? Are you referring to the Lua API reference? If so, I have been reading through it.
Development / How to Receive an Item From an NPC
December 15, 2023, 03:13:11 AM
I watched the tutorial videos on equipment items and I understand how to have an item be found in a chest or dropped when enemy is defeated. However, I was wondering how to have an item be obtained by interacting with NPC.
A while back, I created a custom entity called Follower, who exists to follow the hero. Recently, I created a function entity:swap_sprite() that replaces the Follower's current sprite with the hero's current tunic sprite. Here is that code:

function entity:swap_sprite()
  -- obtain the hero's current sprite:
  repsprite = hero:get_tunic_sprite_id()
  -- replace the current sprite with resprite:
  sprite = entity:create_sprite(repsprite)

I then created another function meant to override the game command "item_2"; this function will do multiple things, but right now, it just calls the entity:swap_sprite() function. I intend to store this function in a separate metatable, but right now, it is inside the Follower.lua file. Here's the code:

function game:on_command_pressed(item_2)
return true

However, when I try to run the quest, instead of the sprite changing on command, it occurs at the very beginning of the quest, and the hero can no longer use the sword (movement still works). What could be causing this error?

EDIT: To try to figure out what went wrong, I added two lines of code to the swap_sprite() function that changes the hero's current tunic sprite to the follower's sprite (both are using hero tunic sprites of different colours; the hero's sprite is hero/tunic1 and the follower's is hero/tunic3). The modified function is as follows:

function entity:swap_sprite()
  -- obtain the hero's current sprite:
  repsprite = hero:get_tunic_sprite_id()

  -- (For testing) replace hero's sprite with follower's
  local sprite_id = sprite:get_animation_set()

  -- replace the current sprite with resprite:
  sprite = entity:create_sprite(repsprite)

Now, the two characters swap sprites as intended, but instead of only doing so during the item_2 game command, they do so any time any game command is called: when the hero starts moving, they swap sprites (the hero still starts moving); when the hero changes direction, they swap sprites (the hero still changes direction); When I press "c" for the hero to use their sword, they swap sprites and the sword isn't used; etc.

EDIT: I looked at some default examples of the use of similar events, such as on_key_pressed() and I realized my error; I stated "game:on_command_pressed(item_2)" when I should have stated "game:on_command_pressed(command)" and then stated: if command = "item_2"...
Development / Re: How to Change the Hero's Sprite
October 29, 2023, 02:01:23 AM
Quote from: PeterB on October 28, 2023, 10:00:46 PM

Tunic level relates to the Hero's defense level.

For example in Zelda Tunic level 1 is green, level 2 is blue and level 3 is red.

You therefore need to have a different colour sprite for each tunic level for every hero sprite.

That way when you upgrade your tunic in your game you can automatically change all your hero sprites to the next colour.

If you have a look in the sample quest that comes with the Quest Editor, in the sprites/hero folder you can see the three tunic sprite sets. It is the items/tunic script then then calls the relevant variant and updates the hero's sprite colour.

Thank you; that makes a lot of sense. There probably will not be tunic upgrades in the game I am making, so I guess I can ignore the stuff about tunic level.

Quote from: PeterB on October 28, 2023, 10:00:46 PM
If you want to set up a different sprite then you just need to add a new animation to tunic1, tunic2 and tunic3 and call that animation from a script, at least i think that is the easiest way to do it.

I see. I should point out that I'm very new to Solarus and I'm still learning how sprites and animations work.

Would adding an animation be sufficient? I want to essentially create another appearance that the hero uses when in a custom state.
Development / How to Change an Entity's Sprite
October 28, 2023, 03:11:41 AM
I am trying to create a custom state for the hero that represents the hero being in a different form (think something like Wolf Link from Twilight Princess): while the hero is in this custom state, the hero uses a different sprite and a different main attack. I was wondering how to change the hero's sprite.

EDIT: I now realize that I probably phrased this question poorly. I was wondering, for any entity, how to program it to use a specific sprite under certain conditions. I watched the video on sprite and animation files, so I know how to create a sprite. But, once I have created a sprite, how do I tell the engine, "under this specific condition, this entity's sprite is now (a specific sprite)"?

I want to know this so I can change the hero's appearance during the game, but I really want to know how to do this with any entity. For instance, I currently have a custom entity that currently only exists to always follow the hero, and I want their appearance to change whenever the hero's appearance changes.

EDIT2: I think I may have found a solution: using entity:remove_sprite() then entity:create_sprite(), I have tested it, and it seems to work.
Development / Re: What does this error mean?
June 05, 2023, 03:40:33 AM
Quote from: PhoenixII54 on June 04, 2023, 10:37:05 AM
The error message says that it can't find the "hero" entity.
The reason is that you never asked the engine to give it to you in the first place.

Since you intend to make  some generic behavior for the hero -hero_meta-, you should use the hero's metatable (using sol.main.get_metatable), and then define what you need here. it works the same as using the hero directly, but more like a model, so multiples heroes will get the same behavior.
If you know the concept of object-oriented programming, then think of modifying the class instead of the instances.

Thank you. This is very helpful.

I am familiar with object-oriented programming; I am a little rusty at using it since I haven't practiced using it in a few years, but I remember all the fundamental concepts.
Development / What does this error mean?
June 01, 2023, 02:39:32 AM
Recently, I created a script specifically for code related to the hero, that I called hero_meta. Here is the code that's currently in this script:

local MAX_BUFFER_SIZE = 48
function hero:on_position_changed(x,y,z)
  local hero = self
  if not hero.position_buffer then hero.position_buffer = {} end
  local hero = self
  local dir = hero:get_sprite():get_direction()
  table.insert(hero.position_buffer, 1, {x=x, y=y, layer=z, direction=dir})

  if #hero.position_buffer > MAX_BUFFER_SIZE then

The purpose of this code is to establish a maximum distance away from the hero and create a table storing the hero's prior movements within that distance.

When I try to run the quest, I keep getting this error:
Error: In main: scripts/meta/hero_meta.lua:2: attempt to index global 'hero' (a nil value)

What does this error mean? Any idea what is causing it to occur?
Quote from: PhoenixII54 on May 29, 2023, 09:02:36 AM
Not sure, but what i would guess is that the on_position_changed event gets triggered the next frame after you stopped moving, so maybe try to have one extra entry in the table and use two indices: one for the current hero step and one for the
NPC that is always 2 steps behind the hero -modulo will be your friend.
By the way, OH uses a standard .solarus zip file so you should be able to check the source code

Thank you.

I asked the developer of Ocean's Heart for the code they used for this, and they provided it, so I have replaced my code with it.
Using the suggestions above, I have refined the code. Here is the current version:

-- Follower's Movement 
  -- Initialize table to store the hero's old coordinates <List[0] = x, List[1] = y>:
  List = {}
  -- Obtain the hero's movement speed
  local speed = hero:get_walking_speed()
  -- Initialize the movement
  local movement = sol.movement.create("target")
  -- Movement pathing:
  function hero:on_position_changed(x, y, layer)
  -- Update list and move accordingly
    -- If this is not the hero's first movement:
    if List[0] ~= nil then
      -- Move the NPC to the old coordinates
      movement:set_target(List[0], List[1])
    -- Record the hero's current position:
    List[0] = x
    List[1] = y

Initially, the movement works exactly as I would like. However, there are three problems that appear after the hero either stops moving or changes direction:

1. The NPC "Follower" continues moving after reaching the target position.
2. It is impossible for the hero to move backwards since Follower is in the way.
3. When I move the hero in a new direction, I want Follower to continue moving forward before then moving in the new direction, as I want Follower to follow the exact same path as the hero. Follower does not do this; I suspect this problem is related to problem 1.

Three questions:
1. How do I remove collision between the NPC Follower and the hero?
2. Any suggestions for how to make Follower stop moving when the hero stops moving?
3. Any idea what could be causing the third problem?

Incidentally, after playing Ocean's Heart recently, I reached a point in the game where an NPC follows the hero, and the NPC does exactly what I'm trying to get Follower to do: they follow the exact path the hero just walked. Does anyone know how Ocean's Heart did this?

UPDATE 2: I added the line, "Follower:set_traversable(true)", so now the hero can pass through Follower. The other two problems remain; Follower continues moving either left-and-right if facing up or down or up-and-down if facing left or right after the hero stops moving, and the movement only works the way that I want it to until the hero changes direction:

When I have the hero go in a new direction, Follower should move to the last place the hero was in the first direction before moving in the new direction, since I want Follower to follow the same path as the hero. Instead, Follower stands still for a while and then catches up. Any idea what is causing this?

There's another problem: despite Follower's speed being set to the hero's speed in the code, when I tested it, Follower slowly begins falling behind.

UPDATE 3: I set Follower to ignore obstacles to see if that was the reason it stood still whenever the hero turned to the left or right. It was the reason, and it revealed a different problem: despite the existence of the table being to record where the hero was previously, whenever the hero stops moving, follower stops at the hero's current position, not the hero's position a step previously.
Quote from: PhoenixII54 on May 09, 2023, 11:40:17 AM
I don't see any obvious mistake (maybe i would have organized the list in the style 1 entry = an {x=x,y=y} pair but that's just a coding preference) so my guess is that your movement gets overwritten each frame you move and doesn't have time to even start until you stop moving.
So what you should try to do is to detect if the follower NPC already has a movement and update it's target (if possible) instead of recreating a new one each frame, and only create it again if it it reached its actual destination when the hero has stopped moving.

Thank you. That is most likely the reason for the first problem. I am currently working on modifying it as suggested.
Using the advice above, I created the following code. Note that "Follower" is the name of the NPC:

-- Table to store the hero's old postion (List[0] = x, List[1] = y, List[2] = layer):
  List = {}
  -- Follower Movement:
  function hero:on_position_changed(x, y, layer)
    -- If not the hero's first movement:
    if List[0] ~= nil then
      -- Move Follower to the old coordinates
      local movement = sol.movement.create("target")
      -- Set the previous position as the target
      movement:set_target(List[0], List[1])
      -- Make the movement speed match the hero
      -- Move
    -- Record the hero's new position
    List[0] = x
    List[1] = y

The current code has two main problems:

1. Follower does not start moving until the moment the hero stops moving, when the idea was that the follower would move while the hero moves and stop when the hero stops.

2. Follower does not stop moving, as if the current target is the hero's current position when it should be the hero's previous position.

Where have I gone wrong in my implementation that is causing these problems?
Quote from: PhoenixII54 on May 05, 2023, 11:30:22 AM
Hello, you can use the hero:on_position_changed() event, which is called each frame Link has moved.

-- <initialization stuff where you get the hero and NPC entities>

function hero:on_position_changed(x, y, layer)
--update your list here and move the NPC accordingly


Thank you very much. This is a big help.

Quote from: Christopho on May 05, 2023, 03:20:57 PM
An alternative way is to make a target movement towards the hero, and stop that movement when the distance between the NPC and the hero gets below some threshold. I think it is done that way in the Zelda A Link to the Dream project.
Your choice, I guess it depends on whether you want the NPC to follow exactly the same path as the hero or not.

Thank you. I do want the NPC to follow the same path as the hero; it's important for another component of the game that I'm trying to make that the NPC follow the same path. But I may fall back on that method if I can't get my current plan to work.