News:

Rings of Reznor!

Main Menu

FF Danger Room Scripts

Started by seraglio, April 14, 2013, 05:55:55 PM

Previous topic - Next topic

seraglio

Basically, im trying to determine which scripts I have to edit in the danger room (skirmish buddy mode) to change how long it takes before it registers the hero as dead. Or a vilain for that matter.

I've looked at a number of the scripts in the skirmish directory...but i'm not seeing what I want, not sure if this isn't controlled somewhere else.

There are a number of attributes that trigger after a character is dead, or that destroy a version of the character. I made a power swap version of living laser, and it occasionally (not always) causes a mission to end, because I think the ffx_tp kills the hero. Also other powers, like Immortal and russian doll dont work on villains, the danger room ends too soon. Hopefully these are easy parameters to change.

Previsionary

Hmm, I don't believe a call is made directly in the skirmish scripts. I believe that call is handled by cshelper.py (?). Eh, I don't remember exact names, and i don't have the game installed currently, so my knowledge is limited to my fading FF knowledge.
Disappear when you least expe--

stumpy

You you probably want to increase the delay used in SkirmishModeCheckEnd(), which is in missions/scripts/m25skirmish.py. Currently, every second, that function checks for a living team. Usually, if it finds one, it does nothing but set a timer to call itself and check again in another second. If it doesn't find one, it sets a timer that waits for a little while (currently 4 seconds) and then calls the function SkirmishCheckEndDelayed(), which checks again and then ends the mission if it looks like no one is alive. If the thing you want to happen hasn't resulted in a living character in those 4 seconds, then it will think the team is KOed and end the mission. Changing that delay to something longer may do what you want.
Courage is knowing it might hurt, and doing it anyway. Stupidity is the same. And that's why life is hard. - Jeremy Goldberg

seraglio

Awesome, thank you Stumpy. i will try this out right away.

seraglio

I don't have a m25skirmish.py on my VM. Is that FF or FFVTR?

More to come...

seraglio

#5
Had some time this weekend to play and work on this, I've discovered a few things, but still don't have it working. Let me define a few things to help understand what I'm trying to do:

Version: Freedom Force with FFX 2.6
Mission: Skirmish Buddy Mode (original, not Alex's, his gives me an error when I try it, does not import his AI)
Morphs = Any NPC who destroys own form and creates a new one, via Involuntary Form, Russian Doll etc

I'm playing my original FF installations, preserved on a Virtual Machine. Its a mess, with all files from a thousand upgrades scattered about. Just wanted to put that out there if it helps with troubleshooting, I may not be using the latest danger room scripts or M25 AI scripts. My FFX version is 2.6 with thousands of changes, including moving in all the code I could from FFX3.3 not dependent on new functions like mlogreader. Nothing related to mission calls should have been changed, since I don't really understand that, and not much of the morph attributes have been changed.

When I am in the danger room, actively controlling a "hero", and defeat a baddie with a "morph" power, and that baddie is the last one to be defeated, the mission ends.

It shouldn't, the baddies is spawning a morphed new baddie, but the regMissionWon thing won't wait. this is very frustrating, some of the coolest powers involve "morphs" and I can only use them on one side.

I found this code in sk.py

code removed, freedomreborn thinks I'm a denial of service attacker :doh:


I was very happy when I found this, I've changed the timer to as high as 10. That partially worked, but then I ran into a second (possibly related) issue.

The template for the baddies "morphs" are game_obj_hero. So even when changing the script above, which slowed down the mission ending, I just wound up controlling my former enemies morph.

So then I looked up something on Dr Mikes site and found this:

QuoteShapechangers and Mimics: NPC Morphing
Shapechanger and Mimic AI may seem a bit wierd in the dangerroom - that character seems to sidestep a bit while morphing. This is due to the fact that a NPC character is actually morphing into a hero (with portrait and everything) - they then clone themselves to produce an NPC copy before destroying the original. Its clumsy but it works :)
When an NPC tries to morph into a given template, they'll first look for versions of the template called _npc or _fb, eg if an NPC is about to morph into 'minute_man', they'll check if either the templates minute_man_npc or minute_man_fb exist first, and use them instead.
This allows you to create NPC versions of your favourite temporary forms with ease - just copy the hero template and set it to GAME_OBJ_MINION and copy the character sheet as it is. The transition will be much smoother then.


So now I think I'm just stupid and not using the attribute properly. I make copies of the baddies hero file and template ending in "_npc". What happens?

The change I made to SK.py seems to be ignored now. The missions end even faster now. I barely get off the killing blow before the cutscene starts. If I leave a non-morphing baddie on the mission...the morphing baddie ignores the _npc and joins my party as a controlled hero as always! Lovely!

Basically I'm pulling my hair out here. Is there anyone who still plays FF that can either confirm or deny what I'm experiencing? Either:

this is normal behaviour
im doing something wrong with the morphs configuration
im using old AI code
im using the wrong danger rooms

I suspect the code Dr Mike is referring to isn't running on my machine. But where to look? There are a lot of possibilities:

SK.py
Skirmish Team Match.py
cshelper.py
ffx.py
m25ainew.py

Which controls skirmish missions ending and or changing a templates AI from hero to villain?

Congratulations if you read all that. Hopefully someone can help me straighten this out.

seraglio

PS I removed the code from the above written pos, cause it wouldnt let me post...thought I was an attacker and told me to die. :doh:

the code has this line in it..if it lets me post it

RegTimer('OnEnemyKilled',1)


stumpy

My time is limited at the moment, but a couple quick comments.

First, make sure that the alternate forms of your villains' templates' classes are set as GAME_OBJECT_MINION as well as naming the template with _npc at the end. I know that the class field was mentioned in DrMike's note, but it is an important thing that may be related to the transformed villains joining the hero side. I would double check that for your villains and all of their alternate forms before going any further.

Check your script folders for a button in Window's Explorer that says something like Compatibility Files. There is an older post around here somewhere that explained that with a picture, but the short version is that Windows doesn't always let you or your software write files where you want them to be. Instead, it creates a pseudo folder elsewhere and puts the files in that folder. In an ideal world, when an app needs those files (like the scripts), Windows will automagically redirect the app to the pseudo folder and all will be well. In the real world, things may not work out so well and it may seem like changes you have made to scripts are being ignored.

My advice is to find if there are any such compatibility files and copy them to where they should be and get rid of any compatibility folders. Then make sure that the game, the editor, and any software you are using to play with those files (like PythonWin) are always run at administrator level, so that it can write the files where the game expects they will be.

Not sure what's going on with the forum software thinking you are attempting a DNS attack when posting code. That usually only happens when your browser attempts to load a page rapidly or submit a form rapidly. Sometimes rebooting can fix the issue and there is also bad behavior by some antiviral programs which reload pages in the background and that can look like a DOS attack to our forum software. See this thread for an instance of this having come up before.
Courage is knowing it might hurt, and doing it anyway. Stupidity is the same. And that's why life is hard. - Jeremy Goldberg

stumpy

Also, this "Install problem with FFEdit2" thread has an example of the Windows compatibility files causing some trouble.
Courage is knowing it might hurt, and doing it anyway. Stupidity is the same. And that's why life is hard. - Jeremy Goldberg

seraglio

Awesome, thanks a ton for the thoughts Stumpy. I'll look into them, but in reference to the hidden file thing and unix like symbolic links, isnt that a win7 up thing? I run a Virtual Machine with an unpatched version of base WinXP. The only other things installed are python 27 for IDLE and Nifskope for keyframe edits. Everything else isnt actually "installed", its just a copy of an old win98 directory I saved. Never really reinstalled it, since I figured I'd never get it working properly after so long...but maybe thats a problem in itself?

I did a new test. I brought the involuntary clone (Montain Man Zu) in as the baddie against one of my other customs I controlled. I deleted the morph in object Hero class morph in FFEdit, leaving only the minion (_npc version). Zu transforms when low on hit points. Guess what, when Zu was killed, nothing spawned, mission took a while to end (probably the sk.py change), but the mission ended. SO I don't think its really looking for the _npc built in. This time I captured the script log.


Quote>>> system/init.py executed
>>> system\tredir.py executed
importing missionobjvar.py
using standard custom settings
using standard puppet settings
using standard ffx ID list
using standard builtins list
using standard extra code
using standard fx lists
using standard transmutation lists
using standard custom settings
using standard puppet settings
using standard ffx ID list
using standard builtins list
using standard extra code
using standard fx lists
using standard transmutes lists
using standard voice ID traits list
using standard multiple attribute sets
using standard AI character data
using standard AI configuration data
using standard object list
>>> C:\Program Files\Irrational Games\Freedom Force\data\temp\Skirmish Buddy Match.py executed
>>> C:\Program Files\Irrational Games\Freedom Force\data\temp\Skirmish Buddy Match.py executed
>>> OnReceiveSelectedEnemies(('custom_vil_0', ))
initialising FFX: skirmish=1
SetupAI:custom_vil_0_1_1
addFFXTactics:custom_vil_0_1_1
['man mountain zu', ['timer', 4]]
remedy for custom_vil_0_1_1 1.0
custom_vil_0_1_1 has no random start markers defined.
SetupAI:ffxgun
addFFXTactics:ffxgun
['ffx_tiggot', ['timer', 4]]
ffxgun has no random start markers defined.
hero_0 has no random start markers defined.
SetupAI:ffxtarget
addFFXTactics:ffxtarget
['ffx_tiggot', ['timer', 4]]
ffxtarget has no random start markers defined.
initialising FFQ_initialiseExtras()
setting default sun
Welcome to the fight of the century!
initCarriers on custom_vil_0_1_1(man mountain zu)
man mountain zu
registering man mountain zu attacks with densitytothrow
determining gender custom_vil_0_1_1 (team=2)
M25 evil!
initCarriers on hero_0(moonwarp)
moonwarp
registering moonwarp attacks with irradiatetodeathray
registering moonwarp attacks with stuntodensity
registering moonwarp attacks with exiletoshrink
registering moonwarp attacks with densitytoteleport
determining gender hero_0 (team=1)
initresistance33: char=<custom_vil_0_1_1>
initresistance33: initializing for custom_vil_0_1_1
setting:custom_vil_0_1_1 custom_vil_0 accidentalchange
accidental change of custom_vil_0_1_1(man mountain zu) triggered by isLowOnHealth
setting:hero_0 custom_template_70 resistance50
initresistance33: char=<hero_0>
initresistance50: initializing for hero_0
Opposing our heroes on team 2 are:
- man mountain zu
flycheck:hero_0 0
flycheck:hero_0 0
doBrawl:custom_vil_0_1_1
50000
hero_0
flycheck:hero_0 0
flycheck:hero_0 0
doBrawl:custom_vil_0_1_1
50000
hero_0
flycheck:hero_0 0
flycheck:hero_0 0
flycheck:hero_0 0
doBrawl:custom_vil_0_1_1
50000
hero_0
flycheck:hero_0 0
flycheck:hero_0 0
Traceback (innermost last):
  File ".\Data\Missions\Scripts\ffx.py", line 2194, in teleportother3
    fallOver(char,1)
  File ".\Data\Missions\Scripts\ffx.py", line 10995, in fallOver
    Trigger_Force(char,0,0,1,intensity*Object_GetAttr(char,'minForce'),TF_ABSOLUTE)
SystemError: NULL result without error in call_object
Traceback (innermost last):
  File ".\Data\Missions\Scripts\ffx.py", line 4359, in morph2
    Object_SetAttr(name,'health',(Mission_GetAttr('health_%s'%(name))*Object_GetAttr(name,'maxHealth'))/Mission_GetAttr('maxHealth_%s'%(name)))
SystemError: NULL result without error in call_object
flycheck:hero_0 0
flycheck:hero_0 0
WinMission() called
playing CS
cshelper: Cutscene [unknown] submitted for playing
cshelper: Now playing cutscene [unknown]
Step 1 of 2
flycheck:hero_0 0
flycheck:hero_0 0
flycheck:hero_0 0
Step 2 of 2
cshelper: Cutscene [unknown] has ended
flycheck:hero_0 0
flycheck:hero_0 0
flycheck:hero_0 0

Here is the relevant code from FFX.

def morph2(event):
    global mimicTarget
    template=event.object
    name=event.string
    pos=(Mission_GetAttr('x_%s'%(name)),Mission_GetAttr('y_%s'%(name)),Mission_GetAttr('z_%s'%(name)))
    Object_SpawnAt(template,pos,name)
    if cshelper.isPlaying():
        RegTimer('ringGo',0.1)
    makeAttrSafe(name)
    if event.user==0:
        Object_SetAttr(name,'health',Mission_GetAttr('health_%s'%(name)))
        Object_SetAttr(name,'maxHealth',Mission_GetAttr('maxHealth_%s'%(name)))
    else:
        Object_SetAttr(name,'health',(Mission_GetAttr('health_%s'%(name))*Object_GetAttr(name,'maxHealth'))/Mission_GetAttr('maxHealth_%s'%(name)))
    Object_SetAttr(name,'energyPoints',Mission_GetAttr('energyPoints_%s'%(name)))
    Object_SetAttr(name,'heroPoints',Mission_GetAttr('heroPoints_%s'%(name)))

    #m25 compatiblity
    try:
        #M25 modified Feb 17
        if usingM25AI():
            m252.SetupOneChar(name)
        Object_SetAttr(name,'team',Mission_GetAttr('team_%s'%(name)))
        setGender(name,1)
        Object_SetAttr(name,'brawler',Mission_GetAttr('brawler_%s'%(name)))
    except:
        pass

    if (Object_GetClass(name) & js.OC_CONTROLLABLE)!=0:
        Mission_SelectHero(name)
    RegTimer('morph3',0.1,0,name)
    if mimicTarget!='':
        cshelper.turnTo(name,mimicTarget)
        mimicTarget=''


Think I might have found something...

    testTemp='%s_npc'%(template)
    if Template_Exists(testTemp):
        template=testTemp
    #from the Strangers football level :)
    testTemp='%s_fb'%(template)
    if Template_Exists(testTemp):
        template=testTemp
    id=FFX_ObjectGetAttr(char,'templateID')
    if type==2:


...looks like the first time the morphing code looks for template with _npc

    ff.RESULTBUBBLE_SCALE=0
    Trigger_Move(char,'lock')
    makecloak(char,10)
    makephase(char,10)
    Object_SetAttr(char,'scannable',0)
    temp=getDummyName()
    tm=Object_GetTemplate(char)
    testtm='%s_npc'%™
    if Template_Exists(testtm):
        tm=testtm


I'm probably just not understanding this , but why is  testTemp='%s_npc'%(template) the NPC built in at first, then, 200 lines later  testtm='%s_npc'%™ appears to be? I dont think I modified or updated this code, as its far beyond my meager cut and paste skills, though its always possible I fat fingered something while snooping around.

seraglio

One more thing...

The name of the character im testing with is "man mountain zu". His built in, imported into FFedit with AFF is "manmountain". The new, game_obj_minion template and built in hero is "manmountain_npc", which is not listed in ffxcustom (I assume the morph scripts are supposed to append this). The entry in ffxcustom is :

QuoteFFX_ACCIDENTALFORM_CUSTOM=[
["default","isEnraged","nuclear_winter_extra"],
["mistral","isEnraged","ffx_abraxas"],
["dr jose san carlo","isBlank","custom_template_22"],
["the amazing mistral","isEnraged",""],
["champion","isOutOfEnergy","champion_invol"],
["champion_invol","isEnraged","champion_invol"],
["skyscraper","isStunned",""],
["shadowdragon","isStunned","ffx_abraxas"],
["man mountain zu","isLowOnHealth","manmountain"],

Just wanted to clarify, in case I'm doing something wrong. I'm having the same problem with others, in all danger rooms, but just wanted to add that info.

And to my previous comment about never re-installing Freedom Force, I remember now that I did install the game just to get the correct pointers and registry entries, and then overwrote it with my old data...just to be clear. I've made so many changes over the years I'd never track them all down if I attempted a "clean install"

stumpy

On the compatibility files: Yes, I think that is a Windows 7 or maybe Vista thing. A Windows XP emulator shouldn't be using that.

The issue with '%s_npc' is just using two different ways to pass a tuple of arguments for string translation. The version with parentheses would be needed if there was more than one argument to pass to the translation string. In this instance, there is only one, so the parentheses is optional to have the tuple recognized. There is more to say about that but, for our purposes, they are different ways to do the same thing and both look fine.

Just to double-check, you made sure that the class type of the template for your accidental form was GAME_OBJECT_MINION, right? That's important and it might be why the character becomes part of the heroes' squad, and why there are no villains left and RR script thinks one side has won. There are other possibilities, too. But making sure the easier ones are not the culprit is the first priority, especially since I am not very familiar with the AI that sets up teams in the RR.  ;)
Courage is knowing it might hurt, and doing it anyway. Stupidity is the same. And that's why life is hard. - Jeremy Goldberg

seraglio

absolutely game object minion for the template for _npc.

seraglio

Still trying to figure this out. Some more pieces of the puzzle...

I get different results in different Dangerooms. I have a lot of different dangerooms installed. I think FFX came with a version with M25 AI. ANd it looks like I have a set from AlexFF as well.

When running danger room "watch" mode, where the AI tries to (mis)manage your main character, so far everything works flawlessly. I have more, boring tests to complete, but basically thats the gist of it. That leads me to believe the core of the problem is either in the different danger rooms themselves, and how they interact with FFX, or maybe the behavior is as intended..perhaps most people play the TV mode, and thats what the AI is designed for?

In a mode just called "Dangerroom" I was able to get slightly further along (Russian Dolls worked with me playing the hero with that power). I got this error message in the script ... this is playing AGAINST the accidental change character....the change TO alternate form WORKED, when changing back to original form...it failed with this error:

  File ".\Data\Missions\Scripts\ffx.py", line 4367, in morph2
    Object_SetAttr(name,'health',(Mission_GetAttr('health_%s'%(name))*Object_GetAttr(name,'maxHealth'))/Mission_GetAttr('maxHealth_%s'%(name)))
SystemError: NULL result without error in call_object



full script

>>> system/init.py executed
>>> system\tredir.py executed
importing missionobjvar.py
using standard custom settings
using standard puppet settings
using standard ffx ID list
using standard builtins list
using standard extra code
using standard fx lists
using standard transmutation lists
using standard custom settings
using standard puppet settings
using standard ffx ID list
using standard builtins list
using standard extra code
using standard fx lists
using standard transmutes lists
using standard voice ID traits list
using standard multiple attribute sets
using standard AI character data
using standard AI configuration data
using standard object list
>>> C:\Program Files\Irrational Games\Freedom Force\data\temp\dangerroom.py executed
>>> C:\Program Files\Irrational Games\Freedom Force\data\temp\dangerroom.py executed
>>> OnReceiveSelectedEnemies(('custom_vil_0', ))
initialising FFX: skirmish=1
hero_0 has no random start markers defined.
SetupAI:ffxgun
addFFXTactics:ffxgun
['ffx_tiggot', ['timer', 4]]
ffxgun has no random start markers defined.
SetupAI:ffxtarget
addFFXTactics:ffxtarget
['ffx_tiggot', ['timer', 4]]
ffxtarget has no random start markers defined.
SetupAI:custom_vil_0_1_1
addFFXTactics:custom_vil_0_1_1
['man mountain zu', ['timer', 4]]
remedy for custom_vil_0_1_1 1.0
custom_vil_0_1_1 has no random start markers defined.
initialising FFQ_initialiseExtras()
setting default sun
OnEnemyKilled:none
initCarriers on hero_0(jackhammer)
jackhammer
registering jackhammer attacks with densitytovertigo
determining gender hero_0 (team=1)
initCarriers on custom_vil_0_1_1(man mountain zu)
man mountain zu
registering man mountain zu attacks with densitytothrow
determining gender custom_vil_0_1_1 (team=2)
M25 evil!
15.0
initresistance25: char=<hero_0>
initresistance25: initializing for hero_0
initresistance33: char=<custom_vil_0_1_1>
initresistance33: initializing for custom_vil_0_1_1
setting:custom_vil_0_1_1 custom_vil_0 accidentalchange
accidental change of custom_vil_0_1_1(man mountain zu) triggered by isLowOnHealth
OnEnemyKilled:none
OnEnemyKilled:none
OnEnemyKilled:none
OnEnemyKilled:none
OnEnemyKilled:none
form_1 has no random start markers defined.
determining gender form_1 (team=2)
M25 evil!
initialising form_1
initCarriers on form_1(manmountain)
manmountain
setting:form_1 manmountain involuntaryform
initinvolform: shapeready=1
I will revert to man mountain zu
initresistance10: char=<form_1>
initresistance10: initializing for form_1
OnEnemyKilled:none
OnEnemyKilled:none
OnEnemyKilled:none
OnEnemyKilled:none
OnEnemyKilled:none
OnEnemyKilled:none
OnEnemyKilled:none
OnEnemyKilled:none
OnEnemyKilled:none
OnEnemyKilled:none
OnEnemyKilled:none
OnEnemyKilled:none
Traceback (innermost last):
  File ".\Data\Missions\Scripts\ffx.py", line 4367, in morph2
    Object_SetAttr(name,'health',(Mission_GetAttr('health_%s'%(name))*Object_GetAttr(name,'maxHealth'))/Mission_GetAttr('maxHealth_%s'%(name)))
SystemError: NULL result without error in call_object
OnEnemyKilled:none
WinMission() called
playing CS
cshelper: Cutscene [unknown] submitted for playing
cshelper: Now playing cutscene [unknown]
Step 1 of 2
Step 2 of 2
cshelper: Cutscene [unknown] has ended
OnEnemyKilled:none
OnEnemyKilled:none

stumpy

Hmm. Not sure what all is going on there. First, what is happening (in the session) that causes the transformation? Is the toon damaged, does he trigger randomly, etc?

The error in your log is usually caused when one of the Object_* functions is called on an object that the game thinks doesn't exist. I guess it's back to basics for debugging: We'll add some print statements to maybe get some idea of what is causing the error. Go to your ffx.py and replace the code for morph2 (search for def morph2). Highlight the whole function and paste the following over it
##def morph2(name,char,template,proportional,hasPortrait):
##    #spawn the new form in the same spot as the old one
##    FFX_Spawn(name,template,char)
##    FFX_ObjectSetAttr(name,'morph',1)
##    #make sure they have a template as appropriate
##    if hasPortrait and (Object_GetClass(name)&OC_CONTROLLABLE)==0:
##        AI_MakeIntoHero(name)
##    if (hasPortrait==0) and (Object_GetClass(name)&OC_CONTROLLABLE)!=0:
##        FFX_DestroyPortrait(name)
##    if hasPortrait:
##        Mission_SelectHero(name)
##    team=Mission_GetAttr('team_%s'%(name))
##    initMinionAI(name,team,'',isEvil(name))
##    if usingM25AI():
##        m25ai.TransferAI(name,char)
##    Object_PlayEffect(name,getMorphFX(char),'',FX_TRACK_OBJECT_FULL)
##    #restore health etc as appropriate
##    if proportional==0:
##        Object_SetAttr(name,'health',Mission_GetAttr('health_%s'%(name)))
##        Object_SetAttr(name,'maxHealth',Mission_GetAttr('maxHealth_%s'%(name)))
##    else:
##        #maxhealth is set by template
##        Object_SetAttr(name,'health',(Mission_GetAttr('health_%s'%(name))*Object_GetAttr(name,'maxHealth'))/Mission_GetAttr('maxHealth_%s'%(name)))
##    Object_SetAttr(name,'energyPoints',Mission_GetAttr('energyPoints_%s'%(name)))
##    Object_SetAttr(name,'heroPoints',Mission_GetAttr('heroPoints_%s'%(name)))
##    if Object_IsAlive(name)==0:
##        Object_SetAttr(name,'health',1)
##        kill(name)

def morph2(name,char,template,proportional,hasPortrait):
    print 'Calling morph2(%s,%s,%s,%d,%d) % (repr(name),repr(char),repr(template),proportional,hasPortrait)
    #spawn the new form in the same spot as the old one
    FFX_Spawn(name,template,char)
    FFX_ObjectSetAttr(name,'morph',1)
    #make sure they have a template as appropriate
    print '    Object_GetClass(%s)=%d' % (repr(name), Object_GetClass(name))
    if hasPortrait and (Object_GetClass(name)&OC_CONTROLLABLE)==0:
        AI_MakeIntoHero(name)
        print '    morph2: AI_MakeIntoHero(%s)' % (repr(name), )
    if (hasPortrait==0) and (Object_GetClass(name)&OC_CONTROLLABLE)!=0:
        FFX_DestroyPortrait(name)
        print '    morph2: FFX_DestroyPortrait(%s)' % (repr(name), )
    if hasPortrait:
        Mission_SelectHero(name)
        print '    morph2: Mission_SelectHero(%s)' % (repr(name), )
    team=Mission_GetAttr('team_%s'%(name))
    initMinionAI(name,team,'',isEvil(name))
    if usingM25AI():
        m25ai.TransferAI(name,char)
        print '    morph2: m25ai.TransferAI(%s,%s)' % (repr(name), repr(char), )
    Object_PlayEffect(name,getMorphFX(char),'',FX_TRACK_OBJECT_FULL)
    print '    morph2: Object_Exists(%s)=%d' % (repr(name),Object_Exists(name))
    #restore health etc as appropriate
    if proportional==0:
        print "    morph2: Object_SetAttr(%s,'health',Mission_GetAttr('health_%%s'%(%s)))" % (repr(name), repr(name), )
        Object_SetAttr(name,'health',Mission_GetAttr('health_%s'%(name)))
        print "    morph2: Object_SetAttr(%s,'maxHealth',Mission_GetAttr('maxHealth_%%s'%(%s)))" % (repr(name), repr(name), )
        Object_SetAttr(name,'maxHealth',Mission_GetAttr('maxHealth_%s'%(name)))
    else:
        #maxhealth is set by template
        print "    morph2: Mission_GetAttr('maxHealth_%%s'%(%s))=%d" % (repr(name), Mission_GetAttr('maxHealth_%s'%(name)), )
        print "    morph2: Mission_GetAttr('health_%%s'%(%s))=%d" % (repr(name), Mission_GetAttr('health_%s'%(name)), )
        print "    morph2: Object_GetAttr(%s,'maxHealth')=%d" % (repr(name), Object_GetAttr(name,'maxHealth'), )
        Object_SetAttr(name,'health',(Mission_GetAttr('health_%s'%(name))*Object_GetAttr(name,'maxHealth'))/Mission_GetAttr('maxHealth_%s'%(name)))
    Object_SetAttr(name,'energyPoints',Mission_GetAttr('energyPoints_%s'%(name)))
    Object_SetAttr(name,'heroPoints',Mission_GetAttr('heroPoints_%s'%(name)))
    if Object_IsAlive(name)==0:
        Object_SetAttr(name,'health',1)
        kill(name)


Then run the same session and let's see what the log says.
Courage is knowing it might hurt, and doing it anyway. Stupidity is the same. And that's why life is hard. - Jeremy Goldberg

seraglio

I'll give this a try as soon as I get home! That was basically my line of thinking too, put print statements after every line till I see what breaks, but was to scared to do it. Thanks so much.