Quote from: Andarix on February 06, 2026, 03:14:05 PM- When Chapter 5 Step D is loaded from save, the fields for building Post (halt, extension) are not highlighted in red.
If you perform Chapter 3 Step D using the automatic step, the track marker will remain. Fix! Way tool select on key not work. Fix! - [pak64.german] Chapter 3 step D and E - autostep build wrong stations lenght
Red mark in CH3 ST D fix here:
https://github.com/simutrans/tutorial_multipak/commit/0c7f5b16f00951a7797e57d32ba262db707b26e3
Update, fix red mark in CH3 ST G too
Key for build roads if fix, topic: "https://forum.simutrans.com/index.php?msg=212102"
Code:
if (tool_id==tool_build_way){
local way_desc = way_desc_x.get_available_ways(gl_wt, gl_st)
local is_execute = false
local str_c = tool.start_pos
local str_way = world.is_coord_valid(str_c)? tile_x(str_c.x, str_c.y, str_c.z).find_object(mo_way) : null
foreach(desc in way_desc){
if(desc.get_name() == name){
is_execute = true
}
}
local last_tool = way_desc_x.get_default_way_desc(wt_road)
if( is_execute || ( name == wt_road.tostring() && last_tool.get_system_type() == gl_st ) ){
for ( local i = 0; i < build_list.len()-1; i++ ) {
if ( ((pos.x==build_list[i].x)&&(pos.y==build_list[i].y)) || ((pos.x==city1_road_depot.x)&&(pos.y==city1_road_depot.y)) ) {
if(cursor_control(build_list[i])){
return null
}
if(!str_way){
return null
}
}
}
return get_tile_message(2, city1_road_depot.x, city1_road_depot.y)//translate("Connect the road here")+" ("+city1_road_depot.tostring()+")."
}
}
The check for road, rail and air is located in the function check_select_way() (https://github.com/simutrans/tutorial_multipak/blob/cda93ef9865172f5be7aa46619a37b4ca2181e1d/class/class_basic_chapter.nut#L3713).
For the power line (l) and canal in the Chapter file.
To my knowledge, runway and taxiway for aircraft don't have keyboard shortcuts. I have no idea if this can be set via the Menuconf.tab file.
Line 1909 in class_basic_chatpter.nut is commented " //local desc = way_desc_x.get_available_ways(wt, st)"
Screenshot_2026-02-18_19-02-38.png
There is a problem when building the first bridge in chapter 3, the bridge must not be built before point B.
I made this code to solve it, but adjustments must be made for this to be a useful function for other bridges.
if (pot[0]==1 && pot[1]==0){
if (pos.x>=bridge2_coords.b.x-1 && pos.y>=bridge2_coords.b.y-1 && pos.x<=bridge2_coords.a.x+1 && pos.y<=bridge2_coords.a.y+1){
if(tool_id==tool_build_way || tool_id==tool_build_bridge){
local pointer = my_tile(bridge2_coords.a).find_object(mo_pointer)
if (pos.x == bridge2_coords.a.x){
return null
}
if (pointer){
gl_control = false
if(pos.x == bridge2_coords.b.x)
gl_control = true
return null
}
else{
if (gl_control){
return null
}
return ""
}
}
}
else
return translate("You must build the bridge here")+" ("+coord3d_to_string(bridge2_coords.a)+")."
}
Screenshot_20260307_175331.png
This seems suspicious to me.
If I click on the level field as a second click, then position 104,158,-1 is displayed, which is correct.
However, if I click on the slope field 103,158,-1, then position 105,158,0 is displayed. This is the field with the river one level higher. And that doesn't match the clicked field.
gl_ctrl_a = false
gl_ctrl_b = false
gl_click_dir = 0
function bridgeConstructionHandler(coord_a, coord_b, pos){
local curr_pos = null
local bridge_pos = {a = 0, b = 0}
if (coord_a.y == coord_b.y && coord_a.x != coord_b.x ){
bridge_pos.a = coord_a.x
bridge_pos.b = coord_b.x
curr_pos = pos.x
}
else{
bridge_pos.a = coord_a.y
bridge_pos.b = coord_b.y
curr_pos = pos.y
}
local pointer_a = my_tile(coord_a).find_object(mo_pointer)
local pointer_b = my_tile(coord_b).find_object(mo_pointer)
if(gl_click_dir == 0 && !pointer_a && !pointer_b){
return null
}
if(pointer_a && gl_click_dir == 0){
gl_click_dir = 1
}
else if (pointer_b && gl_click_dir == 0){
gl_click_dir = 2
}
if(gl_click_dir == 1){
if (curr_pos == bridge_pos.a){
return null
}
if (pointer_a){
gl_ctrl_a = false
if(curr_pos == bridge_pos.b)
gl_ctrl_a = true
return null
}
else{
gl_click_dir = 0
if (gl_ctrl_a){
gl_ctrl_a = false
return null
}
return ""
}
}
else if(gl_click_dir == 2){
if (curr_pos == bridge_pos.b){
return null
}
if (pointer_b){
gl_ctrl_b = false
if(curr_pos == bridge_pos.a)
gl_ctrl_b = true
return null
}
else{
gl_click_dir = 0
if (gl_ctrl_b){
gl_ctrl_b = false
return null
}
return ""
}
}
}
The final result is that it now works in both directions and, in theory, should work for all bridges.
I consider all this code unnecessary. It's perfectly sufficient to simply align the two bridge ends with the click positions.
I believe the current behavior is a bug. When I drag the bridge with the mouse, the coordinate doesn't jump to the mouse pointer (field 3) but back to field 1 (the river) one level higher.
bridge_drag.png
Quote from: Andarix on March 09, 2026, 03:50:52 PMI believe the current behavior is a bug. When I drag the bridge with the mouse, the coordinate doesn't jump to the mouse pointer (field 3) but back to field 1 (the river) one level higher.
Perhaps the builder needs to ensure that there are no objects obstructing in level 0?. I understand that the construction mechanics for bridges and tunnels are quite complex; it might not be easy to fix this if it's a bug.
Wouldn't it be easier to allow the bridge tool to only work in this area? The tool area functions are for this purpose ...
Chapter 4, schedule in areas surrounding the dock.
It seems the function is getting stuck in a loop, we need to review this.
Captura desde 2026-03-19 09-58-02.png
The function `get_tiles_near_stations(tile_list)` returns an incorrect result. I think.
Since this function, like so many others, contains no comments, I can't say what's wrong. I can only say that this function is not mine.
Quote from: Andarix on March 19, 2026, 02:30:15 PMThe function `get_tiles_near_stations(tile_list)` returns an incorrect result. I think.
Since this function, like so many others, contains no comments, I can't say what's wrong. I can only say that this function is not mine.
That's strange, it never failed before, I'll have to investigate this.
`tile_list` has 4 entries.
`area` then has 96 entries.
Either `settings.get_station_coverage()` returns an incorrect value, or the function is doing something wrong. A catchment area of 96 fields is rather unlikely.
The function seems to determine the catchment area for each station field and then add it to `area`. Many fields are likely to be duplicated because the catchment areas of the individual station fields overlap.
Quote from: Andarix on March 19, 2026, 04:37:20 PM`tile_list` has 4 entries.
`area` then has 96 entries.
Either `settings.get_station_coverage()` returns an incorrect value, or the function is doing something wrong. A catchment area of 96 fields is rather unlikely.
The function seems to determine the catchment area for each station field and then add it to `area`. Many fields are likely to be duplicated because the catchment areas of the individual station fields overlap.
I saved the game, loaded the save file, and now everything works fine. :o
I got this far without using the automatic script builder, maybe that's why we didn't detect it.
I suspect that the coordinate variables were not loaded correctly and that caused the problem.
Unfortunately, I've encountered these sporadic errors quite often. I have the feeling that the scripting environment keeps glitching.
The error message states that the script runtime was too long.
I had already drastically increase the runtime during loading of scripts for the tutorial ...
Script AI uses `sleep()` to return control to the program. I have no idea if that's also possible with scenarios.
Two invalid coordinate values are being received for some reason.
This may be causing problems here.
t_list: 134,189,-3 :: 168,190,-3 :: 187,141,-3 :: 178,135,-3 :: 168,190,-3 :: list: 133,189,-3 :: koord3d invalid :: 188,141,-3 :: 179,135,-3 :: koord3d invalid
ch4_ship3_halts <- [coord3d(133, 189, -3), coord3d(188, 141, -3), coord3d(179, 135, -3)]
// add Oil rigg ( factory 4 to schedule passenger ship )
ch4_schedule_line3 <- []
for ( local i = 0; i < ch4_ship3_halts.len(); i++ ) {
ch4_schedule_line3.append(ch4_ship3_halts[i])
if ( i == 0 || i == 2 ) {
ch4_schedule_line3.append(coord_fac_4)
}
}
coord_fac_4 is defined as coord not as coord3d
I added the oil rig to the timetable to improve passenger occupancy on the ferry.
I had tested this at that time using individual steps and received no errors.
There are now two options.
a) Define coord_fac_4 as coord3d
b) Check the coordinates of the list before processing.
check code
try {
local t = coord.z
} catch(ev) {
local c = square_x(coord.x, coord.y).get_ground_tile()
coord = coord3d(c.x, c.y, c.z)
}
Option b is probably the better solution. This ensures that the coordinates are always in 3d.
patch for this
Implemented as an extra function, as this check is likely to be necessary in other areas as well.
[EDIT]
sorry bugs
reupload patch
Tile have an integral value in some cases
https://github.com/simutrans/tutorial_multipak/commit/a5b71f9c174b708828cac118c9f1d901cc8391fc
Captura desde 2026-03-21 16-41-36.png
Quote from: Andarix on March 19, 2026, 06:28:03 PMUnfortunately, I've encountered these sporadic errors quite often. I have the feeling that the scripting environment keeps glitching.
My deduction is that the resources allocated to the scripting virtual machine were being exceeded. When the tutorial starts from the beginning, garbage is accumulating, consuming a certain amount of resources. When you save and reload the game, this waste disappears, which is why the crash doesn't occur when the game is reloaded.
The crash is occurring due to an excess of iterators, because all the tile_lists of all the docks are being analyzed simultaneously. This didn't happen before because (probably) the resource allocation to the scripting virtual machine was higher.
This code reduces the iterators to only one station/dock at a time, but I haven't tested it enough yet to be sure it will work correctly.
function is_stop_allowed_ex(tile, list, pos, wt)
{
local result = get_tile_message(3, tile)
//local t_list = is_water_entry(list)
local t = tile_x(pos.x, pos.y, pos.z)
local buil = t.find_object(mo_building)
local is_wt = buil ? buil.get_waytype():null
local has_way = t.has_way(wt)
if(t.is_water()){
is_wt = wt_water
has_way = true
}
local get_cl = square_x(pos.x, pos.y).get_climate()
local st_count = 0
for ( local j = 0; j < list.len(); j++ ) {
if ( tmpsw[j] == 1 )
st_count++
}
if ( st_count < list.len() ) {
//local t = t_list[st_count]
//foreach(t in t_list){
local c = list[st_count]
local type = typeof(c)
local st_t = tile_x(c.x, c.y, c.z)
local halt = st_t.get_halt()
local tile_list = halt.get_tile_list()
local max = tile_list.len()
//local c_lim_list = {a = tile_list[0], b = tile_list[max-1]}
//gui.add_message(""+j+" :: "+tmpsw[j])
if(tmpsw[st_count] == 0){
//if(max == 1 && t.is_water()) return check_water_tile(result, tile_list[0], pos, j)
//Water weather checker or wt_water
if(wt == wt_water && t.is_water()){
local area = get_tiles_near_stations(tile_list)
for( local i = 0; i < area.len(); i++ ) {
local t_water = my_tile(area[i])
//t_water.mark()
//gui.add_message(""+t_water.x+","+t_water.y+"")
if (pos.x == t_water.x && pos.y == t_water.y){
if ( t_water.is_water() ) {
tmpsw[st_count] = 1
tmpcoor.push(st_t)
result = null
break
}
else
result = format(translate("Select station No.%d"),st_count+1)+" ("+c.tostring()+")."
}
else
result = format(translate("Select station No.%d"),st_count+1)+" ("+c.tostring()+")."
}
return result
}
//If they are land vehicles
foreach(tile in tile_list){
if (pos.x == tile.x && pos.y == tile.y && pos.z == tile.z){
if(has_way && wt == is_wt){
tmpsw[st_count] = 1
tmpcoor.push(st_t)
return null
}
else
return format(translate("Select station No.%d"),st_count+1)+" ("+c.tostring()+")."
}
}
return format(translate("Select station No.%d"),st_count+1)+" ("+c.tostring()+")."
}
//}
}
return result
}Edit.
Perfect! I've tested the tutorial from the beginning (without cheating ;D ), and with this modification, there are no more crashes. 🎉🎉🎉
But this code still needs improvement.
The pop-up window message said there was a bad 3D coordinate conversion: https://github.com/simutrans/tutorial_multipak/commit/231efad910cc092a965cfaf1dec738dc2db82f70
All the callbacks from is_tool_allowed must be really fast or the game would crawl to a standstill for way building with longer ways. Please consider using are the highly optimised rect and cube functions whenever possible.
Lagging for a tutorial may be ok, but for a heavy scenario, this is not much fun.
Quote from: prissi on March 22, 2026, 11:54:19 AMAll the callbacks from is_tool_allowed must be really fast or the game would crawl to a standstill for way building with longer ways. Please consider using are the highly optimised rect and cube functions whenever possible.
...
I wouldn't want to make such extensive changes before the release, especially since I don't fully understand the system yet.
The code revision will take quite some time.
Unfortunately, I haven't yet found a simple solution for the bridge spanning four fields (pak128 has significantly more fields). All the other bridges only have three fields, and the middle field isn't clickable.
The most important thing now is to check the texts. Although some improvements are still needed there.
It's difficult to stay focused and not change too many things at once, especially when dealing with more extensive tasks like route verification during track construction.
I would also like more feedback. But that's probably because there's no release with the current tutorial. Many people now need automation where everything is done automatically with one or two clicks/finger taps.
Quote from: Andarix on March 22, 2026, 01:24:46 PMUnfortunately, I haven't yet found a simple solution for the bridge spanning four fields (pak128 has significantly more fields). All the other bridges only have three fields, and the middle field isn't clickable.
How about this simpler method? It seems like the best way forward here.
"world.get_cursor_pos()" isn't implemented yet, but here's the patch: 0001-Script-API-add-get_cursor_pos.patch
function bridgeConstructionHandler(coord_a, coord_b){
local cursor_pos = world.get_cursor_pos()
//gui.add_message(cursor_pos.tostring()+" "+coord_a.tostring()+" "+coord_b.tostring() )
if( ( cursor_pos.x == coord_a.x && cursor_pos.y == coord_a.y ) || ( cursor_pos.x == coord_b.x && cursor_pos.y == coord_b.y ) ){
return null
}
else {
local pointer_a = my_tile(coord_a).find_object(mo_pointer)
local pointer_b = my_tile(coord_b).find_object(mo_pointer)
if ( pointer_a || pointer_b ){
return null
}
}
return ""
}
I've already tested this and it passed the tests.
https://github.com/simutrans/tutorial_multipak/pull/45
The latest version https://codeload.github.com/simutrans/tutorial_multipak/zip/refs/heads/main fails on start with a compile error when loading chapter 3. I could not test it, both in pak128 and pak64
Quote from: prissi on March 25, 2026, 12:55:38 PMThe latest version https://codeload.github.com/simutrans/tutorial_multipak/zip/refs/heads/main fails on start with a compile error when loading chapter 3. I could not test it, both in pak128 and pak64
The problem with the shorthand notation that omits the curly braces {}.
fixed
Since it's not possible to obtain the cursor position dynamically, I've looked for another alternative that also works. This should solve the bridge problem for now:
function bridgeConstructionHandler(coord_a, coord_b, pos){
local t_a = my_tile(coord_a)
local t_b = my_tile(coord_b)
local cursor_pos = coord(-1,-1)
if ( t_a.is_marked() ){
cursor_pos = coord(coord_a.x, coord_a.y)
}
else if ( t_b.is_marked() ){
cursor_pos = coord(coord_b.x, coord_b.y)
}
if(!t_a.is_marked() && !t_b.is_marked() && ( t_a.find_object(mo_bridge) || t_b.find_object(mo_bridge) ) ){
return null
}
if( ( cursor_pos.x == coord_a.x && cursor_pos.y == coord_a.y ) || ( cursor_pos.x == coord_b.x && cursor_pos.y == coord_b.y ) ){
return null
}
else {
local pointer_a = t_a.find_object(mo_pointer)
local pointer_b = t_b.find_object(mo_pointer)
if ( pointer_a || pointer_b ){
return null
}
}
return translate("You must build the bridge here")+" ("+coord3d_to_string(coord_a)+")."
}