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

Pages: [1] 2 3 ... 8
1
Most of the seasonal changes could be dynamic tiles that the map:on_season_changed() function could set enabled/disabled. Ice, leaves, vines, snow, that could all be done with dynamic tiles.

Using dynamic tiles would be okay if you're just talking about a small number of entities on the map here and there. If the majority of the tiles were dynamic tiles, then I think you'd be losing out on a number of optimizations that are made with static tiles.

An alternate idea would be to create a single seasonless map in the map editor, but then at run time use lua to dynamically output separate map.dat files, one for each season. The seasonless map could use custom entities for season-specific things, such as pile_of_leaves_fall, fireflies_summer.

2
Bugs & Feature requests / Re: Complex movements
« on: June 26, 2018, 01:13:26 am »
Does anyone know what actions this can be used to preform? It gives the example of .wait(length of pause), and .dialog(dialog)

Looks like the actions are defined between lines 128 & 259.

You could add your own functions to that section if you need to do something else not included.

3
Development / Re: function vs. function:enemy
« on: June 10, 2018, 08:50:25 pm »
There's probably some limitation I'm forgetting when it comes to using local functions in an enemy script. You really are better off in the long run using enemy:functions.

what is the difference of calling i.e. self:jump() instead of enemy:jump()? I've seen both used and they seem to work interchangeably, but I'm sure I'm missing some distinction there.

When you have a function enemy:jump(), that is shorthand for writing it as enemy.jump(self), so self is a hidden first parameter that will be a reference to enemy. If you have hierarchy in your scripts, then self could be something other than enemy (but where self behaves like enemy and would have all the same functions enemy does). This is how you can do inheritance in lua.

For your purposes, though, you most likely aren't doing any hierarchy, so you can think of self:jump() and enemy:jump() as the same, but using self is better for having a robust script in case some day you do want to implement hierarchy.

EDIT: Looking through some of the Solarus Team quests it seems that self is generally used instead of enemy, but I see that zelda-roth-se uses enemy instead of self. Bottom line is using self is better, but I don't think you'll run into any issues if you use enemy.

4
Your scripts / Re: Classic Tektite Random Jumping AI
« on: June 10, 2018, 08:39:54 pm »
Oops, I forgot a set of parentheses.

Change self[random_choice](self) to self[random_choice()](self) and it should work.

5
Your scripts / Re: Classic Tektite Random Jumping AI
« on: June 10, 2018, 03:25:22 am »
I didn't understand what you were trying to do when I fixed your code. Now that I see your full code, something like this would be better:
Code: Lua
  1.     local enemy = ...
  2.      
  3.     local choices = {
  4.       pause = 25,
  5.       jump = 75,
  6.     }
  7.     -- these are functions the enemy will choose from randomly proportional to the value associated with each one
  8.    
  9.     local random_max = 0 --sum of all choice values, calculate once when script is first run (in this example, radom_max = 25 + 75 = 100)
  10.     for _,weight in pairs(choices) do random_max = random_max + weight end
  11.    
  12.     --this function could be moved to a different script for common utility functions
  13.     local function random_choice()
  14.         local choice_value = math.random(1, random_max) --choose random number between 1 and random_max
  15.        
  16.         --step through list of choices to find which choice correspondes to the randomly chosen value
  17.         local cumulative = 0 --sum of current choice entry plus all previous
  18.         for choice,weight in pairs(choices) do
  19.                 cumulative = cumulative + weight
  20.                 if choice_value <= cumulative then
  21.                         return choice --current entry corresponds to the randomly chosen value
  22.                 end
  23.         end
  24.        
  25.         --should have returned a value before getting to this line, as contingency returns some choice (undefined which one)
  26.         --you may prefer to throw an error here instead
  27.         local choice = next(choices)
  28.         return choice
  29.     end
  30.    
  31.     function enemy:random_action()
  32.         self[random_choice()](self) --this line calls a random function from the "choices" table at the top
  33.     end
  34.    
  35.     function enemy:on_restarted()
  36.         self:random_action()
  37.     end
I didn't include the full script here. You'll want to replace lines 22 & 36 of your original script with self:random_action() like in the enemy:on_restarted() function.

This is basically equivalent to the sample code posted by Max (yes your code works) except it allows the user to edit the values in the choices table (or add new entries) without having to change the rest of the script. Also note that the sum of the values in the choices table don't need to be 100 either. Giving pause a value of 1 and jump a value of 3 would be equivalent.

Though the values do need to be integers. It would be easy enough to alter it to work with decimal values too if that's what you wanted.

EDIT: code fixed to add the missing parentheses mentioned below

6
Development / Re: function vs. function:enemy
« on: June 10, 2018, 02:31:58 am »
Your first script is declaring the functions as globals which is probably not the behavior you want. Global functions can be used by every script, and the reason you don't want to use it is you'll have conflicts if multiple scripts try to define the same named global function. i.e. if you have a tektite enemy script and a frog enemy script, both with a global jump() function, then one of the scripts will overwrite the other function and you won't get the behavior you intended.

You can get around this by making the functions local instead:
Code: Lua
  1. local function jump()
but with local functions the scripts will only be available to the script that defines the function, so if you want the functions to be available to use in other scripts, the second example you gave is the best way to go. For example, if you wanted to have the enemy jump any time the player hits a hammer on the ground, you wouldn't be able to do that with a local jump function because the script where you define the hammer action wouldn't have access to the local jump function defined in the other enemy script.

So when it comes to defining enemy behaviors, you'll almost always want to go with the second example. If you have a function that you positively know will never need to be called from a different script then you could use a local function. You probably wouldn't ever want to use a global function when defining enemy behaviors. For something you want to apply to all enemies, better to use meta-tables instead.

7
Development / Re: Choosing a Function Randomly
« on: June 09, 2018, 06:36:51 am »
There are a number of problems with what you posted.

For starters, the functions you are listing in the choices table are strings, so you won't be able to execute them like functions.

And secondly, line 4 of the first one just gets the contents of the table, you still have to do something with it.

Try this:
Code: Lua
  1. choices = {"go_hero","random_pause","jump_pause","go_random"}
  2.  
  3.   sol.timer.start(enemy, math.random(1, 3000), function()
  4.     enemy[ choices[math.random( 1, #choices )] ](enemy)
  5.     --NOTE: enemy:go_hero() is equivalent to enemy.go_hero(enemy) which is equivalent to enemy["go_hero"](enemy)
  6. end)

8
Development / Re: how can you draw similarly to the light_manager
« on: June 01, 2018, 03:50:36 pm »
Light manager? Is that script someone wrote?

What you want to use is blend modes, specifically the multiply blend mode.

If you think of black as a value of 0 and white as a value of 1, if you multiply any color by black it becomes black and if you multiply by white it is unchanged. Shades of grey will thus darken the image where the closer to black it is the darker it will get.

So then start with a black surface (or perhaps dark grey if you still want the darkened part of the room to be slightly visible). Next you'll want to draw white circles wherever a torch is visible. You have two options here:
  • You could create a png image of that circle where white pixels are the brightest part, then on the edges have a gradient where the white pixels become more transparent, with fully transparent being the darkest spot. Now all you have to do is draw this circular image in normal blend mode on top of your black surface wherever a torch is present. If two torches are near each other then the transparent pixels from each circle will overlap making it brighter in that spot than if there were only one torch.
  • The second option is to create an image of the circle where white pixels are the brightest part, then on the edges have a gradient where the pixels become grey as you want it to become darker, and eventually black at the darkest point. This image would not use any transparency. Similarly to before you will draw the circle on the black surface wherever you have a torch, but this time use the add blend mode, so where grey pixels overlap they will become more white (brighter).

Then all you have to do is draw that black surface with white circles overlaid on top of the game's surface, and be sure to use the multiply blend mode. Regardless of which option you used above, the result will be the black surface will have black pixels at the darkest spots, white pixels at the brightest spots and shades of grey in between. The best option would be creating a menu to draw the black surface on top of the game surface.

The tricky part is updating the image as the player moves. Once again you have two options:
  • The easier method is to size your black surface to be the size of the map. Then you only have to draw the white circles once when the map is loaded and then you can simply draw the a sub-region of the black surface that is visible on the player's screen over the game surface every frame. If your light sources are dynamic (either they move locations or turn on/off) then you will have to redraw the white circles on the black surface whenever one of these changes occur. The method is better if the lighting is not dynamic or at least doesn't change very often.
  • If you have lots of dynamic light sources (or for example you want to draw a light source at the player's position), then it is probably better to size the black surface to match the size of the game screen and then you'll have to redraw each light source every frame, but you only have to draw the light sources that actually appear on screen.

9
Your scripts / Re: Dodge/Dash/Roll Action
« on: May 28, 2018, 10:30:22 pm »
Code: Lua
  1. hero:register_event("on_state_changed", function()
  2.   if state == "falling" then --some code
  3.   end
  4. end)
  5.  
With this syntax, I don't know how to pass on the state argument
In the multi_events script, all the arguments that would have been passed to the on_state_changed function will also be passed to your callback function.

The original function is function hero:on_state_changed(state), which is equivalent to function hero.on_state_changed(self, state). So state is actually the second argument of the function:
Code: Lua
  1. hero:register_event("on_state_changed", function(self, state)
  2.   if state == "falling" then --some code
  3.   end
  4. end)
  5.  

10
Development / Re: Switch between two sprites by pressing a key?
« on: May 22, 2018, 02:52:47 pm »
      function sol.main:on_key_pressed(key, modifiers) --key-press function--
You don't want to use sol.main:on_key_pressed() here. You should only use this function for responding to key presses that are applicable at all times while your quest is running, including when there is not an active game (such as the character select or title screen). The way you have it now you'll probably get an error if the player exits to the title screen and presses the w key because game and hero won't exist any more. Use game:on_key_pressed() instead.

You could also run into problems if you have multiple scrips calling game:on_key_pressed() as the scripts will end up overriding each other. You might be able to get away with using multi_events for game:on_key_pressed() depending on what you are trying to do. A better solution might be to create a menu to handle processing the key presses and call menu:on_key_pressed() instead.

11
Your projects / Re: Zelda's Adventure: Solarus Edition
« on: May 04, 2018, 02:34:51 pm »
Ok, what's the syntax look like for that? Like I said, I thought I followed the tutorial exactly but I must have misunderstood something somewhere.
Syntax for the last one? In the Solarus Quest Editor open the map and double-click on the lady entity and an edit dialog will open. Then select the correct radio button under "action".

12
Your projects / Re: Zelda's Adventure: Solarus Edition
« on: May 04, 2018, 01:24:32 pm »
Ok, I was able to download the update from GitHub and get it working now.

First of all, you were missing an end statement at line 25 of visionhenge5.lua.

I was also getting errors about missing sounds, so I copied over the sounds from the ALTTP pack to fix it.

Then finally, your real problem is that when you edit the lady NPC entity you have the action "Show a dialog: sample_text" selected. What you want is the "call the map script" option selected instead. Otherwise it does not use the on_interaction() part of your script.

13
Your projects / Re: Zelda's Adventure: Solarus Edition
« on: May 04, 2018, 05:08:23 am »
I don't think you would ever have something like if answer == 6 because the answer is relative to the choice's line in the box, not in the whole text string. So it'll always be 1-4.
Max is correct in that the only way answer == 6 would work is if you modified the dialog script to display at least 6 lines of text. The dialog script currently in your github repo is set to display 4 lines.

I don't see any other problems with the code excerpts you posted on the forum. I'm not going to be able to help you without seeing your map script, dialogs.dat, and probably whatever script is giving you problems when saving.

I downloaded your quest from github but there aren't any maps so it must not be the latest.


14
Your projects / Re: Zelda's Adventure: Solarus Edition
« on: May 03, 2018, 02:30:01 pm »
I'm still looking for programmers who can help me out! My scripts keep not working right! :(
I'm willing to help out. Indicate which scripts are not working and what you'd like them to do.

15
Your scripts / Re: Deku/Turret Type Enemy
« on: April 29, 2018, 06:17:27 am »
FYI--
Your code to set the default properties can be made more concise with the use of a table. This equivalent code replaces lines 26 through 104:
Code: Lua
  1.     local defaults = {
  2.       life = 2,
  3.       damage = 0,
  4.       normal_speed = 32,
  5.       faster_speed = 48,
  6.       size_x = 16,
  7.       size_y = 16,
  8.       hurt_style = "normal",
  9.       pushed_when_hurt = false,
  10.       push_hero_on_sword = false,
  11.       ignore_obstacles = false,
  12.       detection_distance = 80,
  13.       obstacle_behavior = "normal",
  14.       projectile_breed = "misc/octorok_stone",
  15.       shooting_frequency = 1500,
  16.       sword_consequence = 1,
  17.       arrow_consequence = 1,
  18.       explosion_consequence = 1,
  19.       fire_consequence = 1,
  20.       movement_create = function()
  21.         local m = sol.movement.create("random_path")
  22.         return m
  23.       end,
  24.       asleep_animation = "asleep",
  25.       awake_animation = "awake",
  26.       must_be_aligned_to_shoot = true,
  27.       max_range = 100,
  28.       min_range = 45,
  29.       must_be_aligned_to_shoot = true,
  30.     }
  31.    
  32.     -- Set default properties.
  33.     for property,default_value in pairs(defaults) do
  34.       if properties[property] == nil then
  35.         properties[property] = default_value
  36.       end
  37.     end

Also, it looks like the script hard codes some offsets specific to the projectile sprite. It might be helpful if you included an image of the projectile sprite to make it more clear what it is doing. For that matter it might be helpful to include the monster sprite too. It's always nice to be able to replicate the author's setup exactly in order to be sure things are working as intended.

Pages: [1] 2 3 ... 8