• Welcome to Freedom Reborn Archive.
 

FF Active Defense Scripted for FFvTTR

Started by TaskMasterX, October 01, 2007, 02:39:02 PM

Previous topic - Next topic

TaskMasterX

I got several requests to get the original  :ff: active defense into  :ffvstr:. I came up with an attribute that allows you to get pretty close to it. Everything to do with the  :ff: AD is here. Hit Points, Moveable or Not Moveable Option, Regenerating Option, I even added in the ability to use the Remote Option. So think of all the best things of the  :ff: and  :ffvstr: active defense Power rolled into one.
Also, this works like a "true" Active Defense. Meaning it doesn't work like the Energy Shield or Invulnerable Attributes where Health Points are "payed-back" after the damage is already taken and therefore you get situations where the character is KOed even though they had enuff Invulnerable Points to block the damage. What I did was trigger an actual Active Defense Power that is set to Absorb. The code can then determine how much damage is absorbed by detecting how much the character's energy points increase and then deduct it from the "HP" of the Active Defense. When the HP reach 0, the Active Defense disappears. When the Active Defense is on, the character's Max Energy is doubled (like in Energy Tanks), so the code can detect damage even if the character's Energy is "Full" (at 100). Energy Absorbed is instantly deducted, so the character never actually absorbs energy from the attacks and therefore should never get above the maximum 100 Energy Points. (I need to make a note to myself to see how this works with Energy Tanks)

If you set the Regenerate Option to On, then the shield will regenerate 1 HP every second. Does anyone remember if this was the rate of Regeneration for the AD in  :ff:?

I made 4 different grades that represent the 4 levels of HP for AD in :ff: (Low 10, Medium 20, High35, Extreme50). So you need to create 4 attributes in FFEdit: ffactivedefense10, ffactivedefense20, ffactivedefense35 and ffactivedefense50.

Next you copy this code into ffx.py:
#################### FF Active Defense 10-08-2007 ###################################

def initffactivedefense10(char,update=0):
    if isMP():
        return
    FFX_ObjectSetAttr(char,'ffactivedefense',0)
    FFX_ObjectSetAttr(char,'ffactivedefenseMaxHP',10)
    if update==0:
        remote=getByTemplate(char,FFX_FFACTIVEDEFENSE_CUSTOM,7)
        if remote != 'Remote Only':
            addCommandChars('%s_SELF'%getByTemplate(char,FFX_FFACTIVEDEFENSE_CUSTOM,1),char,'OnffActiveDefenseSelf',100,update)
        if remote == 'No Remote':
            return
        else:
            for obj in Mission_GetObjects():
                addCommandChars('%s_REMOTE'%getByTemplate(char,FFX_FFACTIVEDEFENSE_CUSTOM,1),obj,'OnffActiveDefense',30,update,char)

def initffactivedefense20(char,update=0):
    if isMP():
        return
    FFX_ObjectSetAttr(char,'ffactivedefense',0)
    FFX_ObjectSetAttr(char,'ffactivedefenseMaxHP',20)
    if update==0:
        remote=getByTemplate(char,FFX_FFACTIVEDEFENSE_CUSTOM,7)
        if remote != 'Remote Only':
            addCommandChars('%s_SELF'%getByTemplate(char,FFX_FFACTIVEDEFENSE_CUSTOM,1),char,'OnffActiveDefenseSelf',100,update)
        if remote == 'No Remote':
            return
        else:
            for obj in Mission_GetObjects():
                addCommandChars('%s_REMOTE'%getByTemplate(char,FFX_FFACTIVEDEFENSE_CUSTOM,1),obj,'OnffActiveDefense',30,update,char)

def initffactivedefense35(char,update=0):
    if isMP():
        return
    FFX_ObjectSetAttr(char,'ffactivedefense',0)
    FFX_ObjectSetAttr(char,'ffactivedefenseMaxHP',35)
    if update==0:
        remote=getByTemplate(char,FFX_FFACTIVEDEFENSE_CUSTOM,7)
        if remote != 'Remote Only':
            addCommandChars('%s_SELF'%getByTemplate(char,FFX_FFACTIVEDEFENSE_CUSTOM,1),char,'OnffActiveDefenseSelf',100,update)
        if remote == 'No Remote':
            return
        else:
            for obj in Mission_GetObjects():
                addCommandChars('%s_REMOTE'%getByTemplate(char,FFX_FFACTIVEDEFENSE_CUSTOM,1),obj,'OnffActiveDefense',30,update,char)

def initffactivedefense50(char,update=0):
    if isMP():
        return
    FFX_ObjectSetAttr(char,'ffactivedefense',0)
    FFX_ObjectSetAttr(char,'ffactivedefenseMaxHP',50)
    if update==0:
        remote=getByTemplate(char,FFX_FFACTIVEDEFENSE_CUSTOM,7)
        if remote != 'Remote Only':
            addCommandChars('%s_SELF'%getByTemplate(char,FFX_FFACTIVEDEFENSE_CUSTOM,1),char,'OnffActiveDefenseSelf',100,update)
        if remote == 'No Remote':
            return
        else:
            for obj in Mission_GetObjects():
                addCommandChars('%s_REMOTE'%getByTemplate(char,FFX_FFACTIVEDEFENSE_CUSTOM,1),obj,'OnffActiveDefense',30,update,char)

def OnffActiveDefenseSelf(target,char):
    OnffActiveDefense(char,char)

def OnffActiveDefense(target,char):
    print 'OnffActiveDefense'
    print target
    print char
    epCost=getByTemplate(char,FFX_FFACTIVEDEFENSE_CUSTOM,8)
    if chargeEP(char,epCost)==0:
        return
    if not hasAttribute(target,'energytanks') or  not hasAttribute(target,'volatilestorage') or not hasAttribute(target,'energyredirector'):
        base=Object_GetAttr(target,'maxEnergyPoints')
        Object_SetAttr(target,'maxEnergyPoints',2*base)
        FFX_ObjectSetAttr(target,'baseMaxEP',base)
    hp=FFX_ObjectGetAttr(char,'ffactivedefenseMaxHP')
    power=getByTemplate(char,FFX_FFACTIVEDEFENSE_CUSTOM,3)
    duration=getByTemplate(char,FFX_FFACTIVEDEFENSE_CUSTOM,4)
    moveable=getByTemplate(char,FFX_FFACTIVEDEFENSE_CUSTOM,5)
    regenerate=getByTemplate(char,FFX_FFACTIVEDEFENSE_CUSTOM,6)
    FFX_ObjectSetAttr(target,'ivlasteng',Object_GetAttr(target,'energyPoints'))
    ivlasteng=FFX_ObjectGetAttr(target,'ivlasteng')
    if ivlasteng < 0:
        FFX_ObjectSetAttr(target,'ivlasteng',0)
    FFX_ObjectSetAttr(target,'ffactivedefenseHP',hp)
    FFX_ObjectSetAttr(target,'ffactivedefenseMaxHP',hp)
    FFX_ObjectSetAttr(target,'ffADduration',duration)
    FFX_ObjectSetAttr(target,'ffactivedefense',1)
    FFX_Animate(char,getByTemplate(char,FFX_FFACTIVEDEFENSE_CUSTOM,2))
    Trigger_Power(target,char,power,'')
    RegTimer('cancelffADevent',duration,0,target)
    RegTimer('ffADabsorbCheck',0.1,0,target,char)
    if duration > 10:
        RegTimer('ffADupdate',10,0,target,char)
    if moveable == 'Not Moveable':
        pos=Get_ObjectPos(target)
        FFX_ObjectSetAttr(target,'lastY',pos[1])
        FFX_ObjectSetAttr(target,'lastX',pos[0])
        RegTimer('ffADmoveCheck',1,0,target)
    if regenerate == 'Regenerate':
        RegTimer('ffADregenerate',2,0,target)

def ffADabsorbCheck(event):
    char=event.object
    source=event.string
    if FFX_ObjectGetAttr(char,'ffactivedefense')==0:
        return
    if (Object_GetPrimaryState(source)==PCSTATE_EXILE) | (Object_GetPrimaryState(source)==PCSTATE_STUNNED) | (Object_GetPrimaryState(source)==PCSTATE_FROZEN) | (Object_GetPrimaryState(source)==PCSTATE_STATIC) | (Object_GetPrimaryState(source)==PCSTATE_BLANK) | (Object_GetPrimaryState(source)==PCSTATE_ENRAGED) | (Object_GetPrimaryState(source)==PCSTATE_PANICKED) | (Object_IsAlive(source)==0):
        RegTimer('cancelffADevent',0.1,0,char)
        return
    energy=Object_GetAttr(char,'energyPoints')
    lastenergy = FFX_ObjectGetAttr(char,'ivlasteng')
    absorbed = energy - lastenergy
    hp=FFX_ObjectGetAttr(char,'ffactivedefenseHP')
    maxEP=Object_GetAttr(char,'maxEnergyPoints')
    baseMaxEP=FFX_ObjectGetAttr(char,'baseMaxEP')+1
    if not hasAttribute(char,'energytanks') or  not hasAttribute(char,'volatilestorage') or not hasAttribute(char,'energyredirector'):
        if energy>baseMaxEP:
            Object_SetAttr(char,'energy',0)
            energyStat=1
        else:
            Object_SetAttr(char,'energy',FFX_ObjectGetAttr(char,'baseEnergy'))
            if (Object_GetClass(char)&OC_CONTROLLABLE)!=0:
                energyStat=Object_GetAttr(char,'energy')+6
                energyStat=energyStat/10
            else:
                energyStat=Object_GetAttr(char,'energy')+8
                energyStat=energyStat/10
    if energy == maxEP:## if a hero point is used
        Object_SetAttr(char,'energyPoints',maxEP/2)
        FFX_ObjectSetAttr(char,'ivlasteng',Object_GetAttr(char,'energyPoints'))
        return
    if absorbed > energyStat:
        if not hasAttribute(char,'energytanks') or  not hasAttribute(char,'volatilestorage') or not hasAttribute(char,'energyredirector'):
            energyreset=energy-absorbed
            Object_SetAttr(char,'energyPoints',energyreset)
        newHP=hp-absorbed
        if newHP <= 0:
            print 'AD out of HP!'
            cancelffAD(char)
            return
        else:
            FFX_ObjectSetAttr(char,'ffactivedefenseHP',newHP)
            FFX_ObjectSetAttr(char, 'roundedHP', int(round(newHP)))
            roundedHP=FFX_ObjectGetAttr(char, 'roundedHP')
            Mission_StatusText('%d hit points remaining in active defense'%(roundedHP))
    energy=Object_GetAttr(char,'energyPoints')
    FFX_ObjectSetAttr(char,'ivlasteng',energy)
    RegTimer('ffADabsorbCheck',0.1,1,char,source)

def ffADmoveCheck(event):
    char = event.object
    if FFX_ObjectGetAttr(char,'ffactivedefense')==0:
        return
    pos=Get_ObjectPos(char)
    dist=(pos[0]-FFX_ObjectGetAttr(char,'lastX'),pos[1]-FFX_ObjectGetAttr(char,'lastY'),0)
    FFX_ObjectSetAttr(char,'lastY',pos[1])
    FFX_ObjectSetAttr(char,'lastX',pos[0])
    if (dist[0]*dist[0]+dist[1]*dist[1]) > 10:
        cancelffAD(char)
    else:
        RegTimer('ffADmoveCheck',1,0,char)

def ffADregenerate(event):
    char = event.object
    if FFX_ObjectGetAttr(char,'ffactivedefense')==0:
        return
    hp=FFX_ObjectGetAttr(char,'ffactivedefenseHP')
    maxHP=FFX_ObjectGetAttr(char,'ffactivedefenseMaxHP')
    RegTimer('ffADregenerate',2,0,char)
    if hp < maxHP:
        FFX_ObjectSetAttr(char,'ffactivedefenseHP',hp+1)
        if hp > maxHP:
            FFX_ObjectSetAttr(char,'ffactivedefenseHP',maxHP)
        FFX_ObjectSetAttr(char, 'roundedHP', int(round(FFX_ObjectGetAttr(char,'ffactivedefenseHP'))))
        roundedHP=FFX_ObjectGetAttr(char, 'roundedHP')
        Mission_StatusText('%d hit points regenerating!'%(roundedHP))

def ffADupdate(event):
    char = event.object
    source = event.string
    if FFX_ObjectGetAttr(char,'ffactivedefense')==0:
        return
    power=getByTemplate(source,FFX_FFACTIVEDEFENSE_CUSTOM,3)
    Trigger_Power(char,char,power,'')
    RegTimer('ffADupdate',10,0,char,source)
    Trigger_Move(char, 'unlock')

def cancelffADevent(event):
    char = event.object
    if FFX_ObjectGetAttr(char,'ffactivedefense')==0:
        return
    cancelffAD(char)

def cancelffAD(char):
    base=FFX_ObjectGetAttr(char,'baseMaxEP')
    Object_SetAttr(char,'maxEnergyPoints',base)
    removeShield(char, duration = 1, fx = '')
    FFX_ObjectSetAttr(char,'ffactivedefense',0)


and copy this code to the ffxcustom2.py file:
### FF Original Active Defense
FFX_FFACTIVEDEFENSE_CUSTOM=[
["default","CUSTOM_FF_ACTIVE_DEFENSE","active_defence","ff_active_defence",20,"Moveable","Regenerate","",66],
["types","CCommand","AAnimation","sPower","iDuration","PMoveable?","PRegenerate?","PRemote Use?","iEP Cost"],
]

You can put in your own Custom Command, choose your own AD power that is created in FFEdit, choose whether the AD has the Moveable or Regenerative Options On or Off. And Choose whether the AD is Remote Only, Not Remote, or Both!.
You need to create an Active Defense Power in FFEdit, the name of which you place in the correct spot in your FFX_FFACTIVEDEFENSE_CUSTOM list in the ffxcustom2.py file. So you can make different characters with different AD Powers. The Active Defense MUS be set to Absorb, though. And also, set the AD to use NO ENERGY and also set the Animation to no_animation. You choose the EP cost and Animation the character uses to turn on the power in the FFX_FFACTIVEDEFENSE_CUSTOM list. Also, you need to create a ffq_remove_defense power in FFEdit which should be a Direct Power that Removes Defences and set the Magnitude to Extreme and Animation to no_animation.

Xenolith

you are my idol
:wub:

I can't seem to get this set up correctly.  Could you provide a bit of a walk-through?  I don't think I'm setting up my custom power name correctly.  Also, am I purchasing an attribute and a power, or just an attribute?

Thanks!

Epimethee

Quote from: TaskMasterX on October 01, 2007, 02:39:02 PMWhat I did was trigger an actual Active Defense Power that is set to Absorb. The code can then determine how much damage is absorbed by detecting how much the character's energy points increase and then deduct it from the "HP" of the Active Defense.
Brilliant! :thumbup:

QuoteIf you set the Regenerate Option to On, then the shield will regenerate 1 HP every second. Does anyone remember if this was the rate of Regeneration for the AD in  ff?
from dir(ff):
ff.RPG_SHIELD_REGEN_RATE = 0.5 (the value is also there in FFvsTTR, BTW).

Outcast

It's really amazing  what you guys can do.  :o  You guys are really taking the game to a whole new level with all your improvements. :thumbup:

stumpy

I just have to echo Épiméthée. This is a very clever bit of work. 

(And, there are some other possible applications that come to mind as well...)

TaskMasterX

Thanks! I had tried to tackle this a while back because USAgent had asked me to try it, but couldn't come up with anything that would work. Xenolith recently made the same request, and I thought that I'd give it another try since I had learned quite a bit since the last attempt. I had tried this "Energy Point Checking" technique with the Kinetic Absorber and Energy Redirector Attributes (in FFX 3.2.1) and thought it might work with this and tried it.

I updated the above code with some fixes and updates. It should be compatible with Energy Tanks and Volatile Storage, now.
Here's the step-by-step process to get it installed. You do need FFX 3.2 for this attribute.

1.)Go to your C:\<Game Folder>\ffx3\Missions\Scripts folder and open the ffx.py file with Notepad.
Scroll down to the bottom of the file and copy that large block of code from the first post above and paste it into the space right above where you see this:
######################################################################
##
## FFX FOOTER:
## FunctionWrapper import
##
######################################################################

Save and close the file.

2.)Then open the ffxcustom2.py file, which is in the same folder as the ffx.py file, again with Notepad.
Copy and paste that small block of code to the bottom of that file. Save and close.

3.)Open FFEdit and click the Attributes tab. Click the New button and type the name ffactivedefense10. Give it a cost, 1000 should be good enough. Repeat Step 3.) for creating the other three attributes: ffactivedefense20, ffactivedefense35, and ffactivedefense50, and give them all a cost. 1200, 1600, and 2000 respectively. You can, of course give them different costs if you want. Click the Save button.

4.) Now click the Defences tab. You need to create the default power for this attribute. Create a new Active Defense Power called ff_active_defence and set the the Block Type to Absorb. Choose all Damage Types in the Damage Blocked list except Mental and Mystical. For Attacks Blocked, choose Melee, Ranged and Area. Set EP Cost to None and Animation to no_animation. Choose an Attached FX (I chose Alex's Yellow Bubble Shield). Remember, this is the default AD the attributes will use if you don't customize the list in ffxcustom2.py. You need to create another AD power with the appropriate damage and attacks blocked specific to your character. Just remember to set it to Absorb and EP Cost to None and Animation to no_animation. Click the Save button and close FFEdit.

5.)To customize it, go to the list you copied into the ffxcustom2.py file and add a new line with the different customizations you want. First is the character's name, next, the custom command you want to appear on yourself and/or targets if you want to use it remotely. Next, is the name of the animation the character will use when the AD is triggered. After that is the name of the power you created in FFEdit. After the power name is the Duration in seconds. The default is 20. I recommend using increments of 10 seconds, but you can use any number. Just don't put it on quotes! After the Duration is the Moveable option. Type either "Moveable" or "Not Moveable". Next, Regenerate. Type either "Regenerate" or "Not Regenerate". Next to the last is the Remote Use option. Type "Remote Only" if you want the character to only be able to trigger the AD on other targets. "No Remote" means that the character can only use it on themselves. Leaving it blank, like this: "" means that the character can use it on themself and on other targets, as long as they have enough Energy, that is. Which leads us to the last spot which is the Energy Cost of using the power. Type in any number without quotes.
Here's an example of how it should look after you add in a new customization:
### FF Original Active Defense
FFX_FFACTIVEDEFENSE_CUSTOM=[
["default","CUSTOM_FF_ACTIVE_DEFENSE","active_defence","ff_active_defence",20,"Moveable","Regenerate","",66],
["types","CCommand","AAnimation","sPower","iDuration","PMoveable?","PRegenerate?","PRemote Use?","iEP Cost"],
["force field dude","TURN_ON_FORCE_FIELD","active_defence","ffdude_activedefence",30,"Moveable","Not Regenerate","Not Remote",50],
]

The character's name is force field dude, the command will be TURN_ON_FORCE_FIELD, the animation is active_defence, the name of the power I created in FFEdit that he uses is called ffdude_activedefence, the AD will last 30 seconds, if the HP don't reach 0 before that. He can Move with the AD on and the AD does not regenerate. He can not use the AD on other targets, only himself and it cost him 50 energy points to use it.

Whew! Hope that was detailed enough!

Xenolith

Yes, your description is perfect.  Thanks so much.  :)  To be clear, the tactic is not M25AI supported, correct?  Or can I include the line TActiveDefense, etc...?

yell0w_lantern


TaskMasterX

This'll need it's own AI tactic. If you use TActiveDefense tactic then it'll only use the built-in absorb power you made in FFEdit w/out all the extras (HP,Regenerate,Remote,etc.). I'll see what I can do.

Outcast

You've added quite a number of cool new attributes for the game TMX. Not to mention the Wrestling effect.
Hope they could all make it to FFX 3.3 perhaps? Sometime in a distant future? :P

TaskMasterX

I've noticed some bugs with this. For one, damage from thrown objects don't seem to register on the absorb Active Defense. So no energy is absorbed and therefore no damage deducted from the HP of the AD.
Second, when I control the character and I'm flying and I execute the command for the AD, the character lands before the the AD turns on. However, if the AI is controlling them they can turn on the AD without landing.

To get the AI to use this you need to add this to the m25ai.py file:
#------------------------------
# FF Active Defense
#------------------------------


def TActiveDefenseFFRemote(tactic_id, char, object, flt, user, pct=30, time=1, energy=33, mindist=0, maxdist=200, type='friend', subtype='none', situation='none', role='none', priority=TACTIC_PRIORITY_MEDIUM):
target = Target_GetCurrentTarget(char,type,subtype)
defense_time = ffx.getByTemplate(char,FFX_FFACTIVEDEFENSE_CUSTOM,4)
power = ffx.getByTemplate(char,FFX_FFACTIVEDEFENSE_CUSTOM,3)
epCost = ffx.getByTemplate(char,FFX_FFACTIVEDEFENSE_CUSTOM,8)
if not AISupport_CheckConditions(char, target, Target_GetDistance(char,type), pct, priority, mindist, maxdist, subtype, situation, role, energy=epCost):
return 0
AISupport_DoFunction(char, 'ffx.OnffActiveDefense(%s,%s)'%(`target`,`char`), target, priority, time, desc='remote ff active defense')
AddRole(target,power)
AddRole(target, 'active_defense')
m25event.SetTimer(defense_time,'AIActiveDefenseOff',target,power)
return 1

def AIOpt_TActiveDefenseFFRemote(char,tactic):
return {
'mindist': 'Opt_ConvertMinDist(tactic,0)',
'maxdist': 'Opt_ConvertMaxDist(tactic,m25distance.DISTANCE_LONG)',
'pct': 'Opt_Convert(tactic,"pct",PRECALC_PCT_MEDIUM)',
'time': 'Opt_Convert(tactic,"time",3)',
'energy': 'Opt_Convert(tactic,"energy",33)',
'subtype': 'Opt_AddSubtype(tactic,"not active_defense")',
}

def TActiveDefenseFFSelf(tactic_id, char, object, flt, user, pct=30, time=1, energy=None, mindist=0, maxdist=200, type='enemy', subtype='none', situation='none', role='none', priority=TACTIC_PRIORITY_MEDIUM):
target = Target_GetCurrentTarget(char,type,subtype)
defense_time = ffx.getByTemplate(char,FFX_FFACTIVEDEFENSE_CUSTOM,4)
power = ffx.getByTemplate(char,FFX_FFACTIVEDEFENSE_CUSTOM,3)
epCost = ffx.getByTemplate(char,FFX_FFACTIVEDEFENSE_CUSTOM,8)
if not AISupport_CheckConditions(char, target, Target_GetDistance(char,type), pct, priority, mindist, maxdist, subtype, situation, role, energy=epCost):
return 0
AISupport_DoFunction(char, 'ffx.OnffActiveDefense(%s,%s)'%(`char`,`char`), target, priority, time, desc='release free spirit')
AddRole(char,power)
AddRole(char, 'active_defense')
m25event.SetTimer(defense_time,'AIActiveDefenseOff',char,power)
return 1

def AIOpt_TActiveDefenseFFSelf(char,tactic):
return {
'mindist': 'Opt_ConvertMinDist(tactic,0)',
'maxdist': 'Opt_ConvertMaxDist(tactic,m25distance.DISTANCE_VISIBLE)',
'pct': 'Opt_Convert(tactic,"pct",PRECALC_PCT_ALWAYS)',
'time': 'Opt_Convert(tactic,"time",3)',
'situation': 'Opt_AddSituation(tactic,"not active_defense")',
}

Also, in the m25ai.py file, look for the line that says:
import ffxcustom
and paste this on the line below it:
import ffxcustom2
from ffxcustom2 import *


BTW, I did update the ffx.py code, yet again. So, you'll need to copy and paste it again. Sorry.

Outcast, have you checked out the FFX 3.2.1 I did?
http://freedomreborn.net/archive/index.php?topic=43211.0
That has some attributes and other stuff I did and I'll release this other stuff I've done since with my hero file updates, since I'm doing alot of this scripting for them anyway.

TaskMasterX

Well, I think I can live with the fact that Thrown Objects don't deduct HP from the AD. But having my character land before turning on the AD is a bit aggravating. I know it's because you're executing the AD through a Custom Command, which makes the character land before using the Power. I think I have a way to not have this happen, but it will require me to rewrite a bit of the code.

Here's the idea I have. Instead of triggering an AD Power created in FFEdit with a Custom Command and then applying all the bells and whistles (HP, Regenerate, etc.), how about, instead, creating an Absorb AD Power for the hero file, like normal, which the character can then execute normally, flying or not, and then have the code detect when the AD becomes active, then apply the bells and whistles. The only thing I would need is some bit of code that can detect when the AD becomes active. If the character has more than one AD set to absorb, then the code would need to know when the specific AD became active. Unfortunately, I don't know how to go about doing this.

Epimethee

Alas, IIRC, you can't detect active defence state. The nearest would be some sort of insufficient heuristic using MLOG to get the animation used for starting the AD power; even the the FX can't be detected after the first use, if memory serves. And you still wouldn't get the end of the AD state.

The land to use a custom command limitation is supposed to be surmountable, though. In FFvsTTR, Dr Mike discovered that adding a movement command in the custom command activation event function cancelled the landing request (can't remember the attributes he updated to work this way, sorry). However, I think I did a quick test to implement this for Reality Manipulation with no success, so there might be a trick or two to it.

TaskMasterX

That's good news, Ep. Thanks! I'll look and see if I can find it. BTW, what's the difference between ffx.addCommand() and Mission_CustomAction()?

Epimethee

The code says "utilities for adding and removing commands to all objects for simulated attack powers". The addCommand() indeed looks like it adds a custom command with every other character as the target.

I should certainly take a closer look, because I'm stalled on my Driver update for the Thor Reborn's Pilot attribute – everything works fine, except for a few &?%$#@! bugs on the custom commands.

TaskMasterX

I saw your Trigger_move in the Reality Manipulation, but that isn't going to work, because the function isn't executed until the character lands. So, the best you could do with that is to have the character take back to the air after the function is executed.
I found that Plasma Sculptor commands on targets work w/out the character landing, though the commands on the Plasma Sculptor themselves causes them to land before execution. I used the addCommandChars() that the Plasma Sculptor uses and was able to get the remote AD to execute on another target w/out landing. It's when you execute the command on the character themselves, is when they need to land for some reason. So I thought of placing the command to execute the Self-AD on other targets when the character is flying and have command be on the character when they aren't flying. What do you think about this?

Epimethee

So this trick works only on other targets. Makes sense (kind of). Nice find, TMX.

From a usability standpoint, having the self command work normally on land and then only on others while flying... I'd rather simply accept the limitation of having to land to use AD.

Mind you, there might be a workaround, but it's a bit of work. The idea came from playing Patriot City, which has problems with the max custom commands on self limit.

The first solution I thought of would be to create submenus (i.e., clicking on "call ally" replaces this command  by "call Blackbird", "call Quezalcoatl", "call the Bard" and "cancel"), but it add steps to an already slow process (a game which pauses every time you want to use powers, except in the most basic ways, isn't too strong in the action department...) and might be a pain to implement. More to the point, it doesn't solve the flying issue.

The second solution would be to have one or two objects common to all squad members following them (or centered on screen using Camera_GetLookAtInfo()); the custom commands would be put on this target object instead of on the character himself. Of course, since this would get annoying very fast, these targets should be shown only when the game is paused. However, the game doesn't process EVENT_TIMER when paused, so no Python script can run (unless EVENT_DLG_ONSHOW does work?)

The workaround would be to detect unpause instead and show the targets for ~5 secs afterward. Detecting "unpause" (i.e., game running after the end of pause, objectives dialog, general menu or power selection right-click dialog) would be done by comparing elapsed system time value with an EVENT_TIMER and (to avoid false positives due to game performance slowdowns) the "FF: destroying font resources, FF: Recreating font resources") in ff.log.

(Edit: Target objects relationship with characters.)

TaskMasterX

Well, since in :ff: you can execute your AD command on both yourself or on a target, and this attribute is suppose to be a replication (as close a replication as possible, anyway) of the :ff: AD, and I personally like the ability to not have to land when executing my AD (having to waste time landing before the AD turns on can mean the difference between surviving and getting KOed) ;) then I'll put a command to execute the personal AD on both the character and target, whether the character is flying or not. I'll update the code and post it after weekend.

TaskMasterX

Okay, I updated the code to allow for executing the command for the Personal AD on the character or on a target, just like you can trigger a regular, built-in, non-remote AD Power from a command on the target or on yourself. You still can customize the command like before. The code just adds _SELF to the the end of the command for triggering the personal AD and adds _REMOTE to the command for the remote version of the AD so you can tell the difference.