Loading [MathJax]/extensions/Safe.js

Special Aircraft Service

Please login or register.

Login with username, password and session length
Advanced search  
Pages: [1]   Go Down

Author Topic: vehicle/convoy speed parameter  (Read 218 times)

0 Members and 1 Guest are viewing this topic.

cbradbury

  • member
  • Offline Offline
  • Posts: 1077
vehicle/convoy speed parameter
« on: March 30, 2025, 07:28:51 AM »

Hi, I have been spending a considerable amount of time lately adjusting and creating new vehicle convoys in WAW. Mostly gone well, but I have had to adjust convoy speeds at times, as a slow convoy leading a faster one (e.g. KV-1s ahead of T-34s) stops to let the faster one overtake, leading to all sorts of mayhem!

I solved this by changing vehicle speeds in technics.ini so that the speeds of each match (obviously I restore the defaults when I have finished that mission).

Now in technics.ini each vehicle has two given speeds:

SpeedAverage   1.0                    // km/h
SpeedMax       2.0                    // km/h

My question is: does anyone know what criteria the game has for choosing between those speeds? Unlike ships you cannot set a speed in game as far as I can tell, and when in convoy, as expected, SpeedAverage appears to be used. Just wondered if anyone knows when SpeedMax would be used?

Logged

Epervier

  • 4.09 Guardian Angel !
  • SAS Team
  • member
  • Offline Offline
  • Posts: 9600
  • I'm French and Rebel_409! Nobody is perfect!
    • Some tinkering here
Re: vehicle/convoy speed parameter
« Reply #1 on: March 30, 2025, 08:03:39 AM »

In my opinion:
- in convoy, the speed will be that of the slowest vehicle,
- solo, or if all vehicles are identical, the speed will be that of the fastest (SpeedMax).
This is just my opinion, so there's no guarantee.
Check the beginning of the file (technics.ini) for further explanations.
Logged
If your results do not live up to your expectations, tell yourself that the great oak was once an acorn too. - Lao Zi -

cbradbury

  • member
  • Offline Offline
  • Posts: 1077
Re: vehicle/convoy speed parameter
« Reply #2 on: March 30, 2025, 08:24:54 AM »

Hi Gabriel,

convoys definitely travel at the speed of the slowest vehicle, not sure about the other circumstances, but I suspect that you are right.

Yours,

Clive
Logged

WxTech

  • Modder
  • member
  • Online Online
  • Posts: 6181
Re: vehicle/convoy speed parameter
« Reply #3 on: March 30, 2025, 10:38:51 AM »

I suspect the max speed will be in effect if delays cause the vehicle to fall behind schedule for reaching its destination. In a convoy there are considerations of the slowest vehicle's max speed, I should imagine.

From ChiefGround.recomputeUnitsProperties():
Code: [Select]
    public void recomputeUnitsProperties()
    {
        if(unitsPacked.size() > 0)
        {
            recomputeUnitsProperties_Packed();
            return;
        }
        Object aobj[] = getOwnerAttached();
        if(aobj.length <= 0)
            ERR_NO_UNITS("recomputeUnitsProperties");
        int i = aobj.length;
        groupSpeed = 10000F;
        maxSpace = -1F;
        weaponsMask = 0;
        hitbyMask = 0;
        withPreys = false;
        for(int j = 0; j < i; j++)
        {
            UnitInterface unitinterface = (UnitInterface)aobj[j];
            float f = unitinterface.SpeedAverage();  //<<<------------------- look here
            if(f < groupSpeed)
                groupSpeed = f;
            f = unitinterface.BestSpace();
            if(f > maxSpace)
                maxSpace = f;
            if(unitinterface instanceof Predator)
                weaponsMask |= ((Predator)unitinterface).WeaponsMask();
            if(!(unitinterface instanceof Prey))
                continue;
            hitbyMask |= ((Prey)unitinterface).HitbyMask();
            if(!(unitinterface instanceof Predator))
                withPreys = true;
        }

        if(groupSpeed <= 0.001F)
            ERR("group speed is too small");  //<<<------------------ and here
        if(maxSpace <= 0.01F)
            ERR("maxSpace is too small");
    }


To supply some more stuff to chew on:

The relevant class/method from TankGeneric.class (the full class is MUCH bigger). I've identified the key line of code, which calls method set() in Moving.class. Which in turn determines if dontrun is true or false in UnitMove.class. If not reversing, or if dontrun is false, then SPEED_MAX is considered.
Code: [Select]
    class Move extends Interpolate
    {

        public boolean tick()
        {
            if(dying != 0)
            {
                neverDust();
                if(dying == 2)
                    return false;
                if(dyingDelay-- <= 0L)
                {
                    ShowExplode();
                    MakeCrush();
                    return false;
                }
                if(mov.rotatCurTime > 0L)
                {
                    mov.rotatCurTime--;
                    float f = 1.0F - (float)mov.rotatCurTime / (float)mov.rotatTotTime;
                    pos.getAbs(TankGeneric.o);
                    TankGeneric.o.setYaw(mov.angles.getDeg(f));
                    if(mov.normal.z < 0.0F)
                    {
                        Engine.land().N(mov.srcPos.x, mov.srcPos.y, TankGeneric.n);
                        TankGeneric.o.orient(TankGeneric.n);
                    } else
                    {
                        TankGeneric.o.orient(mov.normal);
                    }
                    pos.setAbs(TankGeneric.o);
                }
                return true;
            }
            boolean flag = mov.moveCurTime < 0L && mov.rotatCurTime < 0L;
            if(isNetMirror() && flag)
            {
                mov.switchToStay(30F);
                flag = false;
            }
            if(flag)
            {
                ChiefGround chiefground = (ChiefGround)getOwner();
                float f2 = -1F;
                UnitMove unitmove;
                if(collisionStage == 0)
                {
                    if(prop.meshName2 != null)
                    {
                        TankGeneric.p.x = TankGeneric.Rnd(-0.3D, 0.3D);
                        TankGeneric.p.y = TankGeneric.Rnd(-0.3D, 0.3D);
                        TankGeneric.p.z = 1.0D;
                        unitmove = chiefground.AskMoveCommand(actor, TankGeneric.p, obs);
                    } else
                    {
                        unitmove = chiefground.AskMoveCommand(actor, null, obs);
                    }
                } else
                if(collisionStage == 1)
                {
                    obs.collision(collidee, chiefground, udata);
                    collidee = null;
                    float f3 = TankGeneric.Rnd(-70F, 70F);
                    Vector2d vector2d = TankGeneric.Rotate(collisVector, f3);
                    vector2d.scale((double)prop.AFTER_COLLISION_DIST * TankGeneric.Rnd(0.87D, 1.75D));
                    TankGeneric.p.set(vector2d.x, vector2d.y, -1D);
                    unitmove = chiefground.AskMoveCommand(actor, TankGeneric.p, obs);
                    collisionStage = 2;
                    f2 = prop.SPEED_BACK;
                } else
                {
                    float f4 = TankGeneric.Rnd(0.0F, 359.99F);
                    Vector2d vector2d1 = TankGeneric.Rotate(collisVector, f4);
                    vector2d1.scale((double)prop.AFTER_COLLISION_DIST * TankGeneric.Rnd(0.2D, 0.6D));
                    TankGeneric.p.set(vector2d1.x, vector2d1.y, 1.0D);
                    unitmove = chiefground.AskMoveCommand(actor, TankGeneric.p, obs);
                    collisionStage = 0;
                }
                mov.set(unitmove, actor, prop.SPEED_MAX, f2, prop.ROT_SPEED_MAX, prop.ROT_INVIS_ANG);  //<<<----------- look here
                for(int j = 0; j < arms.length; j++)
                {
                    if(!StayWhenFire(arms[j].aim))
                        continue;
                    if(Head360(prop.gunProperties[j]))
                    {
                        if(!arms[j].aim.isInFiringMode())
                            continue;
                        mov.switchToStay(1.3F);
                        break;
                    }
                    if(!arms[j].aim.isInAimingMode())
                        continue;
                    mov.switchToStay(1.3F);
                    break;
                }

                if(isNetMaster())
                    send_MoveCommand(mov, f2);
            }
            for(int i = 0; i < arms.length; i++)
                arms[i].aim.tick_();

            if(dust != null)
                dust._setIntesity(mov.movingForward ? 1.0F : 0.0F);
            if(mov.dstPos == null)
            {
                mov.moveCurTime--;
                if(engineSFX != null && engineSTimer > 0 && --engineSTimer == 0)
                    engineSFX.stop();
                return true;
            }
            if(engineSFX != null)
                if(engineSTimer == 0)
                {
                    engineSFX.play();
                    engineSTimer = (int)TankGeneric.SecsToTicks(TankGeneric.Rnd(10F, 12F));
                } else
                if(engineSTimer < ticksIn8secs)
                    engineSTimer = (int)TankGeneric.SecsToTicks(TankGeneric.Rnd(10F, 12F));
            pos.getAbs(TankGeneric.o);
            boolean flag1 = false;
            if(mov.rotatCurTime > 0L)
            {
                mov.rotatCurTime--;
                float f1 = 1.0F - (float)mov.rotatCurTime / (float)mov.rotatTotTime;
                TankGeneric.o.setYaw(mov.angles.getDeg(f1));
                flag1 = true;
                if(mov.rotatCurTime <= 0L)
                {
                    mov.rotatCurTime = -1L;
                    mov.rotatingInPlace = false;
                }
            }
            if(!mov.rotatingInPlace && mov.moveCurTime > 0L)
            {
                mov.moveCurTime--;
                double d = 1.0D - (double)mov.moveCurTime / (double)mov.moveTotTime;
                TankGeneric.p.x = mov.srcPos.x * (1.0D - d) + mov.dstPos.x * d;
                TankGeneric.p.y = mov.srcPos.y * (1.0D - d) + mov.dstPos.y * d;
                if(mov.normal.z < 0.0F)
                {
                    TankGeneric.p.z = Engine.land().HQ(TankGeneric.p.x, TankGeneric.p.y) + (double)HeightAboveLandSurface();
                    Engine.land().N(TankGeneric.p.x, TankGeneric.p.y, TankGeneric.n);
                } else
                {
                    TankGeneric.p.z = mov.srcPos.z * (1.0D - d) + mov.dstPos.z * d;
                }
                flag1 = false;
                pos.setAbs(TankGeneric.p);
                if(mov.moveCurTime <= 0L)
                    mov.moveCurTime = -1L;
            }
            if(mov.normal.z < 0.0F)
            {
                if(flag1)
                    Engine.land().N(mov.srcPos.x, mov.srcPos.y, TankGeneric.n);
                TankGeneric.o.orient(TankGeneric.n);
            } else
            {
                TankGeneric.o.orient(mov.normal);
            }
            pos.setAbs(TankGeneric.o);
            return true;
        }

        Move()
        {
        }
    }

The entirety of Moving.class:
Code: [Select]
package com.maddox.il2.ai.ground;

import com.maddox.JGP.Point3d;
import com.maddox.JGP.Vector3f;
import com.maddox.il2.ai.AnglesFork;
import com.maddox.il2.engine.*;
import com.maddox.rts.Time;

// Referenced classes of package com.maddox.il2.ai.ground:
//            UnitInterface, UnitMove

public class Moving
{

    private static final long SecsToTicks(float f)
    {
        long l = (long)(0.5D + (double)(f / Time.tickLenFs()));
        return l >= 1L ? l : 1L;
    }

    public boolean IsLandAligned()
    {
        return normal.z < 0.0F;
    }

    public static final float distance2D(Point3d point3d, Point3d point3d1)
    {
        return (float)Math.sqrt((point3d.x - point3d1.x) * (point3d.x - point3d1.x) + (point3d.y - point3d1.y) * (point3d.y - point3d1.y));
    }

    public Moving()
    {
        moveCurTime = rotatCurTime = -1L;
        srcPos = new Point3d();
        dstPos = new Point3d();
        normal = null;
        angles = new AnglesFork();
        rotatingInPlace = false;
        movingForward = false;
    }

    public void switchToStay(float f)
    {
        dstPos = null;
        moveTotTime = moveCurTime = SecsToTicks(f);
        rotatCurTime = -1L;
        rotatingInPlace = false;
        movingForward = false;
    }

    public void switchToAsk()
    {
        switchToStay(0.0F);
        moveCurTime = rotatCurTime = -1L;
    }

    public void set(UnitMove unitmove, Actor actor, float f, float f1, float f2, float f3)  //f=SPEED_MAX, f1=SPEED_BACK, f2=ROT_SPEED_MAX, f3=ROT_INVIS_ANG
//from TankGeneric:  mov.set(unitmove, actor, prop.SPEED_MAX, f2, prop.ROT_SPEED_MAX, prop.ROT_INVIS_ANG);
    {
        dstPos = new Point3d();
        normal = new Vector3f();
        normal.set(unitmove.normal);
        srcPos.set(actor.pos.getAbsPoint());
        moveTotTime = moveCurTime = SecsToTicks(unitmove.totalTime);
        if(unitmove.pos == null)
        {
            switchToStay(unitmove.totalTime);
            return;
        }
        dstPos.set(unitmove.pos);
        movingForward = true;
        angles.setDeg(actor.pos.getAbsOrient().getYaw());
        double d = dstPos.x - srcPos.x;
        double d1 = dstPos.y - srcPos.y;
        if(Math.abs(d) + Math.abs(d1) > 1.0000000000000001E-005D)
            angles.setDstRad((float)Math.atan2(d1, d));
        boolean flag = false;
        if(f1 > 0.0F && angles.getAbsDiffDeg() > 90F)
        {
            flag = true;
            angles.reverseDst();
            movingForward = false;
        }
        rotatingInPlace = false;
        float f4 = 0.0F;
        d1 = 0.0F;
        float f5 = unitmove.totalTime;
        if(angles.getAbsDiffDeg() > 0.02F)
            if(angles.getAbsDiffDeg() <= f3)
            {
                f4 = angles.getAbsDiffDeg() / (f2 * 0.2F);
                if(f4 > f5)
                {
                    float f6 = f4 * 0.2F;
                    if(unitmove.dontrun)
                        f6 *= 0.6F;
                    if(f6 > f5)
                        f4 = f5 = f6;
                    else
                        f4 = f5;
                }
            } else
            {
                rotatingInPlace = true;
                f4 = angles.getAbsDiffDeg() / f2;
                if(f4 > f5)
                    f5 = f4;
                f5 -= f4;
            }
        d1 = f5;
        float f7 = distance2D(srcPos, dstPos);
        float f8 = flag ? f1 : unitmove.dontrun ? unitmove.walkSpeed : f;  //if not flag=true if reversing, and if not unitmove.walkSpeed, then f8=SPEED_MAX
        if(f7 / f8 > d1)
            d1 = f7 / f8;
        rotatTotTime = SecsToTicks(f4);
        moveTotTime = SecsToTicks(d1);
        moveCurTime = d1 <= 0.0F ? -1L : moveTotTime;
        rotatCurTime = f4 <= 0.0F ? -1L : rotatTotTime;
        if(rotatCurTime < 0L && moveCurTime <= 1L || distance2D(srcPos, dstPos) < 0.05F)
            switchToStay(((UnitInterface)actor).StayInterval());
    }

    public void switchToRotate(Actor actor, float f, float f1)
    {
        if(normal == null)
        {
            movingForward = false;
            moveTotTime = moveCurTime = -1L;
            rotatTotTime = rotatCurTime = -1L;
            return;
        } else
        {
            dstPos = new Point3d();
            srcPos.set(actor.pos.getAbsPoint());
            dstPos.set(srcPos);
            rotatingInPlace = true;
            angles.setDeg(actor.pos.getAbsOrient().getYaw());
            angles.setDstDeg(angles.getSrcDeg() + f);
            float f2 = angles.getAbsDiffDeg() / f1;
            rotatTotTime = SecsToTicks(f2);
            rotatCurTime = rotatTotTime <= 0L ? -1L : rotatTotTime;
            movingForward = true;
            moveTotTime = -1L;
            moveCurTime = -1L;
            return;
        }
    }

    public Point3d srcPos;
    public Point3d dstPos;
    public AnglesFork angles;
    public long moveTotTime;
    public long moveCurTime;
    public long rotatTotTime;
    public long rotatCurTime;
    public Vector3f normal;
    public boolean rotatingInPlace;
    public boolean movingForward;
}

The entirety of UnitMove.class:
Code: [Select]
package com.maddox.il2.ai.ground;

import com.maddox.JGP.Point3d;
import com.maddox.JGP.Vector3f;

public class UnitMove
{

    UnitMove(float f, Vector3f vector3f)
    {
        pos = null;
        totalTime = f;
        normal = new Vector3f(vector3f);
        dontrun = false;
    }

    public UnitMove(float f, Point3d point3d, float f1, Vector3f vector3f, float f2)
    {
        pos = new Point3d(point3d);
        pos.z += f;
        totalTime = f1;
        normal = new Vector3f(vector3f);
        if(f2 > 0.0F)
        {
            dontrun = true;
            walkSpeed = f2;
        } else
        {
            dontrun = false;
        }
    }

    public boolean IsLandAligned()
    {
        return normal.z < 0.0F;
    }

    public Point3d pos;
    public float totalTime;
    public Vector3f normal;
    public boolean dontrun;
    public float walkSpeed;
}
Logged
Great minds discuss ideas. Average minds discuss events. Small minds discuss people. - Hyman Rickover (but probably predating his use.)

Whiskey_Sierra_972

  • Modder
  • member
  • Offline Offline
  • Posts: 6772
  • In memory of my beloved hero: Saburo SAKAI!
Re: vehicle/convoy speed parameter
« Reply #4 on: March 30, 2025, 11:06:55 AM »

Speed max is the fastest the vehicle could drive
Speed average is the normal speed , to cover a delay it run faster up to the max speed that 'll be never exceed....

In my reorganization of BAT files I have unifed the vehicles with the mean speed of the german handbook....

https://www.sas1946.com/main/index.php/topic,73400.msg800014.html#msg800014

https://www.sas1946.com/main/index.php/topic,73400.msg802039.html#msg802039

Convoys 'll travel at their slowest component average speed....historically it should be happen this way....

To overcome overrun you could set a mean average speed for all or build the mission with timing precedence over path...
Logged

cbradbury

  • member
  • Offline Offline
  • Posts: 1077
Re: vehicle/convoy speed parameter
« Reply #5 on: March 30, 2025, 03:00:52 PM »

You are right, Walter, but I can't decide whether I want a universal average speed or not. Do I always want my KV1s travelling at the same speed as my T-34s? I need to have a think about it.

Probably a global average speed is sensible - after all - am I really going to be able to spot  a few km/h difference when flying at 300knots @ 2000 feet?

Not too bad for me to adjust average speed to an individual mission, though, as I have my heavily amended chief.ini with my new convoys as a jgsme-enabled file in BATMODS so that the default BAT file is unaffected. It would be a simple change to add technics.ini into that folder as well and change it per mission as required to get the convoys to run right.

One thing I do know - my smaller convoys with increased spacing (8-10 vehicles, 20m Best Space), run a lot better than the default. I just consider them modular - if I need 20 vehicles I just use 2 convoys - leads to far less holdups. A couple of examples (click to zoom):

Soviet Valentines (20m spacing)



German 88s (30m spacing)



Yours,

Clive

Logged

Whiskey_Sierra_972

  • Modder
  • member
  • Offline Offline
  • Posts: 6772
  • In memory of my beloved hero: Saburo SAKAI!
Re: vehicle/convoy speed parameter
« Reply #6 on: March 31, 2025, 10:30:46 AM »

I have noticed that in combat the tanks assume a wedge or diamond formation where sometimes the central rear tank of the latter hit and kill the one in front of him....

So in combat formations could be a better option to split units in 3 tanks formations or even plattons of 2 + 2 tanks....
Logged

cbradbury

  • member
  • Offline Offline
  • Posts: 1077
Re: vehicle/convoy speed parameter
« Reply #7 on: March 31, 2025, 01:56:14 PM »

I have noticed that in combat the tanks assume a wedge or diamond formation where sometimes the central rear tank of the latter hit and kill the one in front of him....

So in combat formations could be a better option to split units in 3 tanks formations or even platoons of 2 + 2 tanks....

The latter is exactly what I do for combat, Walter. Short 'columns' of two tanks side by side...
Logged
Pages: [1]   Go Up
 

Page created in 0.033 seconds with 20 queries.