diff options
Diffstat (limited to 'advtrains/occupation.lua')
-rw-r--r-- | advtrains/occupation.lua | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/advtrains/occupation.lua b/advtrains/occupation.lua new file mode 100644 index 0000000..db39991 --- /dev/null +++ b/advtrains/occupation.lua @@ -0,0 +1,206 @@ +-- occupation.lua +--[[ +Collects and manages positions where trains occupy and/or reserve/require space + +It turned out that, especially for the TSS, some more, even overlapping zones are required. +Packing those into a data structure would just become a huge mess! +Instead, this occupation system will store the path indices of positions in the corresponding. +train's paths. +So, the occupation is a reverse lookup of paths. +Then, a callback system will handle changes in those indices, as follows: + +Whenever the train generates new path items (path_get/path_create), their counterpart indices will be filled in here. +Whenever a path gets invalidated or path items are deleted, their index counterpart is erased from here. + +When a train needs to know whether a position is blocked by another train, it will (and is permitted to) +query the train.index and train.end_index and compare them to the blocked position's index. + +Callback system for 3rd-party path checkers: +advtrains.te_register_on_new_path(func(id, train)) +-- Called when a train's path is re-initalized, either when it was invalidated +-- or the saves were just loaded +-- It can be assumed that everything is in the state of when the last run +-- of on_update was made, but all indices are shifted by an unknown amount. + +advtrains.te_register_on_update(func(id, train)) +-- Called each step and after a train moved, its length changed or some other event occured +-- The path is unmodified, and train.index and train.end_index can be reliably +-- queried for the new position and length of the train. +-- note that this function might be called multiple times per step, and this +-- function being called does not necessarily mean that something has changed. +-- It is ensured that on_new_path callbacks are executed prior to these callbacks whenever +-- an invalidation or a reload occured. + +advtrains.te_register_on_create(func(id, train)) +-- Called right after a train is created, right after the initial new_path callback +advtrains.te_register_on_remove(func(id, train)) +-- Called right before a train is deleted + + +All callbacks are allowed to save certain values inside the train table, but they must ensure that +those are reinitialized in the on_new_path callback. The on_new_path callback must explicitly +set ALL OF those values to nil or to a new updated value, and must not rely on their existence. + +]]-- +local o = {} + +local occ = {} +local occ_chg = {} + + +local function occget(p) + local t = occ[p.y] + if not t then + occ[p.y] = {} + t = occ[p.y] + end + local s = t + t = t[p.x] + if not t then + s[p.x] = {} + t = s[p.x] + end + return t[p.z] +end +local function occgetcreate(p) + local t = occ[p.y] + if not t then + occ[p.y] = {} + t = occ[p.y] + end + local s = t + t = t[p.x] + if not t then + s[p.x] = {} + t = s[p.x] + end + s = t + t = t[p.z] + if not t then + s[p.z] = {} + t = s[p.z] + end + return t +end + + +function o.set_item(train_id, pos, idx) + local t = occgetcreate(pos) + local i = 1 + while t[i] do + if t[i]==train_id then + break + end + i = i + 2 + end + t[i] = train_id + t[i+1] = idx +end + + +function o.clear_item(train_id, pos) + local t = occget(pos) + if not t then return end + local i = 1 + local moving = false + while t[i] do + if t[i]==train_id then + if moving then + -- if, for some occasion, there should be a duplicate entry, erase this one too + atwarn("Duplicate occupation entry at",pos,"for train",train_id,":",t) + i = i - 2 + end + moving = true + end + if moving then + t[i] = t[i+2] + t[i+1] = t[i+3] + end + i = i + 2 + end +end + +-- Checks whether some other train (apart from train_id) has it's 0 zone here +function o.check_collision(pos, train_id) + local npos = advtrains.round_vector_floor_y(pos) + local t = occget(npos) + if not t then return end + local i = 1 + while t[i] do + local ti = t[i] + if ti~=train_id then + local idx = t[i+1] + local train = advtrains.trains[ti] + + --atdebug("checking train",t[i],"index",idx,"<>",train.index,train.end_index) + if train and idx >= train.end_index and idx <= train.index then + --atdebug("collides.") + return train -- return train it collided with so we can couple when shunting is enabled + end + end + i = i + 2 + end + return false +end + +-- Gets a mapping of train id's to indexes of trains that share this path item with this train +-- The train itself will not be included. +-- If the requested index position is off-track, returns {}. +-- returns (table with train_id->index), position +function o.get_occupations(train, index) + local ppos, ontrack = advtrains.path_get(train, index) + if not ontrack then + atlog("Train",train.id,"get_occupations requested off-track",index) + return {}, ppos + end + local pos = advtrains.round_vector_floor_y(ppos) + local t = occget(pos) + if not t then return {} end + local r = {} + local i = 1 + local train_id = train.id + while t[i] do + if t[i]~=train_id then + r[t[i]] = t[i+1] + end + i = i + 2 + end + return r, pos +end +-- Gets a mapping of train id's to indexes of trains that stand or drive over +-- returns (table with train_id->index) +function o.get_trains_at(ppos) + local pos = advtrains.round_vector_floor_y(ppos) + local t = occget(pos) + if not t then return {} end + local r = {} + local i = 1 + while t[i] do + local train = advtrains.trains[t[i]] + local idx = t[i+1] + if train.end_index - 0.5 <= idx and idx <= train.index + 0.5 then + r[t[i]] = idx + end + i = i + 2 + end + return r +end + +-- Gets a mapping of train id's to indexes of trains that have a path +-- generated over this node +-- returns (table with train_id->index) +function o.get_trains_over(ppos) + local pos = advtrains.round_vector_floor_y(ppos) + local t = occget(pos) + if not t then return {} end + local r = {} + local i = 1 + while t[i] do + local idx = t[i+1] + r[t[i]] = idx + i = i + 2 + end + return r +end + +advtrains.occ = o |