From 79da249c3d32769f7f3821292d479e6e44fdcb31 Mon Sep 17 00:00:00 2001 From: orwell96 Date: Tue, 9 Jan 2018 21:30:56 +0100 Subject: Add bord computer to trains Features: - couple/decouple trains from a driver stand - new couple lock system (owner based, overridable by 'train_remove' privilege) - all train operators can now change the inside/outside text, allows for multilines Accessible via right-click menu or by pressing Sneak+Jump keys --- advtrains/couple.lua | 72 +++++---- advtrains/init.lua | 23 ++- advtrains/textures/advtrains_cpl_lock.png | Bin 0 -> 209 bytes advtrains/textures/advtrains_cpl_unlock.png | Bin 0 -> 213 bytes advtrains/trainlogic.lua | 42 +++-- advtrains/wagons.lua | 236 +++++++++++++++++++++++++--- advtrains_train_subway/init.lua | 2 +- 7 files changed, 290 insertions(+), 85 deletions(-) create mode 100644 advtrains/textures/advtrains_cpl_lock.png create mode 100644 advtrains/textures/advtrains_cpl_unlock.png diff --git a/advtrains/couple.lua b/advtrains/couple.lua index 0e7a481..a102254 100644 --- a/advtrains/couple.lua +++ b/advtrains/couple.lua @@ -32,32 +32,13 @@ minetest.register_entity("advtrains:discouple", { get_staticdata=function() return "DISCOUPLE" end, on_punch=function(self, player) return advtrains.pcall(function() - if not self.wagon or not player or player:get_player_name()=="" then - --our new animal damage code seems to generate problems - return - end - --only if player owns at least one wagon next to this - local own=player:get_player_name() - if self.wagon.owner and self.wagon.owner==own and not self.wagon.lock_couples then - local train=advtrains.trains[self.wagon.train_id] - local nextwgn_id=train.trainparts[self.wagon.pos_in_trainparts-1] - for aoi, le in pairs(minetest.luaentities) do - if le and le.is_wagon then - if le.unique_id==nextwgn_id then - if le.owner and le.owner~=own then - minetest.chat_send_player(own, attrans("You need to own at least one neighboring wagon to destroy this couple.")) - return - end - end - end + local pname = player:get_player_name() + if pname and pname~="" and self.wagon then + if self.wagon:safe_decouple(pname) then + self.object:remove() + else + minetest.add_entity(self.object:getpos(), "advtrains:lockmarker") end - atprint("Discouple punched. Splitting train", self.wagon.train_id) - advtrains.split_train_at_wagon(self.wagon)--found in trainlogic.lua - self.object:remove() - elseif self.wagon.lock_couples then - minetest.chat_send_player(own, "Couples of one of the wagons are locked, can't discouple!") - else - minetest.chat_send_player(own, attrans("You need to own at least one neighboring wagon to destroy this couple.")) end end) end, @@ -75,13 +56,8 @@ minetest.register_entity("advtrains:discouple", { self.object:remove() return end - local velocityvec=self.wagon.object:getvelocity() - self.updatepct_timer=(self.updatepct_timer or 0)-dtime - if not self.old_velocity_vector or not vector.equals(velocityvec, self.old_velocity_vector) or self.updatepct_timer<=0 then--only send update packet if something changed - local flipsign=self.wagon.wagon_flipped and -1 or 1 - self.object:setpos(vector.add(self.wagon.object:getpos(), {y=0, x=-math.sin(self.wagon.object:getyaw())*self.wagon.wagon_span*flipsign, z=math.cos(self.wagon.object:getyaw())*self.wagon.wagon_span*flipsign})) - self.object:setvelocity(velocityvec) - self.updatepct_timer=2 + if self.wagon.old_velocity > 0 or self.wagon.pos_in_trainparts==1 then + self.object:remove() end atprintbm("discouple_step", t) end) @@ -122,6 +98,10 @@ minetest.register_entity("advtrains:couple", { return advtrains.pcall(function() if not self.train_id_1 or not self.train_id_2 then return end + local pname=clicker + if type(clicker)~="string" then pname=clicker:get_player_name() end + if not minetest.check_player_privs(pname, "train_operator") then return end + local id1, id2=self.train_id_1, self.train_id_2 if self.train1_is_backpos and not self.train2_is_backpos then advtrains.do_connect_trains(id1, id2, clicker) @@ -192,4 +172,32 @@ minetest.register_entity("advtrains:couple", { advtrains.atprint_context_tid_full=nil end) end, +}) +minetest.register_entity("advtrains:lockmarker", { + visual="sprite", + textures = {"advtrains_cpl_lock.png"}, + collisionbox = {-0.3,-0.3,-0.3, 0.3,0.3,0.3}, + visual_size = {x=0.7, y=0.7}, + initial_sprite_basepos = {x=0, y=0}, + + is_lockmarker=true, + on_activate=function(self, staticdata) + return advtrains.pcall(function() + if staticdata=="COUPLE" then + --couple entities have no right to exist further... + atprint("Couple loaded from staticdata, destroying") + self.object:remove() + return + end + self.object:set_armor_groups({immmortal=1}) + self.life=5 + end) + end, + get_staticdata=function(self) return "COUPLE" end, + on_step=function(self, dtime) + self.life=(self.life or 5)-dtime + if self.life<0 then + self.object:remove() + end + end, }) diff --git a/advtrains/init.lua b/advtrains/init.lua index 4834a45..d5ef693 100644 --- a/advtrains/init.lua +++ b/advtrains/init.lua @@ -80,6 +80,10 @@ function advtrains.print_concat_table(a) else str=str.."false" end + elseif type(t)=="function" then + str=str.."" + elseif type(t)=="userdata" then + str=str.."" else str=str..t end @@ -90,7 +94,6 @@ function advtrains.print_concat_table(a) end atprint=function() end -atdebug=function() end atlog=function(t, ...) local text=advtrains.print_concat_table({t, ...}) minetest.log("action", "[advtrains]"..text) @@ -102,12 +105,12 @@ atwarn=function(t, ...) end sid=function(id) if id then return string.sub(id, -6) end end ---TEMP +--ONLY use this function for temporary debugging. for consistent debug prints use atprint atdebug=function(t, ...) - local text=advtrains.print_concat_table({t, ...}) - minetest.log("action", "[advtrains]"..text) - minetest.chat_send_all("[advtrains]"..text) - end + local text=advtrains.print_concat_table({t, ...}) + minetest.log("action", "[advtrains]"..text) + minetest.chat_send_all("[advtrains]"..text) +end if minetest.settings:get_bool("advtrains_enable_debugging") then atprint=function(t, ...) @@ -118,11 +121,6 @@ if minetest.settings:get_bool("advtrains_enable_debugging") then --atlog("@@",advtrains.atprint_context_tid,t,...) end - atdebug=function(t, ...) - local text=advtrains.print_concat_table({t, ...}) - minetest.log("action", "[advtrains]"..text) - minetest.chat_send_all("[advtrains]"..text) - end dofile(advtrains.modpath.."/debugringbuffer.lua") end @@ -271,7 +269,8 @@ advtrains.avt_save = function(remove_players_from_wagons) local v=advtrains.save_keys(train, { "last_pos", "last_pos_prev", "movedir", "velocity", "tarvelocity", "trainparts", "savedpos_off_track_index_offset", "recently_collided_with_env", - "atc_brake_target", "atc_wait_finish", "atc_command", "atc_delay", "door_open" + "atc_brake_target", "atc_wait_finish", "atc_command", "atc_delay", "door_open", + "text_outside", "text_inside", "couple_lck_front", "couple_lck_back" }) --then invalidate if train.index then diff --git a/advtrains/textures/advtrains_cpl_lock.png b/advtrains/textures/advtrains_cpl_lock.png new file mode 100644 index 0000000..a25aaf4 Binary files /dev/null and b/advtrains/textures/advtrains_cpl_lock.png differ diff --git a/advtrains/textures/advtrains_cpl_unlock.png b/advtrains/textures/advtrains_cpl_unlock.png new file mode 100644 index 0000000..f58d00a Binary files /dev/null and b/advtrains/textures/advtrains_cpl_unlock.png differ diff --git a/advtrains/trainlogic.lua b/advtrains/trainlogic.lua index 5681817..449d624 100644 --- a/advtrains/trainlogic.lua +++ b/advtrains/trainlogic.lua @@ -36,14 +36,14 @@ local t_accel_all={ [0] = -10, [1] = -3, [2] = -0.5, - [4] = 0, + [4] = 0.5, } --acceleration per engine local t_accel_eng={ [0] = 0, [1] = 0, [2] = 0, - [4] = 2, + [4] = 1.5, } advtrains.mainloop_trainlogic=function(dtime) @@ -239,6 +239,7 @@ function advtrains.train_step_a(id, train, dtime) if train.recently_collided_with_env then tarvel_cap=0 + train.active_control=false if not train_moves then train.recently_collided_with_env=nil--reset status when stopped end @@ -621,6 +622,7 @@ function advtrains.update_trainpart_properties(train_id, invert_flipstate) local count_l=0 for i, w_id in ipairs(train.trainparts) do local wagon=nil + local shift_dcpl_lock=false for aoid,iwagon in pairs(minetest.luaentities) do if iwagon.is_wagon and iwagon.unique_id==w_id then if wagon then @@ -654,6 +656,7 @@ function advtrains.update_trainpart_properties(train_id, invert_flipstate) end if invert_flipstate then wagon.wagon_flipped = not wagon.wagon_flipped + shift_dcpl_lock, wagon.dcpl_lock = wagon.dcpl_lock, shift_dcpl_lock end rel_pos=rel_pos+wagon.wagon_span @@ -667,13 +670,6 @@ function advtrains.update_trainpart_properties(train_id, invert_flipstate) train.max_speed=math.min(train.max_speed, wagon.max_speed) train.extent_h = math.max(train.extent_h, wagon.extent_h or 1); - if i==1 then - train.couple_lock_front=wagon.lock_couples - end - if i==#train.trainparts then - train.couple_lock_back=wagon.lock_couples - end - else atwarn("Did not find save data for wagon",w_id,". The wagon will be deleted.") --what the hell... @@ -732,6 +728,10 @@ function advtrains.split_train_at_wagon(wagon) newtrain.velocity=train.velocity newtrain.tarvelocity=0 newtrain.enter_node_all=true + newtrain.couple_lck_back=train.couple_lck_back + newtrain.couple_lck_front=false + train.couple_lck_back=false + end --there are 4 cases: @@ -798,17 +798,21 @@ function advtrains.collide_and_spawn_couple(id1, pos, id2, t1_is_backpos) atprint("t2_is_backpos="..(t2_is_backpos and "true" or "false")) - local t1_has_couple + local t1_has_couple, t1_couple_lck if t1_is_backpos then t1_has_couple=train1.couple_eid_back + t1_couple_lck=train1.couple_lck_back else t1_has_couple=train1.couple_eid_front + t1_couple_lck=train1.couple_lck_front end - local t2_has_couple + local t2_has_couple, t2_couple_lck if t2_is_backpos then t2_has_couple=train2.couple_eid_back + t2_couple_lck=train2.couple_lck_back else t2_has_couple=train2.couple_eid_front + t2_couple_lck=train2.couple_lck_front end if t1_has_couple then @@ -817,6 +821,10 @@ function advtrains.collide_and_spawn_couple(id1, pos, id2, t1_is_backpos) if t2_has_couple then if minetest.object_refs[t2_has_couple] then minetest.object_refs[t2_has_couple]:remove() end end + if t1_couple_lck or t2_couple_lck then + minetest.add_entity(pos, "advtrains:lockmarker") + return + end local obj=minetest.add_entity(pos, "advtrains:couple") if not obj then atprint("failed creating object") return end local le=obj:get_luaentity() @@ -854,7 +862,7 @@ function advtrains.do_connect_trains(first_id, second_id, player) return false end - if first.couple_lock_back or second.couple_lock_front then + if first.couple_lck_back or second.couple_lck_front then -- trains are ordered correctly! if player then minetest.chat_send_player(player:get_player_name(), "Can't couple: couples locked!") @@ -870,10 +878,15 @@ function advtrains.do_connect_trains(first_id, second_id, player) end --kick it like physics (with mass being #wagons) local new_velocity=((first.velocity*first_wagoncnt)+(second.velocity*second_wagoncnt))/(first_wagoncnt+second_wagoncnt) + local tmp_cpl_lck=second.couple_lck_back advtrains.trains[second_id]=nil advtrains.update_trainpart_properties(first_id) - advtrains.trains[first_id].velocity=new_velocity - advtrains.trains[first_id].tarvelocity=0 + local train1=advtrains.trains[first_id] + train1.velocity=new_velocity + train1.tarvelocity=0 + train1.couple_eid_front=nil + train1.couple_eid_back=nil + train1.couple_lck_back=tmp_cpl_lck return true end @@ -888,6 +901,7 @@ function advtrains.invert_train(train_id) train.path_extent_min, train.path_extent_max = -train.path_extent_max, -train.path_extent_min train.min_index_on_track, train.max_index_on_track = -train.max_index_on_track, -train.min_index_on_track train.detector_old_index, train.detector_old_end_index = -train.detector_old_end_index, -train.detector_old_index + train.couple_lck_back, train.couple_lck_front = train.couple_lck_front, train.couple_lck_back train.velocity=-train.velocity train.tarvelocity=-train.tarvelocity diff --git a/advtrains/wagons.lua b/advtrains/wagons.lua index 11a9099..42466f3 100644 --- a/advtrains/wagons.lua +++ b/advtrains/wagons.lua @@ -60,7 +60,8 @@ function wagon:get_staticdata() end --save to table before being unloaded advtrains.wagon_save[self.unique_id]=advtrains.save_keys(self, { - "seatp", "owner", "ser_inv", "wagon_flipped", "train_id" + "seatp", "owner", "ser_inv", "wagon_flipped", "train_id", + "dcpl_lock" }) advtrains.wagon_save[self.unique_id].entity_name=self.name return self.unique_id @@ -269,6 +270,10 @@ function wagon:on_step(dtime) if has_driverstand then --regular driver stand controls advtrains.on_control_change(pc, self:train(), self.wagon_flipped) + --bordcom + if pc.sneak and pc.jump then + self:show_bordcom(self.seatp[seatno]) + end --sound horn when required if self.horn_sound and pc.aux1 and not pc.sneak and not self.horn_handle then self.horn_handle = minetest.sound_play(self.horn_sound, { @@ -779,7 +784,7 @@ function wagon:show_wagon_properties(pname) checkbox: lock couples button: save ]] - local form="size[5,"..(numsgr*1.5+7).."]" + local form="size[5,"..(numsgr*1.5+3).."]" local at=0 if self.seat_groups then for sgr,sgrdef in pairs(self.seat_groups) do @@ -788,18 +793,196 @@ function wagon:show_wagon_properties(pname) at=at+1 end end - form=form.."checkbox[0,"..(at*1.5)..";lock_couples;"..attrans("Lock couples")..";"..(self.lock_couples and "true" or "false").."]" - if self:train() then --just in case - form=form.."field[0.5,"..(1.5+at*1.5)..";4,1;text_outside;"..attrans("Text displayed outside on train")..";"..(self:train().text_outside or "").."]" - form=form.."field[0.5,"..(2.5+at*1.5)..";4,1;text_inside;"..attrans("Text displayed inside train")..";"..(self:train().text_inside or "").."]" - end form=form.."button_exit[0.5,"..(3+at*1.5)..";4,1;save;"..attrans("Save wagon properties").."]" minetest.show_formspec(pname, "advtrains_prop_"..self.unique_id, form) end + +--BordCom +local function checkcouple(eid) + if not eid then return nil end + local ent=minetest.object_refs[eid] + if not ent or not ent:getyaw() then + eid=nil + return nil + end + local le = ent:get_luaentity() + if not le or not le.is_couple then + eid=nil + return nil + end + return le +end +local function checklock(pname, own1, own2) + return minetest.check_player_privs(pname, "train_remove") or + ((not own1 or own1==pname) or (not own2 or own2==pname)) +end function wagon:show_bordcom(pname) + if not self:train() then return end + local train = self:train() + + local form = "size[11,9]label[0.5,0;AdvTrains Boardcom v0.1]" + form=form.."textarea[0.5,1.5;7,1;text_outside;"..attrans("Text displayed outside on train")..";"..(train.text_outside or "").."]" + form=form.."textarea[0.5,3;7,1;text_inside;"..attrans("Text displayed inside train")..";"..(train.text_inside or "").."]" + --row 5 : train overview and autocoupling + if train.velocity==0 then + form=form.."label[0.5,4.5;Train overview /coupling control:]" + linhei=5 + local pre_own, owns_any = nil, minetest.check_player_privs(pname, "train_remove") + for i, tpid in ipairs(train.trainparts) do + local ent = advtrains.wagon_save[tpid] + if ent then + local ename = ent.entity_name + form = form .. "item_image["..i..","..linhei..";1,1;"..ename.."]" + if i~=1 then + if not ent.dcpl_lock then + form = form .. "image_button["..(i-0.5)..","..(linhei+1)..";1,1;advtrains_discouple.png;dcpl_"..i..";]" + if checklock(pname, ent.owner, pre_own) then + form = form .. "image_button["..(i-0.5)..","..(linhei+2)..";1,1;advtrains_cpl_unlock.png;dcpl_lck_"..i..";]" + end + else + form = form .. "image_button["..(i-0.5)..","..(linhei+2)..";1,1;advtrains_cpl_lock.png;dcpl_ulck_"..i..";]" + end + end + if i == self.pos_in_trainparts then + form = form .. "box["..(i-0.1)..","..(linhei-0.1)..";1,1;green]" + end + pre_own = ent.owner + owns_any = owns_any or (not ent.owner or ent.owner==pname) + end + end + + if train.movedir==1 then + form = form .. "label["..(#train.trainparts+1)..","..(linhei)..";-->]" + else + form = form .. "label[0.5,"..(linhei)..";<--]" + end + --check cpl_eid_front and _back of train + local couple_front = checkcouple(train.couple_eid_front) + local couple_back = checkcouple(train.couple_eid_back) + if couple_front then + form = form .. "image_button[0.5,"..(linhei+1)..";1,1;advtrains_couple.png;cpl_f;]" + end + if couple_back then + form = form .. "image_button["..(#train.trainparts+0.5)..","..(linhei+1)..";1,1;advtrains_couple.png;cpl_b;]" + end + if owns_any then + if train.couple_lck_front then + form = form .. "image_button[0.5,"..(linhei+2)..";1,1;advtrains_cpl_lock.png;cpl_ulck_f;]" + else + form = form .. "image_button[0.5,"..(linhei+2)..";1,1;advtrains_cpl_unlock.png;cpl_lck_f;]" + end + if train.couple_lck_back then + form = form .. "image_button["..(#train.trainparts+0.5)..","..(linhei+2)..";1,1;advtrains_cpl_lock.png;cpl_ulck_b;]" + else + form = form .. "image_button["..(#train.trainparts+0.5)..","..(linhei+2)..";1,1;advtrains_cpl_unlock.png;cpl_lck_b;]" + end + end + + else + form=form.."label[0.5,4.5;Train overview / coupling control is only shown when the train stands.]" + end + form = form .. "button[0.5,8;3,1;Save;save]" + + minetest.show_formspec(pname, "advtrains_bordcom_"..self.unique_id, form) +end +function wagon:handle_bordcom_fields(pname, formname, fields) + local seatno=self:get_seatno(pname) + if not seatno or not self.seat_groups[self.seats[seatno].group].driving_ctrl_access or not minetest.check_player_privs(pname, "train_operator") then + return + end + local train = self:train() + if not train then return end + if fields.text_outside then + if fields.text_outside~="" then + train.text_outside=fields.text_outside + else + train.text_outside=nil + end + end + if fields.text_inside then + if fields.text_inside~="" then + train.text_inside=fields.text_inside + else + train.text_inside=nil + end + end + for i, tpid in ipairs(train.trainparts) do + if fields["dcpl_"..i] then + for _,wagon in pairs(minetest.luaentities) do + if wagon.is_wagon and wagon.initialized and wagon.unique_id==tpid then + wagon:safe_decouple(pname) + end + end + end + if i>1 and fields["dcpl_lck_"..i] then + local ent = advtrains.wagon_save[tpid] + local pent = advtrains.wagon_save[train.trainparts[i-1]] + if ent and pent then + if checklock(pname, ent.owner, pent.owner) then + for _,wagon in pairs(minetest.luaentities) do + if wagon.is_wagon and wagon.initialized and wagon.unique_id==tpid then + wagon.dcpl_lock=true + wagon:get_staticdata() + end + end + end + end + end + if i>1 and fields["dcpl_ulck_"..i] then + local ent = advtrains.wagon_save[tpid] + local pent = advtrains.wagon_save[train.trainparts[i-1]] + if ent and pent then + if checklock(pname, ent.owner, pent.owner) then + for _,wagon in pairs(minetest.luaentities) do + if wagon.is_wagon and wagon.initialized and wagon.unique_id==tpid then + wagon.dcpl_lock=false + wagon:get_staticdata() + end + end + end + end + end + end + --check cpl_eid_front and _back of train + local couple_front = checkcouple(train.couple_eid_front) + local couple_back = checkcouple(train.couple_eid_back) + + if fields.cpl_f and couple_front then + couple_front:on_rightclick(pname) + end + if fields.cpl_b and couple_back then + couple_back:on_rightclick(pname) + end + + local function chkownsany() + local owns_any = minetest.check_player_privs(pname, "train_remove") + for i, tpid in ipairs(train.trainparts) do + local ent = advtrains.wagon_save[tpid] + if ent then + owns_any = owns_any or (not ent.owner or ent.owner==pname) + end + end + return owns_any + end + if fields.cpl_lck_f and chkownsany() then + train.couple_lck_front=true + end + if fields.cpl_lck_b and chkownsany() then + train.couple_lck_back=true + end + if fields.cpl_ulck_f and chkownsany() then + train.couple_lck_front=false + end + if fields.cpl_ulck_b and chkownsany() then + train.couple_lck_back=false + end + - minetest.show_formspec(pname, "advtrains_bordcom_"..self.unique_id, "field[irrel;Not yet implemented;We normally would show the bord computer now.]") + if not fields.quit then + self:show_bordcom(pname) + end end + minetest.register_on_player_receive_fields(function(player, formname, fields) return advtrains.pcall(function() local uid=string.match(formname, "^advtrains_geton_(.+)$") @@ -851,28 +1034,18 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) wagon.seat_access[sgr] = fcont~="" and fcont or nil end end - if fields.lock_couples then - wagon.lock_couples = fields.lock_couples == "true" - end - if fields.text_outside then - if fields.text_outside~="" then - wagon:train().text_outside=fields.text_outside - else - wagon:train().text_outside=nil - end - end - if fields.text_inside then - if fields.text_inside~="" then - wagon:train().text_inside=fields.text_inside - else - wagon:train().text_inside=nil - end - end - end end end end + uid=string.match(formname, "^advtrains_bordcom_(.+)$") + if uid then + for _,wagon in pairs(minetest.luaentities) do + if wagon.is_wagon and wagon.initialized and wagon.unique_id==uid then + wagon:handle_bordcom_fields(player:get_player_name(), formname, fields) + end + end + end end) end) function wagon:seating_from_key_helper(pname, fields, no) @@ -931,6 +1104,17 @@ function wagon:reattach_all() end end +function wagon:safe_decouple(pname) + if self.dcpl_lock then + minetest.chat_send_player(pname, "Couple is locked (ask owner or admin to unlock it)") + return false + end + atprint("wagon:discouple() Splitting train", selftrain_id) + advtrains.split_train_at_wagon(self)--found in trainlogic.lua + return true +end + + function advtrains.register_wagon(sysname_p, prototype, desc, inv_img) local sysname = sysname_p if not string.match(sysname, ":") then diff --git a/advtrains_train_subway/init.lua b/advtrains_train_subway/init.lua index 58677e8..45ad58f 100644 --- a/advtrains_train_subway/init.lua +++ b/advtrains_train_subway/init.lua @@ -55,7 +55,7 @@ advtrains.register_wagon("subway_wagon", { require_doors_open=true, }, }, - assign_to_seat_group = {"pass","dstand"}, + assign_to_seat_group = {"dstand", "pass"}, doors={ open={ [-1]={frames={x=0, y=20}, time=1}, -- cgit v1.2.3