News:

Simutrans Wiki Manual
The official on-line manual for Simutrans. Read and contribute.

[API Script] city.get_pos_nw() and city.get_pos_se() do not work correctly

Started by Yona-TYT, May 15, 2024, 02:52:32 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Yona-TYT

Someone on Steam reported an error in the tutorial that occurred when starting chapter 7 with the map rotated to any side other than north.

I spent several hours testing and came to the conclusion that it is an error in the API when obtaining those coordinates.

I made a small test script to help better visualize the problem.

The test script and savegame are attached.



Pointing North: Load the savegame and then start the script, next step click on the city hall.

As seen in the image, this is the expected behavior for all rotations, you can see the city limits marked in a box.
Captura desde 2024-05-14 22-34-38.png

Pointing West: Rotate the map and repeat the previous process again, now the box is only half drawn.
Captura desde 2024-05-14 22-35-09.png

South and East: In both cases nothing of the city limits is drawn anymore.

Captura desde 2024-05-14 22-36-15.pngCaptura desde 2024-05-14 22-35-42.png

prissi

They are probably no rotated, since they were saved a integers in the code. Will check this

Yona-TYT

Quote from: prissi on May 17, 2024, 09:08:46 AMThey are probably no rotated, since they were saved a integers in the code. Will check this
I have noticed that when the coordinates are saved from "city.get_pos_nw() and city.get_pos_se()" and the map is in its default rotation (towards north), all rotations made will have the correct coordinates.

But when the initial rotation is not towards the north, then the problem occurs. Could it be that when the coordinates are obtained the current orientation of the map is not taken into account? I suspect that this may be the problem.

TurfIt

get_pos_nw() and get_pos_se() appear to function fine, although their names are perhaps misleading.
As the Script API goes to great lengths to translate all squirrel coordinates to the unrotated map coords, if you rotate the map once clockwise, get_pos_nw() returns the coords of the original nw tile but rotated once. Difficult to put in words so I marked up your screen shot:
I also note you have the compass markers in the wrong spots perhaps leading to more confusion.

Captura desde 2024-05-14 22-34-38.JPGCaptura desde 2024-05-14 22-35-09.JPG

The city limits are not marked as your drawing function is faulty - se.x < nw.x after one rotation, but your for loops are trying to increase from nw.x to se.x so terminate without looping.
This is documented in the Script API:
/**
* City limits.
*
* City area is between get_pos_nw().x and get_pos_se().x, and get_pos_nw().y and get_pos_se().y.
*
* It is @b not guaranteed that get_pos_nw().x <= get_pos_se().x or get_pos_nw().y <= get_pos_se().y holds!
* @returns coordinate of another corner of city limit
*/

Yona-TYT

It seems that the method I use to draw the borders does not work correctly if the coordinates are obtained that way, in which case I will have to keep the rudimentary solution I implemented.


There is also a routine to analyze station coverage, maybe that will work here too.

TurfIt

function mark_box(coora, coorb)
{
local c_a = coord(coora.x, coora.y)
local c_b = coord(coorb.x, coorb.y)
gui.add_message("Coord A: "+ c_a.tostring()+" Coord B: "+c_b.tostring())

if(c_b.x<c_a.x){
local c = c_a.x
c_a.x = c_b.x
c_b.x = c
}
if(c_b.y<c_a.y){
local c = c_a.y
c_a.y = c_b.y
c_b.y = c
}

for (local j = c_a.x ;j<=c_b.x;j++){
gui.add_message(j.tostring())
local coor1 = coord(j, c_a.y)
local coor2 = coord(j, c_b.y)
my_tile(coor1).mark()
my_tile(coor2).mark()
}
for (local i = c_a.y;i<=c_b.y;i++){
local coor1 = coord(c_a.x, i)
local coor2 = coord(c_b.x, i)
my_tile(coor1).mark()
my_tile(coor2).mark()
}
}

Yona-TYT

This was the solution I implemented for this:

This determines the orientation of the map
    function my_compass()
    {
        local c_max = {x = map_siz.x-1, y = map_siz.y-1}
        local c = coord(0,0)
        local text = c.tostring()

        local res_c = {x = 0, y = 0}
        local ttx = ""
        local siz = text.len()
        for(local j=0;j<siz;j++){
            local tx = format("%c",text[j])
            try {
                tx.tointeger()
            }
            catch(ev) {
                if(tx==","){
                    res_c.x = ttx.tointeger()
                    ttx = ""
                    continue
                }
                continue
            }
            ttx+=tx
            if(j == siz-1){
                res_c.y = ttx.tointeger()
            }
        }
        //gui.add_message("Res: "+ res_c.x +" -- "+res_c.y)
        //gui.add_message("MAX: "+ c_max.x +" -- "+c_max.y)
        if(res_c.x == 0 && res_c.y == 0){
            gui.add_message("N")
            return 0
        }
        else if(res_c.x == c_max.y && res_c.y == 0){
            gui.add_message("W")
            return 1
        }
        else if(res_c.x == c_max.x && res_c.y == c_max.y){
            gui.add_message("S")
            return 2
        }
        else if(res_c.x == 0 && res_c.y == c_max.x){
            gui.add_message("E")
            return 3
        }
        return null
    }


This creates a list with all possible coordinates
    function load_limits(city)  //Load all limits for citys
    {
        local list = []
        local c_nw = city.get_pos_nw()
        local c_se = city.get_pos_se()

        list.push({a = c_nw, b = c_se})                                            // N
        list.push({a =  coord(c_nw.x, c_se.y), b = coord(c_se.x, c_nw.y)})        // W
        list.push({a = c_se, b = c_nw})                                            // S
        list.push({a =  coord(c_se.x, c_nw.y), b = coord(c_nw.x, c_se.y)})        // E

        return list
    }


compass_nr = my_compass()
c_cty_lim1 = load_limits(cty_buil1)

//Coordinates for city limits
c_cty_lim1[compass_nr].a
c_cty_lim1[compass_nr].b

Edit.

All of this is only executed once per start, and once the coordinates are saved, the next rotations will not affect the order of the limits.

Edit2.

In reality, where it is necessary to apply this is for construction limits in cities.

    function is_work_allowed_here(pl, tool_id, pos) {
        local result=null    // null is equivalent to 'allowed'
        local t = tile_x(pos.x, pos.y, pos.z)
        local way = t.find_object(mo_way)
        my_compass()
        local nr = compass_nr
        switch (this.step) {
            case 1:
                if (tool_id==4096)
                    return null
           
                if ((pos.x>=c_cty_lim1[nr].a.x-(1))&&(pos.y>=c_cty_lim1[nr].a.y-(1))&&(pos.x<=c_cty_lim1[nr].b.x+(1))&&(pos.y<=c_cty_lim1[nr].b.y+(1))){
                    if (way){
                        if(pot0==0){
                            if(tool_id==4115){
                                if(pos.x==stop1.x && pos.y==stop1.y)
                                    return null
                                else
                                    return translate("Build Stop here:")+ " ("+stop1.tostring()+")."
                            }
                            else
                                return translate("Action not allowed") + " ("+pos.tostring()+")."
                        }

                        else if(pot0==1 && pot1==0){
                            if(tool_id==4128){
                                if(pos.x==stop1.x && pos.y==stop1.y)
                                    return null
                                else
                                    return translate("Click on the stop")+ " ("+stop1.tostring()+")."
                            }
                            else
                                return translate("Action not allowed") + " ("+pos.tostring()+")."
                        }

                        else {
                            if ((tool_id==4110)||(tool_id==4115)||(tool_id==4117)||(tool_id==4097)||(tool_id==4108)||(tool_id==4109))
                                return null
                            else
                                return translate("Action not allowed") +" ("+pos.tostring()+")."
                        }
                    }
                    else if(tool_id==4110 && pot1==1)
                        return null
                    else
                        return translate("You can only use this tool on a road.")
                }
                else
                    return translate("You can only use this tool in the city")+ " " + cty1.name.tostring()+" ("+cty1.c.tostring()+")."   
            break;

            case 2:
                if (tool_id==4096)
                    return null
           
                if ((pos.x>=c_cty_lim2[nr].a.x-(1))&&(pos.y>=c_cty_lim2[nr].a.y-(1))&&(pos.x<=c_cty_lim2[nr].b.x+(1))&&(pos.y<=c_cty_lim2[nr].b.y+(1))){
                    if (way){
                        if(pot0==0){
                            if(tool_id==4115){
                                if(pos.x==stop2.x && pos.y==stop2.y)
                                    return null
                                else
                                    return translate("Build Stop here:")+ " ("+stop2.tostring()+")."
                            }
                            else
                                return translate("Action not allowed")+" ("+pos.tostring()+")."
                        }

                        else if(pot0==1 && pot1==0){
                            if(tool_id==4128){
                                if(pos.x==stop2.x && pos.y==stop2.y)
                                    return null
                                else
                                    return translate("Click on the stop")+ " ("+stop2.tostring()+")."
                            }
                            else
                                return translate("Action not allowed")+" ("+pos.tostring()+")."
                        }

                        else {
                            if ((tool_id==4110)||(tool_id==4115)||(tool_id==4117)||(tool_id==4097)||(tool_id==4108)||(tool_id==4109))
                                return null
                            else
                                return translate("Action not allowed")+" ("+pos.tostring()+")."
                        }
                    }
                    else if(tool_id==4110 && pot1==1)
                        return null
                    else
                        return translate("You can only use this tool on a road.")
                }
                else
                    return translate("You can only use this tool in the city")+cty2.name.tostring()+" ("+cty2.c.tostring()+")."   
            break;
        }
        if (tool_id==4096)
            return null
        return tool_id

    }
   

tutorial-tets.zip
tutorial-7.sve

prissi

But cities tend to expand, so I think one needs fresh coordinates each time ...

Yona-TYT

Quote from: prissi on May 19, 2024, 11:22:37 PMBut cities tend to expand, so I think one needs fresh coordinates each time ...

Certainly, the coordinates could be updated periodically (once a month or a year), but in reality this last chapter is just a complement where certain freedoms are given to the player so that when they complete the bus lines in the surrounding cities, as well that it is not so important to check the size of the city all the time. 😉