You may as well move this to the modding forum... since that's what it's become... I have 2 issues for you, both of which I've already solved. Just notifying you of these problems, but they're technically years old, some of it modified in v4.00.
if( disableAutoSheathe )
{
ret = PW_Fists;
}
This is awkward in some situations. In general, issues exist in auto-swapping from fists.
if ( weaponType == PW_Steel || weaponType == PW_Silver )
this.OnEquipMeleeWeapon( weaponType, false );
}
This is also causing issues in swapping from fists, in OnCombatStart, if you hit attack just before combat starts.
if ( weaponType != PW_Fists && weaponType != PW_None && weaponType != this.GetCurrentMeleeWeaponType() )
OnEquipMeleeWeapon( weaponType, false );
}
Or this in OnReactToBeingHit.
if ( targetToDrawAgainst.IsHuman() && ( !hasPhysicalWeapon || ( targetToDrawAgainst.GetAttitude( thePlayer ) != AIA_Hostile ) ) )
{
ret = PW_Fists;
}
There's also this, which is causing Geralt to switch to fists when an archer swaps weapons. Although, technically it is checking their inventory, it is somehow failing this condition. Maybe the melee weapon is not yet equipped and the bow (in inventory) doesn't count as a 'physical' weapon? When the bow is equpped it works differently and only checks if they're hostile and use vitality. Not sure, haven't looked at it in detail.
I've already fixed most of it btw. On top of what's in the previous mod thread, I also added random bolt and petard selection when ammo reaches zero, along the lines of random food and alcohol consumption. Improved menu screen responsiveness, but still need to remove the background during transitions. Need to fix the crossbow boat bug, horses work fine. Added a 'god mode' for player and all characters, in case you need to test weapon onhit effects, etc...
edit: Now added Auto Apply Oils, but I'm getting carried away here, so I need to test all this stuff and add a few extra checks, clean up the code, etc... The two things I need a radial menu for are potions and decoctions, but that's not going to happen any time soon... edit: Yea, it all works, no prob... needs house keeping, I'll fix the boat crossbow issue later.
One thing I wish actually worked is the witcherLog... it only displays 2 lines for 0.5 secs each. Sometimes it fails to display anything at all... Cannot alter the number of lines, or display duration, as script would suggest. Config says 3 lines, but 1 is invisible, var doesn't work, Flash thing... Also wish it could display small icons for items such as as alch/crafting/loot/food.
Managed to squash a few other bugs, here's one, simple enough. The item is not yet selected.
else if( ( slot == EES_Petard1 || slot == EES_Petard2 ) && inv.IsItemBomb( item ) )
/* else if( ( slot == EES_Petard1 || slot == EES_Petard2 ) && inv.IsItemBomb( GetSelectedItemId() ) ) */
{
SelectQuickslotItem( slot );
}
Well, I just tested fisty-cuffs and that still works. Not sure what else is left to test. Will wait till next patch drops before uploading though.
event OnEquipBolt( boltItemId : SItemUniqueId )
if( inv.IsIdValid(boltItemId) && inv.GetItemCategory(boltItemId) == 'petard')
if(thePlayer.IsActionAllowed( EIAB_OpenInventory ))
case 'q703_paint_bomb_red':
I have no words to describe this. It's like the Oil Monster Category functions... but why? petards are bolts now?
Currently, the only way to distinguish them and Snowballs from normal petards is the tag NoAdditionalAmmo?
Nope, snowballs have the exact same cat and tags as standard petards, so have to use the item name or ability name?
Nevermind, I'm adding my own tags... It's the same thing with Oils, which are a disaster... 'level' is not defined for food, etc...
You could just add Quest <tags> to paint bombs and snowballs or PetardSpecial <tags> or whatever to classify them all.
Feromone bombs are another strange exception. It's a petard without Petard <tags>. Should also be considered a PetardSpecial.
Instead of extending the data structure to include EMonsterCategory... you have 5 dozen useless functions and properties...
vsRelic_attack_power
vsOgre_attack_power
vsSpecter_resist_reduction
vsNecrophage_resist_reduction
etc... data structure should include a property for array<EMonsterCategory> instead... so much simpler than this...
I just hacked it into Tags for now... <tags> MC_Necrophage... 'good enough'...
And then, you know, you have something similar in the Bestiary... except it's not in the data structure either...
Or add EMonsterCategory to the ability, so you can attach multiple abilties in an array, each with their own attack, resist, other effects, etc... instead of array<EMonsterType> with shared effects for all creatures in the array.
Or is it that... you want 1 ability to be able to affect all monster types simultaneously, with different attack and resist for each? It's possible... but then, you could just attach an array to each ability instead of all these extra properties.
<ability name="NecrophageOil_3">
<MonsterCategory="MC_Necrophage">
<ModifyDamage type="add" min="1.1" max="1.1" />
<ModifyDefense type="add" min="0.2" max="0.2" />
<StaggerEffect is_ability="true" />
<level type="add" min="3" />
</ability>
Similar to what you have for petards, and can actually be extended to petards. For more creature types use NecrophageOil_3A, 3B, etc... or ie: HangedManVenom_Human_3, HangedManVenom_Animal_3... or just Oil_Human_3, Oil_Animal_3, Oil_Necrophage_3...
Although, technically, it ought to be <ability name="Oil_Necrophage"> then <level name="3"> then effects listed under that:
<ability name="Oil_Necrophage">
<MonsterCategory="MC_Necrophage">
<StaggerEffect is_ability="true" />
<level name="1" />
<level name="2" />
<level name="3">
<ModifyDamage type="add" min="1.1" max="1.1" />
<ModifyDefense type="add" min="0.2" max="0.2" />
</level>
</ability>
I suppose it's a bit less memory efficient to store all levels in the ability, so you may want to just keep levels separate.
Furthermore, there is some oversight in application of oils. When the oil effect already exists, it is reapplied, not removed. However, this circumvents GetSelfInteraction for EET_Oil, unless it is a new oil being applied. This simply means you don't have a chance to set EEffectInteraction, and the oil is always 'cumulated' in that sense if it is already applied, and cannot be denied, since AddEffectCustom( buffParams ) is never called. However, if level 2 is applied, and the player has 'S_Alchemy_s06' (whatever that mutation is), then level 3 will be applied on top of the level 2. Not sure if this is cumulative in the sense that damage can stack from both. It's not an issue as is, since you can't have 2 separate levels of oil, except just after crafting one, but is an obstacle in certain cases. I suppose this can easily be fixed by commenting out the 'existingOil' checks. I'll try that.
edit: Well, not so easy, but managed to fix that too. Oils now correctly pass through the effect interactions processor. Required a reformulation of the ApplyOil() function and some work on oil.ws... but it works... Oils are now removed after it is applied, and ReApply(maxCount) now occurs in CumulateWith() as it should. This is why EEffectInteract exists... but lacks an EI_Cancel to prevent cycling the array for old vs new twice...
I need someone to check my code. I removed half the function. ;o
public function ApplyOil( oilId : SItemUniqueId, usedOnItem : SItemUniqueId ) : bool
{
var oilAbilities : array<name>;
var ammo, ammoBonus : float;
var dm : CDefinitionsManagerAccessor;
var buffParams : SCustomEffectParams;
var oilParams : W3OilBuffParams;
var oilName : name;
var min, max : SAbilityAttributeValue;
var i, A6level : int;
var oils : array<W3Effect_Oil>;
var interactResult : EEffectInteract;
if( !CanApplyOilOnItem(oilId, usedOnItem) ) return false;
dm = theGame.GetDefinitionsManager();
inv.GetItemAbilitiesWithTag(oilId, theGame.params.OIL_ABILITY_TAG, oilAbilities);
oilName = inv.GetItemName(oilId);
oils = inv.GetOilsAppliedOnItem(usedOnItem);
if ( CanUseSkill(S_Alchemy_s06) ) CheckForPreviousLevelOilExploit(oilName,oils);
ammo = CalculateAttributeValue(inv.GetItemAttributeValue(oilId, 'ammo'));
if ( CanUseSkill(S_Alchemy_s06) )
{
ammoBonus = CalculateAttributeValue(GetSkillAttributeValue(S_Alchemy_s06, 'ammo_bonus', false, false));
ammo *= 1 + ammoBonus * GetSkillLevel(S_Alchemy_s06);
}
buffParams.effectType = EET_Oil;
buffParams.creator = this;
oilParams = new W3OilBuffParams in this;
oilParams.iconPath = dm.GetItemIconPath(oilName);
oilParams.localizedName = dm.GetItemLocalisationKeyName(oilName);
oilParams.localizedDescription = dm.GetItemLocalisationKeyName(oilName);
oilParams.sword = usedOnItem;
oilParams.maxCount = RoundMath(ammo);
oilParams.currCount = RoundMath(ammo);
oilParams.oilAbilityName = oilAbilities[0];
oilParams.oilItemName = oilName;
buffParams.buffSpecificParams = oilParams;
interactResult = AddEffectCustom(buffParams);
delete oilParams;
if ( interactResult == EI_Cancel || interactResult == EI_Undefined ) return false;
// recalculate applied oils and remove unwanted oil effects
oils = inv.GetActiveOilsAppliedOnItemCount(usedOnItem);
if ( !CanUseSkill(S_Alchemy_s06) ) A6level = 1;
else A6level = GetSkillLevel(S_Alchemy_s06);
if ( !GetWitcherPlayer() ) inv.RemoveAllOilsFromItem(usedOnItem);
else if ( oils > A6level )
{
for ( i=A6level; i<oils; i+=1 )
{
inv.RemoveOldestOilFromItem(usedOnItem);
}
}
LogOils("Added oil <<" + oilName + ">> to <<" + inv.GetItemName(usedOnItem) + ">>");
SetFailedFundamentalsFirstAchievementCondition(true);
theGame.GetGlobalEventsManager().OnScriptedEvent(SEC_OnOilApplied);
if ( !inv.IsItemHeld(usedOnItem) ) PauseOilBuffs(inv.IsItemSteelSwordUsableByPlayer(usedOnItem));
return true;
}
CheckForPreviousLevelOilExploit(oilName,oils) - do we even need this now that effect processing works for oil levels? Need to look into that... Apparently not... can remove that entire function and CheckIfPreviousOilApplied() as well...
If you must know what EI_Cancel does, it decouples the logic for which effect was 'denied' previously... EI_Deny is now 'remove old effect, new is better' and EI_Cancel is 'cancel new effect, old is better'... part of something I fixed earlier, and allows you to correctly determine result via EEffectInteract. Without it, I suppose this would not be possible to begin with. Had to rework a bunch of effect logic.
---- Update: Well, that took longer than expected, may have fallen on my own sword after all, don't have time for this, but I fixed it, mostly... There are a bunch of issues with the way in which oils are handled. effect init params do not pass custom params. Oil effects are not separate but simply classifed under one W3Effect_Oil, which is problematic, especially combined with the fact they are applied to swords, which can be swapped to inventory, and there are mostly checks for slotted weapons only. Furthermore, I had to circumvent update time methods for Oil effects, and maybe set them inactive when ammo reaches zero, because it was removing the effects immediately after they were being applied.
Actually, I had to move all the S_Alchemy_s06 (Fixative Ability) code to ApplyEffect(), because of overridenEffectsIdxs... but it works better this way anyways. Much simpler, removing the oldest ones, so it doesn't mess up the QueueTimer index. More efficient than RemoveOldestOilFromItem():
oilEffect = (W3Effect_Oil)effects[overridenEffectsIdxs[ i ] ];
if ( oilEffect.GetQueueTimer() >= A6level )
{
RemoveEffectOnIndex( overridenEffectsIdxs[ i ], true );
}
Does this thing even work? LOL... in my tests it was only removing a single effect, because the array is ordered backwards... it adds the array from last to first, but then iterates from last to first, so it gets the lowest item in array, removes it, then fails the rest... haha.
In other words, cumulatIdx was only removing a single item... hehe... I guess that doesn't matter when each item can or 'should' (not reliable) apply only once... which is the case with all items, probably as a result of these bugs... fixed anyways...
edit: ah oh yes, I was just about to say, you can test this by using 'delete effects[overridenEffectsIdxs
[ i ] ];'... ;p
Also, I had to hack in customItemUniqueId parameters for SCustomEffectParams and SEffectInitInfo (to pass the sword ref on init, for use in GetSelfInteraction). Not sure why this isn't by default, upon object initialization. Each item could then have effects, like crossbow bolts which curently lack them, and any inventory or character slot item. Currently I believe it's only on swords, but only in W3OilBuffParams, passed when the effect is created, not initialized. Obviously, that system would require a bit of work.
I can finally apply oils without ending up with 20 copies of the same thing, or 20 oils. 
Well, I still have to test it a bit more, but Oils now seem to pass through the effects processor fine, and calculate 'fixative' skill level for removal of older oils. Inventory screen works fine as well. Sword swapping works fine, and Oils are set inActive correctly. Swords can be swapped to inventory and retain oils. Have to test stash still, saw there's some code for that, to remove the oils. Effects cumulate properly as well.