aboutsummaryrefslogtreecommitdiff
path: root/src/client/joystick_controller.h
blob: 3f361e4ef66e469a53efd2beee6e65f3d28cec19 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/*
Minetest
Copyright (C) 2016 est31, <MTest31@outlook.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#pragma once

#include "irrlichttypes_extrabloated.h"
#include "keys.h"
#include <bitset>
#include <vector>

enum JoystickAxis {
	JA_SIDEWARD_MOVE,
	JA_FORWARD_MOVE,

	JA_FRUSTUM_HORIZONTAL,
	JA_FRUSTUM_VERTICAL,

	// To know the count of enum values
	JA_COUNT,
};

struct JoystickAxisLayout {
	u16 axis_id;
	// -1 if to invert, 1 if to keep it.
	int invert;
};


struct JoystickCombination {

	virtual bool isTriggered(const irr::SEvent::SJoystickEvent &ev) const=0;

	GameKeyType key;
};

struct JoystickButtonCmb : public JoystickCombination {

	JoystickButtonCmb() = default;

	JoystickButtonCmb(GameKeyType key, u32 filter_mask, u32 compare_mask) :
		filter_mask(filter_mask),
		compare_mask(compare_mask)
	{
		this->key = key;
	}

	virtual ~JoystickButtonCmb() = default;

	virtual bool isTriggered(const irr::SEvent::SJoystickEvent &ev) const;

	u32 filter_mask;
	u32 compare_mask;
};

struct JoystickAxisCmb : public JoystickCombination {

	JoystickAxisCmb() = default;

	JoystickAxisCmb(GameKeyType key, u16 axis_to_compare, int direction, s16 thresh) :
		axis_to_compare(axis_to_compare),
		direction(direction),
		thresh(thresh)
	{
		this->key = key;
	}

	virtual ~JoystickAxisCmb() = default;

	bool isTriggered(const irr::SEvent::SJoystickEvent &ev) const override;

	u16 axis_to_compare;

	// if -1, thresh must be smaller than the axis value in order to trigger
	// if  1, thresh must be bigger  than the axis value in order to trigger
	int direction;
	s16 thresh;
};

struct JoystickLayout {
	std::vector<JoystickButtonCmb> button_keys;
	std::vector<JoystickAxisCmb> axis_keys;
	JoystickAxisLayout axes[JA_COUNT];
	s16 axes_deadzone;
};

class JoystickController {

public:
	JoystickController();

	void onJoystickConnect(const std::vector<irr::SJoystickInfo> &joystick_infos);

	bool handleEvent(const irr::SEvent::SJoystickEvent &ev);
	void clear();

	bool wasKeyDown(GameKeyType b)
	{
		bool r = m_past_keys_pressed[b];
		m_past_keys_pressed[b] = false;
		return r;
	}

	bool wasKeyReleased(GameKeyType b)
	{
		return m_keys_released[b];
	}
	void clearWasKeyReleased(GameKeyType b)
	{
		m_keys_released[b] = false;
	}

	bool wasKeyPressed(GameKeyType b)
	{
		return m_keys_pressed[b];
	}
	void clearWasKeyPressed(GameKeyType b)
	{
		m_keys_pressed[b] = false;
	}

	bool isKeyDown(GameKeyType b)
	{
		return m_keys_down[b];
	}

	s16 getAxis(JoystickAxis axis)
	{
		return m_axes_vals[axis];
	}

	s16 getAxisWithoutDead(JoystickAxis axis);

	f32 doubling_dtime;

private:
	void setLayoutFromControllerName(const std::string &name);

	JoystickLayout m_layout;

	s16 m_axes_vals[JA_COUNT];

	u8 m_joystick_id = 0;

	std::bitset<KeyType::INTERNAL_ENUM_COUNT> m_keys_down;
	std::bitset<KeyType::INTERNAL_ENUM_COUNT> m_keys_pressed;

	f32 m_internal_time;

	f32 m_past_pressed_time[KeyType::INTERNAL_ENUM_COUNT];

	std::bitset<KeyType::INTERNAL_ENUM_COUNT> m_past_keys_pressed;
	std::bitset<KeyType::INTERNAL_ENUM_COUNT> m_keys_released;
};
wd">lua_pushnil(L); int rowcount = 0; while(lua_next(L, index) != 0){ int colcount = 0; // key at index -2 and value at index -1 if(!lua_istable(L, -1)) return false; int table2 = lua_gettop(L); lua_pushnil(L); while(lua_next(L, table2) != 0){ // key at index -2 and value at index -1 if(!lua_isstring(L, -1)) return false; recipe.push_back(lua_tostring(L, -1)); // removes value, keeps key for next iteration lua_pop(L, 1); colcount++; } if(rowcount == 0){ width = colcount; } else { if(colcount != width) return false; } // removes value, keeps key for next iteration lua_pop(L, 1); rowcount++; } return width != 0; } // helper for register_craft bool ModApiCraft::readCraftRecipeShapeless(lua_State *L, int index, std::vector<std::string> &recipe) { if(index < 0) index = lua_gettop(L) + 1 + index; if(!lua_istable(L, index)) return false; lua_pushnil(L); while(lua_next(L, index) != 0){ // key at index -2 and value at index -1 if(!lua_isstring(L, -1)) return false; recipe.push_back(lua_tostring(L, -1)); // removes value, keeps key for next iteration lua_pop(L, 1); } return true; } // helper for register_craft bool ModApiCraft::readCraftReplacements(lua_State *L, int index, CraftReplacements &replacements) { if(index < 0) index = lua_gettop(L) + 1 + index; if(!lua_istable(L, index)) return false; lua_pushnil(L); while(lua_next(L, index) != 0){ // key at index -2 and value at index -1 if(!lua_istable(L, -1)) return false; lua_rawgeti(L, -1, 1); if(!lua_isstring(L, -1)) return false; std::string replace_from = lua_tostring(L, -1); lua_pop(L, 1); lua_rawgeti(L, -1, 2); if(!lua_isstring(L, -1)) return false; std::string replace_to = lua_tostring(L, -1); lua_pop(L, 1); replacements.pairs.push_back( std::make_pair(replace_from, replace_to)); // removes value, keeps key for next iteration lua_pop(L, 1); } return true; } // register_craft({output=item, recipe={{item00,item10},{item01,item11}}) int ModApiCraft::l_register_craft(lua_State *L) { NO_MAP_LOCK_REQUIRED; //infostream<<"register_craft"<<std::endl; luaL_checktype(L, 1, LUA_TTABLE); int table = 1; // Get the writable craft definition manager from the server IWritableCraftDefManager *craftdef = getServer(L)->getWritableCraftDefManager(); std::string type = getstringfield_default(L, table, "type", "shaped"); /* CraftDefinitionShaped */ if(type == "shaped"){ std::string output = getstringfield_default(L, table, "output", ""); if(output == "") throw LuaError("Crafting definition is missing an output"); int width = 0; std::vector<std::string> recipe; lua_getfield(L, table, "recipe"); if(lua_isnil(L, -1)) throw LuaError("Crafting definition is missing a recipe" " (output=\"" + output + "\")"); if(!readCraftRecipeShaped(L, -1, width, recipe)) throw LuaError("Invalid crafting recipe" " (output=\"" + output + "\")"); CraftReplacements replacements; lua_getfield(L, table, "replacements"); if(!lua_isnil(L, -1)) { if(!readCraftReplacements(L, -1, replacements)) throw LuaError("Invalid replacements" " (output=\"" + output + "\")"); } CraftDefinition *def = new CraftDefinitionShaped( output, width, recipe, replacements); craftdef->registerCraft(def, getServer(L)); } /* CraftDefinitionShapeless */ else if(type == "shapeless"){ std::string output = getstringfield_default(L, table, "output", ""); if(output == "") throw LuaError("Crafting definition (shapeless)" " is missing an output"); std::vector<std::string> recipe; lua_getfield(L, table, "recipe"); if(lua_isnil(L, -1)) throw LuaError("Crafting definition (shapeless)" " is missing a recipe" " (output=\"" + output + "\")"); if(!readCraftRecipeShapeless(L, -1, recipe)) throw LuaError("Invalid crafting recipe" " (output=\"" + output + "\")"); CraftReplacements replacements; lua_getfield(L, table, "replacements"); if(!lua_isnil(L, -1)) { if(!readCraftReplacements(L, -1, replacements)) throw LuaError("Invalid replacements" " (output=\"" + output + "\")"); } CraftDefinition *def = new CraftDefinitionShapeless( output, recipe, replacements); craftdef->registerCraft(def, getServer(L)); } /* CraftDefinitionToolRepair */ else if(type == "toolrepair"){ float additional_wear = getfloatfield_default(L, table, "additional_wear", 0.0); CraftDefinition *def = new CraftDefinitionToolRepair( additional_wear); craftdef->registerCraft(def, getServer(L)); } /* CraftDefinitionCooking */ else if(type == "cooking"){ std::string output = getstringfield_default(L, table, "output", ""); if(output == "") throw LuaError("Crafting definition (cooking)" " is missing an output"); std::string recipe = getstringfield_default(L, table, "recipe", ""); if(recipe == "") throw LuaError("Crafting definition (cooking)" " is missing a recipe" " (output=\"" + output + "\")"); float cooktime = getfloatfield_default(L, table, "cooktime", 3.0); CraftReplacements replacements; lua_getfield(L, table, "replacements"); if(!lua_isnil(L, -1)) { if(!readCraftReplacements(L, -1, replacements)) throw LuaError("Invalid replacements" " (cooking output=\"" + output + "\")"); } CraftDefinition *def = new CraftDefinitionCooking( output, recipe, cooktime, replacements); craftdef->registerCraft(def, getServer(L)); } /* CraftDefinitionFuel */ else if(type == "fuel"){ std::string recipe = getstringfield_default(L, table, "recipe", ""); if(recipe == "") throw LuaError("Crafting definition (fuel)" " is missing a recipe"); float burntime = getfloatfield_default(L, table, "burntime", 1.0); CraftReplacements replacements; lua_getfield(L, table, "replacements"); if(!lua_isnil(L, -1)) { if(!readCraftReplacements(L, -1, replacements)) throw LuaError("Invalid replacements" " (fuel recipe=\"" + recipe + "\")"); } CraftDefinition *def = new CraftDefinitionFuel( recipe, burntime, replacements); craftdef->registerCraft(def, getServer(L)); } else { throw LuaError("Unknown crafting definition type: \"" + type + "\""); } lua_pop(L, 1); return 0; /* number of results */ } // get_craft_result(input) int ModApiCraft::l_get_craft_result(lua_State *L) { NO_MAP_LOCK_REQUIRED; int input_i = 1; std::string method_s = getstringfield_default(L, input_i, "method", "normal"); enum CraftMethod method = (CraftMethod)getenumfield(L, input_i, "method", es_CraftMethod, CRAFT_METHOD_NORMAL); int width = 1; lua_getfield(L, input_i, "width"); if(lua_isnumber(L, -1)) width = luaL_checkinteger(L, -1); lua_pop(L, 1); lua_getfield(L, input_i, "items"); std::vector<ItemStack> items = read_items(L, -1,getServer(L)); lua_pop(L, 1); // items IGameDef *gdef = getServer(L);