Author Topic: 2 Player  (Read 679 times)

yankscally

  • Newbie
  • *
  • Posts: 19
    • View Profile
2 Player
« on: May 02, 2017, 02:03:26 am »
http://www.solarus-games.org/doc/1.5/lua_api_hero.html

mentioned in this link is

 
Quote
The hero is the character controlled by the player. There is always exactly one hero on the current map. The hero is automatically created by the engine: you cannot create or remove him.

How would I add an extra player??

it seems like the code I want to mess with is hidden somewhere and I can't read/write it. Can anyone point me in the right direction?

Diarandor

  • Hero Member
  • *****
  • Posts: 753
  • Cats are cool! (ΦωΦ)
    • View Profile
Re: 2 Player
« Reply #1 on: May 02, 2017, 02:43:28 am »
To make this feature, there are two cases:
-Multiplayer online: you would need to modify the C++ code of the engine. Very hard, I guess.
-Multiplayer offline: it can already be done with Lua scripts, using custom entities. Of course, it's a lot of work.

Zefk

  • Sr. Member
  • ****
  • Posts: 492
  • Just helping Solarus
    • View Profile
    • Zelzec Business
Re: 2 Player
« Reply #2 on: May 02, 2017, 06:20:05 am »
@yankscally

I am making an ally Ai project, so the functions I make might help you with making a second player. I will be start making functions (probably a custom entity metatable library) after the first release of the Solarus book project I am working on.

This would be unrelated to controlling your player 2 character. It is common to have "wasd" for keyboard related second players. Key 'd' is default for pausing the game...you will need to probably use "wqes".

w = up
s = down
a = left
d = right

w = up
s = down
q = left
e = right

Straight Movement:

Straight trajectory in any direction.
http://www.solarus-games.org/doc/latest/lua_api_movement.html

You can set the angle to the following values to get basic directions, but you can use negatives and other values. I used 3 in straight:set_angle(3) in my book.

Angle:

East (right) = 0
North (up) = math.pi / 2
West (left) = math.pi
South (down)= 3 * math.pi / 2

                          "N" ever (north)
"W"atermelon(West)------|----------"E"at (East)
                          "S"oggy (south)
                   

Code: Lua
  1.  --Straight up
  2.  
  3. function player_up()
  4.  local straight = sol.movement.create("straight")
  5.  straight:set_angle(math.pi / 2)
  6.  straight:start(entity)
  7. end
  8.  

Key press:
A tip is remember to have a true/false Boolean for a menu.
http://www.solarus-games.org/doc/latest/lua_api_main.html#lua_api_main_on_key_pressed

Code: Lua
  1. menu = false
  2.  
  3. function sol.main:on_key_pressed(key)
  4.   if key == "w" and menu == false then
  5.     player_up()
  6.     entity:get_sprite():set_animation("walking")
  7.   else
  8.     straight:stop()
  9.     entity:get_sprite():set_animation("stopped")
  10.   end
  11. end

For "menu = false" probably better to use set_value/get_value

You will have to decide on the action button. It is 'C' for the hero. It probably would be convenient to use key '1' or 'a'. You probably could provide a option to change it.

Code: Lua
  1. local menu = false
  2. local action_button = "a"
  3.  
  4. function sol.main:on_key_pressed(key)
  5.   if key == action_button and menu == false then
  6.     entity:get_sprite():set_animation("sword")
  7.   else
  8.     entity:get_sprite():set_animation("stopped")
  9.   end
  10. end

You might want to use a timer for movements because maybe you want to look in that direction, but not move.
http://www.solarus-games.org/doc/latest/lua_api_custom_entity.html#lua_api_custom_entity_set_direction
http://www.solarus-games.org/doc/latest/lua_api_timer.html

You might want to think about joypads too.
http://www.solarus-games.org/doc/latest/lua_api_main.html#lua_api_main_on_joypad_button_pressed
« Last Edit: May 02, 2017, 07:12:07 am by Zefk »

Diarandor

  • Hero Member
  • *****
  • Posts: 753
  • Cats are cool! (ΦωΦ)
    • View Profile
Re: 2 Player
« Reply #3 on: May 02, 2017, 10:08:54 am »
Note that french keyboards have some differences in the position of the letters. Since there are many french people in the Solarus community, I recommend to use as default keys some of the keys that have the same position for both english and french keyboards.

Christopho

  • Administrator
  • Hero Member
  • *****
  • Posts: 1023
    • View Profile
Re: 2 Player
« Reply #4 on: May 02, 2017, 10:32:01 am »
For networking multiplayer, you don't need to change the C++ code of the engine. You can use a Lua networking library like LuaSocket. It is true that there is no direct networking support in the Solarus API yet: you will have to do a lot of things manually, but no C++.

Zefk

  • Sr. Member
  • ****
  • Posts: 492
  • Just helping Solarus
    • View Profile
    • Zelzec Business
Re: 2 Player
« Reply #5 on: May 02, 2017, 11:17:27 am »
I guess this is what a french keyboard looks like: https://www.branah.com/french

Luasocket installation: http://w3.impa.br/~diego/software/luasocket/installation.html

yankscally

  • Newbie
  • *
  • Posts: 19
    • View Profile
Re: 2 Player
« Reply #6 on: May 02, 2017, 12:32:26 pm »
Note that french keyboards have some differences in the position of the letters. Since there are many french people in the Solarus community, I recommend to use as default keys some of the keys that have the same position for both english and french keyboards.
For networking multiplayer, you don't need to change the C++ code of the engine. You can use a Lua networking library like LuaSocket. It is true that there is no direct networking support in the Solarus API yet: you will have to do a lot of things manually, but no C++.
@yankscally

I am making an ally Ai project, so the functions I make might help you with making a second player. I will be start making functions (probably a custom entity metatable library) after the first release of the Solarus book project I am working on.

This would be unrelated to controlling your player 2 character. It is common to have "wasd" for keyboard related second players. Key 'd' is default for pausing the game...you will need to probably use "wqes".

w = up
s = down
a = left
d = right

w = up
s = down
q = left
e = right

Straight Movement:


Wow, this is really helpful info guys, thanks for the support, and so quickly too. I guess I can do a lot today with this. I will code a 1P/2P system for now and think about luasocket multiplayer when I understand it all a bit better. I'm using an enemy script for now but changing the code to controlling it with set keys seems like about x20 multiplied the lines of code I expected. I will probably use the custom entity.
« Last Edit: May 02, 2017, 12:34:48 pm by yankscally »

MetalZelda

  • Hero Member
  • *****
  • Posts: 501
    • View Profile
Re: 2 Player
« Reply #7 on: May 02, 2017, 12:36:34 pm »
You might be able to do it through game:on_key_pressed(key)

Remember that you might need to configure the player input, in this case, key should be a savegame value

Diarandor

  • Hero Member
  • *****
  • Posts: 753
  • Cats are cool! (ΦωΦ)
    • View Profile
Re: 2 Player
« Reply #8 on: May 02, 2017, 01:01:07 pm »
The only bad thing about controlling the second hero with keyboard may be a possible limitation of maximum number of keys that can be pressed. I recommend to control the second hero with a gamepad.

yankscally

  • Newbie
  • *
  • Posts: 19
    • View Profile
Re: 2 Player
« Reply #9 on: May 02, 2017, 01:13:09 pm »
Code: Lua
  1. local entity = ...
  2. local game = entity:get_game()
  3. local map = entity:get_map()
  4. local hero = map:get_hero()
  5. local sprite
  6. local movement
  7.  
  8.  
  9. function entity:on_created()
  10.   sprite = entity:create_sprite("hero/tunic3"):set_animation("stopped")
  11. end
  12.  
  13. function player_up()
  14.  local straight = sol.movement.create("straight")
  15.  straight:set_angle(math.pi / 2)
  16.  straight:start(entity)
  17.  
  18. end
  19.  
  20. function player_down()
  21.  local straight = sol.movement.create("straight")
  22.  straight:set_angle(3 * math.pi / 2)
  23.  straight:start(entity)
  24. end
  25.  
  26. function player_left()
  27.  local straight = sol.movement.create("straight")
  28.  straight:set_angle(math.pi)
  29.  straight:start(entity)
  30. end
  31.  
  32. function player_right()
  33.  local straight = sol.movement.create("straight")
  34.  straight:set_angle(0)
  35.  straight:start(entity)
  36. end
  37.  
  38. menu = false
  39.  
  40. function sol.main:on_key_pressed(key)
  41.   if key == "i" and menu == false then
  42.     player_up()
  43.     entity:get_sprite():set_animation("walking")
  44.   else
  45.     straight:stop()
  46.     entity:get_sprite():set_animation("stopped")
  47.   end
  48. end
  49.  
  50. function sol.main:on_key_pressed(key)
  51.   if key == "j" and menu == false then
  52.     player_left()
  53.     entity:get_sprite():set_animation("walking")
  54.   else
  55.     straight:stop()
  56.     entity:get_sprite():set_animation("stopped")
  57.   end
  58. end
  59.  
  60. function sol.main:on_key_pressed(key)
  61.   if key == "l" and menu == false then
  62.     player_right()
  63.     entity:get_sprite():set_animation("walking")
  64.   else
  65.     straight:stop()
  66.     entity:get_sprite():set_animation("stopped")
  67.   end
  68. end
  69.  
  70. function sol.main:on_key_pressed(key)
  71.   if key == "k" and menu == false then
  72.     player_down()
  73.     entity:get_sprite():set_animation("walking")
  74.   else
  75.     straight:stop()
  76.     entity:get_sprite():set_animation("stopped")
  77.   end
  78. end
  79.  
  80. function entity:on_restarted()
  81.  
  82.  
  83. end


I got this so far thanks to Zefk, and it loads in a sprite, it does not move but pressing the left key makes the sprite "walking"
I get this error

Error: In on_key_pressed: [string "entities/player_2.lua"]:85: attempt to index global 'straight' (a nil value)


I thought it would be easier to use IJKL for the movement at the minute so I could test quickly with both hands

yankscally

  • Newbie
  • *
  • Posts: 19
    • View Profile
Re: 2 Player
« Reply #10 on: May 02, 2017, 01:28:16 pm »
http://www.solarus-games.org/doc/latest/lua_api_movement.html

what about the code described here??
Quote
movement:get_direction4()

From the four main directions, returns the closest one to the current trajectory.

East is 0, North is 1, West is 2, South is 3. As the real trajectory does not necessarily follows one of the four main directions, it will be converted to the closest one.

If you use this movement to control a sprite (or a map entity that has a sprite), you can use this function to make the sprite face the direction of the movement.

Return value (number): The closest direction corresponding to the angle of this movement.
Example of use:

-- Example of code from an enemy script.

-- This function is called when the enemy should start or restart its movement.
function enemy:on_restarted()

  -- Create a movement that makes random straight trajectories.
  local movement = sol.movement.create("random")

  -- This function is called when the trajectory has changed.
  function movement:on_movement_changed()
    -- The angle of the movement has changed: update the sprite accordingly.
    local direction = movement:get_direction4()
    enemy:get_sprite():set_direction(direction)
  end

  movement:start(enemy)
end

yankscally

  • Newbie
  • *
  • Posts: 19
    • View Profile
Re: 2 Player
« Reply #11 on: May 02, 2017, 04:17:02 pm »
after fine tuning the code, trying to edit out mistakes I made and such, I now get these error messages, similar.
Error: In on_key_pressed: [string "entities/player_2.lua"]:57: attempt to index upvalue 'math' (a nil value)
Error: In on_key_pressed: [string "entities/player_2.lua"]:66: attempt to index global 'straight' (a nil value)


player 2 code so far
Code: Lua
  1. local entity = ...
  2. local game = entity:get_game()
  3. local map = entity:get_map()
  4. local hero = map:get_hero()
  5. local sprite
  6. local math
  7.  
  8.  
  9.  
  10. ---CREATE
  11. function entity:on_created()
  12.   sprite = entity:create_sprite("hero/tunic3"):set_animation("stopped")
  13.   entity:set_can_traverse("hero", false)
  14.   entity:get_direction(2)
  15. end
  16.  
  17. --------------CONTROLS GO HERE
  18. --- UP
  19.  
  20. function player_up()
  21.  local straight = sol.movement.create("straight")
  22.  straight:set_angle(math.pi / 2)
  23.  straight:start(entity)
  24. end
  25. menu = false
  26. function sol.main:on_key_pressed(key)
  27.   if key == "i" and menu == false then
  28.     player_up()
  29.     entity:get_sprite():set_animation("walking")
  30.   else
  31.     straight:stop()
  32.     entity:get_sprite():set_animation("stopped")
  33.   end
  34. end
  35.  
  36. -- DOWN
  37.  
  38. function player_down()
  39.  local straight = sol.movement.create("straight")
  40.  straight:set_angle(3 * math.pi / 2)
  41.  straight:start(entity)
  42. end
  43. menu = false
  44. function entity:on_key_pressed(key)
  45.   if key == "k" and menu == false then
  46.     player_down()
  47.     entity:get_sprite():set_animation("walking")
  48.   else
  49.     straight:stop()
  50.     entity:get_sprite():set_animation("stopped")
  51.   end
  52. end
  53. --- Left
  54.  
  55. function player_left()
  56.  local straight = sol.movement.create("straight")
  57.  straight:set_angle(math.pi)
  58.  straight:start(entity)
  59. end
  60. menu = false
  61. function sol.main:on_key_pressed(key)
  62.   if key == "j" and menu == false then
  63.     player_left()
  64.     entity:get_sprite():set_animation("walking")
  65.   else
  66.     straight:stop()
  67.     entity:get_sprite():set_animation("stopped")
  68.   end
  69. end
  70.  
  71. -- right
  72.  
  73. function player_right()
  74.  local straight = sol.movement.create("straight")
  75.  straight:set_angle(0)
  76.  straight:start(entity)
  77. end
  78. menu = false
  79. function entity:on_key_pressed(key)
  80.   if key == "l" and menu == false then
  81.     player_right()
  82.     entity:get_sprite():set_animation("walking")
  83.   else
  84.     straight:stop()
  85.     entity:get_sprite():set_animation("stopped")
  86.   end
  87. end
  88.  
  89.  
  90. --- on restart
  91.  
  92. function entity:on_restarted()
  93.   movement:set_speed(48)
  94.   movement:start(entity)
  95. end

Zefk

  • Sr. Member
  • ****
  • Posts: 492
  • Just helping Solarus
    • View Profile
    • Zelzec Business
Re: 2 Player
« Reply #12 on: May 02, 2017, 10:35:47 pm »
You might need to have the straight movement variable outside the function. You probably only need to declare it once. The same with the menu variable.

You only need one key press function.

I am not sure why math.pi is not working. Just use 3.14 (That is pi). You got to remove the math variable for math.pi to work. You will have to set the direction. The following script should just work. I added on_released for stopping. Sorry that I did not mention that the code above was pseudo code.
http://www.solarus-games.org/doc/latest/lua_api_custom_entity.html#lua_api_custom_entity_set_direction
http://www.solarus-games.org/doc/latest/lua_api_main.html#lua_api_main_on_key_released

You can use the camera entity to track your player 2, but you might not want that unless player 1 dies.
Code: Lua
  1. local camera = map:get_camera()
  2. camera:start_tracking(entity)

You do not need on_restarted() because I think that is an enemy only function.

Do this for the player speed:

---Create sprite
Code: Lua
  1. function entity:on_created()
  2.   sprite = entity:create_sprite("hero/tunic1"):set_animation("stopped")
  3.   entity:set_can_traverse("hero", false)
  4.   entity:get_direction(2)
  5.   straight:set_speed(80)
  6. end

Script:
Code: Lua
  1. local entity = ...
  2. local game = entity:get_game()
  3. local map = entity:get_map()
  4. local hero = map:get_hero()
  5. local sprite
  6. local straight = sol.movement.create("straight")
  7. menu = false
  8.  
  9. ---Create sprite
  10. function entity:on_created()
  11.   sprite = entity:create_sprite("hero/tunic3"):set_animation("stopped")
  12.   entity:set_can_traverse("hero", false)
  13.   entity:get_direction(2)
  14.   straight:set_speed(80)
  15. end
  16.  
  17. --CONTROLS
  18. --UP
  19. function player_up()
  20.  straight:set_angle(math.pi / 2)
  21.  straight:start(entity)
  22. end
  23.  
  24. --DOWN
  25. function player_down()
  26.  straight:set_angle(3 * math.pi / 2)
  27.  straight:start(entity)
  28. end
  29.  
  30. --Left
  31. function player_left()
  32.  straight:set_angle(math.pi)
  33.  straight:start(entity)
  34. end
  35.  
  36. --right
  37. function player_right()
  38.  straight:set_angle(0)
  39.  straight:start(entity)
  40. end
  41.  
  42.  
  43. --Key press function
  44. function sol.main:on_key_pressed(key)
  45.  
  46. --right
  47.   if key == "l" and menu == false then
  48.     player_right()
  49.     entity:get_sprite():set_animation("walking")
  50.   end
  51.  
  52. --left
  53.   if key == "j" and menu == false then
  54.     player_left()
  55.     entity:get_sprite():set_animation("walking")
  56.   end
  57.  
  58. --down
  59.   if key == "k" and menu == false then
  60.     player_down()
  61.     entity:get_sprite():set_animation("walking")
  62.  
  63.   end
  64.  
  65. --up
  66.   if key == "i" and menu == false then
  67.     player_up()
  68.     entity:get_sprite():set_animation("walking")
  69.   end
  70. end
  71.  
  72.  
  73. function sol.main:on_key_released(key)
  74.  
  75.    if key == "l" then
  76.        straight:stop()
  77.        entity:get_sprite():set_animation("stopped")
  78.    end
  79.    
  80.    if key == "j" then
  81.        straight:stop()
  82.        entity:get_sprite():set_animation("stopped")
  83.    end
  84.    
  85.    if key == "k" then
  86.        straight:stop()
  87.        entity:get_sprite():set_animation("stopped")
  88.    end
  89.    
  90.    if key == "i" then
  91.        straight:stop()
  92.        entity:get_sprite():set_animation("stopped")
  93.    end
  94.  
  95. end
  96.  
« Last Edit: May 02, 2017, 11:27:22 pm by Zefk »

Diarandor

  • Hero Member
  • *****
  • Posts: 753
  • Cats are cool! (ΦωΦ)
    • View Profile
Re: 2 Player
« Reply #13 on: May 02, 2017, 10:59:17 pm »
Why using 3.14? He should use math.pi, which is the standard value in the math library. It is clear that he is hiding the math library with a useless local variable "math", that is the problem.

Zefk

  • Sr. Member
  • ****
  • Posts: 492
  • Just helping Solarus
    • View Profile
    • Zelzec Business
Re: 2 Player
« Reply #14 on: May 02, 2017, 11:03:05 pm »
@Diarandor
Ah! I did not notice that tricky math variable.

@yankscally
No need for the math variable.