• Welcome to Freedom Reborn Archive.
 

Attribute similar to DRIVER? (was: attribute request)

Started by Mystik, December 08, 2007, 07:45:53 PM

Previous topic - Next topic

Mystik

I am humbly requesting an attribute that is similar to driver attribute but is made for bikes/mounts/and vehicles that show the hero from the outside

basically what I'm hoping for is an attribute that is basically a fusion
e.g: robin does a custom action on bike mesh- bike mesh and robin disappear-robin on bike mesh spawns

this would be useful for he-man and his battle cat or the black knight

BentonGrey

I haven't had a chance to mess with the Driver attribute yet (another thing I need to do over the break), but wouldn't that be possible already?  There has to be an object and a character version of each vehicle, right?  Can't you just give them different meshes?

Mystik

no theres just a character and a vehicle

BentonGrey

Hmm, well, in that case, I definitely second this.

Epimethee

Noted.

It's not the first time the request pops up (it was asked for Green Gobin and his flier a couple of years ago IIRC). Maybe it could also be used for a "Gestalt" attribute.

Hopefully, though, somebody else will tackle this before I do, as won't be able to commit to it for a while. :/

C4

I am doing this already for my mod. so I can just as well release the code.

By the weekend.

Epimethee


GGiant

One option is TR and Epimethee's driver attribute with a skoped mesh of robin on a bike.
Although that's off topic, It's much easier than scripting though I'd go with C4 on this one.
*edited*

C4

Quote[C4 is back FFXing!

Heh just like good old times  ;) Now Dr Mike must just script again ^_^

QuoteOne option is TR and Epimethee's driver attribute with a skoped mesh of robin on a bike.
Although that's off topic, It's much easier than scripting though I'd go with C4 on this one.

Yes that is what Thor wants to do but in order to make that happen additions to the code will have to be made.

BentonGrey

Did this ever get released?

C4

No. I could not test anything on vacation. Give me a week and I should have it done.

BentonGrey


Mystik


Mystik

bump just finished a ton of skopes waiting for this attribute

BentonGrey


C4

Yes this has been way to long. My project is keeping me so busy.

I will make sure to force myself to finish this.

Mystik


Epimethee

We're working on it. Slowly.

Hoping to pull it off.

Mystik

thanx guys
I am grateful for the effort

stumpy

(I renamed the thread so that people didn't get the idea that this was a generic attribute request thread.)

Epimethee

Done.  ^_^

This is a variant on Shapshifter/Temporary Form. You create two characters with the attributes: the Gestalt, which is your shapeshifter, and the Amalgam, which is your temporary form. The Gestalt needs to touch (using a custom command) another character or an object to transform into the Amalgam.

The attribute requires changing code in different place, though, so rather than add it manually, you may wish to wait until the next FFX update. Otherwise, don't forget to back up first. ;)

In FFEdit, add the following attributes:
ffqamalgam (cost: -7000)
ffqgestalt (cost: 400)

In strings.txt, add:
ATTRIB_FFQAMALGAM_01, amalgam
ATTRIB_FFQAMALGAM_DESC_01, (not multiplayable) this character is just a temporary form assumed by a GESTALT, and cannot be selected in the squad.
ATTRIB_FFQGESTALT_01, gestalt
ATTRIB_FFQGESTALT_DESC_01, (not multiplayable) you are able to switch between two or more forms by merging with a specific object or person.
CUSTOM_FFQGESTALTMERGE_01, merge
CUSTOM_FFQGESTALTMERGE_DESC_01, amalgam yourself with this target
CUSTOM_FFQGESTALTREVERT_01, revert
CUSTOM_FFQGESTALTREVERT_DESC_01, split this amalgam

and compile the language files using FFEdit or M25's tool.

In ffxcustom2.py, add:
### Gestalt/Amalgam
FFX_FFQGESTALT_CUSTOM=[
["default", "effect_entropy_spawn", "", "", "", "", "", ""],
["types", "EEffect", "ATarget 1", "ANew Form 1", "ATarget 2", "ANew Form 2", "ATarget 3", "ANew Form 3" ],
]

FFX_FFQAMALGAM_CUSTOM=[
["default", "effect_entropy_spawn", "power", "isNever"],
["types", "EEffect", "AAnimation", "fRevert Trigger"],
]

For the attribute to work, you MUST create an FFX_FFQGESTALT_CUSTOM entry for your character. The Gestalt attribute customization allows a character up to three targets, with each having a specific Amalgam. For example:
### Gestalt/Amalgam
FFX_FFQGESTALT_CUSTOM=[
["default", "effect_entropy_spawn", "", "", "", "", "", ""],
["types", "EEffect", "ATarget 1", "ANew Form 1", "ATarget 2", "ANew Form 2", "ATarget 3", "ANew Form 3" ],
["don_blake", "effect_ffx_thunder", "stick", "thor", "", "", "", ""],
["wraith_warrior_gestalt", "effect_entropy_spawn", "subterrestrial", "wraith_manta_amalgam", "", "", "", ""],
["batman", "effect_paper", "batglider_object", "batglider_amalgam", "batcycle_object", "batcycle_amalgam", "", ""],
["symbiote", "effect_ffx_evil", "civilian_male", "venom", "civilian_female", "carnage", "cop", "venom"],
]

FFX_FFQAMALGAM_CUSTOM=[
["default", "effect_entropy_spawn", "power", "isNever"],
["types", "EEffect", "AAnimation", "fRevert Trigger"],
["thor", "effect_ffx_thunder", "power", "isExiled"],
["wraith_manta_amalgam", "effect_entropy_spawn", "power", "isPowerNulled"],
]




in ffx.py, do the following changes:
add:
# Given a character template name, returns the Rumble Room "custom_template_n" name if present in the current mission.
# Returns None if not found.
# Added FFX 3.3 pre-alpha - Ep.
def getCustomTemplateName(realTemplateName):
    for object in Mission_GetDynamicObjects():
        if Object_GetTemplate(object)[0:7] == 'custom_':
            if FFX_GetTemplate(object) == realTemplateName:
                return Object_GetTemplate(object)
    return None


change the first line under the morph() function:
def morph(name,char,template,proportional=0,hasPortrait=1):
    morphVars=['shapeRow','ffqgestaltRow','originalIndex','shots0','shots1','shots2','shots3'] #Ep. 2008-04-20: added ffqgestaltRow for said attribute
(etc.)


replace the existing getMorphFX():
def getMorphFX(char):
    name=FFX_GetTemplate(char)
    fx='effect_entropy_spawn'
    for set in FFX_SHAPESHIFT_CUSTOM:
        if set[0]==name:
            fx=set[1]
    for set in FFX_TEMPFORM_CUSTOM:
        if set[0]==name:
            fx=set[1]
    for set in FFX_MIMIC_CUSTOM:
        if set[0]==name:
            fx=set[1]
    for set in FFX_FFQGESTALT_CUSTOM:
        if set[0]==name:
            fx=set[1]
    return fx


replace the existing initAttribs():
def initAttribs(event):
    #identify which heros have the 'modified' attributes
    #do all tempforms first so we can fill in the default arrays
    for object in js.Mission_GetDynamicObjects():
        if (Object_GetClass(object) & FFX_CHARACTER):
            if (hasAttribute(object,'tempform')!=0) | (hasAttribute(object,'involuntaryform')!=0) | (hasAttribute(object,'ffqamalgam')!=0):
                object=initialiseChar(object)
    for object in js.Mission_GetDynamicObjects():
        if (Object_GetClass(object) & FFX_CHARACTER):
            if (hasAttribute(object,'tempform')==0) & (hasAttribute(object,'involuntaryform')==0) | (hasAttribute(object,'ffqamalgam')!=0):
                object=initialiseChar(object)
    RegTimer('updateAttribs5',0.5)
    RegTimer('updateFFX',1)
    RegTimer('updateStates',1.25)


replace the existing tempCheck():
def tempCheck(event):
    name=event.object
    #stops them doing melee idle in front of villains
    cshelper.disable(name)
    if hasAttribute(name,'tempform') | hasAttribute(name,'involuntaryform') | (hasAttribute(object,'ffqamalgam')!=0):
        Object_Destroy(name)


update Power Analysis' attrDesc list:
attrDesc=[
['flier','levitation','ffqglider','ffqbasiclevitation','ffqlowgravflier','flight'],
['heavy lifter','superstrength'],
['claws','bladed weapon'],
['telepathy','powerscan','enhanced senses'],
['shapeshifter','accidentalchange','ffqgestalt','involuntaryform','tempform','ffqamalgam','metamorphic','icecloud','gaseousform','sandstorm','shapeshifting'],
['groupteleporter','bulktp','teleportation'],
['tough guy','invulnerable','invulnerable0','invulnerable2','invulnerable22a34','invulnerable2a','invulnerable3','invulnerable34','invulnerable4','invulnerability'],
['puppetn','puppetu','transmutation'],
['spinner','superspeed'],
['telekinetic','supertk','earthcontrol','telekinesis'],
['masscontrol','density-modulation'],
['illusionist','image-projection'],
]


and finally, add the attributes code:
####################### GESTALT/AMALGAM #################################
# You can change into amalgamed temporary forms by merging with specific character templates or common objects.
# - If the target is an enemy character, this power can only be used once per specific target *object* (not template),
#   even if using different Gestalt characters.
# - If the Amalgam is KO'ed, the Gestalt source character is too, but not the target.
# - If the target has moved too far, the command won't work.
# - Both attributes are removable. Removing the Amalgam attribute reverts the character to his base Gestalt form;
#   however, the Gestalt attribute will stil be functional and will need to be disabled separately.

# Ripped off Dr. Mike's Shapeshifter and Accidental Change code.
# The following functions and constants have been changed to work with Gestalt:
# - morph()
# - getMorphFX()
# - tempCheck()
# - initAttribs()
# - attrDesc list
# - added getCustomTemplateName()

# v. 1.0
# Added FFX 3.3 pre-alpha
# TO DO:
# - Add AI (good luck with that...)

def initffqgestalt(char, update=0, remove=0):
    if isMP():
        return
    if remove:
        if not FFX_ObjectGetAttr(char, 'ffqgestalt'):
            return
        FFX_ObjectSetAttr(char, 'ffqgestalt', 0)
        for i in (2, 4, 6):
            target = getByTemplate(char, FFX_FFQGESTALT_CUSTOM, i)
            if target != '':
                Mission_RemoveCustomAction('CUSTOM_FFQGESTALTMERGE', char, target)
                #custom action for RR's custom_template_n target characters
                targetRealName = getCustomTemplateName(target)
                if targetRealName != None:
                    Mission_RemoveCustomAction('CUSTOM_FFQGESTALTMERGE', char, targetRealName)
        return
    if FFX_ObjectGetAttr(char, 'ffqgestalt'):
        return
    FFX_ObjectSetAttr(char, 'ffqgestalt', 1)
    #store the original row
    for i in (3, 5, 7):
        form = getByTemplate(char, FFX_FFQGESTALT_CUSTOM, i)
        target = getByTemplate(char, FFX_FFQGESTALT_CUSTOM, i-1)
        if not '' in (form, target):
            action='OnFFQGestaltForm%i'%(i)
            Mission_CustomAction('CUSTOM_FFQGESTALTMERGE', char, target, action, 3, 0)
            #custom action for RR's custom_template_n target characters
            targetRealName = getCustomTemplateName(target)
            if targetRealName != None:
                Mission_CustomAction('CUSTOM_FFQGESTALTMERGE', char, targetRealName, action, 3, 0)
    #store the original row
    template=Object_GetTemplate(char)
    realName=''
    print FFX_FFQGESTALT_CUSTOM
    if template[:6]=='custom':
        realName=FFX_GetTemplate(char)
        newSet=[]
        needToAdd=1
        for set in FFX_FFQGESTALT_CUSTOM:
            if set[0]==realName:
                for entry in set:
                    newSet.append(entry)
            if set[0]==template:
                needToAdd=0
        if needToAdd:
            if len(newSet) > 0:
                newSet[0]=template
            else:
                newSet = FFX_FFQGESTALT_CUSTOM[0][:] #append as a copy to avoid messing with the original data
                newSet[0] = template
            FFX_FFQGESTALT_CUSTOM.append(newSet)
    i=0
    for set in FFX_FFQGESTALT_CUSTOM:
        if (set[0]==template) | (set[0]==realName):
            FFX_ObjectSetAttr(char,'ffqgestaltRow',i)
            break
        i=i+1

def OnFFQGestaltForm3(target, char):
    OnFFQGestaltChange(char, target, 3)

def OnFFQGestaltForm5(target, char):
    OnFFQGestaltChange(char, target, 5)

def OnFFQGestaltForm7(target, char):
    OnFFQGestaltChange(char, target, 7)

def OnFFQGestaltChange(char, target, index):
    print target
    if not Object_Exists(target):
        return
    if isBuilding(target) | ( Object_GetAttr(target, 'minHealth') > 0 ) | ( Object_GetAttr(target, 'physical') == 0 ):
        Mission_StatusText("Invalid target type for Gestalt. You may need to update your FFX Control Centre settings.")
        return
    #if the target is an enemy, has any Gestalt power already been used against this character?
    if FFX_ObjectGetAttr(target, 'ffqgestalt_target', 0):
        Mission_StatusText("This character has become resistant to Gestalt attacks.")
        return
    #is the target still in range or has it moved too far?
    charPos = Get_ObjectPos(char)
    targetPos = Get_ObjectPos(target)
    if ( distance2D(charPos, targetPos) > 60 ):
        Mission_StatusText("Missed! The target has moved away.")
        return
    if chargeEP(char,0)==0:
        return
    #move to the target's position and orientation
    heading = Object_GetOrientation(target)[1]
    teleportToPos(char, targetPos, heading)
    #amalgam identified - let's go! find the original form
    originalForm = FFX_FFQGESTALT_CUSTOM[ int(FFX_ObjectGetAttr(char, 'ffqgestaltRow')) ][0]
    newTemplate = getByTemplate(originalForm, FFX_FFQGESTALT_CUSTOM, index, 1)
    name=getFormName()
    #make the target disappear
    RegTimer("OnFFQGestaltHideTarget", 0.2, 0, target, name)
    morph(name,char,newTemplate,1,1)
   
def OnFFQGestaltHideTarget(event):
    target = event.object
    name = event.string
    #store the amalgam's target component's class type and name
    isCharacter = ( ( Object_GetClass(target) & FFX_CHARACTER ) != 0 )
    missionobjvar.Object_SetVar( name, 'ffqgestalt_target', (isCharacter, FFX_GetTemplate(target), target) )
    #if a character, hide and disable
    if isCharacter:
        Object_SetPrimaryState(target, PCSTATE_EXILE, 10000, 0)
        Object_SetSecondaryState(target, SCSTATE_INVISIBLE, 10000, 0)
        if Object_GetClass(target) & OC_CONTROLLABLE:
            FFX_AdjustPortrait(target, 0)
        AIDisable(target)
        #if the target is an enemy, make the power a one-shot deal against this character
        if not FFQ_isSameSide(name, target):
            FFX_ObjectSetAttr(target, 'ffqgestalt_target', 1)
            RegTimer("OnFFQGestaltTargetEnemy", 15, 0, target)
        FFX_ObjectSetAttr(target, 'ffqgestalt_hidden', 1) #used by OnFFQGestaltTargetEnemy()
    #otherwise, just destroy it
    else:
        Object_Destroy(target)

# If the hidden target(s) is an enemy character and a bad guy, check periodically to see if it's the last standing enemy;
# if it is, display a message to the player once.
def OnFFQGestaltTargetEnemy(event):
    target = event.object
    if not FFX_ObjectGetAttr(target, 'ffqgestalt_hidden'):
        return
    for baddie in cshelper.getAllBaddies(1):
        if not FFX_ObjectGetAttr(baddie, 'ffqgestalt_hidden'):
            RegTimer("OnFFQGestaltTargetEnemy", 15, 0, target)
            return
    #the only bad guys left are gestalt targets; alert the player
    Mission_StatusText("The only enemies left are amalgamed with your Gestalt heroes. Revert to beat them.")
       
   
def initffqamalgam(char,update=0, remove=0):
    global FFX_FFQGESTALT_CUSTOM
    if isMP():
        return
    if remove:
        FFX_ObjectSetAttr(char, 'ffqamalgam', 0)
        FFX_ObjectSetAttr(char, 'ffqgestaltReverting', 1)
        OnFFQAmalgamRevertFromTemp(char, char, 1)
        return
    FFX_ObjectSetAttr(char, 'ffqamalgam', 1)
    #at startup add them into the default slots for gestalt
    if Mission_GetAttr('shapeReady')==0: #shapeReady is used by various attributes
        for hero in cshelper.getAllHeroes():
            if hasAttribute(hero,'ffqgestalt'):
                row=-1
                index=0
                template = Object_GetTemplate(hero)
                for set in FFX_FFQGESTALT_CUSTOM:
                    if set[0] == template:
                        row=index
                    index=index+1
                if row==-1:
                    row=len(FFX_FFQGESTALT_CUSTOM)
                    newSet = FFX_FFQGESTALT_CUSTOM[0]
                    newSet[0] = template
                    FFX_FFQGESTALT_CUSTOM.append(newSet)
                for index in (3, 5, 7):
                    if FFX_FFQGESTALT_CUSTOM[row][index]=='':
                        FFX_FFQGESTALT_CUSTOM[row][index]=Object_GetTemplate(char)
                        break
        RegTimer('destroyTemp',1,0,char)
        return
    #put in a revert command to original form
    Mission_CustomAction('CUSTOM_FFQGESTALTREVERT', char, char, 'OnFFQAmalgamRevertFromTemp', 5, 0)
    #start checking for forced split
    revertFn=getByTemplate(char, FFX_FFQAMALGAM_CUSTOM, 3)
    if revertFn not in ("", "isNever"):
        RegTimer('OnFFQAmalgamForceRevertCheck',1,0,char,revertFn)

def OnFFQAmalgamForceRevertCheck(event):
    char=event.object
    if not Object_Exists(char):
        return
    if FFX_ObjectGetAttr(char,'ffqgestaltReverting',0) != 0:
        return
    #if the amalgam is KO, revert it now, hero points notwistanding
    if not Object_IsAlive(char):
        ffqAmalgamRevertFromTemp(char,char,1)
        return
    fn=event.string
    RegTimer('OnFFQAmalgamForceRevertCheck',1,0,char,fn)
    forceback=0
    code='forceback=%s(char)'%(fn)
    exec code
    if forceback:
        FFX_ObjectSetAttr(char,'ffqgestaltReverting',1)
        OnFFQAmalgamRevertFromTemp(char,char,1)

def OnFFQAmalgamRevertFromTemp(dummy,char,forced=0):
    if forced==0:
        if chargeEP(char,0)==0:
            return
    row=int(FFX_ObjectGetAttr(char,'ffqgestaltRow'))
    template = FFX_FFQGESTALT_CUSTOM[row][0]
    if FFX_TemplateExists(template)==0:
        customTemplate=getTemplateFromRealName(template)
        if FFX_TemplateExists(customTemplate):
            template=customTemplate
    #reveal the target object and set the positions
    pos = Get_ObjectPos(char)
    newPos = (pos[0]+10, pos[1]+10, pos[2])
    targetData = missionobjvar.Object_GetVar(char, 'ffqgestalt_target')
    target = targetData[2]
    if targetData[0]: #if a character
        teleportToPos(target, pos)
        Object_SetPrimaryState(target, PCSTATE_EXILE, 0, REMOVE_STATE)
        Object_SetSecondaryState(target, SCSTATE_INVISIBLE, 0, REMOVE_STATE)
        FFX_ObjectSetAttr(target, 'ffqgestalt_hidden', 0) #used by OnFFQGestaltTargetEnemy()
        if Object_GetClass(target) & OC_CONTROLLABLE:
            FFX_AdjustPortrait(target, 1)
        AIEnable(target)
    else:
        Object_SpawnAt(targetData[1], pos, target)
    teleportToPos(char, newPos)
    alignWith(target, char)
    #morph back the character
    name = getFormName()
    if FFX_HasPortrait(char):
        morph(name,char,template,1,1)
    else:
        morph(name,char,template,1,0)

Mystik

whoot  :thumbup: :thumbup: :thumbup:

gonna check this asap

C4

Great job, Epimethee. Just browsed through the code and it looks very interesting.

So now that guilt factor will come into play...

TaskMasterX


yell0w_lantern

This might solve my problem for getting from Wonder Woman and an empty Invisible Jet to Invisible Jet with Wonder Woman inside.  :wub:

Epimethee

Thanks for the kind words, guys; it appreciated. Hopefully, it does works correctly now; I kept finding new bugs for a long while.

BentonGrey

Fantastic Epi!  I'm thrilled that this is now an option.  Robin-cycle, here I come!  Ha. :D