//---------------------------------------------
// qd_multiclass 
// For adding existing kits to the multiclass kit selection during chargen.
// For use with the Enhanced Infinity Engine v2.0 and later. 
// Author: Aquadrizzt (updated by Subtledoctor, improved by Argent77)
// Version: 0.8
// Published: November 26, 2017 
//---------------------------------------------


DEFINE_PATCH_FUNCTION ~ADD_NEW_EMPTY_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
  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	


//---------------------------------------------------------------
// QD_MULTICLASS (by Aquadrizzt, etc.)
//---------------------------------------------------------------
OUTER_SPRINT qd_multiclass#version "0.8"

<<<<<<<< .../inlined/qd_blank_clab_inline.2da
2DA V1.0
****
            1           2           3           4           5           6           7           8           9           10          11          12          13          14          15          16          17          18          19          20     21          22          23          24          25          26          27          28          29          30          31          32          33          34          35          36          37          38          39          40
ABILITY1    ****        ****        ****        ****        ****        ****        ****        ****        ****        ****        ****        ****        ****        ****        ****        ****        ****        ****        ****        ****   ****        ****        ****        ****        ****        ****        ****        ****        ****        ****        ****        ****        ****        ****        ****        ****        ****        ****        ****        ****
>>>>>>>>

ACTION_IF (NOT FILE_EXISTS_IN_GAME ~CLABMA01.2DA~) BEGIN
  COPY ~.../inlined/qd_blank_clab_inline.2da~ ~override/CLABMA01.2DA~
END

// this will only install the first time a multiclass kit using this macro is installed
ACTION_IF (NOT FILE_EXISTS_IN_GAME ~qd_multi.qd~) BEGIN
  // make sure qdmulti row exists in each base class CLAB
  ACTION_DEFINE_ASSOCIATIVE_ARRAY base_clabs BEGIN
    ~CLABFI01~ => ~F~
    ~CLABTH01~ => ~T~
    ~CLABDR01~ => ~D~
    ~CLABMA01~ => ~M~
    ~CLABPR01~ => ~P~
    ~CLABRN01~ => ~R~
  END
  ACTION_PHP_EACH base_clabs AS resref => type BEGIN
    ACTION_IF (NOT FILE_CONTAINS_EVALUATED (~%resref%.2DA~ ~^QD_MULTI[ %TAB%]~)) BEGIN
      // padding table so that number of CLAB columns is consistent for each row
      OUTER_SET columns = 0
      COPY_EXISTING ~%resref%.2DA~ ~override~
        READ_2DA_ENTRIES_NOW rows 1
        READ_2DA_ENTRY_FORMER rows 1 0 default
        FOR (col = 41; col < 51; ++col) BEGIN // assuming first 40 columns are always defined
          TEXT_SPRINT string ~$ $~ // header and default row are ignored
          SET col2 = col - 1  // compensate for data header row being short by one entry
          PATCH_IF (VARIABLE_IS_SET $rows(~2~ ~%col2%~)) BEGIN
            TEXT_SPRINT string ~%string% $~
          END ELSE BEGIN
            TEXT_SPRINT string ~%string% %col%~
          END
          FOR (row = 3; row < rows; ++row) BEGIN
            PATCH_IF (VARIABLE_IS_SET $rows(~%row%~ ~%col%~)) BEGIN
              TEXT_SPRINT string ~%string% $~
            END ELSE BEGIN
              SET col2 = col - 10 // cloning a previous entry to keep table data consistent
              TEXT_SPRINT value $rows(~%row%~ ~%col2%~)
              TEXT_SPRINT string ~%string% %value%~
            END
          END
          // checking whether column has to be added
          PATCH_IF (INDEX(~[^ %TAB%$]~ ~%string%~) >= 0) BEGIN
            TEXT_SPRINT $columns(~%columns%~) ~%string%~
            SET columns += 1
          END
        END
      BUT_ONLY

      OUTER_FOR (col = 0; col < columns; ++col) BEGIN
        OUTER_TEXT_SPRINT string $columns(~%col%~)
        APPEND_COL ~%resref%.2DA~ ~%string%~
      END

      // adding QD_MULTI row
      OUTER_TEXT_SPRINT line ~QD_MULTI   ~
      OUTER_FOR (i = 1; i < 51; ++i) BEGIN
        ACTION_IF (i < 10) BEGIN
          OUTER_TEXT_SPRINT line ~%line% AP_QD_MC%type%0%i%~
        END ELSE BEGIN
          OUTER_TEXT_SPRINT line ~%line% AP_QD_MC%type%%i%~
        END
      END
      APPEND ~%resref%.2DA~ ~%line%~

      COPY_EXISTING ~%resref%.2DA~ ~override~
        PRETTY_PRINT_2DA
      BUT_ONLY
    END
  END

<<<<<<<< .../inlined/qd_blank_k_x_x_inline.2da
2DA V1.0
*
     KIT
1    0
>>>>>>>>

  // making the multiclass K_X_X
  ACTION_FOR_EACH mc IN ~FM~ ~FC~ ~FT~ ~FMT~ ~MT~ ~CM~ ~CT~ ~FD~ ~FMC~ ~CR~ BEGIN
    ACTION_FOR_EACH race IN ~H~ ~D~ ~G~ ~E~ ~HE~ ~HL~ ~HO~ BEGIN
      ACTION_IF (NOT FILE_EXISTS_IN_GAME ~K_%mc%_%race%.2da~) BEGIN
        COPY ~.../inlined/qd_blank_k_x_x_inline.2da~ ~override/K_%mc%_%race%.2da~
      END
    END
  END

  COPY_EXISTING ~sw1h01.itm~ ~override/qd_multi.qd~ // multiclass macro identifier file
END


DEFINE_ACTION_FUNCTION qd_multiclass
STR_VAR
  kit_name   = ""
  kit_clab   = ""
  base_class = ""
BEGIN
  ACTION_IF !(FILE_EXISTS_IN_GAME ~QD_MC_AP.eff~) BEGIN
    CREATE EFF ~QD_MC_AP~
      WRITE_LONG   0x10 146 // cast spell
      WRITE_LONG   0x14 2   // preset target
      WRITE_LONG   0x20 1   // cast instantly (ignore level)
      WRITE_SHORT  0x24 1   // permament until death
      WRITE_SHORT  0x2c 100 // probability 100%
  END

  ACTION_IF !(FILE_EXISTS_IN_GAME ~QD_MC_GA.eff~) BEGIN
    CREATE EFF ~QD_MC_GA~
      WRITE_LONG   0x10 171 // give spell
      WRITE_LONG   0x14 2   // preset target
      WRITE_SHORT  0x24 1   // permament until death
      WRITE_SHORT  0x2c 100 // probability 100%
  END

  ACTION_IF !(FILE_EXISTS_IN_GAME ~QD_MC_01.spl~) BEGIN
    CREATE SPL ~QD_MC_01~
    COPY_EXISTING ~QD_MC_01.SPL~ ~override~
      LPF ADD_NEW_EMPTY_SPELL_HEADER END
  END

  OUTER_FOR (num = 1; num < 10; ++num) BEGIN
	ACTION_FOR_EACH class IN ~D~ ~F~ ~M~ ~P~ ~R~ ~T~ BEGIN
	  ACTION_IF !(FILE_EXISTS_IN_GAME ~QD_MC%class%0%num%.spl~) BEGIN 
		COPY_EXISTING ~QD_MC_01.SPL~ ~override/QD_MC%class%0%num%.spl~
		  LPF ADD_SPELL_EFFECT INT_VAR opcode = 146 target = 1 parameter1 = 0 parameter2 = 1 timing = 9 STR_VAR resource = EVAL ~QD#MC%class%0%num%~ END
	  END
	  ACTION_IF !(FILE_EXISTS_IN_GAME ~QD#MC%class%0%num%.spl~) BEGIN 
		COPY_EXISTING ~QD_MC_01.SPL~ ~override/QD#MC%class%0%num%.spl~
		  LPF ADD_SPELL_EFFECT INT_VAR opcode = 206 target = 1 parameter1 = (0 - 1) timing = 9 STR_VAR resource = EVAL ~QD_MC%class%0%num%~ END
	  END
	END
  END
  OUTER_FOR (num = 10; num < 51; ++num) BEGIN
	ACTION_FOR_EACH class IN ~D~ ~F~ ~M~ ~P~ ~R~ ~T~ BEGIN
	  ACTION_IF !(FILE_EXISTS_IN_GAME ~QD_MC%class%%num%.spl~) BEGIN 
		COPY_EXISTING ~QD_MC_01.SPL~ ~override/QD_MC%class%%num%.spl~
		  LPF ADD_SPELL_EFFECT INT_VAR opcode = 146 target = 1 parameter1 = 0 parameter2 = 1 timing = 9 STR_VAR resource = EVAL ~QD#MC%class%%num%~ END
	  END
	  ACTION_IF !(FILE_EXISTS_IN_GAME ~QD#MC%class%%num%.spl~) BEGIN 
		COPY_EXISTING ~QD_MC_01.SPL~ ~override/QD#MC%class%%num%.spl~
		  LPF ADD_SPELL_EFFECT INT_VAR opcode = 206 target = 1 parameter1 = (0 - 1) timing = 9 STR_VAR resource = EVAL ~QD_MC%class%%num%~ END
	  END
	END
  END

  // read clab
  COPY_EXISTING ~%kit_clab%.2da~ ~override~
    COUNT_2DA_COLS cols
    READ_2DA_ENTRIES_NOW rows 1
    READ_2DA_ENTRY_FORMER rows 1 0 default
    FOR (row = 3; row < rows; ++row) BEGIN
      FOR (col = 1; col < cols; ++col) BEGIN
        READ_2DA_ENTRY_FORMER rows row col ability
        TO_UPPER ~ability~
        // PATCH_PRINT ~%ability%~
        PATCH_IF (NOT ~%ability%~ STR_EQ ~%default%~) BEGIN
          // SNPRINT 3 abil_type ~%ability%~
          LPF SUBSTRING INT_VAR start = 0 length = 3 STR_VAR string = EVAL ~%ability%~ RET abil_type = substring END
          SET string_length = (STRING_LENGTH ~%ability%~) - 3
          LPF SUBSTRING INT_VAR start = 3 length = string_length STR_VAR string = EVAL ~%ability%~ RET abil_new = substring END
          PATCH_IF (string_length <= 7) BEGIN
            DEFINE_ASSOCIATIVE_ARRAY abil_types BEGIN
              ~AP_~ => ~QD_MC_AP~
              ~GA_~ => ~QD_MC_GA~
            END
            PATCH_IF (VARIABLE_IS_SET $abil_types(~%abil_type%~)) BEGIN
              // granted abilities or bonuses
              TEXT_SPRINT qdmc_eff_resref $abil_types(~%abil_type%~)
              // PATCH_PRINT ~%ability% > %abil_new% , LVL %col%~
              INNER_ACTION BEGIN
                COPY_EXISTING ~%qdmc_eff_resref%.EFF~ ~override/%abil_new%#.EFF~
                  WRITE_ASCIIE 0x30 ~%abil_new%~
                  SET col_length = STRING_LENGTH ~%col%~
                ACTION_IF (col < 10) BEGIN
                  OUTER_TEXT_SPRINT qdmc_spl_resref ~QD_MC%base_class%0%col%~
                END ELSE BEGIN
                  OUTER_TEXT_SPRINT qdmc_spl_resref ~QD_MC%base_class%%col%~
                END
                COPY_EXISTING ~override/%qdmc_spl_resref%.SPL~ ~override~
                  LPF ADD_SPELL_EFFECT INT_VAR insert_point = 0 opcode = 177 target = 1 timing = 9 resist_dispel = 0 power = 0 parameter1 = IDS_OF_SYMBOL(~Kit~ ~%kit_name%~) parameter2 = 9 STR_VAR resource = EVAL ~%abil_new%#~ END
              END
            END ELSE BEGIN
              // exceptions
              PATCH_WARN ~Error reading kit table.~
            END
          END ELSE BEGIN
            PATCH_WARN ~Internal ability name too long.~
          END
        END
      END
    END
  BUT_ONLY
END
