Added first sabotage node

master
Nathan Salapat 2022-06-06 20:49:05 -05:00
parent d11aef2922
commit 1f45cdf5ce
12 changed files with 405 additions and 53 deletions

View File

@ -115,22 +115,30 @@ function doors.door_toggle(pos, node, clicker, close)
local wield_name = wield:get_name()
if not minetest.is_protected(pos, player_name) and minetest.check_player_privs(player_name, { creative = true }) then
else
if lock_status == 1 and (minetest.is_protected(pos, player_name) or not minetest.check_player_privs(player_name, { creative = true })) then
return
elseif lock_status == 2 then
local key = meta:get_string('key')
if wield_name ~= key then
local def = minetest.registered_items[key]
local key_name = def.description
minetest.chat_send_player(player_name, 'This door can be opened/closed with a '..key_name..'.')
local map_id = lobby.game[player_name]
local sabotage_level = lobby.sabotage_level[map_id] or 0--hmmm
local level = meta:get_int('level') or 0
if level > sabotage_level then
if lock_status == 1 and (minetest.is_protected(pos, player_name) or not minetest.check_player_privs(player_name, { creative = true })) then
return
elseif lock_status == 2 then
local key = meta:get_string('key')
if wield_name ~= key then
local def = minetest.registered_items[key]
local key_name = def.description
minetest.chat_send_player(player_name, 'This door can be opened/closed with a '..key_name..'.')
return
else
minetest.after(3, function()
doors.door_toggle(pos, nil, clicker, true)
end)
end
elseif lock_status >= 3 then
minetest.chat_send_player(player_name, 'The lock looks cheap, you might be able to pick it.')
return
else
minetest.after(3, function()
doors.door_toggle(pos, nil, clicker, true)
end)
end
elseif lock_status >= 3 then
minetest.chat_send_player(player_name, 'The lock looks cheap, you might be able to pick it.')
else
minetest.chat_send_player(player_name, 'This door can\'t be used right now.')
return
end
end
@ -367,22 +375,30 @@ function doors.trapdoor_toggle(pos, node, clicker, close)
local wield_name = wield:get_name()
if not minetest.is_protected(pos, player_name) and minetest.check_player_privs(player_name, { creative = true }) then
else
if lock_status == 1 and (minetest.is_protected(pos, player_name) or not minetest.check_player_privs(player_name, { creative = true })) then
return
elseif lock_status == 2 then
local key = meta:get_string('key')
if wield_name ~= key then
local def = minetest.registered_items[key]
local key_name = def.description
minetest.chat_send_player(player_name, 'This door can be opened/closed with a '..key_name..'.')
local map_id = lobby.game[player_name]
local sabotage_level = lobby.sabotage_level[map_id] or 0--hmmm
local level = meta:get_int('level') or 0
if level > sabotage_level then
if lock_status == 1 and (minetest.is_protected(pos, player_name) or not minetest.check_player_privs(player_name, { creative = true })) then
return
elseif lock_status == 2 then
local key = meta:get_string('key')
if wield_name ~= key then
local def = minetest.registered_items[key]
local key_name = def.description
minetest.chat_send_player(player_name, 'This door can be opened/closed with a '..key_name..'.')
return
else
minetest.after(3, function()
doors.trapdoor_toggle(pos, nil, clicker, true)
end)
end
elseif lock_status >= 3 then
minetest.chat_send_player(player_name, 'The lock looks cheap, you might be able to pick it.')
return
else
minetest.after(3, function()
doors.trapdoor_toggle(pos, nil, clicker, true)
end)
end
elseif lock_status >= 3 then
minetest.chat_send_player(player_name, 'The lock looks cheap, you might be able to pick it.')
else
minetest.chat_send_player(player_name, 'This door can\'t be used right now.')
return
end
end
@ -484,28 +500,30 @@ function doors.lock(pos, name)
local meta = minetest.get_meta(pos)
local status = meta:get_int('lock_s')
local key_i = meta:get_int('key_i')
local level = meta:get_int('level')
local infotext = meta:get_string('infotext')
minetest.show_formspec(name, 'doors:configuration', doors.lock_formspec(status, key_i, infotext))
minetest.show_formspec(name, 'doors:configuration', doors.lock_formspec(status, key_i, level, infotext))
end
local keys = {
'doors:key_skeleton', 'doors:key_modern', 'doors:keycard_red', 'doors:keycard_green', 'doors:keycard_blue'
}
function doors.lock_formspec(status, key_i, infotext)
function doors.lock_formspec(status, key_i, level, infotext)
local formspec =
'formspec_version[3]'..
'size[8,8]'..
'textarea[.5,.5;7,4.25;;;Set the lock status here, different numbers mean different things, the following breakdown should be helpful.\n'..
'!!!This is still a work in progress, and may change in the future!!!\n\n'..
'0: Standard door, anybody can open it.\n'..
'\n0: Standard door, anybody can open it.\n'..
'1: Locked door, only people within the area protection can open. Not pickable.\n'..
'2: Locked door, uses Key to open.\n'..
'3-13: locked door, can be picked, higher values are harder to pick. Limited time is offered to pick a door. '..
'Formula is (20-lock Status)+Luck.]'..
'Formula is (20-lock Status)+Luck.\n'..
'\nYou can ignore the level input if you aren\'t adding a node you can sabotage.]'..
'field[1,5;6,.5;infotext;Infotext;'..infotext..']'..
'field[1,6;2,.5;status;Lock Status;'..status..']'..
'dropdown[4,6;3,.5;key;'..table.concat(keys, ',')..';'..key_i..']'..
'field[1,7;2,.5;level;Level;'..level..']'..
'button_exit[5,7;2,.5;save;Submit]'
return formspec
end
@ -521,6 +539,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
local info = fields.infotext
local lower = string.lower(info)
meta:set_int('lock_s', state)
meta:set_int('level', math.min(fields.level, 4))
if state == 0 then
meta:set_string('infotext', info)
elseif state == 2 then

View File

@ -35,13 +35,12 @@ function sabotage.builder_formspec(pos)
'and should include something to let them know where/what was sabotaged and needs to be fixed.\n'..
'This will be a chat message. The node description will also be included in the HUD display.\n'..
'Sabotage level controls which tasks can be completed, or nodes can be interacted with. '..
'Sabotage can be in levels 1-4, configurable nodes can have levels defined, if the level is BELOW the sabotage level it can still be interacted with. '..
'Currently this is in BETA, and many nodes do not care about this data.]'..
'Sabotage can be in levels 1-4, configurable nodes can have levels defined, if the level is BELOW the sabotage level it can still be interacted with.]'..
'textarea[1,5;14,1;alert;Alert Text:;'..alert..']'..
'field[1,6.5;2.5,.75;chance;Chance:;'..chance..']'..
'field[6.75,6.5;2.5,.75;damage;Damage:;'..damage..']'..
'field[12.5,6.5;2.5,.75;timer_input;Timer:;'..timer..']'..
'field[14,6.5;2.5,.75;level;Sabotage level:;'..level..']'..
'field[4.83,6.5;2.5,.75;damage;Damage:;'..damage..']'..
'field[8.66,6.5;2.5,.75;timer_input;Timer:;'..timer..']'..
'field[12.5,6.5;2.5,.75;level;Sabotage level:;'..level..']'..
'button_exit[6.5,7.5;3,1;save;Save]'
return formspec
end

View File

@ -11,7 +11,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
else
local chance = meta:get_int('chance') or 1
local damage = meta:get_int('damage') or 1
local level = meta:get_int('level') or 0
local level = meta:get_int('level') or 1
local player_attributes = player:get_meta()
local luck = player_attributes:get_int('luck')
if math.random(10) + luck >= chance then
@ -21,9 +21,14 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
player:set_hp(new_health)
end
if math.random(10) + luck >= chance then
local node = minetest.get_node(pos).name
local def = minetest.registered_nodes[node]
local node = minetest.get_node(pos)
local node_name = node.name
local def = minetest.registered_nodes[node_name]
local desc = def.description
local flip = def.sabotage_switch
if flip then
minetest.swap_node(pos, {name = flip, param2 = node.param2})
end
meta:set_string('traitor', name)
meta:set_string('map_id', map_id)
meta:set_string('sabotaged', 'true')
@ -57,7 +62,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
local chance = math.min((tonumber(fields.chance) or 2), 10)
local damage = tonumber(fields.damage) or 1
local alert = fields.alert or ''
local level = math.min((tonumber(fields.level) or 0), 4)
local level = math.min((tonumber(fields.level) or 1), 4)
meta:set_int('timer', timer)
meta:set_int('chance', chance)
meta:set_int('damage', damage)
@ -80,6 +85,13 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
player:set_hp(new_health)
end
if math.random(10) + luck >= chance then
local node = minetest.get_node(pos)
local node_name = node.name
local def = minetest.registered_nodes[node_name]
local flip = def.sabotage_switch
if flip then
minetest.swap_node(pos, {name = flip, param2 = node.param2})
end
local time = meta:get_int('timer')
sabotage.clear_hud(map_id)
meta:set_string('sabotaged', 'false')

View File

@ -2,7 +2,6 @@ sabotage = {}
sabotage.player_pos = {}
sabotage.hud = {}
sabotage.timer = {}
sabotage.level = {}
dofile(minetest.get_modpath('sabotage')..'/formspecs.lua')
dofile(minetest.get_modpath('sabotage')..'/functions.lua')

View File

@ -0,0 +1,147 @@
# Blender v2.93.5 OBJ File: 'tasks.blend'
# www.blender.org
o Power_switch_Cube.015
v 0.312500 0.437500 0.500000
v 0.312500 0.437500 0.437500
v -0.312500 0.437500 0.500000
v -0.312500 0.437500 0.437500
v 0.312500 -0.437500 0.500000
v 0.312500 -0.437500 0.437500
v -0.312500 -0.437500 0.500000
v -0.312500 -0.437500 0.437500
v 0.125000 0.250000 0.437500
v 0.125000 0.250000 0.250000
v -0.125000 0.250000 0.437500
v -0.125000 0.250000 0.250000
v 0.125000 -0.250000 0.437500
v 0.125000 -0.250000 0.250000
v -0.125000 -0.250000 0.437500
v -0.125000 -0.250000 0.250000
v 0.250000 -0.309109 0.129175
v 0.250000 -0.363235 0.097925
v -0.250000 -0.309109 0.129175
v -0.250000 -0.363235 0.097925
v 0.250000 -0.340359 0.183301
v 0.250000 -0.394485 0.152051
v -0.250000 -0.340359 0.183301
v -0.250000 -0.394485 0.152051
v 0.187500 0.058339 0.305236
v 0.187500 -0.374674 0.055237
v 0.125000 0.058339 0.305236
v 0.125000 -0.374674 0.055236
v 0.187500 -0.004161 0.413490
v 0.187500 -0.437174 0.163490
v 0.125000 -0.004161 0.413490
v 0.125000 -0.437174 0.163490
v -0.187500 -0.437174 0.163490
v -0.187500 -0.004161 0.413490
v -0.125000 -0.437174 0.163490
v -0.125000 -0.004161 0.413490
v -0.187500 -0.374674 0.055237
v -0.187500 0.058339 0.305236
v -0.125000 -0.374674 0.055236
v -0.125000 0.058339 0.305237
vt 0.031250 0.500000
vt 0.031250 0.468750
vt 0.343750 0.468750
vt 0.343750 0.500000
vt 0.375000 0.468750
vt 0.343750 0.031250
vt 0.375000 0.031250
vt 0.343750 -0.000000
vt 0.031250 0.031250
vt 0.031250 -0.000000
vt 0.000000 0.031250
vt 0.000000 0.468750
vt 0.218750 0.562500
vt 0.218750 0.656250
vt 0.093750 0.656250
vt 0.093750 0.562500
vt -0.000000 0.656250
vt 0.093750 0.906250
vt -0.000000 0.906250
vt 0.093750 1.000000
vt 0.218750 0.906250
vt 0.218750 1.000000
vt 0.312500 0.906250
vt 0.312500 0.656250
vt 0.921875 0.578125
vt 0.921875 0.609375
vt 0.671875 0.609375
vt 0.671875 0.578125
vt 0.640625 0.671875
vt 0.640625 0.640625
vt 0.671875 0.640625
vt 0.671875 0.671875
vt 0.921875 0.640625
vt 0.921875 0.671875
vt 0.953125 0.640625
vt 0.953125 0.671875
vt 0.921875 0.546875
vt 0.671875 0.546875
vt 0.906250 0.687500
vt 0.906250 0.937500
vt 0.875000 0.937500
vt 0.875000 0.687500
vt 1.000000 0.937500
vt 1.000000 1.000000
vt 0.968750 1.000000
vt 0.968750 0.937500
vt 0.968750 0.687500
vt 0.968750 0.625000
vt 1.000000 0.625000
vt 1.000000 0.687500
vt 0.812500 0.937500
vt 0.812500 0.687500
vt 0.593750 0.687500
vt 0.593750 0.625000
vt 0.625000 0.625000
vt 0.625000 0.687500
vt 0.625000 0.937500
vt 0.625000 1.000000
vt 0.593750 1.000000
vt 0.593750 0.937500
vt 0.781250 0.687500
vt 0.781250 0.937500
vt 0.718750 0.937500
vt 0.718750 0.687500
vt 0.687500 0.687500
vt 0.687500 0.937500
vn 0.0000 1.0000 -0.0000
vn -1.0000 0.0000 -0.0000
vn 0.0000 -1.0000 0.0000
vn 1.0000 -0.0000 0.0000
vn 0.0000 0.0000 -1.0000
vn 0.0000 0.5000 -0.8660
vn 0.0000 -0.5000 0.8660
vn 0.0000 -0.8660 -0.5000
vn -0.0000 0.8660 0.5000
s off
f 1/1/1 2/2/1 4/3/1 3/4/1
f 3/5/2 4/3/2 8/6/2 7/7/2
f 7/8/3 8/6/3 6/9/3 5/10/3
f 5/11/4 6/9/4 2/2/4 1/12/4
f 8/6/5 4/3/5 2/2/5 6/9/5
f 9/13/1 10/14/1 12/15/1 11/16/1
f 11/17/2 12/15/2 16/18/2 15/19/2
f 15/20/3 16/18/3 14/21/3 13/22/3
f 13/23/4 14/21/4 10/14/4 9/24/4
f 16/18/5 12/15/5 10/14/5 14/21/5
f 17/25/6 18/26/6 20/27/6 19/28/6
f 19/29/2 20/30/2 24/31/2 23/32/2
f 23/32/7 24/31/7 22/33/7 21/34/7
f 21/34/4 22/33/4 18/35/4 17/36/4
f 24/31/8 20/27/8 18/26/8 22/33/8
f 21/37/9 17/25/9 19/28/9 23/38/9
f 25/39/6 26/40/6 28/41/6 27/42/6
f 32/43/8 28/44/8 26/45/8 30/46/8
f 29/47/9 25/48/9 27/49/9 31/50/9
f 31/50/7 32/43/7 30/46/7 29/47/7
f 29/47/4 30/46/4 26/40/4 25/39/4
f 27/42/2 28/41/2 32/51/2 31/52/2
f 36/53/9 40/54/9 38/55/9 34/56/9
f 33/57/8 37/58/8 39/59/8 35/60/8
f 36/61/4 35/62/4 39/63/4 40/64/4
f 34/56/7 33/57/7 35/60/7 36/53/7
f 38/65/2 37/66/2 33/57/2 34/56/2
f 40/64/6 39/63/6 37/66/6 38/65/6

View File

@ -0,0 +1,147 @@
# Blender v2.93.5 OBJ File: 'tasks.blend'
# www.blender.org
o Power_switch_Cube.015
v 0.312500 0.437500 0.500000
v 0.312500 0.437500 0.437500
v -0.312500 0.437500 0.500000
v -0.312500 0.437500 0.437500
v 0.312500 -0.437500 0.500000
v 0.312500 -0.437500 0.437500
v -0.312500 -0.437500 0.500000
v -0.312500 -0.437500 0.437500
v 0.125000 0.250000 0.437500
v 0.125000 0.250000 0.250000
v -0.125000 0.250000 0.437500
v -0.125000 0.250000 0.250000
v 0.125000 -0.250000 0.437500
v 0.125000 -0.250000 0.250000
v -0.125000 -0.250000 0.437500
v -0.125000 -0.250000 0.250000
v 0.250000 0.340382 0.183342
v 0.250000 0.394508 0.152092
v -0.250000 0.340382 0.183342
v -0.250000 0.394509 0.152092
v 0.250000 0.309132 0.129215
v 0.250000 0.363259 0.097965
v -0.250000 0.309132 0.129215
v -0.250000 0.363259 0.097965
v 0.187500 0.004184 0.413530
v 0.187500 0.437197 0.163530
v 0.125000 0.004184 0.413530
v 0.125000 0.437197 0.163530
v 0.187500 -0.058316 0.305277
v 0.187500 0.374697 0.055277
v 0.125000 -0.058316 0.305277
v 0.125000 0.374697 0.055277
v -0.187500 0.374697 0.055277
v -0.187500 -0.058316 0.305277
v -0.125000 0.374697 0.055277
v -0.125000 -0.058316 0.305277
v -0.187500 0.437197 0.163530
v -0.187500 0.004184 0.413530
v -0.125000 0.437197 0.163530
v -0.125000 0.004184 0.413530
vt 0.031250 0.500000
vt 0.031250 0.468750
vt 0.343750 0.468750
vt 0.343750 0.500000
vt 0.375000 0.468750
vt 0.343750 0.031250
vt 0.375000 0.031250
vt 0.343750 -0.000000
vt 0.031250 0.031250
vt 0.031250 -0.000000
vt 0.000000 0.031250
vt 0.000000 0.468750
vt 0.218750 0.562500
vt 0.218750 0.656250
vt 0.093750 0.656250
vt 0.093750 0.562500
vt -0.000000 0.656250
vt 0.093750 0.906250
vt -0.000000 0.906250
vt 0.093750 1.000000
vt 0.218750 0.906250
vt 0.218750 1.000000
vt 0.312500 0.906250
vt 0.312500 0.656250
vt 0.921875 0.578125
vt 0.921875 0.609375
vt 0.671875 0.609375
vt 0.671875 0.578125
vt 0.640625 0.671875
vt 0.640625 0.640625
vt 0.671875 0.640625
vt 0.671875 0.671875
vt 0.921875 0.640625
vt 0.921875 0.671875
vt 0.953125 0.640625
vt 0.953125 0.671875
vt 0.921875 0.546875
vt 0.671875 0.546875
vt 0.906250 0.687500
vt 0.906250 0.937500
vt 0.875000 0.937500
vt 0.875000 0.687500
vt 1.000000 0.937500
vt 1.000000 1.000000
vt 0.968750 1.000000
vt 0.968750 0.937500
vt 0.968750 0.687500
vt 0.968750 0.625000
vt 1.000000 0.625000
vt 1.000000 0.687500
vt 0.812500 0.937500
vt 0.812500 0.687500
vt 0.593750 0.687500
vt 0.593750 0.625000
vt 0.625000 0.625000
vt 0.625000 0.687500
vt 0.625000 0.937500
vt 0.625000 1.000000
vt 0.593750 1.000000
vt 0.593750 0.937500
vt 0.781250 0.687500
vt 0.781250 0.937500
vt 0.718750 0.937500
vt 0.718750 0.687500
vt 0.687500 0.687500
vt 0.687500 0.937500
vn 0.0000 1.0000 -0.0000
vn -1.0000 0.0000 -0.0000
vn 0.0000 -1.0000 0.0000
vn 1.0000 -0.0000 0.0000
vn 0.0000 0.0000 -1.0000
vn -0.0000 0.5000 0.8660
vn 0.0000 -0.5000 -0.8660
vn 0.0000 0.8660 -0.5000
vn -0.0000 -0.8660 0.5000
s off
f 1/1/1 2/2/1 4/3/1 3/4/1
f 3/5/2 4/3/2 8/6/2 7/7/2
f 7/8/3 8/6/3 6/9/3 5/10/3
f 5/11/4 6/9/4 2/2/4 1/12/4
f 8/6/5 4/3/5 2/2/5 6/9/5
f 9/13/1 10/14/1 12/15/1 11/16/1
f 11/17/2 12/15/2 16/18/2 15/19/2
f 15/20/3 16/18/3 14/21/3 13/22/3
f 13/23/4 14/21/4 10/14/4 9/24/4
f 16/18/5 12/15/5 10/14/5 14/21/5
f 17/25/6 18/26/6 20/27/6 19/28/6
f 19/29/2 20/30/2 24/31/2 23/32/2
f 23/32/7 24/31/7 22/33/7 21/34/7
f 21/34/4 22/33/4 18/35/4 17/36/4
f 24/31/8 20/27/8 18/26/8 22/33/8
f 21/37/9 17/25/9 19/28/9 23/38/9
f 25/39/6 26/40/6 28/41/6 27/42/6
f 32/43/8 28/44/8 26/45/8 30/46/8
f 29/47/9 25/48/9 27/49/9 31/50/9
f 31/50/7 32/43/7 30/46/7 29/47/7
f 29/47/4 30/46/4 26/40/4 25/39/4
f 27/42/2 28/41/2 32/51/2 31/52/2
f 36/53/9 40/54/9 38/55/9 34/56/9
f 33/57/8 37/58/8 39/59/8 35/60/8
f 36/61/4 35/62/4 39/63/4 40/64/4
f 34/56/7 33/57/7 35/60/7 36/53/7
f 38/65/2 37/66/2 33/57/2 34/56/2
f 40/64/6 39/63/6 37/66/6 38/65/6

View File

@ -1,7 +1 @@
minetest.register_node('sabotage:test', {
description = 'Sabotage Testing',
tiles = {'sabotage_test.png'},
groups = {breakable=1, sabotage=1, not_in_creative_inventory=1},
on_rightclick = sabotage.on_rightclick,
on_timer = sabotage.on_timer
})
dofile(minetest.get_modpath('sabotage')..'/nodes/power_switch.lua')

View File

@ -0,0 +1,35 @@
local power_switch_box = {
type = 'fixed',
fixed = {-.3875, -.5, .125, .3875, .5, .5}
}
minetest.register_node('sabotage:power_switch_on', {
description = 'Power Switch',
drawtype = 'mesh',
mesh = 'sabotage_power_switch_on.obj',
tiles = {'sabotage_power_switch.png'},
paramtype = 'light',
paramtype2 = 'facedir',
selection_box = power_switch_box,
collision_box = power_switch_box,
groups = {breakable=1, sabotage=1},
sabotage_switch = 'sabotage:power_switch_off',
on_rightclick = sabotage.on_rightclick,
on_timer = sabotage.on_timer
})
minetest.register_node('sabotage:power_switch_off', {
description = 'Power Switch',
drawtype = 'mesh',
mesh = 'sabotage_power_switch_off.obj',
tiles = {'sabotage_power_switch.png'},
paramtype = 'light',
paramtype2 = 'facedir',
selection_box = power_switch_box,
collision_box = power_switch_box,
groups = {breakable=1, sabotage=1},
sabotage_switch = 'sabotage:power_switch_on',
drop = 'sabotage:power_switch_on',
on_rightclick = sabotage.on_rightclick,
on_timer = sabotage.on_timer
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -15,7 +15,7 @@ minetest.register_tool('tasks:configurator', {
elseif minetest.get_item_group(node.name, 'stash') > 0 then
tasks.stash(pos, name)
elseif minetest.get_item_group(node.name, 'sabotage') > 0 then
sabotage.on_punch(pos, node, user)
sabotage.on_rightclick(pos, node, user)
end
end
end
@ -29,7 +29,7 @@ function tasks.configure(pos, name)
local time_min = meta:get_int('time_min') or 60
local time_max = meta:get_int('time_max') or 120
local xp = meta:get_int('xp') or 5
local level = meta:get_int('level') or 4
local level = meta:get_int('level') or 0
minetest.show_formspec(name, 'tasks:configuration', tasks.configuration_formspec(infotext, time_min, time_max, xp, level))
end

View File

@ -94,7 +94,7 @@ function tasks.right_click_off(pos, node, clicker, formspec)
local sabotage_level = lobby.sabotage_level[map_id] or 0
local meta = minetest.get_meta(pos)
local level = meta:get_int('level') or 0
if level >= sabotage_level then
if level > sabotage_level then
tasks.player_config[name] = pos
minetest.show_formspec(name, 'tasks:part_req_form', formspec)
else