Schmedium
This commit is contained in:
@@ -112,27 +112,21 @@ end
|
|||||||
|
|
||||||
local function clearChangedOffset(vehicle)
|
local function clearChangedOffset(vehicle)
|
||||||
if not vehicle then return end
|
if not vehicle then return end
|
||||||
local md = vehicle:getModData()
|
end
|
||||||
if not md or md["isChangedTowedAttachment"] ~= true then return end
|
|
||||||
|
local function restoreTowBarDefaults(vehicle)
|
||||||
|
if not vehicle then return end
|
||||||
|
|
||||||
local script = vehicle:getScript()
|
local script = vehicle:getScript()
|
||||||
local changedId = md["towBarChangedAttachmentId"] or vehicle:getTowAttachmentSelf()
|
if script and script.getMass then
|
||||||
local attachment = script and script:getAttachmentById(changedId) or nil
|
local scriptMass = tonumber(script:getMass())
|
||||||
if attachment then
|
if scriptMass and scriptMass > 0 then
|
||||||
local offset = attachment:getOffset()
|
vehicle:setMass(scriptMass)
|
||||||
local storedShift = tonumber(md["towBarChangedOffsetZShift"])
|
|
||||||
if storedShift ~= nil then
|
|
||||||
attachment:getOffset():set(offset:x(), offset:y(), offset:z() - storedShift)
|
|
||||||
else
|
|
||||||
local zShift = offset:z() > 0 and -1 or 1
|
|
||||||
attachment:getOffset():set(offset:x(), offset:y(), offset:z() + zShift)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
md["isChangedTowedAttachment"] = false
|
vehicle:constraintChanged()
|
||||||
md["towBarChangedAttachmentId"] = nil
|
vehicle:updateTotalMass()
|
||||||
md["towBarChangedOffsetZShift"] = nil
|
|
||||||
vehicle:transmitModData()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function applyRigidTow(towingVehicle, towedVehicle, attachmentA, attachmentB)
|
local function applyRigidTow(towingVehicle, towedVehicle, attachmentA, attachmentB)
|
||||||
@@ -169,31 +163,7 @@ end
|
|||||||
|
|
||||||
local function refreshTowBarState(vehicle)
|
local function refreshTowBarState(vehicle)
|
||||||
if not vehicle then return end
|
if not vehicle then return end
|
||||||
local md = vehicle:getModData()
|
setTowBarModelVisible(vehicle, vehicle:getVehicleTowedBy() ~= nil)
|
||||||
if not md then return end
|
|
||||||
|
|
||||||
local front = vehicle:getVehicleTowedBy()
|
|
||||||
local rear = vehicle:getVehicleTowing()
|
|
||||||
|
|
||||||
local isTowed = false
|
|
||||||
if front then
|
|
||||||
local frontMd = front:getModData()
|
|
||||||
if md["towed"] == true or (frontMd and frontMd["isTowingByTowBar"] == true) then
|
|
||||||
isTowed = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local isTowing = false
|
|
||||||
if rear then
|
|
||||||
local rearMd = rear:getModData()
|
|
||||||
if rearMd and rearMd["towed"] == true and rearMd["isTowingByTowBar"] == true then
|
|
||||||
isTowing = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
md["towed"] = isTowed
|
|
||||||
md["isTowingByTowBar"] = isTowed or isTowing
|
|
||||||
vehicle:transmitModData()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function refreshAround(vehicle)
|
local function refreshAround(vehicle)
|
||||||
@@ -205,25 +175,10 @@ end
|
|||||||
|
|
||||||
local function restoreDefaultsIfLeadLost(vehicle)
|
local function restoreDefaultsIfLeadLost(vehicle)
|
||||||
if not vehicle then return end
|
if not vehicle then return end
|
||||||
local md = vehicle:getModData()
|
|
||||||
if not md then return end
|
|
||||||
if vehicle:getVehicleTowedBy() ~= nil then return end
|
if vehicle:getVehicleTowedBy() ~= nil then return end
|
||||||
if md["towed"] ~= true and md.towBarOriginalMass == nil and md.towBarOriginalBrakingForce == nil then return end
|
|
||||||
|
|
||||||
if md.towBarOriginalScriptName then vehicle:setScriptName(md.towBarOriginalScriptName) end
|
clearChangedOffset(vehicle)
|
||||||
if md.towBarOriginalMass ~= nil then vehicle:setMass(md.towBarOriginalMass) end
|
restoreTowBarDefaults(vehicle)
|
||||||
if md.towBarOriginalBrakingForce ~= nil then vehicle:setBrakingForce(md.towBarOriginalBrakingForce) end
|
|
||||||
vehicle:constraintChanged()
|
|
||||||
vehicle:updateTotalMass()
|
|
||||||
|
|
||||||
md["towed"] = false
|
|
||||||
md.towBarOriginalScriptName = nil
|
|
||||||
md.towBarOriginalMass = nil
|
|
||||||
md.towBarOriginalBrakingForce = nil
|
|
||||||
if vehicle:getVehicleTowing() == nil then
|
|
||||||
md["isTowingByTowBar"] = false
|
|
||||||
end
|
|
||||||
vehicle:transmitModData()
|
|
||||||
|
|
||||||
setTowBarModelVisible(vehicle, false)
|
setTowBarModelVisible(vehicle, false)
|
||||||
end
|
end
|
||||||
@@ -277,18 +232,6 @@ local function restoreFrontLink(playerObj, link)
|
|||||||
middle:setScriptName("notTowingA_Trailer")
|
middle:setScriptName("notTowingA_Trailer")
|
||||||
sendAttach(playerObj, front, middle, a, b)
|
sendAttach(playerObj, front, middle, a, b)
|
||||||
|
|
||||||
local frontMd = front:getModData()
|
|
||||||
local middleMd = middle:getModData()
|
|
||||||
if frontMd then
|
|
||||||
frontMd["isTowingByTowBar"] = true
|
|
||||||
front:transmitModData()
|
|
||||||
end
|
|
||||||
if middleMd then
|
|
||||||
middleMd["isTowingByTowBar"] = true
|
|
||||||
middleMd["towed"] = true
|
|
||||||
middle:transmitModData()
|
|
||||||
end
|
|
||||||
|
|
||||||
setTowBarModelVisible(middle, true)
|
setTowBarModelVisible(middle, true)
|
||||||
refreshAround(front)
|
refreshAround(front)
|
||||||
refreshAround(middle)
|
refreshAround(middle)
|
||||||
@@ -305,6 +248,14 @@ local function resolveDetachPair(towingVehicle, towedVehicle)
|
|||||||
local resolvedTowing = towingVehicle
|
local resolvedTowing = towingVehicle
|
||||||
local resolvedTowed = resolvedTowing and resolvedTowing:getVehicleTowing() or nil
|
local resolvedTowed = resolvedTowing and resolvedTowing:getVehicleTowing() or nil
|
||||||
|
|
||||||
|
if resolvedTowed == nil and towedVehicle == nil and resolvedTowing and resolvedTowing:getVehicleTowedBy() then
|
||||||
|
local front = resolvedTowing:getVehicleTowedBy()
|
||||||
|
if front and front:getVehicleTowing() == resolvedTowing then
|
||||||
|
resolvedTowed = resolvedTowing
|
||||||
|
resolvedTowing = front
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if resolvedTowed == nil and towedVehicle and towedVehicle:getVehicleTowedBy() then
|
if resolvedTowed == nil and towedVehicle and towedVehicle:getVehicleTowedBy() then
|
||||||
resolvedTowing = towedVehicle:getVehicleTowedBy()
|
resolvedTowing = towedVehicle:getVehicleTowedBy()
|
||||||
resolvedTowed = towedVehicle
|
resolvedTowed = towedVehicle
|
||||||
@@ -488,13 +439,8 @@ LT.performDetachWrapper = function(playerObj, towingVehicle, towedVehicle)
|
|||||||
|
|
||||||
restoreDefaultsIfLeadLost(resolvedTowing)
|
restoreDefaultsIfLeadLost(resolvedTowing)
|
||||||
restoreDefaultsIfLeadLost(resolvedTowed)
|
restoreDefaultsIfLeadLost(resolvedTowed)
|
||||||
refreshAround(resolvedTowing)
|
|
||||||
refreshAround(resolvedTowed)
|
|
||||||
|
|
||||||
queueAction(playerObj, 12, function(_, v) restoreDefaultsIfLeadLost(v); refreshAround(v) end, resolvedTowing)
|
queueAction(playerObj, 12, function(_, v) restoreDefaultsIfLeadLost(v); refreshAround(v) end, resolvedTowing)
|
||||||
queueAction(playerObj, 12, function(_, v) restoreDefaultsIfLeadLost(v); refreshAround(v) end, resolvedTowed)
|
queueAction(playerObj, 12, function(_, v) restoreDefaultsIfLeadLost(v); refreshAround(v) end, resolvedTowed)
|
||||||
queueAction(playerObj, 30, function(_, v) restoreDefaultsIfLeadLost(v); refreshAround(v) end, resolvedTowing)
|
|
||||||
queueAction(playerObj, 30, function(_, v) restoreDefaultsIfLeadLost(v); refreshAround(v) end, resolvedTowed)
|
|
||||||
|
|
||||||
setTowBarModelVisible(resolvedTowed, false)
|
setTowBarModelVisible(resolvedTowed, false)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -20,8 +20,13 @@ import org.objectweb.asm.tree.MethodNode;
|
|||||||
public final class BaseVehicleConstraintPatch {
|
public final class BaseVehicleConstraintPatch {
|
||||||
private static final String TARGET_NAME = "addPointConstraint";
|
private static final String TARGET_NAME = "addPointConstraint";
|
||||||
private static final String CONSTRAINT_CHANGED_NAME = "constraintChanged";
|
private static final String CONSTRAINT_CHANGED_NAME = "constraintChanged";
|
||||||
|
private static final String IS_ENTER_BLOCKED_NAME = "isEnterBlocked";
|
||||||
|
private static final String IS_ENTER_BLOCKED2_NAME = "isEnterBlocked2";
|
||||||
private static final String CLINIT_NAME = "<clinit>";
|
private static final String CLINIT_NAME = "<clinit>";
|
||||||
private static final String VOID_NOARG_DESC = "()V";
|
private static final String VOID_NOARG_DESC = "()V";
|
||||||
|
private static final String ENTER_BLOCKED_DESC = "(Lzombie/characters/IsoGameCharacter;I)Z";
|
||||||
|
private static final String EXIT_BLOCKED_DESC = "(Lzombie/characters/IsoGameCharacter;I)Z";
|
||||||
|
private static final String EXIT_BLOCKED2_DESC = "(I)Z";
|
||||||
private static final String PATCH_LOG_LINE = "[Landtrain][BaseVehiclePatch] BaseVehicle override enabled";
|
private static final String PATCH_LOG_LINE = "[Landtrain][BaseVehiclePatch] BaseVehicle override enabled";
|
||||||
private static final String BREAK_DESC_OBJECT_BOOL = "(ZLjava/lang/Boolean;)V";
|
private static final String BREAK_DESC_OBJECT_BOOL = "(ZLjava/lang/Boolean;)V";
|
||||||
private static final String BREAK_DESC_PRIMITIVE_BOOL = "(ZZ)V";
|
private static final String BREAK_DESC_PRIMITIVE_BOOL = "(ZZ)V";
|
||||||
@@ -31,6 +36,11 @@ public final class BaseVehicleConstraintPatch {
|
|||||||
private static final String HELPER_METHOD = "resolveConstraintDriver";
|
private static final String HELPER_METHOD = "resolveConstraintDriver";
|
||||||
private static final String HELPER_DESC =
|
private static final String HELPER_DESC =
|
||||||
"(Lzombie/vehicles/BaseVehicle;)Lzombie/characters/IsoGameCharacter;";
|
"(Lzombie/vehicles/BaseVehicle;)Lzombie/characters/IsoGameCharacter;";
|
||||||
|
private static final String HELPER_ENTER_BLOCKED = "isEnterBlockedLandtrain";
|
||||||
|
private static final String HELPER_ENTER_BLOCKED_DESC =
|
||||||
|
"(Lzombie/vehicles/BaseVehicle;Lzombie/characters/IsoGameCharacter;I)Z";
|
||||||
|
private static final String HELPER_ENTER_BLOCKED2 = "isEnterBlocked2Landtrain";
|
||||||
|
private static final String HELPER_ENTER_BLOCKED2_DESC = "(Lzombie/vehicles/BaseVehicle;I)Z";
|
||||||
|
|
||||||
private BaseVehicleConstraintPatch() {
|
private BaseVehicleConstraintPatch() {
|
||||||
}
|
}
|
||||||
@@ -51,6 +61,8 @@ public final class BaseVehicleConstraintPatch {
|
|||||||
int removedCalls = 0;
|
int removedCalls = 0;
|
||||||
int inspectedAddPointMethods = 0;
|
int inspectedAddPointMethods = 0;
|
||||||
int patchedConstraintDriverCalls = 0;
|
int patchedConstraintDriverCalls = 0;
|
||||||
|
int patchedEnterBlockedCalls = 0;
|
||||||
|
int patchedEnterBlocked2Calls = 0;
|
||||||
|
|
||||||
for (MethodNode method : classNode.methods) {
|
for (MethodNode method : classNode.methods) {
|
||||||
if (TARGET_NAME.equals(method.name) && isTargetAddPointConstraint(method.desc)) {
|
if (TARGET_NAME.equals(method.name) && isTargetAddPointConstraint(method.desc)) {
|
||||||
@@ -59,6 +71,22 @@ public final class BaseVehicleConstraintPatch {
|
|||||||
} else if (CONSTRAINT_CHANGED_NAME.equals(method.name)
|
} else if (CONSTRAINT_CHANGED_NAME.equals(method.name)
|
||||||
&& VOID_NOARG_DESC.equals(method.desc)) {
|
&& VOID_NOARG_DESC.equals(method.desc)) {
|
||||||
patchedConstraintDriverCalls += patchConstraintChangedDriverCalls(method);
|
patchedConstraintDriverCalls += patchConstraintChangedDriverCalls(method);
|
||||||
|
} else if (IS_ENTER_BLOCKED_NAME.equals(method.name)
|
||||||
|
&& ENTER_BLOCKED_DESC.equals(method.desc)) {
|
||||||
|
patchedEnterBlockedCalls += patchEnterBlockedCall(
|
||||||
|
method,
|
||||||
|
"isExitBlocked",
|
||||||
|
EXIT_BLOCKED_DESC,
|
||||||
|
HELPER_ENTER_BLOCKED,
|
||||||
|
HELPER_ENTER_BLOCKED_DESC);
|
||||||
|
} else if (IS_ENTER_BLOCKED2_NAME.equals(method.name)
|
||||||
|
&& ENTER_BLOCKED_DESC.equals(method.desc)) {
|
||||||
|
patchedEnterBlocked2Calls += patchEnterBlockedCall(
|
||||||
|
method,
|
||||||
|
"isExitBlocked2",
|
||||||
|
EXIT_BLOCKED2_DESC,
|
||||||
|
HELPER_ENTER_BLOCKED2,
|
||||||
|
HELPER_ENTER_BLOCKED2_DESC);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,6 +103,14 @@ public final class BaseVehicleConstraintPatch {
|
|||||||
"Expected to patch at least 1 constraintChanged getDriver call, patched "
|
"Expected to patch at least 1 constraintChanged getDriver call, patched "
|
||||||
+ patchedConstraintDriverCalls);
|
+ patchedConstraintDriverCalls);
|
||||||
}
|
}
|
||||||
|
if (patchedEnterBlockedCalls < 1) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Expected to patch isEnterBlocked call, patched " + patchedEnterBlockedCalls);
|
||||||
|
}
|
||||||
|
if (patchedEnterBlocked2Calls < 1) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Expected to patch isEnterBlocked2 call, patched " + patchedEnterBlocked2Calls);
|
||||||
|
}
|
||||||
if (!ensureClassInitLog(classNode)) {
|
if (!ensureClassInitLog(classNode)) {
|
||||||
throw new IllegalStateException("Failed to inject BaseVehicle class-init debug log");
|
throw new IllegalStateException("Failed to inject BaseVehicle class-init debug log");
|
||||||
}
|
}
|
||||||
@@ -89,6 +125,10 @@ public final class BaseVehicleConstraintPatch {
|
|||||||
+ removedCalls
|
+ removedCalls
|
||||||
+ ", constraint driver hooks: "
|
+ ", constraint driver hooks: "
|
||||||
+ patchedConstraintDriverCalls
|
+ patchedConstraintDriverCalls
|
||||||
|
+ ", enter-block hooks: "
|
||||||
|
+ patchedEnterBlockedCalls
|
||||||
|
+ "/"
|
||||||
|
+ patchedEnterBlocked2Calls
|
||||||
+ ", class-init debug log: enabled");
|
+ ", class-init debug log: enabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,6 +198,41 @@ public final class BaseVehicleConstraintPatch {
|
|||||||
return patched;
|
return patched;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int patchEnterBlockedCall(
|
||||||
|
MethodNode method,
|
||||||
|
String targetCallName,
|
||||||
|
String targetCallDesc,
|
||||||
|
String helperMethod,
|
||||||
|
String helperDesc) {
|
||||||
|
int patched = 0;
|
||||||
|
InsnList insns = method.instructions;
|
||||||
|
for (AbstractInsnNode node = insns.getFirst(); node != null; ) {
|
||||||
|
AbstractInsnNode next = node.getNext();
|
||||||
|
if (!(node instanceof MethodInsnNode call)) {
|
||||||
|
node = next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!BASE_VEHICLE_OWNER.equals(call.owner)
|
||||||
|
|| !targetCallName.equals(call.name)
|
||||||
|
|| !targetCallDesc.equals(call.desc)) {
|
||||||
|
node = next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodInsnNode replacement =
|
||||||
|
new MethodInsnNode(
|
||||||
|
Opcodes.INVOKESTATIC,
|
||||||
|
HELPER_OWNER,
|
||||||
|
helperMethod,
|
||||||
|
helperDesc,
|
||||||
|
false);
|
||||||
|
insns.set(call, replacement);
|
||||||
|
patched++;
|
||||||
|
node = next;
|
||||||
|
}
|
||||||
|
return patched;
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean ensureClassInitLog(ClassNode classNode) {
|
private static boolean ensureClassInitLog(ClassNode classNode) {
|
||||||
MethodNode clinit = null;
|
MethodNode clinit = null;
|
||||||
for (MethodNode method : classNode.methods) {
|
for (MethodNode method : classNode.methods) {
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
package zombie.vehicles;
|
package zombie.vehicles;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
import zombie.characters.IsoGameCharacter;
|
import zombie.characters.IsoGameCharacter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves the effective driver for constraint auth in chained towing.
|
* Resolves the effective driver for constraint auth in chained towing.
|
||||||
* For middle vehicles in a chain, prefer the front/lead driver's authority.
|
* For middle vehicles in a chain, scan through links so long trains still resolve authority.
|
||||||
*/
|
*/
|
||||||
public final class LandtrainConstraintAuthHelper {
|
public final class LandtrainConstraintAuthHelper {
|
||||||
private LandtrainConstraintAuthHelper() {
|
private LandtrainConstraintAuthHelper() {
|
||||||
@@ -15,24 +17,49 @@ public final class LandtrainConstraintAuthHelper {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
IsoGameCharacter driver = vehicle.getDriver();
|
IsoGameCharacter driver = findDriverAlongChain(vehicle, true);
|
||||||
|
if (driver != null) return driver;
|
||||||
|
return findDriverAlongChain(vehicle, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IsoGameCharacter findDriverAlongChain(BaseVehicle start, boolean towardFront) {
|
||||||
|
BaseVehicle cursor = start;
|
||||||
|
Set<Integer> visited = new HashSet<>();
|
||||||
|
|
||||||
|
while (cursor != null) {
|
||||||
|
int id = cursor.getId();
|
||||||
|
if (!visited.add(id)) {
|
||||||
|
// Safety: malformed link graph; stop scanning.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
IsoGameCharacter driver = cursor.getDriver();
|
||||||
if (driver != null) {
|
if (driver != null) {
|
||||||
return driver;
|
return driver;
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseVehicle front = vehicle.getVehicleTowedBy();
|
cursor = towardFront ? cursor.getVehicleTowedBy() : cursor.getVehicleTowing();
|
||||||
if (front != null) {
|
|
||||||
driver = front.getDriver();
|
|
||||||
if (driver != null) {
|
|
||||||
return driver;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BaseVehicle rear = vehicle.getVehicleTowing();
|
|
||||||
if (rear != null) {
|
|
||||||
return rear.getDriver();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isEnterBlockedLandtrain(
|
||||||
|
BaseVehicle vehicle, IsoGameCharacter chr, int seat) {
|
||||||
|
if (isVehicleBeingTowedInLandtrain(vehicle)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return vehicle != null && vehicle.isExitBlocked(chr, seat);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isEnterBlocked2Landtrain(BaseVehicle vehicle, int seat) {
|
||||||
|
if (isVehicleBeingTowedInLandtrain(vehicle)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return vehicle != null && vehicle.isExitBlocked2(seat);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isVehicleBeingTowedInLandtrain(BaseVehicle vehicle) {
|
||||||
|
return vehicle != null && vehicle.getVehicleTowedBy() != null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user