Jump to content

Ardanis

Modders
  • Posts

    2,793
  • Joined

  • Last visited

Posts posted by Ardanis

  1. There's no guaranteed hit on a character using a ranged weapon, nor a +4 damage. There's only a +4 bonus to THAC0, which is what the manual says IIRC.

     

    Melee attacks against targets without melee weapon equipped receive +4 thaco/damage bonus.

     

    If I were to hazard a guess, you're playing as monk and for some reason his fists don't function as melee weapons. Honestly, I don't remember if it was supposed to be like that in vanilla.

  2. Try wrapping it into ~SetInterrupt(FALSE) /* ... */ SetInterrupt(TRUE)~. I don't remember if it works on actors only or from area/global scripts as well, but uninterruptible actions prevent saving until they're complete.

  3. BIO strref means nothing, really.

     

    Check if creature's script name exists in PDIALOG.2DA, iirc this is how they're normally detected as valid party members.

    What Kathos said about SoD also applies, there maybe special cases when a character is supposed to be high enough level for plot purpose, either as ally or as enemy.

     

    PS Have a look at NPCLEVEL.2DA, should be possible to replace all level 2+ CREs with level 1 version, so even if you face an NPC as a high level boss, when they JoinParty() they'll drop to level 1 (I think it works this way). EE v2.0 has actually done exactly that, basically killing the entire purpose of that table and replacing the level growth with scripted XP adjustment, so you may need to fix that if it blocks your design.

    • Goddamn Cromwell shouldn't goddamn take a whole goddamn day to goddamn forge his goddamn stuff.

     

     

     

    COPY_EXISTING cromwell.bcs override
      DECOMPILE_AND_PATCH BEGIN
        REPLACE_TEXTUALLY ~RestParty\(Ex\)?(.*)[%WNL%%LNL%%MNL%%TAB% ]+AdvanceTime(.+)~ ~~
      END
    BUT_ONLY
    
    
    ACTION_IF FILE_EXISTS_IN_GAME cutskip2.bcs BEGIN // if pre-EE v2.0
      COPY_EXISTING cutskip2.bcs override
        DECOMPILE_AND_PATCH BEGIN
          REPLACE_TEXTUALLY ~ActionOverride("wsmith01",Face(\(SE\|14\)))[%WNL%%LNL%%MNL%%TAB% ]+RestParty\(Ex\)?(.*)[%WNL%%LNL%%MNL%%TAB% ]+AdvanceTime(.+)~ ~ActionOverride("wsmith01",Face(SE))~
        END
      BUT_ONLY
    END

     

     

  4. My version was like:

     

     

    Preliminary:

    - Scan all official areas for triggers, doors and containers that are trapped & detectable & have difficulty !=100.

    - Manually review all the scripts on the obtained list and write appropriate "trap type is X" new string for each script. A few will need to be excluded, because even while they're set up as traps and detected as such by the check routine, they still aren't a trap.

    - Now you have a list of trap scripts and a list of detected type strings, match them up via associative array or something.

     

    Prepare the script extension (top):

    IF Range([PC],15) !GlobalTimerNotExpired("%script_name%","myarea") Global("%script_name%_detected","myarea",0) Global("%script_name%_disarmed","myarea",0) THEN RESPONSE #100 SetGlobalTimer("%script_name%","myarea",6) DisplayStringHead(Myself,~Trap Detected~) END
    
    IF Range([PC],15) !GlobalTimerNotExpired("%script_name%","myarea") Global("%script_name%_detected","myarea",1) Global("%script_name%_disarmed","myarea",0) THEN RESPONSE #100 SetGlobalTimer("%script_name%","myarea",6) DisplayStringHead(Myself,~trap_type_string_that_matches_the_scriptname~) END
    
    IF Detected() Global("%script_name%_detected","myarea",0) Global("%script_name%_disarmed","myarea",0) THEN RESPONSE #100 SetGlobal("%script_name%_detected","myarea",1) SetGlobalTimer("%script_name%","myarea",6) DisplayStringHead(Myself,~trap_type_string_that_matches_the_scriptname~) END
    
    IF Disarmed() Global("%script_name%_disarmed","myarea",0) THEN RESPONSE #100 SetGlobal("%script_name%_disarmed","myarea",1) END
    
    IF OR() Entered() Opened() AndProbablyThereWasAnotherTrigger() THEN RESPONSE #100 SetGlobal("%script_name%_disarmed","myarea",1) Continue() END
    
    IF Reset() THEN RESPONSE #100 SetGlobal("%script_name%_disarmed","myarea",0) Continue() END
    I think it was kinda like that... Forgot if Continue()'s are correct, better test it.

     

    Area patching:

    - Scan all areas for the scripts on the list. There will be multiple instances of the same script used in different trigger regions. For each duplicate copy the original script under unique new name, extend it and eval (those %script_name% vars), and write new script entry into the trigger.

    - Keep a track of each trap type's max number of duplicates in a single area (I used arrays), so that if you run into another area with duplicates of that type then you can just use existing copies of the trap script created for previously processed areas.

     

     

  5.  

    Dead Player1 also breaks most of cutscenes, as they typically use him as CutsceneID (and sometimes it *must* be player1, e.g. for LeaveAreaLUA).

    If i win a fight which will immediate trigger a cut scene, it's possible to break the game?

     

    Dead actor usually can run the cutscene, but they're unable to Wait(). Area transitions also require to be run by Player1 (especially in multiplayer), and I think a couple times I've had issues with complex StartCutsceneMode() + CutsceneLite(TRUE) + breakable cutscene combinations if they weren't done by Player1 as well. Waiting was also required for some area transitions (e.g. dreams) to not fall apart in multiplayer mode.

    So, as a rule of thumb, consider the possibility of playing with dead Charname to be a bug that must be rectified ASAP via resurrection, not a stable feature you can rely upon.

  6. Instead of adding your door image to the tileset, make it a BAM file and place in the area as ambient animation. Positioning can be calculated based on the tile size and tile offset.

    You can set time of day when animation is active, so one for day version and another for night will work fine. Dusk and dawn are problematic, however, because of dynamic lighting adjustment. I've seen it almost working for dusk with day animation's light source flagged off, but not for dawn.

  7. If you need it for a mod, just ship the updated tileset. I don't even think there was at least one that added new tiles to existing area? That, and since doors themselves are among the most complex structures for random people to bother, you aren't gonna run into compatibility issues.

     

    PS You can also kind of fake the door with a bam overlay, without touching the tileset itself, but it doesn't work too well for day/night areas (namely during dawn/dusk transitions) and would need scripting activate/deactivate, so you can't really use it as a "normal" door that player can interact with.

  8. ADD_SPELL_HEADER

    ADD_ITEM_HEADER

    CREATE_EFFECT

    Also CREATE_SPELL, but it's probably not a thing many would care about.

     

    Parameters should be self-explanatory. Can also copy an existing one.

    I had this for items as well, but I don't think I've got the code anywhere in close vicinity.

    EDIT Here it is https://github.com/Gibberlings3/ItemRevisions/blob/master/item_rev/lib/macros.tpa

     

     

     

     

     

     

     

     

    DEFINE_PATCH_FUNCTION ~ADD_SPELL_HEADER~
      INT_VAR
        type=1
        location=4
        target=1
        target_count=0
        range=0
        required_level=1
        speed=0
        projectile=1
    
    
        copy_header=0
        insert_point=~-1~
      STR_VAR
        icon=~~
      RET
        insert_point
    BEGIN
      LPF ~FJ_SPL_ITM_REINDEX~ END
      hs=0x28
    
    
      READ_LONG 0x64 ho
      READ_SHORT 0x68 hc
      READ_LONG 0x6a eo
      insert_point = (insert_point>hc || insert_point<0) ? hc : insert_point
      copy_header = (copy_header<0) ? 0 : copy_header
    
    
      PATCH_IF copy_header>hc BEGIN
        PATCH_WARN ~Unable to copy %copy_header%th header, %SOURCE_FILE% contains only %hc% headers!~
      END ELSE BEGIN
        INSERT_BYTES ho+insert_point*hs hs
        hc+=1
        eo+=hs
        PATCH_IF copy_header BEGIN
          READ_SHORT ho+(copy_header - 1)*hs+0x1e ec
          READ_SHORT ho+(copy_header - 1)*hs+0x20 ei
          READ_ASCII eo+ei*0x30 effs (ec*0x30)
          READ_ASCII ho+(copy_header - 1)*hs copy (hs)
          WRITE_ASCIIE ho+insert_point*hs ~%copy%~ (hs)
        END
        WRITE_SHORT 0x68 hc
        WRITE_LONG 0x6a eo
    
    
        READ_SHORT 0x70 ei // technically, it is a counter
        FOR (i=ho;i<ho+hc*hs;i+=hs) BEGIN
          READ_SHORT i+0x1e ec
          WRITE_SHORT i+0x20 ei
          ei+=ec
        END
    
    
        PATCH_IF copy_header BEGIN
          READ_SHORT ho+insert_point*hs+0x1e ec
          READ_SHORT ho+insert_point*hs+0x20 ei
          INSERT_BYTES eo+ei*0x30 ec*0x30
          WRITE_ASCIIE eo+ei*0x30 ~%effs%~ (ec*0x30)
        END ELSE BEGIN
          off=ho+insert_point*hs
          WRITE_BYTE off type
          WRITE_BYTE off+0x2 location
          WRITE_ASCIIE off+0x4 ~%icon%~ (8)
          WRITE_BYTE off+0xc target
          WRITE_BYTE off+0xd target_count
          WRITE_SHORT off+0xe range
          WRITE_SHORT off+0x10 required_level
          WRITE_LONG off+0x12 speed
          WRITE_SHORT off+0x26 projectile
        END
      END
    END

     

     

    update_item_descriptions_to_bgee

    Updates vanilla item descriptions to EE format, i.e. stripping Usable By section, so that you don't have to provide two strings for vanilla and EE descriptions.

    https://github.com/Gibberlings3/ItemRevisions/blob/master/item_rev/lib/usability_description.tpa#L77

     

     

    // make sure to add these two to your TRA files, adjust the numbers if needed
    //OUTER_SPRINT usab @900000 // ~Usable[ %tab%]+[Bb]y[ %tab%]*:~
    //OUTER_SPRINT unus @900001 // ~\(Not[ %tab%]+\|Un\)[Uu]sable[ %tab%]+[Bb]y[ %tab%]*:~
    OUTER_SPRINT usab ~Usable[ %tab%]+[Bb]y[ %tab%]*:~
    OUTER_SPRINT unus ~\(Not[ %tab%]+\|Un\)[Uu]sable[ %tab%]+[Bb]y[ %tab%]*:~
     
    DEFINE_PATCH_FUNCTION ~update_item_descriptions_to_bgee~ BEGIN
      PATCH_IF (ENGINE_IS ~bgee bg2ee~) BEGIN
        FOR (index = 0x54 ; index >= 0x50 ; index -= 4) BEGIN // loop through descriptions
          READ_LONG index strref
          PATCH_IF (strref < 2147483646 && strref >= 0) BEGIN // verify description is valid
            READ_STRREF index description
            INNER_PATCH_SAVE new_desc ~%description%~ BEGIN
              REPLACE_TEXTUALLY ~\(\([%LNL%%MNL%%WNL%][ %TAB%]*\(%usab%\|%unus%\)[ %TAB%]*\)\(\([%LNL%%MNL%%WNL%].*\)*\)?\)~  ~~
            END
            SAY_EVALUATED index ~%new_desc%~
          END
        END
      END
    END

     

     

    Example usage 1, by item:

     

     

    COPY_EXISTING ~mymod/myitem.itm~ override
      SAY_DESC ~Whatever~
      LPF update_item_descriptions_to_bgee END

     

    Example usage 2, batch update (caution, only really useful if you've got a simple content mod and don't do any level 80 patching:

     

     

    // run this after all items have been copied into the game, i.e. near the end of tp2
    ACTION_IF (ENGINE_IS ~bgee bg2ee~) BEGIN
    
      ACTION_FOR_EACH directory IN items itm any_other_subfolder_that_contains_installable_items BEGIN
        ACTION_BASH_FOR ~MyModFolder/%directory%~ ~.*\.itm~ BEGIN // get a list of .itm files in the mod directory
          ACTION_IF FILE_EXISTS_IN_GAME ~%BASH_FOR_FILE%~ BEGIN // if those files have been copied into override
            COPY_EXISTING ~%BASH_FOR_FILE%~ override // load those files from override, i.e. with descriptions already SAY'ed
              LPF update_item_descriptions_to_bgee END // patch the description
            BUT_ONLY
          END
        END
      END
    
    END
    

     

  9. I'd be very happy for a "Dungeon-B-gone" type mod for SoD that skips through first dungeon, with an optional component that skips through whole BG city and puts you in the first wilderness area in SoD.

     

     

    Add SetPrivateProfileString('Script','QAMODE','1') line to baldur.lua, then in-game select Player1 and press B key. Only works in SoD chapters, though.


  10. I hope he will include some other tweaks as well; if for nothing than for SoD AI to work properly (mages use Haste to speed up entire armies, but with SR they fail with it...).
    I think the only place where it really counts is at the beginning of castle assault, and only to ensure that enough allies will run through the gates before crusaders can bottleneck it.
  11. About Dead() and NumDead(), are those found in the creatures ai scripts? or is it something that have logic behind it and the developers put it in some known script for such things?

     

    Can be found in any script or dialog, so it's better to compile a list of quest actors first, then refer to it.

  12. I am not checking for quest items, cause i think it doesn't matter if you've got 2 quest items. Do you think it can cause a problem down the line? if so i'll fix it.

     

    There may be death variable checks, that can interfere with SoD's scripting if the actors get duplicated. I think most should get filtered out by the checks you mentioned, though... If you want to be on a safe side, then read scripts/dialogs for a list of Dead(), NumDead**(), Global**("SPRITE_IS_DEADxxxx",) etc. names and exclude actors with DVs from the list. Also note that embedded actors may have their scripting name/DV overriden by the bit 3 flag, so it's usually a good idea to not clone those either.

  13. I need to interpret the BCS file and duplicate CreateCreature on specific conditions.

     

    What are the conditions, btw? It's the most interesting part that you've omitted :)

    Because I can't really imagine an automated bullet-proof way to separate quest-related actors from others.

  14. I agree, but I don't really fiddle with cast'n'attack stuff - best I could do is remove this spell from liches which would make them try to attack melee (they're awfully slow-moving and it would be horrible).

     

    I made the passive attack routine for SoD mages - they'll attack if target is within range or if they're out of spells, otherwise they stay rooted.

     

    ///////////////////////////////////////////////////////////////////////
    // if in melee range, equip melee and attack nearest
    // if have ranged equipped, attack nearest
    // attack with ranged weapon if more than 1 round has passed since the last time i've used a spell
    // otherwise just equip ranged without attacking
    IF
    See(NearestEnemyOf(Myself))
    Range(LastSeenBy(Myself),5)
    THEN RESPONSE #100
    EquipMostDamagingMelee()
    AttackReevaluate(NearestEnemyOf(Myself),30)
    END
    IF
    See(NearestEnemyOf(Myself))
    IsWeaponRanged(Myself)
    THEN RESPONSE #100
    AttackReevaluate(NearestEnemyOf(Myself),30)
    END
    IF
    See(NearestEnemyOf(Myself))
    !GlobalTimerNotExpired("bd_cast","locals")
    THEN RESPONSE #100
    EquipRanged()
    AttackReevaluate(NearestEnemyOf(Myself),30)
    END
    IF
    See(NearestEnemyOf(Myself))
    Delay(2)
    THEN RESPONSE #100
    EquipRanged()
    END

     

×
×
  • Create New...