Author Topic: Timed Combat Party Port Problem  (Read 421 times)

Legacy_Sadira of Tyr

  • Sr. Member
  • ****
  • Posts: 299
  • Karma: +0/-0
Timed Combat Party Port Problem
« on: July 13, 2011, 01:06:06 pm »


               I've been trying to create a timed combat sequence to simulate a fight onboard a burning pirate ship. The captain takes the player(s) on deck to make them walk the plank, but during the conversation a fire starts and combat begins. After a period of time the ship will explode and all players will be ported to a new location on a beach near a wrecked pirate ship. This is part of a quest and the party port is the only way to advance to this location. Defeating the pirate caption rewards the player with the key to the pirate captain's house with some nice items stored inside.

If the player(s) defeat the pirates, the script works just fine, and everyone is ported. However the problem is that the party port will not fire if the fight has not been completed. My first guess would be that the port will not happen during combat. Is this a NWN limitation, or is it possible to make it work during combat?

The script is fired in a conversation with the pirate captain. Another script on the last line of the conversation will turn the captain hostile. The captain's corpse has been set to max, so his death will not end the script. I thought to have the script destroy the pirates just before time runs out to stop the combat, but I think that will end the script because the captain is gone. Perhaps using effect death on the captain would work. I figured I would ask here on this forum for a solution before making any changes. I know there are some expert scripters here, and I'm sure one of you would know the best solution for my problem.

Any help would be greatly appreciated. Thank you.

Here is the script:

#include "nw_i0_2q4luskan"
void main()
{
object oPC = GetPCSpeaker();
object oCaster;
oCaster = GetObjectByTag("sot_firebarrel");
object oTarget;
oTarget = GetObjectByTag("sot_firebarrel");
AssignCommand(oCaster, ActionCastSpellAtObject(SPELL_FIREBALL, oTarget, METAMAGIC_ANY, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE));
object oSpawn;
location lTarget;
oTarget = GetWaypointByTag("sot_WPfire1a");
lTarget = GetLocation(oTarget);
oSpawn = CreateObject(OBJECT_TYPE_PLACEABLE, "plc_flamelarge", lTarget);
oTarget = GetWaypointByTag("sot_WPfire1b");
lTarget = GetLocation(oTarget);
DelayCommand(30.0, CreateObjectVoid(OBJECT_TYPE_PLACEABLE, "plc_flamelarge", lTarget));
oTarget = GetWaypointByTag("sot_WPfire1f");
lTarget = GetLocation(oTarget);
DelayCommand(60.0, CreateObjectVoid(OBJECT_TYPE_CREATURE, "northlandpirate", lTarget));
oTarget = GetWaypointByTag("sot_WPfire1c");
lTarget = GetLocation(oTarget);
DelayCommand(90.0, CreateObjectVoid(OBJECT_TYPE_PLACEABLE, "plc_flamelarge", lTarget));
oTarget = GetWaypointByTag("sot_WPfire1d");
lTarget = GetLocation(oTarget);
DelayCommand(120.0, CreateObjectVoid(OBJECT_TYPE_PLACEABLE, "plc_flamelarge", lTarget));
oTarget = GetWaypointByTag("sot_WPfire1f");
lTarget = GetLocation(oTarget);
DelayCommand(150.0, CreateObjectVoid(OBJECT_TYPE_CREATURE, "northpiratemage", lTarget));
oTarget = GetWaypointByTag("sot_WPfire1e");
lTarget = GetLocation(oTarget);
DelayCommand(180.0, CreateObjectVoid(OBJECT_TYPE_PLACEABLE, "plc_flamelarge", lTarget));
oTarget = GetWaypointByTag("sot_WPfire1g");
lTarget = GetLocation(oTarget);
DelayCommand(210.0, CreateObjectVoid(OBJECT_TYPE_PLACEABLE, "plc_flamelarge", lTarget));
oTarget = GetWaypointByTag("sot_WPfire1f");
lTarget = GetLocation(oTarget);
DelayCommand(240.0, CreateObjectVoid(OBJECT_TYPE_CREATURE, "northlandpirate", lTarget));
lTarget = GetLocation(oTarget);
DelayCommand(250.0, CreateObjectVoid(OBJECT_TYPE_PLACEABLE, "plc_flamelarge", lTarget));
oTarget = GetWaypointByTag("sot_WPfire1h");
lTarget = GetLocation(oTarget);
DelayCommand(270.0, CreateObjectVoid(OBJECT_TYPE_PLACEABLE, "plc_flamelarge", lTarget));
oTarget = GetWaypointByTag("sot_WPfire1i");
lTarget = GetLocation(oTarget);
DelayCommand(290.0, CreateObjectVoid(OBJECT_TYPE_PLACEABLE, "plc_flamelarge", lTarget));
oTarget = GetWaypointByTag("sot_WPfire1j");
lTarget = GetLocation(oTarget);
DelayCommand(310.0, CreateObjectVoid(OBJECT_TYPE_PLACEABLE, "plc_flamelarge", lTarget));
oTarget = GetWaypointByTag("sot_WPfire1k");
lTarget = GetLocation(oTarget);
DelayCommand(330.0, CreateObjectVoid(OBJECT_TYPE_PLACEABLE, "plc_flamelarge", lTarget));
oTarget = GetWaypointByTag("sot_WPfire1l");
lTarget = GetLocation(oTarget);
DelayCommand(340.0, CreateObjectVoid(OBJECT_TYPE_PLACEABLE, "plc_flamelarge", lTarget));
DelayCommand(350.0, AssignCommand(oPC, ActionSpeakString("THE SHIP'S GOING TO BLOW! JUMP IN THE WATER!")));
oCaster = GetObjectByTag("sot_firebarrel");
oTarget = GetObjectByTag("sot_WPfire1a");
DelayCommand(360.0, AssignCommand(oCaster, ActionCastSpellAtLocation(SPELL_FIREBALL, GetLocation(oTarget), METAMAGIC_ANY, TRUE,
PROJECTILE_PATH_TYPE_DEFAULT, FALSE)));
oCaster = GetObjectByTag("sot_firebarrel");
oTarget = GetObjectByTag("sot_WPfire1b");
DelayCommand(360.0, AssignCommand(oCaster, ActionCastSpellAtLocation(SPELL_FIREBALL, GetLocation(oTarget), METAMAGIC_ANY, TRUE,
PROJECTILE_PATH_TYPE_DEFAULT, FALSE)));
oCaster = GetObjectByTag("sot_firebarrel");
oTarget = GetObjectByTag("sot_WPfire1c");
DelayCommand(360.0, AssignCommand(oCaster, ActionCastSpellAtLocation(SPELL_FIREBALL, GetLocation(oTarget), METAMAGIC_ANY, TRUE,
PROJECTILE_PATH_TYPE_DEFAULT, FALSE)));
oCaster = GetObjectByTag("sot_firebarrel");
oTarget = GetObjectByTag("sot_WPfire1d");
DelayCommand(360.0, AssignCommand(oCaster, ActionCastSpellAtLocation(SPELL_FIREBALL, GetLocation(oTarget), METAMAGIC_ANY, TRUE,
PROJECTILE_PATH_TYPE_DEFAULT, FALSE)));
oCaster = GetObjectByTag("sot_firebarrel");
oTarget = GetObjectByTag("sot_WPfire1e");
DelayCommand(360.0, AssignCommand(oCaster, ActionCastSpellAtLocation(SPELL_FIREBALL, GetLocation(oTarget), METAMAGIC_ANY, TRUE,
PROJECTILE_PATH_TYPE_DEFAULT, FALSE)));
oCaster = GetObjectByTag("sot_firebarrel");
oTarget = GetObjectByTag("sot_WPfire1f");
DelayCommand(360.0, AssignCommand(oCaster, ActionCastSpellAtLocation(SPELL_FIREBALL, GetLocation(oTarget), METAMAGIC_ANY, TRUE,
PROJECTILE_PATH_TYPE_DEFAULT, FALSE)));
oCaster = GetObjectByTag("sot_firebarrel");
oTarget = GetObjectByTag("sot_WPfire1g");
DelayCommand(360.0, AssignCommand(oCaster, ActionCastSpellAtLocation(SPELL_FIREBALL, GetLocation(oTarget), METAMAGIC_ANY, TRUE,
PROJECTILE_PATH_TYPE_DEFAULT, FALSE)));
oCaster = GetObjectByTag("sot_firebarrel");
oTarget = GetObjectByTag("sot_WPfire1h");
DelayCommand(360.0, AssignCommand(oCaster, ActionCastSpellAtLocation(SPELL_FIREBALL, GetLocation(oTarget), METAMAGIC_ANY, TRUE,
PROJECTILE_PATH_TYPE_DEFAULT, FALSE)));
oCaster = GetObjectByTag("sot_firebarrel");
oTarget = GetObjectByTag("sot_WPfire1i");
DelayCommand(360.0, AssignCommand(oCaster, ActionCastSpellAtLocation(SPELL_FIREBALL, GetLocation(oTarget), METAMAGIC_ANY, TRUE,
PROJECTILE_PATH_TYPE_DEFAULT, FALSE)));
oCaster = GetObjectByTag("sot_firebarrel");
oTarget = GetObjectByTag("sot_WPfire1j");
DelayCommand(360.0, AssignCommand(oCaster, ActionCastSpellAtLocation(SPELL_FIREBALL, GetLocation(oTarget), METAMAGIC_ANY, TRUE,
PROJECTILE_PATH_TYPE_DEFAULT, FALSE)));
oCaster = GetObjectByTag("sot_firebarrel");
oTarget = GetObjectByTag("sot_WPfire1k");
DelayCommand(360.0, AssignCommand(oCaster, ActionCastSpellAtLocation(SPELL_FIREBALL, GetLocation(oTarget), METAMAGIC_ANY, TRUE,
PROJECTILE_PATH_TYPE_DEFAULT, FALSE)));
oCaster = GetObjectByTag("sot_firebarrel");
oTarget = GetObjectByTag("sot_WPfire1l");
DelayCommand(360.0, AssignCommand(oCaster, ActionCastSpellAtLocation(SPELL_FIREBALL, GetLocation(oTarget), METAMAGIC_ANY, TRUE,
PROJECTILE_PATH_TYPE_DEFAULT, FALSE)));
object oParty = GetFirstFactionMember(oPC);
oTarget = GetWaypointByTag("sot_WPruinedshipcove");
lTarget = GetLocation(oTarget);
if (GetAreaFromLocation(lTarget)==OBJECT_INVALID) return;
AssignCommand(oPC, ClearAllActions());
DelayCommand(365.0, AssignCommand(oPC, ActionJumpToLocation(lTarget)));
oTarget = oPC;
   while (GetIsObjectValid(oParty))
   {
      if(GetArea(oPC)==GetArea(oParty))
      {
DelayCommand(365.0, AssignCommand(oParty, ActionJumpToLocation(lTarget)));
            }
      oParty = GetNextFactionMember(oPC);
      }
}
               
               

               
            

Legacy_henesua

  • Hero Member
  • *****
  • Posts: 6519
  • Karma: +0/-0
Timed Combat Party Port Problem
« Reply #1 on: July 13, 2011, 08:07:14 pm »


               I'm not sure what the specific problem is that you are encountering... but (while I could be wrong) I am not confidant that you have enough control over the situation after 365 seconds to trust that the players can be jumped at that moment. One question I have for example is whether all PCs are still commandable at that moment.

So a couple suggestions:
(1) if you find that you have many DelayCommands with the same delay, 360.0 for example. I'd write a function, delay that 360.0. And then pile your actions in that function without any delays since the function call itself is delayed.

(2) Put your timed port of PCs and the party in a function. Delay the call of that function by 365.0.
ClearALLActions for each of the PCs before you jump them. Also add to this function a check of a local int set on the PC that indicates whether the PC has been jumped to the beach or not, do not jump them if they have already been jumped. Once each PC is teleported flag them as teleported to the beach.

(3) Capture the event of the Pirate captain's death/defeat. When the captain is defeated signal the final explosions and transport of the PCs AND then when the PCs arrive on the beach flag them as having been teleported.
               
               

               
            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0
Timed Combat Party Port Problem
« Reply #2 on: July 13, 2011, 09:59:33 pm »


               NWN's jumping is very unreliable if you're adding it to the action queue, as you are - players themselves can clear it, along with a host of other possible screwups. Instead, use this function, for when you absolutely, positively need to port every last - er, for when you need to get them somewhere, no matter what. Not only is it designed to block common bugs like yours, as well as a host of exploits - it'll keep trying if it fails:


void ForceJump (object oTarget, location lTarget, int bClearCombat=TRUE, int bForceCommandable=FALSE) {
    if (!GetIsObjectValid(oTarget) || GetObjectType(oTarget) != OBJECT_TYPE_CREATURE)
        return;

    if (!GetIsObjectValid(GetArea(oTarget))) {
        DelayCommand(5.0, ForceJump(oTarget, lTarget, bClearCombat, bForceCommandable));
        return;
    }

    if (GetIsDead(oTarget)) {
        ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectResurrection(), oTarget);
        ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectHeal(GetMaxHitPoints(oTarget)), oTarget);

        effect eBad = GetFirstEffect(oTarget);
        while (GetIsEffectValid(eBad)) {
            if (GetEffectIsNegative(eBad))
                RemoveEffect(oTarget, eBad);

            eBad = GetNextEffect(oTarget);
        }

        if (GetIsPC(oTarget))
            AssignCommand(oTarget, ExecuteScript("fky_deathprocess", oTarget));

        DelayCommand(0.1, ForceJump(oTarget, lTarget, bClearCombat, bForceCommandable));
    } else if (!GetCommandable(oTarget)) {
        if (bForceCommandable) {
            AssignCommand(oTarget, SetCommandable(TRUE));

            if (bForceCommandable >= 5)
                WriteTimestampedLogEntry("FORCEJUMP : " + GetPCPlayerName(oTarget) + " : " +
                    GetName(oTarget) + " : more than 5 attempts to force commandable jumping to " +
                    GetResRef(GetAreaFromLocation(lTarget)));
        }

        DelayCommand(1.0, ForceJump(oTarget, lTarget, bClearCombat, ++bForceCommandable));
    } else {
        AssignCommand(oTarget, ClearAllActions(bClearCombat));

        AssignCommand(oTarget, ActionJumpToLocation(lTarget));
        AssignCommand(oTarget, ActionDoCommand(SetCommandable(TRUE)));
        AssignCommand(oTarget, SetCommandable(FALSE));
    }
}

Just slap it in at the top of the script, then substitute ForceJump for the ActionJump call. You should also probably have the delay run on the area:

AssignCommand(GetArea(oPC), DelayCommand(365.0, ForceJump(oParty, lTarget)));

Funky
               
               

               


                     Modifié par FunkySwerve, 13 juillet 2011 - 09:00 .
                     
                  


            

Legacy_Sadira of Tyr

  • Sr. Member
  • ****
  • Posts: 299
  • Karma: +0/-0
Timed Combat Party Port Problem
« Reply #3 on: July 14, 2011, 12:05:45 am »


               Ok, I'll try adding that script. Thank you Funky. 'Posted
               
               

               
            

Legacy_Sadira of Tyr

  • Sr. Member
  • ****
  • Posts: 299
  • Karma: +0/-0
Timed Combat Party Port Problem
« Reply #4 on: July 14, 2011, 12:54:38 am »


               Well, I tried adding the script to the top, but I could not get it to compile.

For this line:            if (GetEffectIsNegative(eBad))

It said no right bracket after expression.               ;

I imagine this line will give errors too since our module does not use that script "fky_deathprocess"

              AssignCommand(oTarget, ExecuteScript("fky_deathprocess", oTarget));

Thank you for helping though.       
               
               

               
            

Legacy_Sadira of Tyr

  • Sr. Member
  • ****
  • Posts: 299
  • Karma: +0/-0
Timed Combat Party Port Problem
« Reply #5 on: July 14, 2011, 01:24:22 am »


               

henesua wrote...

I'm not sure what the specific problem is that you are encountering... but (while I could be wrong) I am not confidant that you have enough control over the situation after 365 seconds to trust that the players can be jumped at that moment. One question I have for example is whether all PCs are still commandable at that moment.

So a couple suggestions:
(1) if you find that you have many DelayCommands with the same delay, 360.0 for example. I'd write a function, delay that 360.0. And then pile your actions in that function without any delays since the function call itself is delayed.

(2) Put your timed port of PCs and the party in a function. Delay the call of that function by 365.0.
ClearALLActions for each of the PCs before you jump them. Also add to this function a check of a local int set on the PC that indicates whether the PC has been jumped to the beach or not, do not jump them if they have already been jumped. Once each PC is teleported flag them as teleported to the beach.

(3) Capture the event of the Pirate captain's death/defeat. When the captain is defeated signal the final explosions and transport of the PCs AND then when the PCs arrive on the beach flag them as having been teleported.


Sorry, I missed your reply.

The problem I am having is that while the party port works fine if the combat is complete, it does not work at all if there is still combat at 365 seconds. The combat takes place on a small ship, so everyone is pretty close together. While trying it with other players, we killed all the pirates with about 30 seconds left, so everyone got ported at the exact time even though I was trading an item to another player. I tried doing it alone, but when I reached 365 seconds, I was still fighting and the port never happened.

I understand what you are trying to tell me about the functions, however I don't have the skills to do that yet. I made the script using the script generator and a little help. I have started using variables, but I don't know much about them yet. I am the only builder on our server, so I pretty much have had to learn as I go.

Thank you for your help.
               
               

               


                     Modifié par Sadira of Tyr, 14 juillet 2011 - 12:25 .
                     
                  


            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0
Timed Combat Party Port Problem
« Reply #6 on: July 14, 2011, 01:35:47 am »


               

Sadira of Tyr wrote...

Well, I tried adding the script to the top, but I could not get it to compile.

For this line:            if (GetEffectIsNegative(eBad))

It said no right bracket after expression.               ;

I imagine this line will give errors too since our module does not use that script "fky_deathprocess"

              AssignCommand(oTarget, ExecuteScript("fky_deathprocess", oTarget));

Thank you for helping though.       


Sorry about that, was kind of in a rush posting. You can remove the ExecuteScript line, or substitute your own scripted 'on resurrection' event if you have one. As for GetEffectIsNegative, here it is:


int GetEffectIsNegative (effect eEff, int nRestoreLevel=3) {
    int nEffType = GetEffectType(eEff);
    int nSpellId = GetEffectSpellId(eEff);


    if (nSpellId == SPELLABILITY_BARBARIAN_RAGE)
        return FALSE;
    if (nSpellId == SPELLABILITY_EPIC_CURSE_SONG)
        return TRUE;

    switch (GetEffectType(eEff)) {
        case EFFECT_TYPE_ABILITY_DECREASE:
        case EFFECT_TYPE_AC_DECREASE:
        case EFFECT_TYPE_ATTACK_DECREASE:
        case EFFECT_TYPE_DAMAGE_DECREASE:
        case EFFECT_TYPE_DAMAGE_IMMUNITY_DECREASE:
        case EFFECT_TYPE_SAVING_THROW_DECREASE:
        case EFFECT_TYPE_SPELL_RESISTANCE_DECREASE:
        case EFFECT_TYPE_SKILL_DECREASE:
            if (nRestoreLevel > 0)
                return nEffType;

        case EFFECT_TYPE_BLINDNESS:
        case EFFECT_TYPE_DEAF:
        case EFFECT_TYPE_PARALYZE:
        case EFFECT_TYPE_NEGATIVELEVEL:
            if (nRestoreLevel > 1)
                return nEffType;

        case EFFECT_TYPE_CURSE:
        case EFFECT_TYPE_DISEASE:
        case EFFECT_TYPE_POISON:
        case EFFECT_TYPE_CHARMED:
        case EFFECT_TYPE_DOMINATED:
        case EFFECT_TYPE_DAZED:
        case EFFECT_TYPE_CONFUSED:
        case EFFECT_TYPE_FRIGHTENED:
        case EFFECT_TYPE_SLOW:
        case EFFECT_TYPE_STUNNED:
            if (nRestoreLevel > 2)
                return nEffType;
    }

    return 0;
}

Let me know if you have any further difficulties.

Funky
               
               

               
            

Legacy_henesua

  • Hero Member
  • *****
  • Posts: 6519
  • Karma: +0/-0
Timed Combat Party Port Problem
« Reply #7 on: July 14, 2011, 01:46:33 am »


               Ultimately once you get Funky's script working with your other scripts. Its the way to go.

What I was trying to impress on you is that unforeseen things are interfering with the teleport. I've often been unable to add to a character's action stack unless I clear all their actions. BUT Funky's code addresses many more situations that could crop up. PCs could be killed during combat for example, in which case they would not be ported to the new location.

You should make his function work for you.
               
               

               
            

Legacy_Sadira of Tyr

  • Sr. Member
  • ****
  • Posts: 299
  • Karma: +0/-0
Timed Combat Party Port Problem
« Reply #8 on: July 14, 2011, 03:42:23 am »


               Well, I just finished testing the new script, and it worked great. 'Posted

I will have to test it later on the server with a party, but it worked just fine with me still in combat.

Thank you very much. 'Posted