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 ... 9
1
Development / Re: Wait, I don't understand how "require" works
« on: October 12, 2018, 12:31:56 am »
The other thing to consider is that even if assigning sol.main.game to local game did work, you'd still have problems for the case where the player exits the game to the title screen and starts a different game, in which case the local game would have a reference to the wrong instance of the game.

2
Development / Re: Wait, I don't understand how "require" works
« on: October 07, 2018, 11:10:50 pm »
The require command will just run the script, which might return a table, if you tell it to.
There's one subtlety that I'm not sure if you picked up on, and that's that the require command will only run the script once. So if the script is also required somewhere else, then instead of running the script a second time, the return values saved from the first time the require command was used will simply be passed along to wherever else the script gets required.

To see an example of this, imagine a script (print_script.lua) to be required that simply contains the line print("some text").

Now if you add require("/scripts/print_script.lua") to both main.lua and game_manager.lua then you'll only see one print statement when the game is loaded.

If instead you change it to sol.main.do_file("scripts/print_script.lua"), then this time the script will actually be run twice and you'll see two print statements. Put the sol.main.do_file statement in a map script and you'll actually see the line get printed every time the map loads, which is not the case if using require.

If the script were to return a table, then using require would result in the same table reference being returned at both places. If using do_file, then two separate table instances (that probably look identical) would be returned. Generally, using require is what you are going to want to use the majority of the time, but there may be instances where sol.main.do_file is better for what you want to accomplish.


3
Development / Re: Wait, I don't understand how "require" works
« on: October 04, 2018, 07:02:56 am »
local shop in your map is a table. backsmith() is a function in that table.

When using require, you need to have the script being required return a table. Anywhere that that script is required, it will get a reference to the table, the advantage being that the script only has to be loaded once.
EDIT: the script that gets required does not have to return a table if instead it simply defines global variables/functions

You'll want to do something like the following for your blacksmith script:
Code: Lua
  1. local blacksmith = {}
  2.  
  3. function blacksmith:start_dialog()
  4.   --do stuff
  5. end
  6.  
  7. function blacksmith:function2()
  8.   --some other function
  9. end
  10.  
  11. return blacksmith

The problem with this, though, is that the blacksmith script here won't be able to use any references to game since it doesn't know what that is. If the same game instance will be used everywhere you use the script then you can get away with something like the following: (be warned that every time you require the script the game will get set, and only the last one will stick, but in the case of a map it will be set every time the map loads)
Code: Lua
  1. local blacksmith = {}
  2.  
  3. local game
  4.  
  5. function blacksmith:set_game(current_game)
  6.   game = current_game
  7. end
  8.  
  9. function blacksmith:start_dialog()
  10.   game:start_dialog("_yarrowmouth.npcs.blacksmith", function(answer)
  11.     --blah blah
  12.   end)
  13. end
  14.  
  15. function blacksmith:function2()
  16.   --some other function
  17. end
  18.  
  19. return blacksmith

Then your map script becomes:
Code: Lua
  1. local blacksmith_funcs = require("scripts/shops/blacksmith")
  2. blacksmith_funcs:set_game(game)
  3.  
  4. function blacksmith:on_interaction()
  5.   blacksmith_funcs:start_dialog()
  6. end

But if you're going to have multiple game instances then this solution is no good and you'll want to do something like the following instead:
Code: Lua
  1. local blacksmith = {}
  2.  
  3. function blacksmith:start_dialog(game)
  4.   game:start_dialog("_yarrowmouth.npcs.blacksmith", function(answer)
  5.     --blah blah
  6.   end)
  7. end
  8.  
  9. function blacksmith:function2(game)
  10.   --some other function
  11. end
  12.  
  13. return blacksmith

And map script:
Code: Lua
  1. local blacksmith_funcs = require("scripts/shops/blacksmith")
  2.  
  3. function blacksmith:on_interaction()
  4.   blacksmith_funcs:start_dialog(game)
  5. end

4
Development / Re: LUA IDE?
« on: September 21, 2018, 05:15:40 am »
I use BBEdit which is just a fancy text editor for the mac. Has syntax highlighting and a drop-down menu that lets you jump to another function in the same file.

I think you can get a plugin for eclipse to use it for lua.

5
Development / Re: Is it possible to make the hero face the cursor?
« on: August 17, 2018, 01:51:00 am »
Yes, it is very possible.

Mouse coordinates are given relative to the quest window, which you'll need to convert to map coordinates. Only part of the current map will be visible on the screen, as determined by the camera.

Functions you'll need are:
sol.input.get_mouse_position()
camera:get_position_on_screen()
camera:get_position()
hero:get_position()

Updating the hero's facing direction every 1ms seems like a bad idea. That would essentially make it so that the hero is always facing the cursor. A better implementation would be to only update the direction when the mouse moves. That way the player could choose to use the arrow keys to move the hero, not ever touch the mouse, and all would be well.

6
Development / Re: Required Sound Effects?
« on: July 27, 2018, 12:22:24 am »
Neato.

7
General discussion / Re: Stealth Action Adventure Game Possible?
« on: July 20, 2018, 12:44:26 am »
Yes, there is huge flexibility in what you can accomplish through the use of lua scripting, but the downside is you'll have to write some custom scripts to do so because to the best of my knowledge no one else has done something like that yet. Shouldn't be too difficult, though, depending on how ambitious you want to be.

8
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.

9
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.

10
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.

11
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.

12
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

13
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.

14
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)

15
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.

Pages: [1] 2 3 ... 9