I had implemented the 2nd hero as a custom entity, and now changed this to an actual hero entity, created with map:create_hero. However, this seems to break the key binding of "pause" to "p", with game:set_command_keyboard_binding("pause", "p"), which did not break with the custom entity. This means that the "d" key is now mapped to pause, as well as to the hero movement.
I'll post my script here in case anyone knows how to debug this. The code probably has plenty of other problems, since I've never programmed Lua before. The WASD control is now implemented like the mouse_control script. But perhaps it would be better to have it run as an entity script, as with enemies or NPCs? Only there's no GUI button to link it yet.
To make this script work, you have to include it in "features.lua", and then place a custom entity on the map and name it "wasd_hero". To see it work as a custom entity without the bug, you have to comment out lines 43-48.
require("scripts/multi_events")
-- This replaces an entity named "wasd_hero" on the map
-- with a 2nd hero using a goblin sprite, who can be
-- steered with the WASD keys and throw axes with left Ctrl.
--
-- FIXME: When pressing the WASD keys to steer the 2nd hero,
-- the "d" key is again mapped to "pause", which did not happen
-- when the hero is a custom entity.
local function initialize_wasd_control_features(game)
game:set_command_keyboard_binding("pause", "p")
local hero2
local wasd_control = {}
local movement = sol.movement.create("straight")
local current_direction = 3
local current_angle = 3 * math.pi / 2
local is_key_pushed = false
print("WASD control startup")
-- Movement of hero 2.
local directions = {
right = false,
up = false,
left = false,
down = false,
attack = false
}
-- Replace entity "wasd_hero" with a 2nd hero
-- using the goblin green sprite.
-- FIXME: should be called once on map creation
function wasd_control:set_hero2()
if hero2 ~= nil then
return
end
map = game:get_map()
if map:has_entity("wasd_hero") then
hero2 = map:get_entity("wasd_hero")
-- FIXME: Comment out these 6 lines to play as custom entity. This fixes
-- the "pause" bug, but it's not a hero anymore.
local x, y, z = hero2:get_position()
hero2:remove()
hero2 = map:create_hero {x = x, y = y, layer = z}
hero2:set_name("wasd_hero")
hero2:remove_sprite(hero2:get_sprite("tunic"))
hero2:create_sprite("enemies/goblin_green", "tunic")
end
end
-- detect pressed WASD keys
function wasd_control:on_key_pressed(key, modifiers)
if key == "w" then
directions.up = true
end
if key == "a" then
directions.left = true
end
if key == "s" then
directions.down = true
end
if key == "d" then
directions.right = true
end
if key == "left control" then
directions.attack = true
end
self:update_movement()
end
-- detect released WASD keys
function wasd_control:on_key_released(key)
if key == "w" then
directions.up = false
end
if key == "a" then
directions.left = false
end
if key == "s" then
directions.down = false
end
if key == "d" then
directions.right = false
end
if key == "left control" then
directions.attack = false
end
self:update_movement()
end
-- calc direction for sprite:
-- right = 0, up = 1, left = 2, down = 3
function wasd_control:calc_direction()
if directions.right then
current_direction = 0
elseif directions.left then
current_direction = 2
elseif directions.up then
current_direction = 1
elseif directions.down then
current_direction = 3
else
-- direction stays as it is
end
return current_direction
end
-- cals angle
function wasd_control:calc_angle()
if directions.right and directions.up then
current_angle = 1 * math.pi / 4
elseif directions.right and directions.down then
current_angle = 7 * math.pi / 4
elseif directions.left and directions.up then
current_angle = 3 * math.pi / 4
elseif directions.left and directions.down then
current_angle = 5 * math.pi / 4
elseif directions.right then
current_angle = 0 * math.pi / 2
elseif directions.up then
current_angle = 1 * math.pi / 2
elseif directions.left then
current_angle = 2 * math.pi / 2
elseif directions.down then
current_angle = 3 * math.pi / 2
else
-- angle stays as it is
end
return current_angle
end
-- loop over hero2 sprite and set animation
function wasd_control:set_hero2_anim(anim_name)
for _, s in hero2:get_sprites() do
if s:has_animation(anim_name) then s:set_animation(anim_name) end
end
end
-- update movement for the hero, and spawn axes when attacking
function wasd_control:update_movement()
local map = game:get_map()
self:set_hero2()
if (map == nil or movement == nil or hero2 == nil) then
return
end
hero2:set_direction(self:calc_direction())
if (hero2:get_sprite("tunic") ~= nil) then
hero2:get_sprite("tunic"):set_direction(self:calc_direction())
end
-- when attacking, throw goblin axes
if directions.attack then
local proj_sprite_id = "enemies/goblin_axe"
local x, y, layer = hero2:get_position()
local prop = {x=x, y=y, layer=layer, direction=self:calc_direction(), width=16, height=16}
local projectile = map:create_custom_entity(prop)
local proj_sprite = projectile:create_sprite(proj_sprite_id)
proj_sprite:set_animation("thrown")
projectile:stop_movement()
local m = sol.movement.create("straight")
m:set_angle(self:calc_angle())
m:set_speed(100)
m:set_max_distance(300)
m:start(projectile)
projectile:set_can_traverse("enemy", false)
function projectile:on_obstacle_reached() projectile:remove() end
function projectile:on_movement_finished() projectile:remove() end
-- FIXME: collision not always detected, perhaps due to on_obstacle_reached
projectile:add_collision_test("overlapping", function(projectile, other)
if other:get_type() == "enemy" then
other:hurt(1)
projectile:remove()
end
end)
end
if not directions.right and not directions.left and not directions.up and not directions.down then
movement:stop()
hero2:stop_movement()
self:set_hero2_anim("stopped")
return
end
self:set_hero2_anim("walking")
movement:set_speed(90)
movement:set_angle(self:calc_angle())
movement:start(hero2)
end
sol.menu.start(game, wasd_control)
end
local game_meta = sol.main.get_metatable("game")
game_meta:register_event("on_started", initialize_wasd_control_features)
return true