noxworld-dev / opennox

OpenNox main repository.
GNU General Public License v3.0
451 stars 25 forks source link

Scripts should be able to set immunities for NPCs/monsters #597

Open Ephreaym opened 1 year ago

Ephreaym commented 1 year ago

I need a function to make it so that Warriors can't be stunned but get slowed instead. However, when stunned through a trap or ghost the stun should take effect.

It seems this feature is shared by large monsters. Maybe if I can set the subclass of the war bot to large_monster it'll get this feature? Not sure how else to resolve this issue. Since I don't know how to differentiate between regular stun and trap/ghost stun.

Or am I missing something?

Ephreaym commented 1 year ago

I'm using this at the moment to make them immune to stun.

if war.unit.HasEnchant(enchant.HELD) { ns.CastSpell(spell.SLOW, war.unit, war.unit) war.unit.EnchantOff(enchant.HELD) }

But it's a problem for the Ghosts attack and stun from a trap.

Ephreaym commented 1 year ago

Ah, and of course when the warrior bots miss their charge they need to be stunned.

dennwc commented 1 year ago

Three direction where we could take this:

  1. Set some flag on the object so that it acts like a large monster.
  2. Add a special "before enchant" event that will have enchant reason passed to it.
  3. Add some new generic immune system (enchant + source/reason).

The first one would be the easiest, but I don't think I want to allow that before we figure out how it's used in the engine. Subclass is tightly coupled with the data structures and setting the wrong one will likely make it crash at some point. So let's avoid that as long as we can.

Second option is the most flexible - scripts decide what happens on each enchant. However this puts the script functions inside the main loop of the engine, which may cause performance issues long-term. So the third option is an alternative that is more performant.

Third option may work by setting "immune" flags for the object. For example, something like this:

// Enables immune for all sources
obj.SetEnchantImmune(enchant.HELD, source.Trap | source.Spell | source.Ability | source.Weapon)
// Only immune to weapon effect
obj.SetEnchantImmune(enchant.HELD, source.Weapon)
// Disable immune.
obj.SetEnchantImmune(enchant.HELD, 0)

What is cool about this is that we could use similar system for immune to spells/damage types.

Would that be flexible enough? Any other ideas of how it might look like?

Ephreaym commented 1 year ago

If we can differentiate between the source that'd work!

KILAH4716 commented 1 year ago

This sounds like a silly question, but I'll ask it anyway: What in the game's codes depicts a Warrior player apart from a Conjurer or Wizard player? Shouldn't we be looking there, and find a way to apply the Warrior player's codes to NPCs?

dennwc commented 1 year ago

@KILAH4716 That's the right call, but the engine does it really in ad-hoc manner, like "if the object is a player and is a warrior ...". I don't really want to find all spots which does this check and add another one like "if the object is a monster and is an NPC and it's marked as warrior by the script" - this is a bit too much.

For us it's easier to make one generic system where any object can get these kind of immune protections. So engine would only need to check: "if object (whatever it is) has immune". This gives much more freedom for scripts and future mods.

Ephreaym commented 11 months ago

Fixed with coding through adding booleans and conditions to check for bomber collision.

if war.unit.HasEnchant(enchant.HELD) && !war.abilities.BerserkerStunActive && !war.abilities.BomberStunActive {
            ns.CastSpell(spell.SLOW, war.unit, war.unit)
            war.unit.EnchantOff(enchant.HELD)
        }
dennwc commented 11 months ago

I'd still like to keep it open to track the immunity system implementation. Still think it's a good idea to have something like this. Will rename the ticket.

dennwc commented 9 months ago

After speaking with had_zeng, we think that it would be nice to also add things like damage resist that is controlled by scripts.

For example:

// Set 20% damage resist from fire (from weapons and traps)
obj.SetDamageResist(damage.FIRE, 0.2, source.Trap | source.Weapon)