|
|
|
|
|
DefineClass.UpdateObject = { |
|
__parents = {"Object"}, |
|
|
|
update_thread_on_init = true, |
|
update_interval = 10000, |
|
update_thread = false, |
|
} |
|
|
|
RecursiveCallMethods.OnObjUpdate = "call" |
|
|
|
local Sleep = Sleep |
|
local procall = procall |
|
local GameTime = GameTime |
|
function UpdateObject:Init() |
|
if self.update_thread_on_init then |
|
self:StartObjUpdateThread() |
|
end |
|
end |
|
|
|
function UpdateObject:ObjUpdateProc(update_interval) |
|
self:InitObjUpdate(update_interval) |
|
while true do |
|
procall(self.OnObjUpdate, self, GameTime(), update_interval) |
|
Sleep(update_interval) |
|
end |
|
end |
|
|
|
function UpdateObject:StartObjUpdateThread() |
|
if not self:IsSyncObject() or not mapdata.GameLogic or not self.update_interval then |
|
return |
|
end |
|
DeleteThread(self.update_thread) |
|
self.update_thread = CreateGameTimeThread(self.ObjUpdateProc, self, self.update_interval) |
|
if Platform.developer then |
|
ThreadsSetThreadSource(self.update_thread, "ObjUpdateThread", self.ObjUpdateProc) |
|
end |
|
end |
|
|
|
function UpdateObject:StopObjUpdateThread() |
|
DeleteThread(self.update_thread) |
|
self.update_thread = nil |
|
end |
|
|
|
function UpdateObject:InitObjUpdate(update_interval) |
|
Sleep(1 + self:Random(update_interval, "InitObjUpdate")) |
|
end |
|
|
|
function UpdateObject:Done() |
|
self:StopObjUpdateThread() |
|
end |
|
|
|
|
|
|
|
|
|
DefineClass.ReservedObject = { |
|
__parents = { "InitDone" }, |
|
properties = { |
|
{ id = "reserved_by", editor = "object", default = false, no_edit = true }, |
|
}, |
|
} |
|
|
|
function TryInterruptReserved(reserved_obj) |
|
local reserved_by = reserved_obj.reserved_by |
|
if IsValid(reserved_by) then |
|
return reserved_by:OnReservationInterrupted() |
|
end |
|
reserved_obj.reserved_by = nil |
|
end |
|
|
|
ReservedObject.Disown = TryInterruptReserved |
|
|
|
AutoResolveMethods.CanReserverBeInterrupted = "or" |
|
ReservedObject.CanReserverBeInterrupted = empty_func |
|
|
|
function ReservedObject:CanBeReservedBy(obj) |
|
return not self.reserved_by or self.reserved_by == obj or self:CanReserverBeInterrupted(obj) |
|
end |
|
|
|
function ReservedObject:TryReserve(reserved_by) |
|
if not self:CanBeReservedBy(reserved_by) then return false end |
|
if self.reserved_by and self.reserved_by ~= reserved_by then |
|
if not TryInterruptReserved(self) then return end |
|
end |
|
return self:Reserve(reserved_by) |
|
end |
|
|
|
function ReservedObject:Reserve(reserved_by) |
|
assert(IsKindOf(reserved_by, "ReserverObject")) |
|
local previous_reservation = reserved_by.reserved_obj |
|
if previous_reservation and previous_reservation ~= self then |
|
|
|
previous_reservation:CancelReservation(reserved_by) |
|
end |
|
self.reserved_by = reserved_by |
|
reserved_by.reserved_obj = self |
|
self:OnReserved(reserved_by) |
|
return true |
|
end |
|
|
|
ReservedObject.OnReserved = empty_func |
|
ReservedObject.OnReservationCanceled = empty_func |
|
|
|
function ReservedObject:CancelReservation(reserved_by) |
|
if self.reserved_by == reserved_by then |
|
self.reserved_by = nil |
|
reserved_by.reserved_obj = nil |
|
self:OnReservationCanceled() |
|
return true |
|
end |
|
end |
|
|
|
function ReservedObject:Done() |
|
self:Disown() |
|
end |
|
|
|
DefineClass.ReserverObject = { |
|
__parents = { "CommandObject" }, |
|
|
|
reserved_obj = false, |
|
} |
|
|
|
function ReserverObject:OnReservationInterrupted() |
|
return self:TrySetCommand("CmdInterrupt") |
|
end |
|
|
|
|
|
|
|
DefineClass.OwnershipStateBase = { |
|
OnStateTick = empty_func, |
|
OnStateExit = empty_func, |
|
|
|
CanDisown = empty_func, |
|
CanBeOwnedBy = empty_func, |
|
} |
|
|
|
DefineClass("ConcreteOwnership", "OwnershipStateBase") |
|
|
|
local function SetOwnerObject(owned_obj, owner) |
|
assert(not owner or IsKindOf(owner, "OwnerObject")) |
|
owner = owner or false |
|
local prev_owner = owned_obj.owner |
|
if owner ~= prev_owner then |
|
owned_obj.owner = owner |
|
|
|
local notify_owner = not prev_owner or prev_owner:GetOwnedObject(owned_obj.ownership_class) == owned_obj |
|
if notify_owner then |
|
if prev_owner then |
|
prev_owner:SetOwnedObject(false, owned_obj.ownership_class) |
|
end |
|
if owner then |
|
owner:SetOwnedObject(owned_obj) |
|
end |
|
end |
|
end |
|
end |
|
|
|
function ConcreteOwnership.OnStateTick(owned_obj, owner) |
|
return SetOwnerObject(owned_obj, owner) |
|
end |
|
|
|
function ConcreteOwnership.OnStateExit(owned_obj) |
|
return SetOwnerObject(owned_obj, false) |
|
end |
|
|
|
function ConcreteOwnership.CanDisown(owned_obj, owner, reason) |
|
return owned_obj.owner == owner |
|
end |
|
|
|
function ConcreteOwnership.CanBeOwnedBy(owned_obj, owner, ...) |
|
return owned_obj.owner == owner |
|
end |
|
|
|
DefineClass("SharedOwnership", "OwnershipStateBase") |
|
SharedOwnership.CanBeOwnedBy = return_true |
|
|
|
DefineClass("ForbiddenOwnership", "OwnershipStateBase") |
|
|
|
DefineClass.OwnedObject = { |
|
__parents = { "ReservedObject" }, |
|
properties = { |
|
{ id = "owner", editor = "object", default = false, no_edit = true }, |
|
{ id = "can_change_ownership", name = "Can change ownership", editor = "bool", default = true, help = "If true, the player can change who owns the object", }, |
|
{ id = "ownership_class", name = "Ownership class", editor = "combo", default = false, items = GatherComboItems("GatherOwnershipClasses"), }, |
|
}, |
|
|
|
ownership = "SharedOwnership", |
|
} |
|
|
|
AutoResolveMethods.CanDisown = "and" |
|
function OwnedObject:CanDisown(owner, reason) |
|
return g_Classes[self.ownership].CanDisown(self, owner, reason) |
|
end |
|
|
|
function OwnedObject:Disown() |
|
ReservedObject.Disown(self) |
|
self:TrySetSharedOwnership() |
|
end |
|
|
|
AutoResolveMethods.CanBeOwnedBy = "and" |
|
function OwnedObject:CanBeOwnedBy(obj, ...) |
|
if not self:CanBeReservedBy(obj) then return end |
|
return g_Classes[self.ownership].CanBeOwnedBy(self, obj, ...) |
|
end |
|
|
|
AutoResolveMethods.CanChangeOwnership = "and" |
|
function OwnedObject:CanChangeOwnership() |
|
return self.can_change_ownership |
|
end |
|
|
|
function OwnedObject:GetReservedByOrOwner() |
|
return self.reserved_by or self.owner |
|
end |
|
|
|
OwnedObject.OnOwnershipChanged = empty_func |
|
|
|
function OwnedObject:TrySetOwnership(ownership, forced, ...) |
|
assert(ownership) |
|
if not ownership or not forced and not self:CanChangeOwnership() then return end |
|
|
|
local prev_owner = self.owner |
|
local prev_ownership = self.ownership |
|
self.ownership = ownership |
|
if prev_ownership ~= ownership then |
|
g_Classes[prev_ownership].OnStateExit(self, ...) |
|
end |
|
g_Classes[ownership].OnStateTick(self, ...) |
|
self:OnOwnershipChanged(prev_ownership, prev_owner) |
|
end |
|
|
|
local function TryInterruptReservedOnDifferentOwner(owned_obj) |
|
local reserved_by = owned_obj.reserved_by |
|
if IsValid(reserved_by) and reserved_by ~= owned_obj.owner then |
|
reserved_by:OnReservationInterrupted() |
|
owned_obj.reserved_by = nil |
|
end |
|
end |
|
|
|
local OwnershipChangedReactions = { |
|
ConcreteOwnership = { |
|
ConcreteOwnership = TryInterruptReservedOnDifferentOwner, |
|
ForbiddenOwnership = TryInterruptReserved, |
|
}, |
|
SharedOwnership = { |
|
ConcreteOwnership = TryInterruptReservedOnDifferentOwner, |
|
ForbiddenOwnership = TryInterruptReserved, |
|
} |
|
} |
|
|
|
function OwnedObject:OnOwnershipChanged(prev_ownership, prev_owner) |
|
local transition = table.get(OwnershipChangedReactions, prev_ownership, self.ownership) |
|
if transition then |
|
transition(self) |
|
end |
|
end |
|
|
|
|
|
|
|
function OwnedObject:TrySetConcreteOwnership(forced, owner) |
|
return self:TrySetOwnership("ConcreteOwnership", forced, owner) |
|
end |
|
|
|
function OwnedObject:SetConcreteOwnership(...) |
|
return self:TrySetConcreteOwnership("forced", ...) |
|
end |
|
|
|
function OwnedObject:HasConcreteOwnership() |
|
return self.ownership == "ConcreteOwnership" |
|
end |
|
|
|
function OwnedObject:TrySetSharedOwnership(forced, ...) |
|
return self:TrySetOwnership("SharedOwnership", forced, ...) |
|
end |
|
|
|
function OwnedObject:SetSharedOwnership(...) |
|
return self:TrySetSharedOwnership("forced", ...) |
|
end |
|
|
|
function OwnedObject:HasSharedOwnership() |
|
return self.ownership == "SharedOwnership" |
|
end |
|
|
|
function OwnedObject:TrySetForbiddenOwnership(forced, ...) |
|
return self:TrySetOwnership("ForbiddenOwnership", forced, ...) |
|
end |
|
|
|
function OwnedObject:SetForbiddenOwnership(...) |
|
return self:TrySetForbiddenOwnership("forced", ...) |
|
end |
|
|
|
function OwnedObject:HasForbiddenOwnership() |
|
return self.ownership == "ForbiddenOwnership" |
|
end |
|
|
|
|
|
|
|
DefineClass.OwnedByUnit = { |
|
__parents = { "OwnedObject" }, |
|
properties = { |
|
{ id = "can_have_dead_owners", name = "Can have dead owners", editor = "bool", default = false, help = "If true, the object can have dead units as owners", }, |
|
} |
|
} |
|
|
|
function OwnedByUnit:CanBeOwnedBy(obj, ...) |
|
if not self.can_have_dead_owners and obj:IsDead() then return end |
|
return OwnedObject.CanBeOwnedBy(self, obj, ...) |
|
end |
|
|
|
DefineClass.OwnerObject = { |
|
__parents = { "ReserverObject" }, |
|
owned_objects = false, |
|
} |
|
|
|
function OwnerObject:Init() |
|
self.owned_objects = {} |
|
end |
|
|
|
function OwnerObject:Owns(object) |
|
local ownership_class = object.ownership_class |
|
if not ownership_class then return end |
|
return self.owned_objects[ownership_class] == object |
|
end |
|
|
|
function OwnerObject:DisownObjects(reason) |
|
local owned_objects = self.owned_objects |
|
for _, ownership_class in ipairs(owned_objects) do |
|
local owned_object = owned_objects[ownership_class] |
|
if owned_object and owned_object:CanDisown(self, reason) then |
|
owned_object:Disown() |
|
end |
|
end |
|
end |
|
|
|
function OwnerObject:GetOwnedObject(ownership_class) |
|
assert(ownership_class) |
|
return self.owned_objects[ownership_class] |
|
end |
|
|
|
function OwnerObject:SetOwnedObject(owned_obj, ownership_class) |
|
assert(not owned_obj or owned_obj:IsKindOf("OwnedObject")) |
|
if owned_obj then |
|
ownership_class = ownership_class or owned_obj.ownership_class |
|
assert(ownership_class == owned_obj.ownership_class) |
|
end |
|
assert(ownership_class) |
|
if not ownership_class then |
|
return false |
|
end |
|
local prev_owned_obj = self:GetOwnedObject(ownership_class) |
|
if prev_owned_obj == owned_obj then |
|
return false |
|
end |
|
local owned_objects = self.owned_objects |
|
|
|
owned_objects[ownership_class] = owned_obj |
|
table.remove_entry(owned_objects, ownership_class) |
|
if owned_obj then |
|
table.insert(owned_objects, ownership_class) |
|
if prev_owned_obj then |
|
prev_owned_obj:TrySetSharedOwnership() |
|
end |
|
owned_obj:TrySetConcreteOwnership(nil, self) |
|
end |
|
return true |
|
end |
|
|
|
if Platform.developer then |
|
|
|
function OwnedObject:GetTestData(data) |
|
data.ReservedBy = self.reserved_by |
|
end |
|
|
|
end |
|
|
|
|