Faust wrote:The combo that I thought you were first talking about Kaivan was the common explosion, ebolt, hally combo used here on UOSA. This combo involves prepping explosion, hally, target, ebolt, target, hally after initially hitting with a decent hally blow from the beginning. After this prolonged discussion went on for ages, it appeared that you shifted that combo to the explosion, ebolt(target), and than hally. So to be honest it's been unclear to me several times what combo you were talking about to begin with here. The fact of the matter is if we are talking about the two different variations of the explosion(target), ebolt(prep or target), hally combo there is still a significant amount of time left over for the hally delay no matter what the situation is with it being prepped or targetted. This would not matter if you equipped casted during the ebolt spell or not(would be stupid to equip cast if you could weapon cycle). There is no disputing this known fact despite any misinterpretation between what combo is being discussed.
If you believe that this is due to some sort of mis-communication, then we'll leave it at that. However, I will note that I have not claimed (at least intentionally) any one specific combo. I have only attempted to address the combos that have been presented, which has commonly been the explosion/energy bolt/hally combo. If we're talking about the explosion/prep ebolt/hally/release ebolt combo, then it is
much more plausible that such a combo could be pulled off. In fact, the prep time doesn't even necessarily have to be some extremely small period of time for that to work with relatively good results. All it has to do is be effective within 3 seconds of equipping, which leaves multiple options for creating a system that allows for a 'prep time'.
As for the code:
Aside from the fact that your code does change the way that the system works drastically by moving the actual swing to the beginning, the code is specifically tailor made for hally cycling. In fact, the
only possible 'exploit' of this system is disarming a weapon and then re-arming (not weapon switching or any of the fantastic ideas), which brings us right back to the mini-patch, that specifically states that it was fixed.
An additional problem is that if the code actually operated this way on OSI servers, then OSI couldn't actually fix this code as they said they did in they did in the mini-patch. This is because of the fact that two things are dependent on your PrepTime, the transition to state 1, and ResetSwingState(0) for archery movement. Thus, if
any number other than 4 is used, this system breaks archery by making the delay for movement extremely long. As a result, this system 'unfixable' via modification to the PrepTime. If, however, they decided to use 4 states (this theory is equally valid due to the scope of what it changes), this wouldn't be an issue as OSI could have scaled the point of the swing to any position in the swing, and it wouldn't hinder archery.
Finally, in a somewhat ironic twist considering the previous bit about archery, this system makes archery completely broken another way. Because of the fact that you have modified ResetSwingState to pass in a 0 when moving with archery, it is possible to wait until just after your shot should hit or miss and then move a single tile, resetting your swing counter and state to 0. This produces 2 second archery shots under all conditions and is certainly not a pragmatic result.
One other thing, your code suffers from a problem that all code under this type of system suffers: The code does not produce true insta-hit; it still has a 0.25 second delay between the animation and the damage because it must pass through state 1 and state 2, which takes 1 tick to do so. This requires the same fix under all systems which is to add new lines of code that forcefully set all of the right variables to complete a swing, and set all of the appropriate variables to 0 on the mobile.
Further, here is a much simpler combat system, using the same combat syntax:
Code: Select all
public virtual int AdvanceSwingState()
{
int OldState, NewState;
IWeapon weapon = this.Weapon;
int swingDelay = weapon.GetDelay(this);
int swingCounter = this.SwingCounter;
int AnimationState = (weapon != null) && weapon.IsRanged() ? 4 : 0; // ranged or melee
AnimationState = swingDelay <= AnimationState ? 0 : swingDelay - AnimationState;
int PrepState = AnimationState <= 4 ? 0 : AnimationState - 4;
OldState = SwingState;
NewState = OldState;
switch (OldState)
{
case 0: if (swingCounter >= PrepState)
{
swingCounter = PrepState;
NewState = 1;
}
break;
case 1: if (swingCounter >= AnimationState)
{
swingCounter = AnimationState;
NewState = 2;
}
break;
default: if (swingCounter > swingDelay)
{
swingCounter = 0;
NewState = 3;
}
break;
}
SetSwingCounter(swingCounter);
SetSwingState(NewState == 3 ? 0 : NewState);
return NewState;
}
public virtual void ResetSwingCounter( Item item ) // Equip/arm delay, only fired on equip packet
{
if (item is IWeapon || item is IArmor || item is IClothing)
{
Some code here to set the swing counter to a variable, can be statically set, or dynamically set.
Or a prep time can be set here while the swing counter is left alone. The options are limitless and flexible.
}
}
public virtual void ResetSwingState(int TargetState) // Routine is only used as the movement restriction for ranged weapons.
{
if (TargetState == 0)
{
SetSwingCounter(0);
SetSwingState(TargetState);
}
else
{
SetSwingState(TargetState - 1);
AdvanceSwingState();
}
}
public virtual void RangeWeaponMovement() // Routine is called during movement.
{
if ( weapon != null && weapon.IsRanged() )
ResetSwingState(1);
}
public virtual void CombatHeartBeat()
{
if (SwingCounter < 1000)
SwingCounter++;
if (Alive && AccessLevel != AccessLevel.Counselor)
{
if ( Combatant != null )
{
int OldState = SwingState;
int NewState = AdvanceSwingState();
if (OldState != NewState && NewState >= 2)
{
IWeapon weapon = this.Weapon;
if (InRange(Combatant, weapon.MaxRange))
{
if (NewState == 2) // Start Animation/Swing
{
weapon.OnStartSwing(this, Combatant);
if(!weapon.IsRanged)
{
SetSwingState(0);
SetSwingCounter(0);
weapon.OnFinishSwing(this, Combatant);
}
}
else // Start Damage/End Swing
{
weapon.OnFinishSwing(this, Combatant);
}
}
}
}
}
}
Finally, here is a mock up system that uses 4 states instead of 3:
Code: Select all
public virtual int AdvanceSwingState()
{
int OldState, NewState;
IWeapon weapon = this.Weapon;
int swingDelay = weapon.GetDelay(this);
int swingCounter = this.SwingCounter;
int swingEnd = swingDelay <= n ? 0 : swingDelay - n;
int swingAnimate = (weapon != null) && weapon.IsRanged() ? 4 : 0; // ranged or melee
swingAnimate = swingEnd - SwingAnimate <= 0 ? 0 : swingEnd - swingAnimate;
int archeryHold = swingAnimate <= 4 ? 0 : swingAnimate - 4;
OldState = SwingState;
NewState = OldState;
switch (OldState)
{
case 0: if (swingCounter >= archeryHold)
{
swingCounter = archeryHold;
NewState = 1;
}
break;
case 1: if (swingCounter >= animateSwing)
{
swingCounter = animateSwing;
NewState = 2;
}
break;
case 3: if (swingCounter >= swingEnd)
{
swingCounter = swingEnd;
NewState = 3;
}
break;
defaut: if (swingCounter > swingDelay)
{
swingCounter = 0;
NewState = 4;
}
}
SetSwingCounter(swingCounter);
SetSwingState(NewState == 4 ? 0 : NewState);
return NewState;
}
public virtual void ResetSwingCounter( Item item ) // Equip/arm delay, only fired on equip packet
{
if (item is IWeapon || item is IArmor || item is IClothing)
{
Some code here to set the swing counter to a variable, can be statically set, or dynamically set.
Or a prep time can be set here while the swing counter is left alone. The options are limitless and flexible.
}
}
public virtual void ResetSwingState(int TargetState) // Routine is only used as the movement restriction for ranged weapons.
{
if (TargetState == 0)
{
SetSwingCounter(0);
SetSwingState(TargetState);
}
else
{
SetSwingState(TargetState - 1);
AdvanceSwingState();
}
}
public virtual void RangeWeaponMovement() // Routine is called during movement.
{
if ( weapon != null && weapon.IsRanged() )
ResetSwingState(1);
}
public virtual void CombatHeartBeat()
{
if (SwingCounter < 1000)
SwingCounter++;
if (Alive && AccessLevel != AccessLevel.Counselor)
{
if ( Combatant != null )
{
int OldState = SwingState;
int NewState = AdvanceSwingState();
if (OldState != NewState && NewState >=2 && NewState <= 3)
{
IWeapon weapon = this.Weapon;
if (InRange(Combatant, weapon.MaxRange))
{
if (NewState == 2) // Start Animation/Swing
{
weapon.OnStartSwing(this, Combatant);
if(!weapon.IsRanged)
{
SetSwingState(3);
weapon.OnFinishSwing(this, Combatant);
}
}
else // Start Damage/End Swing
{
weapon.OnFinishSwing(this, Combatant);
}
}
}
}
}
}
This system has the advantage of preserving more of the functionality and isolating all of the relevant changes one place: the advanceSwingState functon. Even ResetSwingState and ResetSwingCounter remain untouched. Also, this system is highly adaptable, and it only requires a single number to be changed, the n value in the calculation of swingEnd to change the position from the end of the swing that you are at with . As a final bonus, this system does not suffer from the problems surrounding using ResetSwingState(0) for archery.