aboutsummaryrefslogtreecommitdiff
path: root/src/objdef.cpp
blob: 08d6844fc135487818ffc73f8a33bcba49e90b32 (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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
/*
Minetest
Copyright (C) 2010-2015 kwolekr, Ryan Kwolek <kwolekr@minetest.net>

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.
*/

#include "objdef.h"
#include "util/numeric.h"
#include "log.h"
#include "gamedef.h"

ObjDefManager::ObjDefManager(IGameDef *gamedef, ObjDefType type)
{
	m_objtype = type;
	m_ndef = gamedef ? gamedef->getNodeDefManager() : NULL;
}


ObjDefManager::~ObjDefManager()
{
	for (size_t i = 0; i != m_objects.size(); i++)
		delete m_objects[i];
}


ObjDefHandle ObjDefManager::add(ObjDef *obj)
{
	assert(obj);

	if (obj->name.length() && getByName(obj->name))
		return OBJDEF_INVALID_HANDLE;

	u32 index = addRaw(obj);
	if (index == OBJDEF_INVALID_INDEX)
		return OBJDEF_INVALID_HANDLE;

	obj->handle = createHandle(index, m_objtype, obj->uid);
	return obj->handle;
}


ObjDef *ObjDefManager::get(ObjDefHandle handle) const
{
	u32 index = validateHandle(handle);
	return (index != OBJDEF_INVALID_INDEX) ? getRaw(index) : NULL;
}


ObjDef *ObjDefManager::set(ObjDefHandle handle, ObjDef *obj)
{
	u32 index = validateHandle(handle);
	if (index == OBJDEF_INVALID_INDEX)
		return NULL;

	ObjDef *oldobj = setRaw(index, obj);

	obj->uid    = oldobj->uid;
	obj->index  = oldobj->index;
	obj->handle = oldobj->handle;

	return oldobj;
}


u32 ObjDefManager::addRaw(ObjDef *obj)
{
	size_t nobjects = m_objects.size();
	if (nobjects >= OBJDEF_MAX_ITEMS)
		return -1;

	obj->index = nobjects;

	// Ensure UID is nonzero so that a valid handle == OBJDEF_INVALID_HANDLE
	// is not possible.  The slight randomness bias isn't very significant.
	obj->uid = myrand() & OBJDEF_UID_MASK;
	if (obj->uid == 0)
		obj->uid = 1;

	m_objects.push_back(obj);

	infostream << "ObjDefManager: added " << getObjectTitle()
		<< ": name=\"" << obj->name
		<< "\" index=" << obj->index
		<< " uid="     << obj->uid
		<< std::endl;

	return nobjects;
}


ObjDef *ObjDefManager::getRaw(u32 index) const
{
	return m_objects[index];
}


ObjDef *ObjDefManager::setRaw(u32 index, ObjDef *obj)
{
	ObjDef *old_obj = m_objects[index];
	m_objects[index] = obj;
	return old_obj;
}


ObjDef *ObjDefManager::getByName(const std::string &name) const
{
	for (size_t i = 0; i != m_objects.size(); i++) {
		ObjDef *obj = m_objects[i];
		if (obj && !strcasecmp(name.c_str(), obj->name.c_str()))
			return obj;
	}

	return NULL;
}


void ObjDefManager::clear()
{
	for (size_t i = 0; i != m_objects.size(); i++)
		delete m_objects[i];

	m_objects.clear();
}


u32 ObjDefManager::validateHandle(ObjDefHandle handle) const
{
	ObjDefType type;
	u32 index;
	u32 uid;

	bool is_valid =
		(handle != OBJDEF_INVALID_HANDLE)         &&
		decodeHandle(handle, &index, &type, &uid) &&
		(type == m_objtype)                       &&
		(index < m_objects.size())                &&
		(m_objects[index]->uid == uid);

	return is_valid ? index : -1;
}


ObjDefHandle ObjDefManager::createHandle(u32 index, ObjDefType type, u32 uid)
{
	ObjDefHandle handle = 0;
	set_bits(&handle, 0, 18, index);
	set_bits(&handle, 18, 6, type);
	set_bits(&handle, 24, 7, uid);

	u32 parity = calc_parity(handle);
	set_bits(&handle, 31, 1, parity);

	return handle ^ OBJDEF_HANDLE_SALT;
}


bool ObjDefManager::decodeHandle(ObjDefHandle handle, u32 *index,
	ObjDefType *type, u32 *uid)
{
	handle ^= OBJDEF_HANDLE_SALT;

	u32 parity = get_bits(handle, 31, 1);
	set_bits(&handle, 31, 1, 0);
	if (parity != calc_parity(handle))
		return false;

	*index = get_bits(handle, 0, 18);
	*type  = (ObjDefType)get_bits(handle, 18, 6);
	*uid   = get_bits(handle, 24, 7);
	return true;
}
n class="hl kwa">then return false end -- now the track must be two-conn, else it wouldn't be allowed to have track_place_group set. --assert(#conns == 2) -- cannot check here, because of legacy crossing hack -- Is player and game allowed to do this? if not advtrains.can_dig_or_modify_track(pos) then return false end if not advtrains.check_track_protection(pos, pname) then return false end -- we confirmed that track can be modified. Does there exist a suitable connection candidate? -- check if there are any unbound ends local bound_connids = {} for connid, conn in ipairs(conns) do local adj_pos, adj_connid = advtrains.get_adjacent_rail(pos, conns, connid) if adj_pos then bound_connids[#bound_connids+1] = connid end end -- depending on the nummber of ends, decide if #bound_connids == 2 then -- rail is within a fixed track, do not break up return false end -- obtain the group table local g = tp.groups[ndef.advtrains.track_place_group] if #bound_connids == 1 then -- we can attempt double local bound_dir = conns[bound_connids[1]].c if g.double[back_dir.."_"..bound_dir] then if commit then advtrains.ndb.swap_node(pos, g.double[back_dir.."_"..bound_dir]) end return true end else -- rail is entirely unbound, we can attempt single1 if g.single1[back_dir] then if commit then advtrains.ndb.swap_node(pos, g.single1[back_dir]) end return true end end end local function track_place_node(pos, node, ndef_p, pname) --atdebug("track_place_node: ",pos, node) advtrains.ndb.swap_node(pos, node) local ndef = ndef_p or minetest.registered_nodes[node.name] if ndef and ndef.after_place_node then -- resolve player again local player = pname and core.get_player_by_name(pname) or nil ndef.after_place_node(pos, player) -- note: itemstack and pointed_thing are NOT available here anymore (crap!) end end -- Main API function to place a track. Replaces the older "placetrack" -- This function will attempt to place a track of the specified track placing group at the specified position, connecting it -- with neighboring rails. Neighboring rails can themselves be replaced ("bent") within their own track place group, -- if the player is permitted to do this. -- Order of preference is: -- Connect two track ends if possible -- Connect one track end if any rail is near -- Place the default track if no tracks are near -- The function returns true on success. function tp.place_track(pos, tpg, pname, yaw) -- 1. collect neighboring tracks and whether they can be connected --atdebug("tp.place_track(",pos, tpg, pname, yaw,")") local cand = {} for i=0,15 do if check_or_bend_rail(pos, i, pname) then cand[#cand+1] = i end end --atdebug("Candidates: ",cand) -- obtain the group table local g = tp.groups[tpg] if not g then error("tp.place_track: for tpg="..tpg.." couldn't find the group table") end --atdebug("Group table:",g) -- 2. try all possible two-endpoint connections for k1, conn1 in ipairs(cand) do for k2, conn2 in ipairs(cand) do if k1~=k2 then -- order of conn1/conn2: prefer conn1 being in the direction of the player facing. -- the combination the other way round will be run through in a later loop iteration if advtrains.yawToDirection(yaw, conn1, conn2) == conn1 then -- does there exist a suitable double-connection rail? --atdebug("Try double conn: ",conn1, conn2) local node = g.double[conn1.."_"..conn2] if node then check_or_bend_rail(pos, conn1, pname, true) check_or_bend_rail(pos, conn2, pname, true) track_place_node(pos, node, nil, pname) -- calls after_place_node implicitly return true end end end end end -- 3. try all possible one_endpoint connections for k1, conn1 in ipairs(cand) do -- select single1 or single2? depending on yaw local single if advtrains.yawToDirection(yaw, conn1, advtrains.oppd(conn1)) == conn1 then single = g.single1 else single = g.single2 end --atdebug("Try single conn: ",conn1) local node = single[conn1] if node then check_or_bend_rail(pos, conn1, pname, true) track_place_node(pos, node, nil, pname) -- calls after_place_node implicitly return true end end -- 4. if nothing worked, set the default local node = g.default track_place_node(pos, node, nil, pname) -- calls after_place_node implicitly return true end -- TRACK WORKER -- minetest.register_craftitem("advtrains:trackworker",{ description = attrans("Track Worker Tool\n\nLeft-click: change rail type (straight/curve/switch)\nRight-click: rotate object"), groups = {cracky=1}, -- key=name, value=rating; rating=1..3. inventory_image = "advtrains_trackworker.png", wield_image = "advtrains_trackworker.png", stack_max = 1, on_place = function(itemstack, placer, pointed_thing) local name = placer:get_player_name() if not name then return end local has_aux1_down = placer:get_player_control().aux1 if pointed_thing.type=="node" then local pos=pointed_thing.under if not advtrains.check_track_protection(pos, name) then return end local node=minetest.get_node(pos) -- New since 2.5: only the fields in the node definition are considered, no more hacky pattern matching on the nodename local ndef = minetest.registered_nodes[node.name] if not ndef.advtrains or not ndef.advtrains.trackworker_next_rot then minetest.chat_send_player(placer:get_player_name(), attrans("This node can't be rotated using the trackworker!")) return end -- check if the node is modify-protected if advtrains.is_track(node.name) then -- is a track, we can query local can_modify, reason = advtrains.can_dig_or_modify_track(pos) if not can_modify then local str = attrans("This track can not be rotated!") if reason then str = str .. " " .. reason end minetest.chat_send_player(placer:get_player_name(), str) return end end if has_aux1_down then --feature: flip the node by 180° --i've always wanted this! advtrains.ndb.swap_node(pos, {name=node.name, param2=(node.param2+2)%4}) return end local new_node = {name = ndef.advtrains.trackworker_next_rot, param2 = node.param2} if ndef.advtrains.trackworker_rot_incr_param2 then new_node.param2 = ((node.param2 + 1) % 4) end advtrains.ndb.swap_node(pos, new_node) end end, on_use=function(itemstack, player, pointed_thing) local name = player:get_player_name() if not name then return end if pointed_thing.type=="node" then local pos=pointed_thing.under local node=minetest.get_node(pos) if not advtrains.check_track_protection(pos, name) then return end -- New since 2.5: only the fields in the node definition are considered, no more hacky pattern matching on the nodename local ndef = minetest.registered_nodes[node.name] if not ndef.advtrains or not ndef.advtrains.trackworker_next_var then minetest.chat_send_player(name, attrans("This node can't be changed using the trackworker!")) return end -- check if the node is modify-protected if advtrains.is_track(node.name) then -- is a track, we can query local can_modify, reason = advtrains.can_dig_or_modify_track(pos) if not can_modify then local str = attrans("This track can not be rotated!") if reason then str = str .. " " .. reason end minetest.chat_send_player(name, str) return end end local new_node = {name = ndef.advtrains.trackworker_next_var, param2 = node.param2} advtrains.ndb.swap_node(pos, new_node) end end, }) --putting into right place advtrains.trackplacer=tp