• Welcome to Freedom Reborn Archive.
 

Hypnosis State Swap - Huh?!

Started by TaskMasterX, October 04, 2007, 06:37:19 AM

Previous topic - Next topic

TaskMasterX

Yes, I know some of you are saying "Hypnosis Swap?! Has TMX gone over the deep-end with this scripting?!" I assure you that I'm okay and I'm not seeing codes everywhere I go. :wacko:

I came up with this because I was disappointed at the way Enemy vs. Enemy Hypnosis was always resisted. Also, Hypnotized characters don't behave correctly when there's more than two teams in the Rumble Room. They don't get put on the correct "team", but instead get they're reaction swapped, which is fine for only two teams battling each other, but when you have more than two, then the hypnotized character will only think his original team is the enemy and not the other teams that are enemies to the character that hypnotized him. Also, if there are only one member on each team, then all enemies become allies and the character just stands there until the hypnosis wears off.

How It Works:
You can swap your existing Hypnosis Power for this Hypnosis Swap. Of course, it'll need to be a ranged or melee power, not a direct or area Power because this is a swap. But it can be Hypnosis to Hypnosis. Once you have your Hypnosis Power set for the character, close the game and load up FFXCC and choose hypnosis to hypnosis, just like you do any other swap. When the power is used, the code checks if the character using the power or if the target is Player Controlled, if either one is, then it lets the Hypnosis Power work like normal. If it sees the Power is being used by the AI on another AI controlled target, it'll check the resistance vs. mental attacks of the target. If the target is Highly Resistant there's a 10% chance Hypnosis will be placed on the target, Resistant=30%, No Resistance=60%, Vulnerable=75%, Defenseless=90%. Unforunately, I have no way of detecting the Magnitude of the Hypnosis Power so the Magnitude doesn't get calculated in, only the resistance of the target to mental attacks. If the target has Passive or Active Defenses vs. Mental, it'll be blocked as normal. Now, if you want, you can make the Power you're swapping, say, Rage, and then swap Rage for Hypnosis, and then the Magnitude of the Rage along with the target's resistances will calculate into whether or the Hypnosis succeeds. Only if you choose Hypnosis to Hypnosis will the Magnitude not be a factor.
Also, the Hypnotized character gets put on the team of the character that Hypnotized them, instead of having their reaction swapped. This will probably be most useful to those of us that like to play alot of Watch Mode or Team Mode with alot of Teams battling each other.

Here's the code to put in the ffx.py:
######################## Hypnosis Swap ##############################################

def empathytohypnosis(event):
    char=event.object
    Trigger_Explosion(char,'ffx_blankarea')
    RegTimer('llhypnosisCheck',1,stateintensity[int(event.user)],char,event.string)

def displacetohypnosis(event):
    char=event.object
    Trigger_Explosion(char,'ffx_blankarea')
    RegTimer('llhypnosisCheck',1,stateintensity[int(event.user)],char,event.string)

def cloaktohypnosis(event):
    char=event.object
    Trigger_Explosion(char,'ffx_blankarea')
    RegTimer('llhypnosisCheck',1,stateintensity[int(event.user)],char,event.string)

def ragetohypnosis(event):
    hypnosisP(event,PCSTATE_ENRAGED,COST_RAGE)
def exiletohypnosis(event):
    hypnosisP(event,PCSTATE_EXILE,COST_EXILE)
def blanktohypnosis(event):
    hypnosisP(event,PCSTATE_BLANK,COST_BLANK)
def panictohypnosis(event):
    hypnosisP(event,PCSTATE_PANICKED,COST_PANIC)
def frozentohypnosis(event):
    hypnosisP(event,PCSTATE_FROZEN,COST_FROZEN)
def stasistohypnosis(event):
    hypnosisP(event,PCSTATE_STATIC,COST_STASIS)
def hypnosistohypnosis(event):
    print 'hypnosistohypnosis'
    char=event.object
    source=event.string
    if FFX_ObjectGetAttr(source,'hypnosistohypnosis')==1:
        FFX_ObjectSetAttr(source,'hypnosistohypnosis',0)
        return
    if Object_GetClass(char) & OC_CONTROLLABLE:
        return
    if Object_GetClass(source) & OC_CONTROLLABLE:
        return
    else:
        FFX_ObjectSetAttr(source,'hypnosistohypnosis',1)
        intensity=COST_BLANK
        mentalResist=getMentalResistanceValue(char, passive=0, active=0, attribs=1, states=0, material=1)
        if mentalResist < 1:
            if mentalResist > 0:
                print mentalResist
                return
        x=randint(1,100)
        if mentalResist == 1:
            print mentalResist
            print x
            if x < 60:
                makehypnosis(char, source, intensity)
                return
        if mentalResist == 0.5:
            print mentalResist
            print x
            if x < 30:
                makehypnosis(char, source, intensity)
                return
        if mentalResist == 0.25:
            print mentalResist
            print x
            if x < 10:
                makehypnosis(char, source, intensity)
                return
        if mentalResist == 2:
            print mentalResist
            print x
            if x < 75:
                makehypnosis(char, source, intensity)
                return
        if mentalResist == 4:
            print mentalResist
            print x
            if x < 90:
                makehypnosis(char, source, intensity)
                return

def hextohypnosis(event):
    hypnosisS(event,SCSTATE_HEXED,COST_HEX)
def blindtohypnosis(event):
    hypnosisS(event,SCSTATE_BLIND,COST_BLIND)
def powernulltohypnosis(event):
    hypnosisS(event,SCSTATE_POWER_NULLIFICATION,COST_NULL)
def weakentohypnosis(event):
    hypnosisS(event,SCSTATE_WEAKENED,COST_WEAKEN)
def speeddecreasetohypnosis(event):
    hypnosisS(event,SCSTATE_SPEED_DECREASED,COST_DECREASE)

def densitytohypnosis(event):
    char=event.object
    source=event.string
    if densityaltered(char):
        Object_SetGenericState(char,OBJSTATE_DENSITY_CHANGE,0,REMOVE_STATE)
        FFX_UpdateStates(char)
        makehypnosis(char,source,COST_DENSITY)

def hypnosisS(event,state,intensity):
    char=event.object
    source=event.string
    if Object_GetSecondaryState(char)==state:
        Object_SetPrimaryState(char,state,0,REMOVE_STATE)
        FFX_UpdateStates(char)
        makehypnosis(char,source,intensity)

def hypnosisP(event,state,intensity):
    char=event.object
    source=event.string
    if Object_GetPrimaryState(char)==state:
        Object_SetPrimaryState(char,state,0,REMOVE_STATE)
        FFX_UpdateStates(char)
        makehypnosis(char,source,intensity)

def limeyluretohypnosis(event):
    focus=getLure()
    if focus=='':
        return
    Trigger_Explosion(focus,'ffx_blankarea')
    RegTimer('llhypnosisCheck',1,stateintensity[int(event.user)],focus,event.string)
    RegTimer('destroyTemp',1.1,0,focus)

def llhypnosisCheck(event):
    char=event.object
    hero=event.string
    for obj in Mission_GetDynamicObjects():
        if obj==hero:
            Object_SetPrimaryState(obj,PCSTATE_BLANK,0,REMOVE_STATE)
        if (obj!=char) & (obj!=hero):
            if Object_GetClass(obj)&FFX_CHARACTER:
                if distanceSq(obj,char)<40000:
                    if Object_GetPrimaryStates(obj)&PCSTATE_BLANK:
                        Object_SetPrimaryState(obj,PCSTATE_BLANK,0,REMOVE_STATE)
                        intensity=COST_BLANK
                        makehypnosis(obj, hero, intensity)

def makehypnosis(target, user, intensity):
    duration=intensity*10
    print 'makehypnosis'
    print duration
    Object_SetPrimaryState(target,PCSTATE_HYPNOTISED,duration,0)
    RegCharState(target,PCSTATE_NORMAL,'OnHypnosisEnd',0,0,user)
    oldTeam=m25ai.GetTeam(target)
    FFX_ObjectSetAttr(target,'targetOldTeam',oldTeam)
    m25ai.SetTeam(target,m25ai.GetTeam(user))
    m25ai.ClearAIStandardOnly(target)
    m25ai.AIEnableWatch(target)
    m25ai.AIEnable(target)
    m25ai.AIEnableCustom(target)
    m25ai.SetBrawler(target)
    m25event.SetTimer(1.0, 'AIRecoverBroken', target, user=m25ai.GetAITimeStamp(target))

def OnHypnosisEnd(event):
    print 'OnHypnosisEnd'
    target = event.object
    char = event.string
    team = FFX_ObjectGetAttr(target,'targetOldTeam')
    m25ai.SetTeam(target,team)
    m25ai.AIRestart(target,100000)


Also, you need to place this line:
["hypnosis"],
into the FFX_TO_TYPES=[] list in the ffxdefault.py file.

Epimethee

Nice work, J. You're certainly keeping busy. :)

A couple of notes/nitpicking:

I didn't look at your code in depth, but a couple of things in hypnosistohypnosis() caught my eye:

def hypnosistohypnosis(event):
    print 'hypnosistohypnosis'
    char=event.object
    source=event.string
    if FFX_ObjectGetAttr(source,'hypnosistohypnosis')==1:
        FFX_ObjectSetAttr(source,'hypnosistohypnosis',0)
        return
    if Object_GetClass(char) & OC_CONTROLLABLE:
        return
    if Object_GetClass(source) & OC_CONTROLLABLE:
        return
##    else:   the systematic return make this unnecessary
    FFX_ObjectSetAttr(source,'hypnosistohypnosis',1)
    intensity=COST_BLANK #? Why use the Mental Blank constant if the source is hypnosis? Unless there is a relationship to blank, it would be clearer to create a new constant or to write 3.333 directly
    mentalResist=getMentalResistanceValue(char, passive=0, active=0, attribs=1, states=0, material=1)
##    #these two if lines (which could also be written as "if 0 < mentalResist < 1") mean that some of the cases you had below would never be executed, wouldn't they?
##    if mentalResist < 1:
##        if mentalResist > 0:
##            print mentalResist
##            return
    x=randint(1,100)
   #while your use of successive "return" followed by "if" works, you could either remove the return and use "elif" instead of "if"... or avoid evil redundant code altogether by using a dictionary:
    mentals = {0.25: 10, 0.5: 30, 1: 60, 2: 75, 4:90}
    if x < mentals[mentalResist]:
        makehypnosis(char, source, intensity)


More generally, since this is done to solve a global issue with the game rather than adding new choices for character customization, I'm not sure that using the power swap system is the best way to tackle it. After all, the key advantages of the FFX carrier setup is that we can know that the attack was successful (which is the whole point of only working with character states), it can be customized on a character by character basis and, for built-ins, on a power-by-power basis (excluding areas, explosives and directs). Here, the advantages might become inconveniences, IMO. The player would either want the system for every case, character and every power, or not at all.

Maybe an alternative approach could involve using ffx.RegCharState(char, PCSTATE_HYPNOTISED, 'functionName', persistent=1) (or mlogreader.regMLOGDamage(), if you really, really want to try to detect hypnosis attempts on allies which are in fact part of a different M25 AI team). This global change could be made optional for the player by setting using a user-set toggle variable or making it as a plugin.

Xenolith

This is another needed fix, thanks for tackling it head on.

TaskMasterX

Quote from: Epimethee on October 04, 2007, 08:24:33 PMMore generally, since this is done to solve a global issue with the game rather than adding new choices for character customization, I'm not sure that using the power swap system is the best way to tackle it. After all, the key advantages of the FFX carrier setup is that we can know that the attack was successful (which is the whole point of only working with character states), it can be customized on a character by character basis and, for built-ins, on a power-by-power basis (excluding areas, explosives and directs). Here, the advantages might become inconveniences, IMO. The player would either want the system for every case, character and every power, or not at all.

Maybe an alternative approach could involve using ffx.RegCharState(char, PCSTATE_HYPNOTISED, 'functionName', persistent=1) (or mlogreader.regMLOGDamage(), if you really, really want to try to detect hypnosis attempts on allies which are in fact part of a different M25 AI team). This global change could be made optional for the player by setting using a user-set toggle variable or making it as a plugin.

I was thinking at first to add this in like the Strength vs. Stasis or Frozen damage Options and have it check for Hypnosis on characters in ffx.updateFFX() like the others. The only problem was that I had no way of getting the name of the character using the hypnosis to then get their team and place the target on that team. Also, I thought that ffx.RegCharState(char, PCSTATE_HYPNOTISED, 'functionName', persistent=1) or logreader.regMLOGDamage() only executes 'functionName' if the character is in the hypnosis state and since enemy vs. enemy hypnosis is always resisted, it would never detect it and run the 'functionName'. So, if you're saying that will pick up a hypnosis attempt rather than the actual hypnosis state, then all I need to know is how to get the name of the source.

Epimethee

Oops, wasn't thinking too clearly when I posted about RegCharState(); regMLOGDamage() should work, though (even if it has never been used seriously, so the odd bug may still occur). It should give you the attacker for any attack type, even if the attack fail (just set success=0; cf. FFX manual). For example, when I first released the module, my example screenshots showed Microwave using his genetic damage generic state direct attack on Alchemiss swapped to Toadify on genetic damage failure.

TaskMasterX

Okay, I added this line:
regMLOGDamage('OnEnemyHypnoEnemy', object=char, target='', event_type='PCSTATE_HYPNOTISED', success=-1, persistent=1, float=0.0, user=0.0, string='')
to the 2nd to the last line in ffx.initialiseChar(). I'm not sure if this is the best place to put it, but anyway, the 'OnEnemyHypnoEnemy' function was never called. I set success, to -1 because only if the hypnosis attempt was unsuccessful should it be called. I tried setting it to 0 but still, no go. I set a print line as the first line in OnEnemyHypnoEnemy() and never saw it in the script.log so that's how I know it was never called. Any ideas on what I'm doing wrong? Thanks!

Oh Yeah, BTW, does the regMLOGDamage() detect the damage from ANY Power, including Direct and Area Powers?

Epimethee

mlogreader isn't imported natively (there's no from "mlogreader import *"; these should usually be avoided), so if you haven't already done, you need to prefix the function: mlogreader.regMLOGDamage().

Direct and area are supported.

TaskMasterX

I added the prefix and still, no go. I'm not getting any errors in the script.log, either. Would this detect Hypnosis by the Alteration Power and the Beautiful Attribute? Not that that's how I'm testing this. I was just wondering.

Epimethee

When I rewrote the code to use Stumpy's pseudo-events model, I apparently borked things and didn't test enough. :( It seems like there's a bug in the "hypnosis" name conversion to/from the name used in MLOG. I won't be able to attack it tonight (and maybe not tomorrow either), but I'll try to tackle it ASAP. Sorry about that.

Beautiful: it is detected as hypnotism.

Alteration, on the other hand, is detected as alteration, but there's an interesting FF bug! Even if the target is on you team, the hypnosis makes him switch allegiance... The alteration is the power which is calculated against and then an effect is selected randomly, apparently; IG simply forgot to add the same check for side as in the real hypnotism. So there's no issue to fix, since the bug prevents the issue... err.

TaskMasterX

Thanks, Ep. I did try to play around with it and tried PCSTATE_STASIS and that didn't work either. I tried using the mlogreader.regMLOGDamage() line in the Bouncy Attribute and it did call that function. It seemed to only work if event_type=''. Hopefully this is helpful.

Also, I noticed while testing this that characters spawned by the Private Army attribute of an Enemy CAN be hypnotized by another Enemy. Probably not something valuable to know, but I thought it was interesting.