diff --git a/tptmp/client/client.lua b/tptmp/client/client.lua index 616d050..3d41c2c 100644 --- a/tptmp/client/client.lua +++ b/tptmp/client/client.lua @@ -293,13 +293,7 @@ function client_i:handle_selecttool_37_() local index = bit.rshift(tool, 14) local xtype = bit.band(tool, 0x3FFF) member[index_to_lrax[index]] = util.to_tool[xtype] and xtype or util.from_tool.TPTMP_PT_UNKNOWN -end - -function client_i:handle_stepsim_50_() - local member = self:member_prefix_() - tpt.set_pause(1) - sim.framerender(1) - log_event(config.print_prefix .. colours.commonstr.event .. "Single-frame step from " .. member.formatted_nick) + member.last_toolslot = index end local simstates = { @@ -464,6 +458,49 @@ function client_i:handle_rectstart_45_() member.rect_x, member.rect_y = self:read_xy_12_() end +function client_i:handle_custgolinfo_46_() + local member = self:member_prefix_() + local ruleset = bit.band(self:read_24be_(), 0x1FFFFF) + local primary = self:read_24be_() + local secondary = self:read_24be_() + local begin = bit.band(bit.rshift(ruleset, 8), 0x1FE) + local stay = bit.band(ruleset, 0x1FF) + local states = bit.band(bit.rshift(ruleset, 17), 0xF) + 2 + local repr = {} + table.insert(repr, "B") + for i = 0, 8 do + if bit.band(bit.lshift(1, i), begin) ~= 0 then + table.insert(repr, i) + end + end + table.insert(repr, "/") + table.insert(repr, "S") + for i = 0, 8 do + if bit.band(bit.lshift(1, i), stay) ~= 0 then + table.insert(repr, i) + end + end + if states ~= 2 then + table.insert(repr, "/") + table.insert(repr, states) + end + member[index_to_lrax[member.last_toolslot]] = { + type = "cgol", + repr = table.concat(repr), + ruleset = ruleset, + primary = primary, + secondary = secondary, + elem = bit.bor(elem.DEFAULT_PT_LIFE, bit.lshift(ruleset, sim.PMAPBITS)), + } +end + +function client_i:handle_stepsim_50_() + local member = self:member_prefix_() + tpt.set_pause(1) + sim.framerender(1) + log_event(config.print_prefix .. colours.commonstr.event .. "Single-frame step from " .. member.formatted_nick) +end + function client_i:handle_sparkclear_60_() local member = self:member_prefix_() tpt.reset_spark() @@ -834,6 +871,14 @@ function client_i:send_rectstart(index, x, y) self:write_flush_() end +function client_i:send_custgolinfo(ruleset, primary, secondary) + self:write_("\46") + self:write_24be_(ruleset) + self:write_24be_(primary) + self:write_24be_(secondary) + self:write_flush_() +end + function client_i:send_stepsim() self:write_flush_("\50") end diff --git a/tptmp/client/init.lua b/tptmp/client/init.lua index 8115f1d..5162eab 100644 --- a/tptmp/client/init.lua +++ b/tptmp/client/init.lua @@ -164,6 +164,11 @@ local function run() [ 1 ] = " REPL", [ 2 ] = " SDEL", } + local function decode_rulestring(tool) + if tool.type == "cgol" then + return tool.repr + end + end local function handle_tick() local now = socket.gettime() if should_reconnect_at and now >= should_reconnect_at then @@ -192,7 +197,7 @@ local function run() sx, sy = 0, 0 end local tool = member.last_tool or member.tool_l - local tool_name = util.to_tool[tool] or "TPTMP_PT_UNKNOWN" + local tool_name = util.to_tool[tool] or decode_rulestring(tool) or "TPTMP_PT_UNKNOWN" local tool_class = util.xid_class[tool] if elem[tool_name] and tool ~= 0 then local real_name = elem.property(elem[tool_name], "Name") diff --git a/tptmp/client/profile/vanilla.lua b/tptmp/client/profile/vanilla.lua index aafe9d7..79265bf 100644 --- a/tptmp/client/profile/vanilla.lua +++ b/tptmp/client/profile/vanilla.lua @@ -11,6 +11,12 @@ local index_to_lrax = { [ 2 ] = "tool_a_", [ 3 ] = "tool_x_", } +local index_to_lraxid = { + [ 0 ] = "tool_lid_", + [ 1 ] = "tool_rid_", + [ 2 ] = "tool_aid_", + [ 3 ] = "tool_xid_", +} local toolwarn_tools = { [ "DEFAULT_UI_PROPERTY" ] = "prop", [ "DEFAULT_TOOL_CYCL" ] = "cycl", @@ -24,14 +30,16 @@ local toolwarn_tools = { [ "TPTMP_PT_UNKNOWN" ] = "unknown", } local toolwarn_messages = { - prop = "The PROP tool does not sync, you will have to use /sync", - cycl = "The CYCL tool does not sync, you will have to use /sync", - mix = "The MIX tool does not sync, you will have to use /sync", - ligh = "LIGH does not sync, you will have to use /sync", - stkm = "Stickmen do not sync, you will have to use /sync", - cbrush = "Custom brushes do not sync, you will have to use /sync", - ipcirc = "The old circle brush does not sync, you will have to use /sync", - unknown = "This custom element is not supported, please avoid using it while connected", + prop = "The PROP tool does not sync, you will have to use /sync", + cycl = "The CYCL tool does not sync, you will have to use /sync", + mix = "The MIX tool does not sync, you will have to use /sync", + ligh = "LIGH does not sync, you will have to use /sync", + stkm = "Stickmen do not sync, you will have to use /sync", + cbrush = "Custom brushes do not sync, you will have to use /sync", + ipcirc = "The old circle brush does not sync, you will have to use /sync", + unknown = "This custom element is not supported, please avoid using it while connected", + cgol = "This custom GOL type is not supported, please avoid using it while connected", + cgolcolor = "Custom GOL currently syncs without colours, use /sync to get colours across", } local log_event = print @@ -44,6 +52,46 @@ while sim.signs[MAX_SIGNS + 1] do MAX_SIGNS = MAX_SIGNS + 1 end +local function rulestring_bits(str) + local bits = 0 + for i = 1, #str do + bits = bit.bor(bits, bit.lshift(1, str:byte(i) - 48)) + end + return bits +end + +local function get_custgolinfo(identifier) + -- * TODO[api]: add an api for this to tpt + local pref = io.open("powder.pref") + if not pref then + return + end + local pref_data = pref:read("*a") + pref:close() + local types = pref_data:match([=["Types"%s*:%s*%[([^%]]+)%]]=]) + if not types then + return + end + for name, ruleset, primary, secondary in types:gmatch([["(%S+)%s+(%S+)%s+(%S+)%s+(%S+)"]]) do + if "DEFAULT_PT_LIFECUST_" .. name == identifier then + local begin, stay, states = ruleset:match("^B([1-8]+)/S([0-8]+)/([0-9]+)$") + if not begin then + begin, stay = ruleset:match("^B([1-8]+)/S([0-8]+)$") + states = "2" + end + states = tonumber(states) + states = states >= 2 and states <= 17 and states + ruleset = begin and stay and states and bit.bor(bit.lshift(rulestring_bits(begin), 8), rulestring_bits(stay), bit.lshift(states - 2, 17)) + primary = tonumber(primary) + secondary = tonumber(secondary) + if ruleset and primary and secondary then + return ruleset, primary, secondary + end + break + end + end +end + local function get_sign_data() local sign_data = {} for i = 1, MAX_SIGNS do @@ -60,13 +108,6 @@ local function get_sign_data() return sign_data end -local function tool_lrax() - return util.from_tool[tpt.selectedl ] or util.from_tool.TPTMP_PT_UNKNOWN, - util.from_tool[tpt.selectedr ] or util.from_tool.TPTMP_PT_UNKNOWN, - util.from_tool[tpt.selecteda ] or util.from_tool.TPTMP_PT_UNKNOWN, - util.from_tool[tpt.selectedreplace] or util.from_tool.TPTMP_PT_UNKNOWN -end - local function perfect_circle() return sim.brush(1, 1, 1, 1, 0)() == 0 end @@ -319,6 +360,17 @@ end function profile_i:report_tool_(index) if self.client then self.client:send_selecttool(index, self[index_to_lrax[index]]) + local identifier = self[index_to_lraxid[index]] + if identifier:find("^DEFAULT_PT_LIFECUST_") then + local ruleset, primary, secondary = get_custgolinfo(identifier) + if ruleset then + self.client:send_custgolinfo(ruleset, primary, secondary) + -- * TODO[api]: add an api for setting gol colour + self.display_toolwarn_["cgolcolor"] = true + else + self.display_toolwarn_["cgol"] = true + end + end end end @@ -659,27 +711,37 @@ function profile_i:update_shape_() end function profile_i:update_tools_() - local tl, tr, ta, tx = tool_lrax() - if self.tool_l_ ~= tl then - self.tool_l_ = tl + local tlid = tpt.selectedl + local trid = tpt.selectedr + local taid = tpt.selecteda + local txid = tpt.selectedreplace + if self.tool_lid_ ~= tlid then + self.tool_l_ = util.from_tool[tlid] or util.from_tool.TPTMP_PT_UNKNOWN + self.tool_lid_ = tlid self:report_tool_(0) end - if self.tool_r_ ~= tr then - self.tool_r_ = tr + if self.tool_rid_ ~= trid then + self.tool_r_ = util.from_tool[trid] or util.from_tool.TPTMP_PT_UNKNOWN + self.tool_rid_ = trid self:report_tool_(1) end - if self.tool_a_ ~= ta then - self.tool_a_ = ta + if self.tool_aid_ ~= taid then + self.tool_a_ = util.from_tool[taid] or util.from_tool.TPTMP_PT_UNKNOWN + self.tool_aid_ = taid self:report_tool_(2) end - if self.tool_x_ ~= tx then - self.tool_x_ = tx + if self.tool_xid_ ~= txid then + self.tool_x_ = util.from_tool[txid] or util.from_tool.TPTMP_PT_UNKNOWN + self.tool_xid_ = txid self:report_tool_(3) end local new_tool = util.to_tool[self[index_to_lrax[self.last_toolslot_]]] + local new_tool_id = self[index_to_lraxid[self.last_toolslot_]] if self.last_tool_ ~= new_tool then - if toolwarn_tools[new_tool] then - self.display_toolwarn_[toolwarn_tools[new_tool]] = true + if not new_tool_id:find("^DEFAULT_PT_LIFECUST_") then + if toolwarn_tools[new_tool] then + self.display_toolwarn_[toolwarn_tools[new_tool]] = true + end end self.last_tool_ = new_tool end @@ -1154,6 +1216,7 @@ function profile_i:handle_keypress(key, scan, rep, shift, ctrl, alt) end end self:report_framestep_() + self.simstate_invalid_ = true elseif scan == sdl.SDL_SCANCODE_B and not ctrl then self.simstate_invalid_ = true elseif scan == sdl.SDL_SCANCODE_Y then @@ -1311,7 +1374,10 @@ local function new(params) clear = { x = gfx.WIDTH - 159, y = gfx.HEIGHT - 16, w = 17, h = 15 }, }, }, profile_m) - prof.tool_l_, prof.tool_r_, prof.tool_a_, prof.tool_x_ = tool_lrax() + prof.tool_l_ = util.from_tool.TPTMP_PT_UNKNOWN + prof.tool_r_ = util.from_tool.TPTMP_PT_UNKNOWN + prof.tool_a_ = util.from_tool.TPTMP_PT_UNKNOWN + prof.tool_x_ = util.from_tool.TPTMP_PT_UNKNOWN prof.last_tool_ = prof.tool_l_ prof.deco_ = sim.decoColour() prof:update_pos_(tpt.mousex, tpt.mousey) diff --git a/tptmp/client/util.lua b/tptmp/client/util.lua index 755eadb..38ab728 100644 --- a/tptmp/client/util.lua +++ b/tptmp/client/util.lua @@ -20,7 +20,6 @@ do end local tools = { - -- * TODO[api]: Could do something about custom GOL; would probably need an API for it first though. "DEFAULT_PT_LIFE_GOL", "DEFAULT_PT_LIFE_HLIF", "DEFAULT_PT_LIFE_ASIM", @@ -314,7 +313,10 @@ local function create_parts_any(x, y, rx, ry, xtype, brush, member) sim.decoBrush(x, y, rx, ry, member.deco_r, member.deco_g, member.deco_b, member.deco_a, xtype - xid_first.DECOR, brush) return elseif class == "PT_LIFE" then - xtype = elem.DEFAULT_PT_LIFE + bit.lshift(xtype - xid_first.PT_LIFE, PMAPBITS) + xtype = bit.bor(elem.DEFAULT_PT_LIFE, bit.lshift(xtype - xid_first.PT_LIFE, PMAPBITS)) + elseif type(xtype) == "table" and xtype.type == "cgol" then + -- * TODO[api]: add an api for setting gol colour + xtype = xtype.elem end local ov = create_override[xtype] if ov then @@ -410,7 +412,10 @@ local function create_line_any(x1, y1, x2, y2, rx, ry, xtype, brush, member, con sim.decoLine(x1, y1, x2, y2, rx, ry, member.deco_r, member.deco_g, member.deco_b, member.deco_a, xtype - xid_first.DECOR, brush) return elseif class == "PT_LIFE" then - xtype = elem.DEFAULT_PT_LIFE + bit.lshift(xtype - xid_first.PT_LIFE, PMAPBITS) + xtype = bit.bor(elem.DEFAULT_PT_LIFE, bit.lshift(xtype - xid_first.PT_LIFE, PMAPBITS)) + elseif type(xtype) == "table" and xtype.type == "cgol" then + -- * TODO[api]: add an api for setting gol colour + xtype = xtype.elem end local ov = create_override[xtype] if ov then @@ -442,7 +447,10 @@ local function create_box_any(x1, y1, x2, y2, xtype, member) sim.decoBox(x1, y1, x2, y2, member.deco_r, member.deco_g, member.deco_b, member.deco_a, xtype - xid_first.DECOR) return elseif class == "PT_LIFE" then - xtype = elem.DEFAULT_PT_LIFE + bit.lshift(xtype - xid_first.PT_LIFE, PMAPBITS) + xtype = bit.bor(elem.DEFAULT_PT_LIFE, bit.lshift(xtype - xid_first.PT_LIFE, PMAPBITS)) + elseif type(xtype) == "table" and xtype.type == "cgol" then + -- * TODO[api]: add an api for setting gol colour + xtype = xtype.elem end local _ local ov = create_override[xtype] @@ -471,7 +479,10 @@ local function flood_any(x, y, xtype, part_flood_hint, wall_flood_hint, member) elseif class == "DECOR" or class == "TOOL" then return elseif class == "PT_LIFE" then - xtype = elem.DEFAULT_PT_LIFE + bit.lshift(xtype - xid_first.PT_LIFE, PMAPBITS) + xtype = bit.bor(elem.DEFAULT_PT_LIFE, bit.lshift(xtype - xid_first.PT_LIFE, PMAPBITS)) + elseif type(xtype) == "table" and xtype.type == "cgol" then + -- * TODO[api]: add an api for setting gol colour + xtype = xtype.elem end local _ local ov = create_override[xtype] diff --git a/tptmp/common/config.lua b/tptmp/common/config.lua index e180978..46cf695 100644 --- a/tptmp/common/config.lua +++ b/tptmp/common/config.lua @@ -6,7 +6,7 @@ return { -- *********************************************************************** -- * Protocol version, between 0 and 254. 255 is reserved for future use. - version = 19, -- * TODO[fin]: Give this a bump. + version = 20, -- * TODO[fin]: Give this a bump. -- * Client-to-server message size limit, between 0 and 255, the latter -- limit being imposted by the protocol. diff --git a/tptmp/server/client.lua b/tptmp/server/client.lua index 441634f..fb9daa1 100644 --- a/tptmp/server/client.lua +++ b/tptmp/server/client.lua @@ -260,6 +260,7 @@ forward_to_room( "pointsstart", 42, 4) forward_to_room( "pointscont", 43, 3) forward_to_room( "linestart", 44, 4) forward_to_room( "rectstart", 45, 4) +forward_to_room( "custgolinfo", 46, 9) forward_to_room( "stepsim", 50, 0) forward_to_room( "sparkclear", 60, 0) forward_to_room( "airclear", 61, 0)