code:
;-------------------------------------------------
;マップデータに関する関数
;-------------------------------------------------
@HO_GET_MAPDATA(MAP_ID, ROOM_IDX, DATANAME, FROM_SAVEDATA = 0)
#DIM  MAP_ID
#DIM  ROOM_IDX
#DIMS DATANAME
#DIM  FROM_SAVEDATA

VARSET RESULTS
IF !FROM_SAVEDATA
    SIF DIC_GET(_DIC_HO_MAPDATA:MAP_ID:ROOM_IDX, DATANAME) == ""
        RETURN -1

    ISNUMERIC DIC_GET(_DIC_HO_MAPDATA:MAP_ID:ROOM_IDX, DATANAME)
    IF RESULT
        RETURN TOINT(DIC_GET(_DIC_HO_MAPDATA:MAP_ID:ROOM_IDX, DATANAME))
    ELSE
        RESULTS = %DIC_GET(_DIC_HO_MAPDATA:MAP_ID:ROOM_IDX, DATANAME)%
        RETURN
    ENDIF
ELSE
    SIF DIC_GET(_DIC_HO_SAVED_MAPDATA:MAP_ID:ROOM_IDX, DATANAME) == ""
        RETURN -1
    ISNUMERIC DIC_GET(_DIC_HO_SAVED_MAPDATA:MAP_ID:ROOM_IDX, DATANAME)
    IF RESULT
        RETURN TOINT(DIC_GET(_DIC_HO_SAVED_MAPDATA:MAP_ID:ROOM_IDX, DATANAME))
    ELSE
        RESULTS = %DIC_GET(_DIC_HO_SAVED_MAPDATA:MAP_ID:ROOM_IDX, DATANAME)%
        RETURN
    ENDIF
ENDIF

@HO_SET_MAPDATA(ROOM_IDX, DATANAME, VALUES, MAP_ID = -1)
#DIM  ROOM_IDX
#DIMS DATANAME
#DIMS VALUES
#DIM  MAP_ID

SIF MAP_ID == -1
    MAP_ID = HO_有効マップID

_DIC_HO_MAPDATA:MAP_ID:ROOM_IDX = %DIC_SET(_DIC_HO_MAPDATA:MAP_ID:ROOM_IDX, DATANAME, VALUES)%
RETURN

@HO_SAVE_MAPDATA(MAP_ID = -1)
#DIM MAP_ID
#DIMS DYNAMIC WRITE_DATA
IF MAP_ID == -1
    MAP_ID = HO_有効マップID
ENDIF

FOR LOCAL, 0, HO_MAX_ROOMNUM
    IF DIC_CONTAINSKEY(_DIC_HO_MAPDATA:MAP_ID:LOCAL, "ROOM_ID")
        WRITE_DATA '= ""
        WRITE_DATA = %DIC_SET(WRITE_DATA, "ROOM_ID",DIC_GET(_DIC_HO_MAPDATA:MAP_ID:LOCAL, "ROOM_ID"))%
        WRITE_DATA = %DIC_SET(WRITE_DATA, "MODULE_SLOT",DIC_GET(_DIC_HO_MAPDATA:MAP_ID:LOCAL, "MODULE_SLOT"))%
        WRITE_DATA = %DIC_SET(WRITE_DATA, "ROOM_PATH",DIC_GET(_DIC_HO_MAPDATA:MAP_ID:LOCAL, "ROOM_PATH"))%
        _DIC_HO_SAVED_MAPDATA:HO_有効マップID:LOCAL = %WRITE_DATA%
    ELSE
        _DIC_HO_SAVED_MAPDATA:HO_有効マップID:LOCAL '= "-1"
    ENDIF
NEXT

;;セーブデータに保存されたマップデータを読み出す
@HO_LOAD_MAPDATA(MAP_ID)
#DIM MAP_ID

FOR LOCAL, 0, HO_MAX_ROOMNUM
    _DIC_HO_MAPDATA:MAP_ID:LOCAL = %_DIC_HO_SAVED_MAPDATA:MAP_ID:LOCAL%
NEXT
;セーブデータが空なら初期設定マップデータを読み出し、モジュールリストとモジュールスロットデータを生成する
IF _DIC_HO_MAPDATA:MAP_ID:0 == ""
    CALL HO_SET_MAPDATA_DEFAULT(MAP_ID)
    CALL HO_INIT_MODULE_LIST(MAP_ID)
    CALL HO_INIT_MODULE_SLOT(MAP_ID)
    ;"MODULE_LIST"は初回読み出し時のみ使用するため、設定完了後に削除する
    FOR LOCAL, 0, HO_MAX_ROOMNUM
        _DIC_HO_MAPDATA:HO_有効マップID:LOCAL = %DIC_REMOVE(_DIC_HO_MAPDATA:HO_有効マップID:LOCAL, "MODULE_LIST")%
    NEXT
ENDIF

;MODULE_LISTからMODULE_SLOTデータを生成する。
@HO_INIT_MODULE_SLOT(MAP_ID = -1)
#DIM MAP_ID
#DIM DYNAMIC ROOM_IDX
#DIM DYNAMIC MODULE_PLACED_ROOM_IDX
#DIM DYNAMIC MODULE_LIST_IDX
#DIM DYNAMIC MODULE_SLOT_IDX
#DIMS DYNAMIC _LIST_MODULE_SLOT

SIF MAP_ID == -1
    MAP_ID = HO_有効マップID

FOR ROOM_IDX, 0, HO_MAX_ROOMNUM
    VARSET _LIST_MODULE_SLOT
    FOR MODULE_LIST_IDX, 0, HO_MAX_MODULENUM
        CALL HO_GET_MODULE_LIST(MODULE_LIST_IDX, "ROOM_IDX",MAP_ID)
        MODULE_PLACED_ROOM_IDX = RESULT
        IF MODULE_PLACED_ROOM_IDX == ROOM_IDX
            CALL HO_GET_MODULE_LIST(MODULE_LIST_IDX, "MODULE_SLOT_IDX",MAP_ID)
            IF RESULT != -1
                MODULE_SLOT_IDX = RESULT
                _LIST_MODULE_SLOT = %LIST_INSERTAT(_LIST_MODULE_SLOT, MODULE_SLOT_IDX, TOSTR(MODULE_LIST_IDX))%
            ENDIF
        ENDIF
    NEXT
    CALL HO_SET_MAPDATA(ROOM_IDX, "MODULE_SLOT", _LIST_MODULE_SLOT, MAP_ID)
NEXT

@HO_GET_ROOM_DATA(ROOM_ID, DATANAME)
#DIM ROOM_ID
#DIMS DATANAME
#DIMS DYNAMIC _DIC_ROOM_DATA
#DIMS DYNAMIC STR_DATA

TRYCCALLFORM HO_ROOM_DATA_{ROOM_ID}
    _DIC_ROOM_DATA = %RESULTS%
CATCH
    RETURN -1
ENDCATCH

STR_DATA = %DIC_GET(_DIC_ROOM_DATA, DATANAME)%
ISNUMERIC STR_DATA
IF RESULT
    RETURN TOINT(STR_DATA)
ELSE
    RESULTS = %STR_DATA%
    RETURN
ENDIF

;ROOM_IDを取得
@HO_GET_ROOM_ID(ROOM_IDX, MAP_ID = -1)
#DIM ROOM_IDX
#DIM MAP_ID

SIF MAP_ID == -1
    MAP_ID = HO_有効マップID
CALL HO_GET_MAPDATA(MAP_ID, ROOM_IDX, "ROOM_ID")
RETURN RESULT

@HO_GET_ROOM_NAME(ROOM_IDX)
#DIM ROOM_IDX
#DIM ROOM_ID
#DIM MAP_ID

SIF MAP_ID == -1
    MAP_ID = HO_有効マップID
CALL HO_GET_MAPDATA(MAP_ID, ROOM_IDX, "ROOM_NAME")
IF RESULT == -1
    CALL HO_GET_MAPDATA(MAP_ID, ROOM_IDX, "ROOM_ID")
    ROOM_ID = RESULT
    CALL HO_GET_ROOM_DATA(ROOM_ID, "ROOM_NAME")
ENDIF
RETURN

;ROOM_TAGを取得
@HO_GET_ROOM_TAG(ROOM_IDX, FROM_SAVEDATA = 0)
#DIM ROOM_IDX
#DIM MAP_ID
#DIM FROM_SAVEDATA

SIF MAP_ID == -1
    MAP_ID = HO_有効マップID
CALL HO_GET_MAPDATA(MAP_ID, ROOM_IDX, "ROOM_ID", FROM_SAVEDATA)
CALL HO_GET_ROOM_DATA(RESULT, "ROOM_TAG")
RETURN

@HO_CHECK_ROOM_TAG(ROOM_IDX, ROOM_TAG)
#DIM ROOM_IDX
#DIM MAP_ID
#DIMS ROOM_TAG
#DIMS _LIST_TAG
CALL HO_GET_MAPDATA(MAP_ID, ROOM_IDX, "ROOM_ID")
CALL HO_GET_ROOM_DATA(RESULT, "ROOM_TAG")
_LIST_TAG = %RESULTS%
IF LIST_INDEXOF(_LIST_TAG, ROOM_TAG) != -1
    RETURN 1
ELSE
    RETURN 0
ENDIF

;-------------------------------------------------
;;;@HO_FIND_ROOM_BY_TAG
;;;概要 :ROOM_TAGに該当する部屋をIDXで取得
;;;ARGS:1 = ROOM_TAG,ARG:2 = 0(複数合致する場合最初の要素を返却),1(ランダムに返却), ARG:3 = MAP_ID
;;;@RETURN  ROOM_IDX
;;;@RESULTS 合致したROOMのROOM_IDXリスト
;-------------------------------------------------
@HO_FIND_ROOM_BY_TAG(ROOM_TAG, RANDOM = 0, MAP_ID = -1, FROM_SAVEDATA = 0)
#DIMS ROOM_TAG
#DIM  MAP_ID
#DIM  RANDOM
#DIM  FROM_SAVEDATA
#DIM  DYNAMIC ROOM_IDX
#DIMS DYNAMIC _LIST_ROOM
VARSET RESULTS
SIF MAP_ID == -1
    MAP_ID = HO_有効マップID
WHILE ROOM_IDX < HO_MAX_ROOMNUM
    CALL HO_GET_ROOM_TAG(ROOM_IDX, FROM_SAVEDATA)
    IF LIST_INDEXOF(RESULTS, ROOM_TAG) != -1
        _LIST_ROOM = %LIST_ADD(_LIST_ROOM, TOSTR(ROOM_IDX))%
    ENDIF
    ROOM_IDX ++
WEND

RESULTS '= ""
IF LIST_COUNT(_LIST_ROOM) > 0
    RESULTS = %_LIST_ROOM%
    IF !RANDOM
        RETURN TOINT(LIST_GET(_LIST_ROOM, 0))
    ELSE
        LOCAL = LIST_COUNT(_LIST_ROOM)
        RETURN TOINT(LIST_GET(_LIST_ROOM, RAND(0, LOCAL)))
    ENDIF
ELSE
    ;タグに該当する部屋が見つからなかった場合
    RETURN -1
ENDIF

;ROOM_TAGに該当するFROMから最も近い部屋をIDXで取得
@HO_FIND_NEAREST_ROOM_BY_TAG(ROOM_TAG, ROOM_IDX_FROM, MAP_ID = -1)
#DIMS ROOM_TAG
#DIM ROOM_IDX_FROM
#DIM MAP_ID
#DIM DYNAMIC DIST
#DIM  DYNAMIC ROOM_IDX
#DIM  DYNAMIC LIST_IDX
#DIMS DYNAMIC _DIC_ROOM_LIST, HO_MAX_ROOMNUM + 1

VARSET _DIC_ROOM_LIST, "-1"
SIF MAP_ID == -1
    MAP_ID = HO_有効マップID
WHILE ROOM_IDX < HO_MAX_ROOMNUM
    CALL HO_GET_ROOM_TAG(ROOM_IDX)
    IF LIST_INDEXOF(RESULTS, ROOM_TAG) != -1
        _DIC_ROOM_LIST:LIST_IDX '= ""
        _DIC_ROOM_LIST:LIST_IDX = %DIC_SET(_DIC_ROOM_LIST:LIST_IDX, "ROOM_IDX", TOSTR(ROOM_IDX))%
        CALL HO_GET_ROUTE(ROOM_IDX_FROM, ROOM_IDX)
        DIST = RESULT
        _DIC_ROOM_LIST:LIST_IDX = %DIC_SET(_DIC_ROOM_LIST:LIST_IDX, "DISTANCE", TOSTR(DIST))%
        LIST_IDX++
    ENDIF
    ROOM_IDX ++
WEND
IF FINDELEMENT(_DIC_ROOM_LIST, "-1") > 0
    CALL ARRAYSORT_DIC(_DIC_ROOM_LIST, "DISTANCE")
    ROOM_IDX = TOINT(DIC_GET(_DIC_ROOM_LIST:0, "ROOM_IDX"))
    RETURN ROOM_IDX
ELSE
    ;タグに該当する部屋が見つからなかった場合
    RETURN -1
ENDIF

;-------------------------------------------------
; 設備を条件としたエリア探索
;-------------------------------------------------
;-------------------------------------------------
;使用したいモジュールの用途から条件を満たす最も近い部屋を探す
;MODULE_USAGE
; 設備用途を指定("入浴"など)
;ROOM_IDX_FROM
; 現在のエリアIDXを指定
;CONDS
; 最も近いエリア  = "NEAREST"
; 最も遠いエリア  = "FARTHEST"
; ランダムなエリア = "RANDOM"
;OPTION
; 現在地を含む = 0
; 現在地を除外 = 1
; 現在地のみ = 2
;-------------------------------------------------
@HO_FIND_ROOM_BY_MODULE_USAGE(MODULE_USAGE, ROOM_IDX_FROM, CONDS = "NEAREST", OPTION = 0, MAP_ID = -1)
; #region
#DIMS MODULE_USAGE
#DIM  ROOM_IDX_FROM
#DIM  OPTION
#DIMS CONDS
#DIM  MAP_ID

#DIM  CONST 現在地を含む = 0
#DIM  CONST 現在地を除外 = 1
#DIM  CONST 現在地のみ = 2

#DIM  DYNAMIC DIST
#DIM  DYNAMIC ROOM_IDX
#DIM  DYNAMIC LIST_IDX
#DIM  DYNAMIC MODULE_ID
#DIMS DYNAMIC _LIST_MODULE
#DIMS DYNAMIC _LIST_USAGE
#DIMS DYNAMIC _DIC_ROOM_LIST, HO_MAX_ROOMNUM + 1
#DIM  DYNAMIC MATCHED_ROOMNUM

VARSET _DIC_ROOM_LIST, "-1"
SIF MAP_ID == -1
    MAP_ID = HO_有効マップID
WHILE ROOM_IDX < HO_MAX_ROOMNUM
    IF ROOM_IDX == ROOM_IDX_FROM && OPTION == 現在地を除外
        ROOM_IDX ++
        CONTINUE
    ELSEIF ROOM_IDX != ROOM_IDX_FROM && OPTION == 現在地のみ
        ROOM_IDX ++
        CONTINUE
    ENDIF
    CALL HO_GET_MAPDATA(MAP_ID, ROOM_IDX, "MODULE_SLOT")
    _LIST_MODULE = %RESULTS%
    FOR LOCAL, 0, LIST_COUNT(_LIST_MODULE)
        CALL HO_GET_MODULE_ID2(ROOM_IDX, LOCAL)
        MODULE_ID = RESULT
        CALL HO_GET_MODULE_DATA(MODULE_ID, "用途")
        _LIST_USAGE = %RESULTS%
        ;探索対象のルームに一つでも条件を満たすモジュールがあれば、その部屋のIDXと距離を取って次の部屋を探索
        IF LIST_INDEXOF(_LIST_USAGE, MODULE_USAGE) != -1
            CALL HO_GET_ROUTE(ROOM_IDX_FROM, ROOM_IDX)
            DIST = RESULT
            SIF DIST == -1
                CONTINUE
            _DIC_ROOM_LIST:LIST_IDX '= ""
            _DIC_ROOM_LIST:LIST_IDX = %DIC_SET(_DIC_ROOM_LIST:LIST_IDX, "ROOM_IDX", TOSTR(ROOM_IDX))%
            _DIC_ROOM_LIST:LIST_IDX = %DIC_SET(_DIC_ROOM_LIST:LIST_IDX, "DISTANCE", TOSTR(DIST))%
            LIST_IDX ++
            BREAK
        ENDIF
    NEXT
    ROOM_IDX ++
WEND

MATCHED_ROOMNUM = FINDELEMENT(_DIC_ROOM_LIST, "-1")
IF MATCHED_ROOMNUM < 1
    RETURN -1
ENDIF
IF CONDS == "NEAREST"
    CALL ARRAYSORT_DIC(_DIC_ROOM_LIST, "DISTANCE")
    ROOM_IDX = TOINT(DIC_GET(_DIC_ROOM_LIST:0, "ROOM_IDX"))
    RETURN ROOM_IDX
ELSEIF CONDS == "FARTHEST"
    CALL ARRAYSORT_DIC(_DIC_ROOM_LIST, "DISTANCE", 1)
    ROOM_IDX = TOINT(DIC_GET(_DIC_ROOM_LIST:0, "ROOM_IDX"))
    RETURN ROOM_IDX
ELSEIF CONDS == "RANDOM"
    ROOM_IDX = TOINT(DIC_GET(_DIC_ROOM_LIST:(RAND(0, MATCHED_ROOMNUM)), "ROOM_IDX"))
    RETURN ROOM_IDX
ENDIF
; #endregion

;-------------------------------------------------
;使用したいモジュールの設備から条件を満たす最も近い部屋を探す
;MODULE_ELEMENT
; 設備属性を指定("遮蔽"など)
; '/'で区切られている場合は全条件を満たすモジュールのある部屋を返す
;ROOM_IDX_FROM
; 現在のエリアIDXを指定
;CONDS
; 最も近いエリア  = "NEAREST"
; 最も遠いエリア  = "FARTHEST"
; ランダムなエリア = "RANDOM"
;OPTION
; 現在地を含む = 0
; 現在地を除外 = 1
; 現在地のみ = 2
;-------------------------------------------------
@HO_FIND_ROOM_BY_MODULE_ELEMENT(MODULE_ELEMENT, ROOM_IDX_FROM, CONDS = "NEAREST", OPTION = 0, MAP_ID = -1)
; #region
#DIMS MODULE_ELEMENT
#DIM  ROOM_IDX_FROM
#DIM  OPTION
#DIMS CONDS
#DIM  MAP_ID

#DIM  CONST 現在地を含む = 0
#DIM  CONST 現在地を除外 = 1
#DIM  CONST 現在地のみ = 2

#DIMS DYNAMIC ELEMENT, 10
#DIM  DYNAMIC ELEMENT_NUM
#DIM  DYNAMIC ELEMENT_COUNT
#DIM  DYNAMIC UNMATCH_FLAG

#DIM  DYNAMIC DIST
#DIM  DYNAMIC ROOM_IDX
#DIM  DYNAMIC LIST_IDX
#DIM  DYNAMIC MODULE_ID
#DIMS DYNAMIC _LIST_MODULE
#DIMS DYNAMIC _LIST_ELEM
#DIMS DYNAMIC _DIC_ROOM_LIST, HO_MAX_ROOMNUM + 1
#DIM  DYNAMIC MATCHED_ROOMNUM

VARSET _DIC_ROOM_LIST, "-1"
SPLIT MODULE_ELEMENT, "/", ELEMENT
ELEMENT_NUM = RESULT

SIF MAP_ID == -1
    MAP_ID = HO_有効マップID
WHILE ROOM_IDX < HO_MAX_ROOMNUM
    IF ROOM_IDX == ROOM_IDX_FROM && OPTION == 現在地を除外
        ROOM_IDX ++
        CONTINUE
    ELSEIF ROOM_IDX != ROOM_IDX_FROM && OPTION == 現在地のみ
        ROOM_IDX ++
        CONTINUE
    ENDIF
    CALL HO_GET_MAPDATA(MAP_ID, ROOM_IDX, "MODULE_SLOT")
    _LIST_MODULE = %RESULTS%
    FOR LOCAL, 0, LIST_COUNT(_LIST_MODULE)
        CALL HO_GET_MODULE_ID2(ROOM_IDX, LOCAL)
        MODULE_ID = RESULT
        CALL HO_GET_MODULE_DATA(MODULE_ID, "属性")
        _LIST_ELEM = %RESULTS%
        ;'/'で区切られている全条件を満たすかを確認
        UNMATCH_FLAG = 0
        FOR ELEMENT_COUNT, 0, ELEMENT_NUM
            IF LIST_INDEXOF(_LIST_ELEM, ELEMENT:ELEMENT_COUNT) == -1
                UNMATCH_FLAG ++
            ENDIF
        NEXT
        ;探索条件を満たす設備があれば、その部屋のIDXと距離を取って次の部屋を探索
        IF UNMATCH_FLAG == 0
            CALL HO_GET_ROUTE(ROOM_IDX_FROM, ROOM_IDX)
            DIST = RESULT
            SIF DIST == -1
                CONTINUE
            _DIC_ROOM_LIST:LIST_IDX '= ""
            _DIC_ROOM_LIST:LIST_IDX = %DIC_SET(_DIC_ROOM_LIST:LIST_IDX, "ROOM_IDX", TOSTR(ROOM_IDX))%
            _DIC_ROOM_LIST:LIST_IDX = %DIC_SET(_DIC_ROOM_LIST:LIST_IDX, "DISTANCE", TOSTR(DIST))%
            LIST_IDX ++
            BREAK
        ENDIF
    NEXT
    ROOM_IDX ++
WEND

MATCHED_ROOMNUM = FINDELEMENT(_DIC_ROOM_LIST, "-1")
IF MATCHED_ROOMNUM < 1
    RETURN -1
ENDIF
IF CONDS == "NEAREST"
    CALL ARRAYSORT_DIC(_DIC_ROOM_LIST, "DISTANCE")
    ROOM_IDX = TOINT(DIC_GET(_DIC_ROOM_LIST:0, "ROOM_IDX"))
    RETURN ROOM_IDX
ELSEIF CONDS == "FARTHEST"
    CALL ARRAYSORT_DIC(_DIC_ROOM_LIST, "DISTANCE", 1)
    ROOM_IDX = TOINT(DIC_GET(_DIC_ROOM_LIST:0, "ROOM_IDX"))
    RETURN ROOM_IDX
ELSEIF CONDS == "RANDOM"
    ROOM_IDX = TOINT(DIC_GET(_DIC_ROOM_LIST:(RAND(0, MATCHED_ROOMNUM)), "ROOM_IDX"))
    RETURN ROOM_IDX
ENDIF
; #endregion

;-------------------------------------------------
;使用したいモジュールの属性から条件を満たす最も近い部屋を探す
;MODULE_ELEMENT'/'で区切られている場合は全条件を満たすモジュールのある部屋を返す
;OPTIONの値は
;現在地を含む = 0
;現在地を除外 = 1
;現在地のみ = 2
;-----------------------------------------------
@HO_FIND_NEAREST_ROOM_BY_MODULE_ELEMENT(MODULE_ELEMENT, ROOM_IDX_FROM, OPTION = 0, MAP_ID = -1)
#DIMS MODULE_ELEMENT
#DIM ROOM_IDX_FROM
#DIM OPTION
#DIM MAP_ID

#DIM CONST 現在地を含む = 0
#DIM CONST 現在地を除外 = 1
#DIM CONST 現在地のみ = 2

#DIMS DYNAMIC ELEMENT, 10
#DIM  DYNAMIC ELEMENT_NUM
#DIM  DYNAMIC ELEMENT_COUNT
#DIM  DYNAMIC UNMATCH_FLAG

#DIM DYNAMIC DIST
#DIM  DYNAMIC ROOM_IDX
#DIM  DYNAMIC LIST_IDX
#DIM  DYNAMIC MODULE_ID
#DIMS DYNAMIC _LIST_MODULE
#DIMS DYNAMIC _LIST_ELEM
#DIMS DYNAMIC _DIC_ROOM_LIST, HO_MAX_ROOMNUM + 1

VARSET _DIC_ROOM_LIST, "-1"
SPLIT MODULE_ELEMENT, "/", ELEMENT
ELEMENT_NUM = RESULT

SIF MAP_ID == -1
    MAP_ID = HO_有効マップID
WHILE ROOM_IDX < HO_MAX_ROOMNUM
    IF ROOM_IDX == ROOM_IDX_FROM && OPTION == 現在地を除外
        ROOM_IDX ++
        CONTINUE
    ELSEIF ROOM_IDX != ROOM_IDX_FROM && OPTION == 現在地のみ
        ROOM_IDX ++
        CONTINUE
    ENDIF
    CALL HO_GET_MAPDATA(MAP_ID, ROOM_IDX, "MODULE_SLOT")
    _LIST_MODULE = %RESULTS%
    FOR LOCAL, 0, LIST_COUNT(_LIST_MODULE)
        CALL HO_GET_MODULE_ID2(ROOM_IDX, LOCAL)
        MODULE_ID = RESULT
        CALL HO_GET_MODULE_DATA(MODULE_ID, "属性")
        _LIST_ELEM = %RESULTS%
        UNMATCH_FLAG = 0
        ;探索対象のルームに一つでも条件を満たすモジュールがあれば、その部屋のIDXと距離を取って次の部屋を探索
        FOR ELEMENT_COUNT, 0, ELEMENT_NUM
            IF LIST_INDEXOF(_LIST_ELEM, ELEMENT:ELEMENT_COUNT) == -1
                UNMATCH_FLAG ++
            ENDIF
        NEXT
        IF !UNMATCH_FLAG
            _DIC_ROOM_LIST:LIST_IDX '= ""
            _DIC_ROOM_LIST:LIST_IDX = %DIC_SET(_DIC_ROOM_LIST:LIST_IDX, "ROOM_IDX", TOSTR(ROOM_IDX))%
            CALL HO_GET_ROUTE(ROOM_IDX_FROM, ROOM_IDX)
            DIST = RESULT
            _DIC_ROOM_LIST:LIST_IDX = %DIC_SET(_DIC_ROOM_LIST:LIST_IDX, "DISTANCE", TOSTR(DIST))%
            LIST_IDX ++
            BREAK
        ENDIF
    NEXT
    ROOM_IDX ++
WEND
IF FINDELEMENT(_DIC_ROOM_LIST, "-1") > 0
    CALL ARRAYSORT_DIC(_DIC_ROOM_LIST, "DISTANCE")
    ROOM_IDX = TOINT(DIC_GET(_DIC_ROOM_LIST:0, "ROOM_IDX"))
    RETURN ROOM_IDX
ELSE
    ;条件に該当する部屋が見つからなかった場合
    RETURN -1
ENDIF

;マップデータから対象の部屋との接続情報を取得しLIST形式で返却
@HO_GET_PATH(ROOM_IDX, MAP_ID = -1)
#DIM MAP_ID
#DIM ROOM_IDX

VARSET RESULTS
SIF MAP_ID == -1
    MAP_ID = HO_有効マップID
CALL HO_GET_MAPDATA(MAP_ID, ROOM_IDX, "ROOM_PATH")
RETURN

;互換用ラッパ。OBSOLETED
@HO_FIND_NEAREST_ROOM_BY_MODULE_USAGE(MODULE_USAGE, ROOM_IDX_FROM, OPTION = 0, MAP_ID = -1)
#DIMS MODULE_USAGE
#DIM  ROOM_IDX_FROM
#DIM  OPTION
#DIMS DISTS
#DIM  MAP_ID

CALL HO_FIND_ROOM_BY_MODULE_USAGE(MODULE_USAGE, ROOM_IDX_FROM, "NEAREST", OPTION, MAP_ID)
RETURN RESULT

;-------------------------------------------------
;ルームモジュールに関する関数
;-------------------------------------------------
;マップデータに保持している対象の部屋のモジュールリストよりモジュールIDを取得する
; @HO_GET_MODULE_ID(ROOM_IDX, MODULE_IDX, MAP_ID = -1)
; #DIM ROOM_IDX
; #DIM MODULE_IDX
; #DIM MAP_ID
; #DIMS DYNAMIC _LIST_MODULE_LIST
; #DIMS DYNAMIC MODULE_LIST_IDX

; SIF MAP_ID == -1
;     MAP_ID = HO_有効マップID

; CALL HO_GET_MAPDATA(MAP_ID, ROOM_IDX, "MODULE_LIST")
; _LIST_MODULE_LIST = %RESULTS%

; IF LIST_COUNT(_LIST_MODULE_LIST) == 0
;     RETURN -1
; ELSE
;     LOCALS = %LIST_GET(_LIST_MODULE_LIST, MODULE_IDX)%
;     SIF LOCALS == ""
;         RETURN -1
;     RETURN TOINT(LOCALS)
; ENDIF

@HO_GET_MODULE_LIST_IDX(ROOM_IDX, MODULE_SLOT_IDX, MAP_ID = -1)
#DIM ROOM_IDX
#DIM MODULE_SLOT_IDX
#DIM MAP_ID
#DIMS DYNAMIC _LIST_MODULE_SLOT
#DIM DYNAMIC MODULE_LIST_IDX

SIF MAP_ID == -1
    MAP_ID = HO_有効マップID

CALL HO_GET_MAPDATA(MAP_ID, ROOM_IDX, "MODULE_SLOT")
_LIST_MODULE_SLOT = %RESULTS%

IF LIST_COUNT(_LIST_MODULE_SLOT) == 0
    RETURN -1
ELSE
    MODULE_LIST_IDX = TOINT(LIST_GET(_LIST_MODULE_SLOT, MODULE_SLOT_IDX))
    RETURN MODULE_LIST_IDX
ENDIF

@HO_GET_MODULE_ID2(ROOM_IDX, MODULE_SLOT_IDX, MAP_ID = -1)
#DIM ROOM_IDX
#DIM MODULE_SLOT_IDX
#DIM MAP_ID
#DIMS DYNAMIC _LIST_MODULE_SLOT
#DIMS DYNAMIC MODULE_LIST_IDX

SIF MAP_ID == -1
    MAP_ID = HO_有効マップID

CALL HO_GET_MAPDATA(MAP_ID, ROOM_IDX, "MODULE_SLOT")
_LIST_MODULE_SLOT = %RESULTS%

IF LIST_COUNT(_LIST_MODULE_SLOT) == 0
    RETURN -1
ELSE
    MODULE_LIST_IDX = %LIST_GET(_LIST_MODULE_SLOT, MODULE_SLOT_IDX)%
    SIF MODULE_LIST_IDX == ""
        RETURN -1
    CALL HO_GET_MODULE_LIST(TOINT(MODULE_LIST_IDX), "MODULE_ID", MAP_ID)
ENDIF
RESULT:1 = TOINT(MODULE_LIST_IDX)
RETURN RESULT

;モジュールIDからモジュール情報定義を取得
@HO_GET_MODULE_DATA(MODULE_ID, DATANAME)
#DIM MODULE_ID
#DIMS DATANAME
#DIMS _DIC_MODULE_DATA
#DIMS STR_DATA

TRYCCALLFORM HO_MODULE_DATA_{MODULE_ID}
    _DIC_MODULE_DATA = %RESULTS%
CATCH
    ;THROW MODULE_ID{MODULE_ID}に該当するモジュールデータが存在しません
    RETURN -1
ENDCATCH

STR_DATA = %DIC_GET(_DIC_MODULE_DATA, DATANAME)%
ISNUMERIC STR_DATA
IF RESULT
    RETURN TOINT(STR_DATA)
ELSE
    RESULTS = %STR_DATA%
    RETURN
ENDIF

;モジュール属性を取得
@HO_CHECK_MODULE_ELEMENT(ROOM_IDX, MODULE_IDX, ELEMENT_NAME)
#DIM  ROOM_IDX
#DIM  MODULE_IDX
#DIM  MODULE_ID
#DIMS ELEMENT_NAME

CALL HO_GET_MODULE_ID2(ROOM_IDX, MODULE_IDX)
MODULE_ID  = RESULT
SIF RESULT == -1
    RETURN 0

CALL HO_GET_MODULE_DATA(MODULE_ID, "属性")
IF LIST_INDEXOF(RESULTS, ELEMENT_NAME) != -1
    RETURN 1
ENDIF

RETURN 0

;キャラが使っているモジュール情報を取得する
@HO_GET_USE_MODULE_INFO(CHARA_ID)
#DIM DYNAMIC CHARA_ID
#DIM DYNAMIC CHARA_IDX
#DIM DYNAMIC ROOM_IDX
#DIM DYNAMIC MODULE_IDX
#DIM DYNAMIC MODULE_ID

VARSET RESULTS
IF CHARA_ID != MASTER
    CALL HO_GET_CHARAIDX(CHARA_ID)
    CHARA_IDX = RESULT
    CALL HO_GET_CHARADATA(CHARA_IDX, "ROOM_IDX")
    ROOM_IDX = RESULT
    CALL HO_GET_CHARADATA(CHARA_IDX, "MODULE_USAGE")
    MODULE_IDX = RESULT
    SIF RESULT == -1
        RETURN -1
ELSE
    ROOM_IDX = HO_MASTER現在地
    CALL HO_GET_MASTER_DATA("MODULE_USAGE")
    MODULE_IDX = RESULT
    SIF RESULT == -1
        RETURN -1
ENDIF

CALL HO_GET_MODULE_ID2(ROOM_IDX, MODULE_IDX)
MODULE_ID = RESULT
CALL HO_GET_MODULE_DATA(MODULE_ID, "MODULE_NAME")
RETURN MODULE_ID

;対象キャラクターが使っているモジュールの用途が引数と合致してるか確認
@HO_CHECK_USE_MODULE_USAGE(CHARA_ID, USAGE)
#DIM CHARA_ID
#DIMS USAGE
#DIM DYNAMIC MODULE_ID

CALL HO_GET_USE_MODULE_INFO(CHARA_ID)
MODULE_ID = RESULT
SIF MODULE_ID == -1
    RETURN 0
CALL HO_GET_MODULE_DATA(MODULE_ID, "用途")
IF LIST_INDEXOF(RESULTS, USAGE) != -1
    RETURN 1
ELSE
    RETURN 0
ENDIF

;対象キャラクターが使っているモジュールの属性が引数と合致してるか確認
@HO_CHECK_USE_MODULE_ELEMENT(CHARA_ID, ELEMENT_NAME)
#DIM CHARA_ID
#DIMS ELEMENT_NAME
#DIM DYNAMIC MODULE_ID

CALL HO_GET_USE_MODULE_INFO(CHARA_ID)
MODULE_ID = RESULT
SIF MODULE_ID == -1
    RETURN 0
CALL HO_GET_MODULE_DATA(MODULE_ID, "属性")
IF LIST_INDEXOF(RESULTS, ELEMENT_NAME) != -1
    RETURN 1
ELSE
    RETURN 0
ENDIF
;-------------------------------------------------
;キャラデータに関する関数
;-------------------------------------------------
;-------------------------------------------------
;CHARA_IDに指定したキャラクターと同所属のキャラを取得しリスト形式で返却する
;RESULT:0  対象キャラクターの数
;RESULTS:0 対象しているキャラクターID(_LIST形式)
;-------------------------------------------------
@GET_MEMBER_LIST(CHARA_ID = -1, 非捕虜をカウント = 1, 捕虜をカウント = 1)
#DIM CHARA_ID
#DIM 非捕虜をカウント
#DIM 捕虜をカウント
#DIM TARGET_CHARA_ID
#DIMS DYNAMIC _LIST_MEMBER
VARSET RESULTS
SIF CHARA_ID == -1
    CHARA_ID = MASTER
FOR TARGET_CHARA_ID, 0, CHARANUM
    SIF CHARA_ID == TARGET_CHARA_ID
        CONTINUE
    ;非捕虜(同所属のキャラ)
    IF 非捕虜をカウント > 0 && (CFLAG:CHARA_ID:所属 == CFLAG:TARGET_CHARA_ID:所属)
        _LIST_MEMBER = %LIST_ADD(_LIST_MEMBER, TOSTR(TARGET_CHARA_ID))%
    ;捕虜
    ELSEIF 捕虜をカウント > 0 && (CFLAG:CHARA_ID:所属 == CFLAG:TARGET_CHARA_ID:捕虜先)
        _LIST_MEMBER = %LIST_ADD(_LIST_MEMBER, TOSTR(TARGET_CHARA_ID))%
    ENDIF
NEXT

RESULTS = %_LIST_MEMBER%
RETURN LIST_COUNT(_LIST_MEMBER)

@HO_GET_CHARADATA(IDX, DATANAME)
#DIM IDX
#DIMS DATANAME
VARSET RESULTS
SIF IDX < 0
    RETURN -1
SIF DIC_GET(_DIC_HO_CHARALIST:IDX, DATANAME) == ""
    RETURN -1

ISNUMERIC DIC_GET(_DIC_HO_CHARALIST:IDX, DATANAME)
IF RESULT
    RETURN TOINT(DIC_GET(_DIC_HO_CHARALIST:IDX, DATANAME))
ELSE
    RESULTS = %DIC_GET(_DIC_HO_CHARALIST:IDX, DATANAME)%
    RETURN
ENDIF

;式中関数版
@HO_GET_CHARADATAF(IDX, DATANAME)
#FUNCTION
#DIM IDX
#DIMS DATANAME
VARSET RESULTS
IF IDX < 0
    RETURNF -1
ENDIF
IF DIC_GET(_DIC_HO_CHARALIST:IDX, DATANAME) == ""
    RETURNF -1
ENDIF
ISNUMERIC DIC_GET(_DIC_HO_CHARALIST:IDX, DATANAME)
IF RESULT
    RETURNF TOINT(DIC_GET(_DIC_HO_CHARALIST:IDX, DATANAME))
ELSE
    RESULTS = %DIC_GET(_DIC_HO_CHARALIST:IDX, DATANAME)%
    RETURNF
ENDIF

@HO_SET_CHARADATA(CHARA_IDX, DATANAME, VALUES)
#DIM CHARA_IDX
#DIMS DATANAME
#DIMS VALUES
_DIC_HO_CHARALIST:CHARA_IDX = %DIC_SET(_DIC_HO_CHARALIST:CHARA_IDX, DATANAME, VALUES)%
RETURN

@HO_SET_CHARAACT(CHARA_ID, ACTION)
#DIM CHARA_ID
#DIMS ACTION
CALL HO_GET_CHARAIDX(CHARA_ID)
CALL HO_SET_CHARADATA(RESULT, "ACTION", ACTION)

@HO_GET_CHARAACT(CHARA_ID)
#DIM CHARA_ID
CALL HO_GET_CHARAIDX(CHARA_ID)
CALL HO_GET_CHARADATA(RESULT, "ACTION")

@HO_SET_CHARAPLACE(CHARA_ID, ROOM_IDX)
#DIM CHARA_ID
#DIM ROOM_IDX
CALL HO_GET_CHARAIDX(CHARA_ID)
CALL HO_SET_CHARADATA(RESULT, "ROOM_IDX", TOSTR(ROOM_IDX))

@HO_GET_CHARAPLACE(CHARA_ID)
#DIM CHARA_ID
CALL HO_GET_CHARAIDX(CHARA_ID)
CALL HO_GET_CHARADATA(RESULT, "ROOM_IDX")
RETURN RESULT

@HO_SET_MOVELIST(CHARA_ID, _LIST_MOVELIST)
#DIM CHARA_ID
#DIMS _LIST_MOVELIST
CALL HO_GET_CHARAIDX(CHARA_ID)
CALL HO_SET_CHARADATA(RESULT, "MOVELIST", _LIST_MOVELIST)

@HO_GET_MOVELIST(CHARA_ID)
#DIM CHARA_ID
CALL HO_GET_CHARAIDX(CHARA_ID)
CALL HO_GET_CHARADATA(RESULT, "MOVELIST")

@HO_GET_CHARAIDX(CHARA_ID)
#DIM CHARA_ID
#DIM DYNAMIC IDX
WHILE _DIC_HO_CHARALIST:IDX != "-1"
    CALL HO_GET_CHARADATA(IDX, "CHARA_ID")
    SIF CHARA_ID == RESULT
        RETURN IDX
    IDX ++
WEND
;キャラリストに存在しないIDなら-1を返す
RETURN -1

;式中関数版
@HO_GET_CHARAIDXF(CHARA_ID)
#FUNCTION
#DIM CHARA_ID
#DIM DYNAMIC IDX
WHILE _DIC_HO_CHARALIST:IDX != "-1"
    SIF CHARA_ID == HO_GET_CHARADATAF(IDX, "CHARA_ID")
        RETURNF IDX
    IDX ++
WEND
;キャラリストに存在しないIDなら-1を返す
RETURNF -1

@HO_SET_CHARADATA_BY_ID(CHARA_ID, DATANAME, VALUES)
#DIM CHARA_ID
#DIMS DATANAME
#DIMS VALUES
CALL HO_GET_CHARAIDX(CHARA_ID)
SIF RESULT == -1
    RETURN -1
CALL HO_SET_CHARADATA(RESULT, DATANAME, VALUES)
RETURN RESULT

@HO_GET_CHARADATA_BY_ID(CHARA_ID, DATANAME)
#DIM CHARA_ID
#DIMS DATANAME
CALL HO_GET_CHARAIDX(CHARA_ID)
CALL HO_GET_CHARADATA(RESULT, DATANAME)
RETURN RESULT

;特定の条件に合致するキャラのIDをリストで返却する
;DATANAME未指定の場合は領内活動に参加している全キャラクター(ゲスト含む)
@HO_FIND_CHARA(DATANAME = "", DATAVALUES = "")
#DIMS DATANAME
#DIMS DATAVALUES
#DIMS DYNAMIC VALUES
#DIMS DYNAMIC _LIST_CHARA
FOR LOCAL, 0, FINDELEMENT(_DIC_HO_CHARALIST, "-1",,,1)
    SIF _DIC_HO_CHARALIST:LOCAL == "DELETED"
        CONTINUE
    IF DATANAME != ""
        CALL HO_GET_CHARADATA(LOCAL, DATANAME)
        VALUES = %RESULTS%
        SIF RESULT == -1
            CONTINUE
        IF DATAVALUES == VALUES
            CALL HO_GET_CHARADATA(LOCAL, "CHARA_ID")
            _LIST_CHARA = %LIST_ADD(_LIST_CHARA, TOSTR(RESULT))%
        ENDIF
    ELSE
        CALL HO_GET_CHARADATA(LOCAL, "CHARA_ID")
        _LIST_CHARA = %LIST_ADD(_LIST_CHARA, TOSTR(RESULT))%
    ENDIF
NEXT
RESULTS = %_LIST_CHARA%
RETURN LIST_COUNT(_LIST_CHARA)

;-------------------------------------------------
;プレイヤーデータに関する関数
;-------------------------------------------------
@HO_GET_MASTER_DATA(DATANAME)
#DIMS DATANAME

VARSET RESULTS
SIF DIC_GET(_DIC_HO_MASTER_DATA, DATANAME) == ""
    RETURN -1

ISNUMERIC DIC_GET(_DIC_HO_MASTER_DATA, DATANAME)
IF RESULT
    RETURN TOINT(DIC_GET(_DIC_HO_MASTER_DATA, DATANAME))
ELSE
    RESULTS = %DIC_GET(_DIC_HO_MASTER_DATA, DATANAME)%
    RETURN
ENDIF

@HO_SET_MASTER_DATA(DATANAME, VALUES)
#DIMS DATANAME
#DIMS VALUES

_DIC_HO_MASTER_DATA = %DIC_SET(_DIC_HO_MASTER_DATA, DATANAME, VALUES)%
RETURN

;-------------------------------------------------
; @ARRAYSORT_DIC
;	連想配列を持つ一次元配列を指定したキーで昇順または降順で挿入ソートする
;	指定するデータは数値型に変換可能であること
;	　※一次元配列に-1が格納されていた場合はデータの末尾とみなす
;	引数:1 連想配列を持つ文字列型一次元配列
;	引数:2 ソート対象のキー値
;	引数:3 0 = 昇順, 1 = 降順
;-------------------------------------------------
@ARRAYSORT_DIC(DIC, KEY, IS_DESCEND)
#DIMS REF DIC
#DIMS DYNAMIC KEY
#DIM DYNAMIC IS_DESCEND
#DIM DYNAMIC ELEMENT_COUNT

ELEMENT_COUNT = VARSIZE("DIC")
FINDELEMENT DIC, "-1"
IF RESULT != -1
    ELEMENT_COUNT = RESULT
ENDIF

FOR LOCAL:0, 1, ELEMENT_COUNT
    LOCAL:1 = LOCAL:0
    IF IS_DESCEND == 0
        WHILE (LOCAL:1 > 0) && (TOINT(DIC_GET(DIC:(LOCAL:1 - 1), KEY))) > (TOINT(DIC_GET(DIC:(LOCAL:1), KEY)))
            SWAP DIC:(LOCAL:1 - 1), DIC:(LOCAL:1)
            LOCAL:1 --
        WEND
    ELSE
        WHILE (LOCAL:1 > 0) && (TOINT(DIC_GET(DIC:(LOCAL:1 - 1), KEY))) < (TOINT(DIC_GET(DIC:(LOCAL:1), KEY)))
            SWAP DIC:(LOCAL:1 - 1), DIC:(LOCAL:1)
            LOCAL:1 --
        WEND
    ENDIF
NEXT


code:
;-------------------------------------------------
;モジュールリスト管理関数
;各モジュールを一意に管理する_DIC_HO_MODULE_LISTの管理関数
;[MODULE_ID] : (必須)MODULE_ID
;[ROOM_IDX] : (必須)MODULE配置先のROOM_IDX
;[MODULE_SLOT_IDX] : (必須)配置先スロット番号
;-------------------------------------------------
@HO_SET_MODULE_LIST(MODULE_LIST_IDX, DATANAME, VALUES, MAP_ID = -1)
#DIM  MODULE_LIST_IDX
#DIMS DATANAME
#DIMS VALUES
#DIM  MAP_ID

SIF MAP_ID == -1
    MAP_ID = HO_有効マップID
SIF _DIC_HO_MODULE_LIST:MAP_ID:MODULE_LIST_IDX == "-1"
    _DIC_HO_MODULE_LIST:MAP_ID:MODULE_LIST_IDX '= ""

_DIC_HO_MODULE_LIST:MAP_ID:MODULE_LIST_IDX = %DIC_SET(_DIC_HO_MODULE_LIST:MAP_ID:MODULE_LIST_IDX, DATANAME, VALUES)%
RETURN

@HO_GET_MODULE_LIST(MODULE_LIST_IDX, DATANAME, MAP_ID = -1, FROM_SAVEDATA = 0)
#DIM  MAP_ID
#DIM  MODULE_LIST_IDX
#DIM  ROOM_IDX
#DIMS DATANAME
#DIM  FROM_SAVEDATA

SIF MAP_ID == -1
    MAP_ID = HO_有効マップID

VARSET RESULTS
IF !FROM_SAVEDATA
    SIF DIC_GET(_DIC_HO_MODULE_LIST:MAP_ID:MODULE_LIST_IDX, DATANAME) == ""
        RETURN -1

    ISNUMERIC DIC_GET(_DIC_HO_MODULE_LIST:MAP_ID:MODULE_LIST_IDX, DATANAME)
    IF RESULT
        RETURN TOINT(DIC_GET(_DIC_HO_MODULE_LIST:MAP_ID:MODULE_LIST_IDX, DATANAME))
    ELSE
        RESULTS = %DIC_GET(_DIC_HO_MODULE_LIST:MAP_ID:MODULE_LIST_IDX, DATANAME)%
        RETURN
    ENDIF
ELSE
    SIF DIC_GET(_DIC_HO_SAVED_MODULE_LIST:MAP_ID:MODULE_LIST_IDX, DATANAME) == ""
        RETURN -1

    ISNUMERIC DIC_GET(_DIC_HO_SAVED_MODULE_LIST:MAP_ID:MODULE_LIST_IDX, DATANAME)
    IF RESULT
        RETURN TOINT(DIC_GET(_DIC_HO_SAVED_MODULE_LIST:MAP_ID:MODULE_LIST_IDX, DATANAME))
    ELSE
        RESULTS = %DIC_GET(_DIC_HO_SAVED_MODULE_LIST:MAP_ID:MODULE_LIST_IDX, DATANAME)%
        RETURN
    ENDIF
ENDIF

;モジュールリストの空きを見つけてモジュール情報を登録
@HO_ADD_MODULE_LIST(MODULE_ID, ROOM_IDX, MODULE_SLOT_IDX, MAP_ID = -1)
#DIM  MODULE_ID
#DIM  ROOM_IDX
#DIM  MODULE_SLOT_IDX
#DIM  MAP_ID
#DIM  DYNAMIC MODULE_LIST_IDX

#DIM DYNAMIC TARGET_ROOM_IDX
#DIM DYNAMIC TARGET_MODULE_LIST_IDX
#DIM DYNAMIC TARGET_MODULE_SLOT_IDX

SIF MAP_ID == -1
    MAP_ID = HO_有効マップID

MODULE_LIST_IDX = 0
FOR MODULE_LIST_IDX, 0, HO_MAX_MODULENUM
    IF _DIC_HO_MODULE_LIST:MAP_ID:MODULE_LIST_IDX == "-1"
        _DIC_HO_MODULE_LIST:MAP_ID:MODULE_LIST_IDX '= "DELETED"
        CALL HO_SET_MODULE_LIST(MODULE_LIST_IDX, "MODULE_ID", TOSTR(MODULE_ID), MAP_ID)
        CALL HO_SET_MODULE_LIST(MODULE_LIST_IDX, "ROOM_IDX", TOSTR(ROOM_IDX), MAP_ID)
        CALL HO_SET_MODULE_LIST(MODULE_LIST_IDX, "MODULE_SLOT_IDX", TOSTR(MODULE_SLOT_IDX), MAP_ID)
        ;追加されたモジュールと同一のルームにあり、かつMODULE_SLOT_IDXと同値以上のSLOT_IDXを持つデータについて、スロット情報を加算する
        FOR TARGET_MODULE_LIST_IDX, 0, HO_MAX_MODULENUM
            CALL HO_GET_MODULE_LIST(TARGET_MODULE_LIST_IDX, "ROOM_IDX", MAP_ID)
            TARGET_ROOM_IDX = RESULT
            IF ROOM_IDX == TARGET_ROOM_IDX
                CALL HO_GET_MODULE_LIST(TARGET_MODULE_LIST_IDX, "MODULE_SLOT_IDX", MAP_ID)
                TARGET_MODULE_SLOT_IDX = RESULT
                IF MODULE_SLOT_IDX <= TARGET_MODULE_SLOT_IDX
                    CALL HO_SET_MODULE_LIST(TARGET_MODULE_LIST_IDX, "MODULE_SLOT_IDX", TOSTR(TARGET_MODULE_SLOT_IDX ++))
                ENDIF
            ENDIF
        NEXT
        RETURN MODULE_LIST_IDX
    ENDIF
NEXT
RETURN -1

;指定インデックスのモジュール情報を削除
@HO_DELETE_MODULE_LIST(MODULE_LIST_IDX, MAP_ID = -1)
#DIM  MODULE_LIST_IDX
#DIM  MAP_ID

#DIM DYNAMIC DELDATA_ROOM_IDX
#DIM DYNAMIC DELDATA_MODULE_SLOT_IDX
#DIM DYNAMIC TARGET_ROOM_IDX
#DIM DYNAMIC TARGET_MODULE_LIST_IDX
#DIM DYNAMIC TARGET_MODULE_SLOT_IDX
#DIM DYNAMIC CHARA_ID
#DIM DYNAMIC TIMETABLE

SIF MAP_ID == -1
    MAP_ID = HO_有効マップID
SIF _DIC_HO_MODULE_LIST:MAP_ID:MODULE_LIST_IDX == "-1"
    RETURN 0

;削除されたモジュールと同一のルームにあり、かつ後続のスロットに配置されているデータについて、スロット情報を詰める
CALL HO_GET_MODULE_LIST(MODULE_LIST_IDX, "ROOM_IDX", MAP_ID)
DELDATA_ROOM_IDX = RESULT
CALL HO_GET_MODULE_LIST(MODULE_LIST_IDX, "MODULE_SLOT_IDX", MAP_ID)
DELDATA_MODULE_SLOT_IDX = RESULT

FOR TARGET_MODULE_LIST_IDX, 0, HO_MAX_MODULENUM
    CALL HO_GET_MODULE_LIST(TARGET_MODULE_LIST_IDX, "ROOM_IDX", MAP_ID)
    TARGET_ROOM_IDX = RESULT
    IF DELDATA_ROOM_IDX == TARGET_ROOM_IDX
        CALL HO_GET_MODULE_LIST(TARGET_MODULE_LIST_IDX, "MODULE_SLOT_IDX", MAP_ID)
        TARGET_MODULE_SLOT_IDX = RESULT
        IF DELDATA_MODULE_SLOT_IDX < TARGET_MODULE_SLOT_IDX
            CALL HO_SET_MODULE_LIST(TARGET_MODULE_LIST_IDX, "MODULE_SLOT_IDX", TOSTR(TARGET_MODULE_SLOT_IDX --))
        ENDIF
    ENDIF
NEXT
_DIC_HO_MODULE_LIST:MAP_ID:MODULE_LIST_IDX '= "-1"

;キャラクターの作業場リストから削除対象のモジュールIDXを削除
FOR CHARA_ID, 0, CHARANUM
    FOR TIMETABLE, 0, HO_TIMETABLENUM
        IF LIST_INDEXOF(_LIST_HO_WORKPLACE:CHARA_ID:TIMETABLE, TOSTR(MODULE_LIST_IDX)) != -1
            _LIST_HO_WORKPLACE:CHARA_ID:TIMETABLE = %LIST_REMOVEALL(_LIST_HO_WORKPLACE:CHARA_ID:TIMETABLE, TOSTR(MODULE_LIST_IDX))%
        ENDIF
    NEXT
NEXT

;作業指示を削除
CALL HO_REMOVE_CRAFT_ORDER(MODULE_LIST_IDX)

RETURN 1

;セーブデータにモジュールリストを保存
@HO_SAVE_MODULE_LIST(MAP_ID = -1)
#DIM MAP_ID
#DIMS DYNAMIC WRITE_DATA
IF MAP_ID == -1
    MAP_ID = HO_有効マップID
ENDIF

FOR LOCAL, 0, HO_MAX_MODULENUM
    IF DIC_CONTAINSKEY(_DIC_HO_MODULE_LIST:MAP_ID:LOCAL, "MODULE_ID")
        WRITE_DATA '= ""
        WRITE_DATA = %DIC_SET(WRITE_DATA, "MODULE_ID",DIC_GET(_DIC_HO_MODULE_LIST:MAP_ID:LOCAL, "MODULE_ID"))%
        WRITE_DATA = %DIC_SET(WRITE_DATA, "ROOM_IDX",DIC_GET(_DIC_HO_MODULE_LIST:MAP_ID:LOCAL, "ROOM_IDX"))%
        WRITE_DATA = %DIC_SET(WRITE_DATA, "MODULE_SLOT_IDX",DIC_GET(_DIC_HO_MODULE_LIST:MAP_ID:LOCAL, "MODULE_SLOT_IDX"))%
        _DIC_HO_SAVED_MODULE_LIST:MAP_ID:LOCAL = %WRITE_DATA%
    ELSE
        _DIC_HO_SAVED_MODULE_LIST:MAP_ID:LOCAL '= "-1"
    ENDIF
NEXT

;セーブデータに保存されたモジュールリストを読み出す
@HO_LOAD_MODULE_LIST(MAP_ID = -1)
#DIM MAP_ID
IF MAP_ID == -1
    MAP_ID = HO_有効マップID
ENDIF

SIF _DIC_HO_SAVED_MODULE_LIST:MAP_ID:0 == ""
    RETURN

FOR LOCAL, 0, HO_MAX_MODULENUM
    _DIC_HO_MODULE_LIST:MAP_ID:LOCAL = %_DIC_HO_SAVED_MODULE_LIST:MAP_ID:LOCAL%
NEXT

;初期設定マップからモジュールリストを生成する
@HO_INIT_MODULE_LIST(MAP_ID = -1)
#DIM MAP_ID
#DIMS DYNAMIC _LIST_MOUDLE
#DIM MODULE_NUM

#DIMS DYNAMIC _LIST_MODULE

#DIM DYNAMIC MODULE_LIST_IDX
#DIM DYNAMIC MODULE_ID
#DIM DYNAMIC ROOM_IDX
#DIM DYNAMIC MODULE_SLOT_IDX

IF MAP_ID == -1
    MAP_ID = HO_有効マップID
ENDIF

VARSET _DIC_HO_MODULE_LIST:MAP_ID:0, "-1"
FOR ROOM_IDX, 0, HO_MAX_ROOMNUM
    CALL HO_GET_MAPDATA(MAP_ID, ROOM_IDX, "MODULE_LIST")
    _LIST_MODULE = %RESULTS%
    MODULE_NUM = LIST_COUNT(_LIST_MODULE)
    FOR MODULE_SLOT_IDX, 0, MODULE_NUM
        FOR LOCAL, 0, HO_MAX_MODULENUM
            IF _DIC_HO_MODULE_LIST:MAP_ID:LOCAL == "-1"
                MODULE_LIST_IDX = LOCAL
                BREAK
            ENDIF
        NEXT
        MODULE_ID = TOINT(LIST_GET(_LIST_MODULE, MODULE_SLOT_IDX))
        CALL HO_SET_MODULE_LIST(MODULE_LIST_IDX, "MODULE_ID", TOSTR(MODULE_ID), MAP_ID)
        CALL HO_SET_MODULE_LIST(MODULE_LIST_IDX, "ROOM_IDX", TOSTR(ROOM_IDX), MAP_ID)
        CALL HO_SET_MODULE_LIST(MODULE_LIST_IDX, "MODULE_SLOT_IDX", TOSTR(MODULE_SLOT_IDX), MAP_ID)
    NEXT
NEXT

;指定したキーと値に合致するモジュールのIDXをリストで返却する
@HO_FIND_MODULE(KEYS, VALUES, MAP_ID = -1)
#DIMS KEYS
#DIMS VALUES
#DIM MAP_ID
#DIMS DYNAMIC _LIST_MODULE
#DIM DYNAMIC MODULE_ID

SIF MAP_ID == -1
    MAP_ID = HO_有効マップID

FOR LOCAL, 0, HO_ENDIDXOF_MODULE_LIST()
    CALL HO_GET_MODULE_LIST(LOCAL, "MODULE_ID")
    MODULE_ID = RESULT
    SIF MODULE_ID == -1
        BREAK
    CALL HO_GET_MODULE_DATA(MODULE_ID, KEYS)
    IF LIST_INDEXOF(RESULTS, VALUES) != -1
        _LIST_MODULE = %LIST_ADD(_LIST_MODULE, TOSTR(LOCAL))%
    ENDIF
NEXT
RESULTS = %_LIST_MODULE%
RETURN

;用途に合致するモジュールのIDXをリストで返却する
@HO_FIND_MODULE_BY_USAGE(USAGE,MAP_ID = -1, FROM_SAVEDATA = 0)
#DIMS USAGE
#DIM MAP_ID
#DIM FROM_SAVEDATA
#DIMS DYNAMIC _LIST_MODULE
#DIM DYNAMIC MODULE_ID

SIF MAP_ID == -1
    MAP_ID = HO_有効マップID

FOR LOCAL, 0, HO_ENDIDXOF_MODULE_LIST()
    CALL HO_GET_MODULE_LIST(LOCAL, "MODULE_ID",,FROM_SAVEDATA)
    MODULE_ID = RESULT
    CALL HO_GET_MODULE_DATA(MODULE_ID, "用途")
    IF LIST_INDEXOF(RESULTS, USAGE) != -1
        _LIST_MODULE = %LIST_ADD(_LIST_MODULE, TOSTR(LOCAL))%
    ENDIF
NEXT
RESULTS = %_LIST_MODULE%
RETURN

;モジュールリストの末尾IDXを取得する
@HO_ENDIDXOF_MODULE_LIST()
#FUNCTION

;末尾を算出済みならそれを返す
SIF HO_CACHE_ENDIDXOF_MODULE_LIST > 0
    RETURNF HO_CACHE_ENDIDXOF_MODULE_LIST

FOR LOCAL, HO_MAX_MODULENUM - 1, 0, -1
    IF _DIC_HO_MODULE_LIST:0:LOCAL != "-1"
        HO_CACHE_ENDIDXOF_MODULE_LIST = LOCAL + 1
        BREAK
    ENDIF
NEXT
RETURNF HO_CACHE_ENDIDXOF_MODULE_LIST

;-------------------------------------------------
;空き独房数を取得する関数
;RETURN 空き独房数
;-------------------------------------------------
@HO_GET_VACANT_PRISON_CELL_COUNT
#DIMS DYNAMIC PRISON_CELL_LIST
#DIM DYNAMIC PRISON_CELL_COUNT
#DIM DYNAMIC PRISONER_COUNT

;居房リストを取得
CALL HO_FIND_MODULE_BY_USAGE("居房",,1)
PRISON_CELL_LIST = %RESULTS%
PRISON_CELL_COUNT = LIST_COUNT(PRISON_CELL_LIST)

;捕虜数を取得
CALL GET_MEMBER_LIST(MASTER,-1,1)
PRISONER_COUNT = RESULT

RETURN PRISON_CELL_COUNT - PRISONER_COUNT

repo_name: pervysquirrelz/erafl

license: unknown


code:
;-------------------------------------------------
;汎用関数仮置
;-------------------------------------------------
;リスト型配列のシャッフル関数
@LIST_SHUFFLE(_LIST, 回数 = 1)
#DIMS REF _LIST
#DIM 回数
#DIM LIST_LENGTH
LIST_LENGTH = LIST_COUNT(_LIST)
FOR LOCAL:0, 0, 回数
    WHILE LIST_LENGTH > 1
        LIST_LENGTH --
        LOCAL:1 = RAND(0, LIST_LENGTH + 1)
        LOCALS:0 = %LIST_GET(_LIST, LOCAL:1)%
        _LIST = %LIST_SET(_LIST, LOCAL:1, LIST_GET(_LIST, LIST_LENGTH))%
        _LIST = %LIST_SET(_LIST, LIST_LENGTH, LOCALS:0)%
    WEND
NEXT
RETURN

;与えられたリスト型変数からランダムに要素を選出して返す
@LIST_RAND(_LIST)
#FUNCTIONS
#DIMS _LIST
RETURNF LIST_GET(_LIST, RAND(0, LIST_COUNT(_LIST)))

;ARGS:1～5のいずれかに合致する場合1を返す
@LIST_GMATCH(_LIST, AND_OR = "OR", ARGS:2, ARGS:3 = "", ARGS:4 = "", ARGS:5 = "", ARGS:6 = "")
#FUNCTION
#DIMS _LIST
#DIMS AND_OR
#DIM DYNAMIC MATCHED

FOR LOCAL, 2, 6
    IF ARGS:LOCAL == ""
        IF LOCAL == 2
            RETURNF 0
        ENDIF
        BREAK
    ENDIF
    IF LIST_INDEXOF(_LIST, ARGS:LOCAL) == -1
        SIF AND_OR == "AND"
            RETURNF 0
    ELSE
        IF AND_OR == "OR"
            RETURNF 1
        ELSEIF AND_OR == "AND"
            MATCHED ++
        ELSE ;"AND,OR以外"
            RETURNF 0
        ENDIF
    ENDIF
NEXT

IF MATCHED
    RETURNF 1
ENDIF
RETURNF 0

;-------------------------------------------------
; 単発コマンドまたは継続状態をみて、対象のキャラが指定の部位を使用して奉仕しているか判定する
;-------------------------------------------------
@IS_SERVEAT(CHARA_ID, 部位, オプション定義 = "")
#FUNCTION
#DIM CHARA_ID
#DIMS 部位
#DIMS オプション定義
SELECTCASE 部位
    CASE "口"
        ;キスのみ
        SELECTCASE オプション定義
            CASE "キス"
                IF GROUPMATCH(SELECTCOM, 20, 340, 341, 342) || IS_EQUIP(CHARA_ID, 20, 342)
                    RETURNF 1
                ELSE
                    RETURNF 0
                ENDIF
            CASE "フェラ", "口淫"
                IF (GROUPMATCH(SELECTCOM, 11, 19, 107, 330) && IS_MPLY(CHARA_ID)) || IS_EQUIP_PLAYER(CHARA_ID, 11, 19, 107, 330)
                    RETURNF 1
                ELSE
                    RETURNF 0
                ENDIF
            CASEELSE
        ENDSELECT
        ;口を使う動作全般
        SIF IS_M_HOLD(CHARA_ID)
            RETURNF 1
        IF (IS_MTAR(CHARA_ID) ||  IS_MPLY(CHARA_ID))
            SIF GROUPMATCH(SELECTCOM, 20, 340, 341, 342)
                RETURNF 1
            SIF IS_MTAR(CHARA_ID) && GROUPMATCH(SELECTCOM, 86, 90, 101, 102, 105, 132, 210)
                RETURNF 1
            SIF IS_MPLY(CHARA_ID) && GROUPMATCH(SELECTCOM, 2, 8, 9, 11, 19, 20, 24, 28, 104, 107, 117, 330)
                RETURNF 1
        ENDIF
    CASE "手"
        ;手淫
        SELECTCASE オプション定義
            CASE "手コキ"
                {
                    SIF (GROUPMATCH(SELECTCOM, T_N("亀頭責め手コキ"), T_N("手コキ"), T_N("玉揉み")) && IS_MPLY(CHARA_ID))
                        || IS_EQUIP_PLAYER(CHARA_ID, T_N("亀頭責め手コキ"), T_N("手コキ"), T_N("玉揉み"))
                }
                    RETURNF 1
            CASEELSE
        ENDSELECT
    CASE "胸"
        {
            SIF (GROUPMATCH(SELECTCOM, T_N("パイズリ")) && IS_MPLY(CHARA_ID))
                || IS_EQUIP_PLAYER(CHARA_ID, T_N("パイズリ"))
        }
            RETURNF 1
    CASEELSE
        RETURNF 0
ENDSELECT
RETURNF 0

;-------------------------------------------------
;CHARA_IDの継続中コマンドの相手のうち、先頭のキャラ番号を返す
;COM_ARRAYに確認対象のコマンドを格納したLISTを渡す
;-------------------------------------------------
@GET_EQUIP_TARGET(CHARA_ID, _LIST_COM)
#FUNCTION
#DIM CHARA_ID
#DIMS _LIST_COM

FOR LOCAL:0, 0, MEQUIP_NUM
    IF LIST_INDEXOF(_LIST_COM, TOSTR(MEQUIP:(LOCAL:0))) != -1
        FOR LOCAL:1, 0, MEQUIP_PLAYER_NUM:(LOCAL:0)
            IF MEQUIP_PLAYER:(LOCAL:0):(LOCAL:1) == CHARA_ID
                RETURNF MEQUIP_TARGET:(LOCAL:0):0
            ENDIF
        NEXT
    ENDIF
NEXT
RETURNF -1

;-------------------------------------------------
;CHARA_IDの継続中コマンドの実行者のうち、先頭のキャラ番号を返す
;_LIST_COMに確認対象のコマンドを格納したLISTを渡す
;-------------------------------------------------
@GET_EQUIP_PLAYER(CHARA_ID, _LIST_COM)
#FUNCTION
#DIM CHARA_ID
#DIMS _LIST_COM

FOR LOCAL:0, 0, MEQUIP_NUM
    IF LIST_INDEXOF(_LIST_COM, TOSTR(MEQUIP:(LOCAL:0))) != -1
        FOR LOCAL:1, 0, MEQUIP_TARGET_NUM:(LOCAL:0)
            IF MEQUIP_TARGET:(LOCAL:0):(LOCAL:1) == CHARA_ID
                RETURNF MEQUIP_PLAYER:(LOCAL:0):0
            ENDIF
        NEXT
    ENDIF
NEXT
RETURNF -1

;-------------------------------------------------
;TARGETを性的対象とみなすかを判定する
;異性またはBL,レズが一定のしきい値以上なら性的対象とみなす
;-------------------------------------------------
@IS_SEXUAL(PLAYER_ID, TARGET_ID)
#FUNCTION
#DIM PLAYER_ID
#DIM TARGET_ID

IF IS_SAMESEX(PLAYER_ID, TARGET_ID)
    IF IS_MALE(PLAYER_ID)
        IF IS_MALE(TARGET_ID)
            IF ABL:PLAYER_ID:ＢＬ >= 3
                RETURNF 1
            ELSE
                RETURNF 0
            ENDIF
        ELSE
            RETURNF 1
        ENDIF
    ELSEIF IS_FEMALE(PLAYER_ID) && ABL:PLAYER_ID:レズ >= 3
        RETURNF 1
    ELSEIF TALENT:PLAYER_ID:両刀
        RETURNF 1
    ENDIF
ELSE
    RETURNF 1
ENDIF
RETURNF 0

;-------------------------------------------------
;MPLYとMTARの双方が全員合意持ち
;-------------------------------------------------
@ACCEPT_EACH()
#FUNCTION
#DIM DYNAMIC ITER_I

FOR ITER_I, 0, MPLY_NUM
    IF !TALENT:(MPLY:ITER_I):合意 && (MPLY:ITER_I) != MASTER
        RETURNF 0
    ENDIF
NEXT
FOR ITER_I, 0, MTAR_NUM
    IF !TALENT:(MTAR:ITER_I):合意 && (MTAR:ITER_I) != MASTER
        RETURNF 0
    ENDIF
NEXT
RETURNF 1

;-------------------------------------------------
;MPLYとMTARの双方にSM関係が成立するか判定
;IS_MPLY_SADISTが0の場合TARGET側がSか判定
;-------------------------------------------------
@REL_SM(IS_MPLY_SADIST = 1)
#FUNCTION
#DIM IS_MPLY_SADIST
#DIM DYNAMIC ITER_I
FOR ITER_I, 0, MPLY_NUM
    IF IS_MPLY_SADIST
        SIF !IS_NASTY(MPLY:ITER_I, 素質_淫乱_サド) && !ABL:(MPLY:ITER_I):サド < 4
            RETURNF 0
    ELSE
        SIF !IS_NASTY(MPLY:ITER_I, 素質_淫乱_マゾ) && !ABL:(MPLY:ITER_I):マゾ < 4
            RETURNF 0
    ENDIF
NEXT
FOR ITER_I, 0, MTAR_NUM
    IF IS_MPLY_SADIST
        SIF !IS_NASTY(MTAR:ITER_I, 素質_淫乱_マゾ) && !ABL:(MTAR:ITER_I):マゾ < 4
            RETURNF 0
    ELSE
        SIF !IS_NASTY(MTAR:ITER_I, 素質_淫乱_サド) && !ABL:(MTAR:ITER_I):サド < 4
            RETURNF 0
    ENDIF
NEXT
RETURNF 1

;-------------------------------------------------
;売春実行可能か
;-------------------------------------------------
@CAN_PROSTITUTE(CHARA_ID)
#FUNCTION
#DIM CHARA_ID

SIF JOB_NAME:(TALENT:CHARA_ID:職業) == "娼婦"
    RETURNF 1
SIF IS_NASTY(CHARA_ID, 素質_淫乱_娼婦)
    RETURNF 1
SIF IS_SLAVE(CHARA_ID)
    RETURNF 1
SIF CFLAG:CHARA_ID:捕虜先 == 所属_プレイヤー
    RETURNF 1
SIF SKILL_GET_LEVELF(CHARA_ID, スキル_大分類_性技, "SEDUCTION") > 0
    RETURNF 1

RETURNF 0

;-------------------------------------------------
; 淫乱系素質の有無を判定
; ARG:1は 素質_淫乱_生ハメ依存 など淫乱系素質の番号を与える
;-------------------------------------------------
@IS_NASTY(CHARA_ID, 淫乱系素質番号)
#FUNCTION
#DIM CHARA_ID
#DIM 淫乱系素質番号

RETURNF GETBIT(TALENT:(CHARA_ID):淫乱系, 淫乱系素質番号)

;-------------------------------------------------
; 陥落深度版GROUPMATCH
; ARG:0 CHARA_ID
; ARG:1 陥落度文字列。以下の文字列を指定可
; ','で複数条件判定可能(OR判定)、頭に'!'をつけることで真偽反転
; EX1. "恋人,烙印" -> 恋人または烙印
; EX2. "!LOVER, "恋人" -> 恋慕、親愛、恋人以外、ただし恋人は可
; 使用可能な文字列
; TALENTの陥落値(恋慕、親愛など)の他に以下の表記が使用できます
; LOVER(恋慕、親愛、恋人のいずれか)
; SLAVE(服従、隷属、烙印のいずれか)
; SLAVED_BY(主人、所有者のいずれか)
; FALLEN（上記二つまたは誰かの奴隷）
;-------------------------------------------------
@GMATCH_FALLEN(CHARA_ID, STR_FALLEN)
#FUNCTION
#DIM CHARA_ID
#DIMS STR_FALLEN
#DIMS DYNAMIC VALUES, 10
#DIM  DYNAMIC IS_NOT
#DIM  DYNAMIC IS_MATCH

SPLIT STR_FALLEN, ",", VALUES
FOR LOCAL, 0, RESULT
    IF REGEXPMATCH(VALUES:LOCAL, "!")
        IS_NOT = 1
        VALUES:LOCAL = %REPLACE(VALUES:LOCAL, "[!\\s]", "")%
    ELSE
        IS_NOT = 0
    ENDIF
    SELECTCASE VALUES:LOCAL
        CASE "恋慕", "親愛", "服従", "隷属", "烙印", "主人", "所有者"
            IF GETVAR(@"TALENT:CHARA_ID:%VALUES:LOCAL%") && !IS_NOT
                IS_MATCH ++
            ELSEIF !GETVAR(@"TALENT:CHARA_ID:%VALUES:LOCAL%") && IS_NOT
                IS_MATCH ++
            ENDIF
        CASE "LOVER"
            IF IS_LOVER(CHARA_ID) && !IS_NOT
                IS_MATCH ++
            ELSEIF !IS_LOVER(CHARA_ID) && IS_NOT
                IS_MATCH ++
            ENDIF
        CASE "FALLEN"
            IF IS_FALLEN(CHARA_ID) && !IS_NOT
                IS_MATCH ++
            ELSEIF !IS_FALLEN(CHARA_ID) && IS_NOT
                IS_MATCH ++
            ENDIF
        CASE "SLAVE"
            IF IS_SLAVE(CHARA_ID) && !IS_NOT
                IS_MATCH ++
            ELSEIF !IS_SLAVE(CHARA_ID) && IS_NOT
                IS_MATCH ++
            ENDIF
        CASE "SLAVED_BY"
            IF IS_SLAVED_BY(CHARA_ID) && !IS_NOT
                IS_MATCH ++
            ELSEIF !IS_SLAVED_BY(CHARA_ID) && IS_NOT
                IS_MATCH ++
            ENDIF
    ENDSELECT
NEXT
IF IS_MATCH
    RETURNF 1
ELSE
    RETURNF 0
ENDIF

;-------------------------------------------------
;主にCOM実行時の口上制御などで用いる関数
;-------------------------------------------------
;region
;-------------------------------------------------
;;;口上で参照できる補助的なシチュエーションタグをセットする
;;;セットしたタグは @AT_SITU でアクセスする
;;;※このリストはTRAINループの開始時( @SHOW_STATUS )に初期化される
;;;あくまで使い切りで補助的な情報を与える目的で使用される
;;;@ARGS
;;;- ARG {string} :付帯するシチュエーションタグ(ex."セクハラ")
;-------------------------------------------------
@ADD_SITU(VALUES)
#DIMS VALUES

TSTR:COM_SITUATION = %LIST_ADD(TSTR:COM_SITUATION, VALUES)%

;-------------------------------------------------
;;;セットされたシチュエーションタグの存否を確認する
;;;@ARGS
;;;- ARG {string}:シチュエーションタグ名
;;;@RETURN
;;;- RESULT
;;;  - RESULT:0 {bool} :タグの有無
;;;  - RESULT:1 {int}  :タグのリスト上のインデックス
;-------------------------------------------------
;RESULTS:0で要素の有無を返す。要素のインデックスをREUSLT:1で返す
@AT_SITU(VALUES)
#FUNCTION
#DIMS VALUES
#DIM INDEX
INDEX = LIST_INDEXOF(TSTR:COM_SITUATION, VALUES)
IF INDEX != -1
    RESULT:1 = INDEX
    RETURNF 1
ELSE
    RETURNF 0
ENDIF
;#endregion

;-------------------------------------------------
; ハウジングモードのキャラデータに口上や地の文からアクセスするための式中関数
;-------------------------------------------------
;口上の式中関数が参照できるよう、口上出力の直前でハウジングモードの諸キャラデータをまとめてCSTRに格納する
@HOKJ_SET_CHARADATA_TO_CSTR(CHARA_ID)
#DIM CHARA_ID
#DIM DYNAMIC CHARA_IDX
#DIM DYNAMIC ROOM_IDX
#DIM DYNAMIC ROOM_ID
#DIM DYNAMIC MODULE_LIST_IDX
#DIM DYNAMIC MODULE_ID

SIF !FLAG:ハウジングモード
    RETURN 0

CALL HO_GET_CHARAIDX(CHARA_ID)
IF RESULT != -1
    CHARA_IDX = RESULT
    CALL HO_GET_CHARADATA(CHARA_IDX, "ROOM_IDX")
    ROOM_IDX = RESULT
ELSEIF CHARA_ID == MASTER
    ROOM_IDX = HO_MASTER現在地
ELSE
    RETURN 0
ENDIF
;使用しているエリアに関する情報
CSTR:CHARA_ID:USE_ROOM_IDX = %TOSTR(ROOM_IDX)%
CALL HO_GET_ROOM_ID(ROOM_IDX)
CSTR:CHARA_ID:USE_ROOM_ID = %TOSTR(RESULT)%
ROOM_ID = RESULT
CALL HO_GET_ROOM_NAME(ROOM_IDX)
CSTR:CHARA_ID:USE_ROOM_NAME = %RESULTS%
CALL HO_GET_ROOM_DATA(ROOM_ID,"ROOM_TAG")
CSTR:CHARA_ID:_LIST_USE_ROOM_TAG = %RESULTS%
;使用している設備に関する情報
IF FLAG:クエストモード
    MODULE_ID = TFLAG:クエスト_ダミー設備ID
ELSE
    CALL HO_GET_USE_MODULE_INFO(CHARA_ID)
    MODULE_ID = RESULT
ENDIF
CSTR:CHARA_ID:USE_MODULE_ID = %TOSTR(MODULE_ID)%

IF MODULE_ID == HO_CREW_PRIVATE_ROOM_ID
    CSTR:CHARA_ID:USE_MODULE_NAME '= @"%SNAME(CHARA_ID)%の部屋"
ELSE
    CALL HO_GET_MODULE_DATA(MODULE_ID, "MODULE_NAME")
    CSTR:CHARA_ID:USE_MODULE_NAME = %RESULTS%
ENDIF

CALL HO_GET_MODULE_DATA(MODULE_ID, "用途")
CSTR:CHARA_ID:_LIST_USE_MODULE_USAGE = %RESULTS%
CALL HO_GET_MODULE_DATA(MODULE_ID, "属性")
CSTR:CHARA_ID:_LIST_USE_MODULE_ELEMENT = %RESULTS%
IF CHARA_ID != MASTER
    CALL HO_GET_CHARADATA(CHARA_IDX, "USE_MODULE_IDX")
    CSTR:CHARA_ID:USE_MODULE_IDX = %TOSTR(RESULT)%
ELSE
    CSTR:CHARA_ID:USE_MODULE_IDX = %TOSTR(HO_MASTER_USE_MODULE_LIST_IDX)%
ENDIF

;キャラクターの実行中のアクションを式中関数として取得する
@HOKJ_IS_DOING(CHARA_ID)
#FUNCTIONS
#DIM CHARA_ID
#DIM DYNAMIC CHARA_IDX
SIF !FLAG:ハウジングモード
    RETURNF ""
CHARA_IDX = HO_GET_CHARAIDXF(CHARA_ID)
CALLF HO_GET_CHARADATAF(CHARA_IDX, "ACTION")
RETURNF RESULTS

;キャラクターが使用している設備の属性を取得する
@HOKJ_CHECK_ELEMENT(CHARA_ID, ELEMENT)
#FUNCTION
#DIM CHARA_ID
#DIMS ELEMENT
SIF !FLAG:ハウジングモード
    RETURNF 0
IF LIST_INDEXOF(CSTR:CHARA_ID:_LIST_USE_MODULE_ELEMENT, ELEMENT) != -1
    RETURNF 1
ELSE
    RETURNF 0
ENDIF

;-------------------------------------------------
;ハウジングモードの汎用関数
;-------------------------------------------------
@HO_GET_STR_CLOCK()
#DIM CLOCK
RESULT = (HO_TIME / 60) * 100 + (HO_TIME % 60)
RESULTS = %TOSTR(HO_TIME / 60, "00")%:%TOSTR(HO_TIME % 60, "00")%

;時刻を分換算
@HO_CLOCK(OCLOCK)
#FUNCTION
#DIM OCLOCK
RETURNF (((OCLOCK / 100) * 60) + (OCLOCK % 100))

;現在時刻を0700,2200など数値型の時刻で返す
@HO_OCLOCK()
#FUNCTION
RETURNF (HO_TIME / 60) * 100 + (HO_TIME % 60)

@HO_WEEK()
#FUNCTION
#DIM WEEK
WEEK = DAY % 8
RETURNF WEEK != 0 ? WEEK # 8

@HO_SEASON()
#FUNCTION
#DIM SEASON
SEASON = (DAY - 1) / 8
SEASON %= 4
RETURNF SEASON

;-------------------------------------------------
;重み付き乱択@COM_AUTO_RANDOMIZED_WEIGHTEDの利用頻度が高いため
;短縮形ラッパー関数を用意
;-------------------------------------------------
@RAND_WT_INIT()
CALL COM_AUTO_RANDOMIZED_WEIGHTED_START

;-------------------------------------------------
; 同RESULT値を許容 = 1の場合オリジナルと同じ挙動をする
; 0の場合、同じLOCAL_RESULTにLOCAL_NUM(重み)を2回セットした場合、元のLOCAL_RESULTのLOCAL_NUM(重み)が上書きされる
;-------------------------------------------------
@RAND_WT_SET(LOCAL_RESULT, LOCAL_NUM, 同RESULT値を許容 = 0)
#DIM LOCAL_RESULT
#DIM LOCAL_NUM
#DIM 同RESULT値を許容
#DIM DYNAMIC IDX
IF 同RESULT値を許容
    CALL COM_AUTO_RANDOMIZED_WEIGHTED_VALUE(LOCAL_RESULT, LOCAL_NUM)
ELSE
    IDX = FINDELEMENT(COM_AUTO_RANDOMIZED_NUM, LOCAL_RESULT)
    IF IDX != -1 && COM_AUTO_RANDOMIZED_COUNTER
        COM_AUTO_RANDOMIZED_WEIGHTED:IDX = LOCAL_NUM
    ELSE
        CALL COM_AUTO_RANDOMIZED_WEIGHTED_VALUE(LOCAL_RESULT, LOCAL_NUM)
    ENDIF
ENDIF

RETURN 0

@RAND_WT_RESET()
VARSET COM_AUTO_RANDOMIZED_WEIGHTED

@RAND_WT_RESULT()
#FUNCTION
RETURNF COM_AUTO_RANDOMIZED_WEIGHTED_RESULT()

;-------------------------------------------------
; メッセージ関連
;-------------------------------------------------
@KJ_PRINT(CHARA_ID, MESSAGE, DO_WAIT = 0)
#DIM CHARA_ID
#DIMS MESSAGE
#DIM  DO_WAIT
#DIM  F_COL
IF CFLAG:CHARA_ID:口上カラーコード
    F_COL = CFLAG:CHARA_ID:口上カラーコード
ELSE
    F_COL = GETCOLOR()
ENDIF
CALLF TAG_PRINTH(@"@C:{F_COL}@%MESSAGE%@/C@", 1)
SIF DO_WAIT
    WAIT

@PRINT_NOTICE(MESSAGE, COLOR = カラー_注意, NO_WAIT = 0)
#DIMS MESSAGE
#DIM  COLOR
#DIM  NO_WAIT
CALLF TAG_PRINT(@"@C:{COLOR}@―― %MESSAGE%@/C@", 1)
SIF !NO_WAIT
    WAIT

;引数分の文字列からランダムに一つ出力
@PRINT_RAND(ARGS:0 = "" ,ARGS:1 = "" ,ARGS:2 = "" ,ARGS:3 = "" ,ARGS:4 = "" ,ARGS:5 = "" ,ARGS:6 = "" ,ARGS:7 = "" , ARGS:8 = "" ,ARGS:9 = "")
#FUNCTIONS
#DIMS DYNAMIC LIST_TEXT
FOR LOCAL, 0, 10
    IF ARGS:LOCAL != ""
        LIST_TEXT = %LIST_ADD(LIST_TEXT, ARGS:LOCAL)%
    ENDIF
NEXT
CALLF TAG_PRINTH(@"%LIST_RAND(LIST_TEXT)%", 0)

;引数分の文字列から条件を満たすもののみランダムに一つ出力
@PRINT_RANDC(ARGS:0 = "" ,ARG:0 = 1 ,ARGS:1 = "", ARG:1 = 1 ,ARGS:2 = "", ARG:2 = 1 ,ARGS:3 = "", ARG:3 = 1 ,ARGS:4 = "", ARG:4 = 1 ,ARGS:5 = "", ARG:5 = 1 ,ARGS:6 = "", ARG:6 = 1 ,ARGS:7 = "", ARG:7 = 1 , ARGS:8 = "", ARG:8 = 1 ,ARGS:9 = "", ARG:9 = 1,ARGS:14 = "" ,ARG:14 = 1 ,ARGS:15 = "", ARG:15 = 1 ,ARGS:16 = "", ARG:16 = 1 ,ARGS:17 = "", ARG:17 = 1 ,ARGS:18 = "", ARG:18 = 1,ARGS:19 = "", ARG:19 = 1)
#FUNCTIONS
#DIMS DYNAMIC LIST_TEXT
FOR LOCAL, 0, 20
    SIF !ARG:LOCAL
        CONTINUE
    IF ARGS:LOCAL != ""
        LIST_TEXT = %LIST_ADD(LIST_TEXT, ARGS:LOCAL)%
    ENDIF
NEXT
SIF LIST_COUNT(LIST_TEXT) == 0
    RETURNF
CALLF TAG_PRINTH(@"%LIST_RAND(LIST_TEXT)%", 0)

;引数分の文字列から条件を満たすもののみランダムに一つ返却
@TEXT_RANDC(ARGS:0 = "" ,ARG:0 = 1 ,ARGS:1 = "", ARG:1 = 1 ,ARGS:2 = "", ARG:2 = 1 ,ARGS:3 = "", ARG:3 = 1 ,ARGS:4 = "", ARG:4 = 1 ,ARGS:5 = "", ARG:5 = 1 ,ARGS:6 = "", ARG:6 = 1 ,ARGS:7 = "", ARG:7 = 1 , ARGS:8 = "", ARG:8 = 1 ,ARGS:9 = "", ARG:9 = 1,ARGS:14 = "" ,ARG:14 = 1 ,ARGS:15 = "", ARG:15 = 1 ,ARGS:16 = "", ARG:16 = 1 ,ARGS:17 = "", ARG:17 = 1 ,ARGS:18 = "", ARG:18 = 1,ARGS:19 = "", ARG:19 = 1)
#FUNCTIONS
#DIMS DYNAMIC LIST_TEXT
FOR LOCAL, 0, 20
    SIF !ARG:LOCAL
        CONTINUE
    IF ARGS:LOCAL != ""
        LIST_TEXT = %LIST_ADD(LIST_TEXT, ARGS:LOCAL)%
    ENDIF
NEXT
SIF LIST_COUNT(LIST_TEXT) == 0
    RETURNF
RETURNF LIST_RAND(LIST_TEXT)

;引数分の文字列を結合して出力
@PRINT_CONCATE(ARGS:0 = "" ,ARGS:1 = "" ,ARGS:2 = "" ,ARGS:3 = "" ,ARGS:4 = "" ,ARGS:5 = "" ,ARGS:6 = "" ,ARGS:7 = "" , ARGS:8 = "" ,ARGS:9 = "")
#FUNCTIONS
LOCALS '= ""
FOR LOCAL, 0, 10
    IF ARGS:LOCAL != ""
        LOCALS += ARGS:LOCAL
    ENDIF
NEXT
CALLF TAG_PRINTH(@"%LOCALS%", 0)

;テキストを結合して返却
@TEXT_CONCATE(ARGS:0 = "" ,ARGS:1 = "" ,ARGS:2 = "" ,ARGS:3 = "" ,ARGS:4 = "" ,ARGS:5 = "" ,ARGS:6 = "" ,ARGS:7 = "" , ARGS:8 = "" ,ARGS:9 = "")
#FUNCTIONS
LOCALS '= ""
FOR LOCAL, 0, 10
    IF ARGS:LOCAL != ""
        LOCALS += ARGS:LOCAL
    ENDIF
NEXT
RETURNF LOCALS

;引数分の文字列から条件を満たすもののみ結合
@TEXT_CONCATEC(ARGS:0 = "" ,ARG:0 = 1 ,ARGS:1 = "", ARG:1 = 1 ,ARGS:2 = "", ARG:2 = 1 ,ARGS:3 = "", ARG:3 = 1 ,ARGS:4 = "", ARG:4 = 1 ,ARGS:5 = "", ARG:5 = 1 ,ARGS:6 = "", ARG:6 = 1 ,ARGS:7 = "", ARG:7 = 1 , ARGS:8 = "", ARG:8 = 1 ,ARGS:9 = "", ARG:9 = 1,ARGS:14 = "" ,ARG:14 = 1 ,ARGS:15 = "", ARG:15 = 1 ,ARGS:16 = "", ARG:16 = 1 ,ARGS:17 = "", ARG:17 = 1 ,ARGS:18 = "", ARG:18 = 1,ARGS:19 = "", ARG:19 = 1)
#FUNCTIONS
LOCALS '= ""
FOR LOCAL, 0, 20
    SIF !ARG:LOCAL
        CONTINUE
    IF ARGS:LOCAL != ""
        LOCALS += ARGS:LOCAL
    ENDIF
NEXT
RETURNF LOCALS

@PRINT_TITLE(TEXT, TYPE)
#DIMS TEXT
#DIMS TYPE

SELECTCASE TYPE
    CASE "コマンド", "COMMAND"
        CALL COLOR_LINE
        CALLF TAG_PRINT(@"@C:{カラー_白}@@S:1@<< %TEXT% >>@/S@@/C@", 1)
        CALL COLOR_LINE
    CASE "リアクション", "REACTION"
        CALL COLOR_LINE
        CALLF TAG_PRINT(@"@C:{カラー_ピンク}@@S:1@<< ♡ Reaction ♡ %TEXT% >>@/S@@/C@", 1)
        CALL COLOR_LINE
    CASE "割込", "INTERRUPTION"
        CALL COLOR_LINE
        CALLF TAG_PRINT(@"@C:{カラー_黄}@@S:1@<< ♡ Interruption ♡ %TEXT% >>@/S@@/C@", 1)
        CALL COLOR_LINE
    CASE "エロイベント", "ERO"
        CALL COLOR_LINE
        CALLF TAG_PRINTH(@"@C:{カラー_ピンク}@<< @S:1@ ♡ EVENT ♡ @/S@ %TEXT% >>@/C@", 1)
        CALL COLOR_LINE
    CASE "エロトラップ", "EROTRAP"
        CALL COLOR_LINE
        CALLF TAG_PRINT(@"@C:{カラー_ピンク}@<< @S:1@♡ TRAP ♡@/S@ %TEXT% >>@/C@", 1)
        CALL COLOR_LINE
    CASE "トラップ", "TRAP"
        CALL COLOR_LINE
        CALLF TAG_PRINT(@"@C:{カラー_黄}@<< @S:1@!! TRAP !!@/S@ %TEXT% >>@/C@", 1)
        CALL COLOR_LINE
    CASE "戦闘", "BATTLE"
        CALL COLOR_LINE
        CALLF TAG_PRINT(@"@C:{カラー_黄}@<< @S:1@!! BATTLE !!@/S@ %TEXT% >>@/C@", 1)
        CALL COLOR_LINE
    CASE "ボス", "BOSS"
        CALL COLOR_LINE
        CALLF TAG_PRINT(@"@C:{カラー_レッド}@<< @S:1@!! ―― BOSS BATTLE ―― !!@/S@ %TEXT% >>@/C@", 1)
        CALL COLOR_LINE
    CASE "クエスト", "QUEST"
        CALL COLOR_LINE
        CALLF TAG_PRINT(@"@C:{カラー_白}@@S:1@『 %TEXT% 』@/S@@/C@", 1)
        CALL COLOR_LINE
    CASE "判定", "ROLL"
        CALL COLOR_LINE
        CALLF TAG_PRINT(@"@C:{カラー_白}@@S:1@<< %TEXT% >>@/S@@/C@", 1)
        CALL COLOR_LINE
    CASE "戦利品", "COMBAT_REWARD"
        CALL COLOR_LINE
        CALLF TAG_PRINT(@"@C:{カラー_黄}@@S:1@☆ ―― %TEXT% ―― ☆@/S@@/C@", 1)
        CALL COLOR_LINE
    CASE "季節", "SEASON"
        CALL COLOR_LINE(1, HO_SEASON_COLOR:HO_SEASON())
        CALLF TAG_PRINT(@"@C:{HO_SEASON_COLOR:HO_SEASON()}@@S:1@<< めぐる季節:%HO_SEASON_STR:HO_SEASON()% >>@/S@@/C@", 1)
        CALL COLOR_LINE(1, HO_SEASON_COLOR:HO_SEASON())
    CASE "TIPS"
        CALL COLOR_LINE
        CALLF TAG_PRINT(@"@C:{カラー_黄}@<< @S:1@ TIPS @/S@ %TEXT% >>@/C@", 1)
        CALL COLOR_LINE
    CASEELSE
        CALL COLOR_LINE
        CALLF TAG_PRINT(@"@C:{カラー_白}@<< @S:1@ EVENT @/S@ %TEXT% >>@/C@", 1)
        CALL COLOR_LINE
ENDSELECT

;-------------------------------------------------
;;; 複数行の長文テキストをまとめて出力するPRINT用関数
;;; 500行まで対応
;;; - 書式
;;; \nを改行とみなす
;;; [キャラ識別名]:「テキスト」 のように記述されている場合、そのキャラクターの口上色でテキストを印字する
;;; その他文中の基本的な書式は口上XMLと同じ
;-------------------------------------------------
@PRINT_DIAG(TEXT)
#DIMS TEXT
#DIMS DYNAMIC COLUMN, 500

VARSET COLUMN, "-1"
CALL TEXT_SPLIT_RAWTEXT(TEXT, COLUMN)

CALL PRINT_TEXT_COLUMN(COLUMN)

;-------------------------------------------------
;ARG:0をARG:1倍する。TIMESのような関数
;ARG:1はデフォルト百分率指定。ARG:2で変更可
;-------------------------------------------------
@FTIMES(ARG:0, ARG:1, ARG:2 = 100)
#FUNCTION
RETURNF ARG:0 * ARG:1 / ARG:2

;-------------------------------------------------
;ARG%の判定
;-------------------------------------------------
@PERCENT(ARG)
#FUNCTION
RETURNF RAND:100 < ARG


code:
;対象キャラクタの引数で指定した部位が衣服で覆われている場合は覆っている衣装の部位とIDを返す
;被覆判定は身体の上から順番に見て、最も表層の衣装を返す
;RESULT:0 衣装部位
;RESULT:1 衣装ID
;RESULTS:0 衣装名
;RESULTS:1 衣装描写（未定義の場合は衣装名）
@IS_COVERED(CHARA_ID, 被覆部位, 下着のみ判定 = 0)
#DIM CHARA_ID
#DIM 被覆部位
#DIM 下着のみ判定
#DIM DYNAMIC 衣装部位
#DIM DYNAMIC CLOTH_ID
#DIM DYNAMIC CLOTH_PART
#DIM DYNAMIC COVERED_PARTS, CLOTH_PART_NUM
#DIMS DYNAMIC 被覆衣装名
#DIMS DYNAMIC 被覆衣装描写

VARSET RESULT
VARSET RESULTS

SELECTCASE 被覆部位
        ;上半身
    CASE 被覆部位_乳房,被覆部位_乳首,被覆部位_腹
        IF 調教用衣装:CHARA_ID:衣装部位_上下服 != 0 && 衣装状態:CHARA_ID:衣装部位_上下服 == 0 && 下着のみ判定 == 0
            衣装部位 = 衣装部位_上下服
            CALL CLOTH_GET_COVERAGE(COVERED_PARTS, 衣装部位, 調教用衣装:CHARA_ID:衣装部位)
            IF COVERED_PARTS:被覆部位 == 1
                CLOTH_ID = 調教用衣装:CHARA_ID:衣装部位
            ELSE
                ;服で被覆されていないなら下着を対象に再判定
                CALL IS_COVERED(CHARA_ID, 被覆部位, 1)
                RETURN RESULT
            ENDIF
        ELSEIF 調教用衣装:CHARA_ID:衣装部位_上服 != 0 && 衣装状態:CHARA_ID:衣装部位_上服 == 0 && 下着のみ判定 == 0
            衣装部位 = 衣装部位_上服
            CALL CLOTH_GET_COVERAGE(COVERED_PARTS, 衣装部位, 調教用衣装:CHARA_ID:衣装部位)
            IF COVERED_PARTS:被覆部位 == 1
                CLOTH_ID = 調教用衣装:CHARA_ID:衣装部位
            ELSE
                ;服で被覆されていないなら下着を対象に再判定
                CALL IS_COVERED(CHARA_ID, 被覆部位, 1)
                RETURN RESULT
            ENDIF
        ELSEIF 調教用衣装:CHARA_ID:衣装部位_下着上 != 0 && 衣装状態:CHARA_ID:衣装部位_下着上 == 0
            衣装部位 = 衣装部位_下着上
            CALL CLOTH_GET_COVERAGE(COVERED_PARTS, 衣装部位, 調教用衣装:CHARA_ID:衣装部位)
            IF COVERED_PARTS:被覆部位 == 1
                CLOTH_ID = 調教用衣装:CHARA_ID:衣装部位
            ENDIF
        ELSEIF 調教用衣装:CHARA_ID:衣装部位_上下下着 != 0 && 衣装状態:CHARA_ID:衣装部位_上下下着 == 0
            衣装部位 = 衣装部位_上下下着
            CALL CLOTH_GET_COVERAGE(COVERED_PARTS, 衣装部位, 調教用衣装:CHARA_ID:衣装部位)
            IF COVERED_PARTS:被覆部位 == 1
                CLOTH_ID = 調教用衣装:CHARA_ID:衣装部位
            ENDIF
        ENDIF
        IF CLOTH_ID
            TRYCALLFORM CLOTH_NAME_%CLOTH_PART_NAME_ENG:衣装部位%_{CLOTH_ID}
            被覆衣装名 = %RESULTS%
            TRYCCALLFORM CLOTH_DESC_%CLOTH_PART_NAME_ENG:衣装部位%_{CLOTH_ID}
                被覆衣装描写 = %RESULTS%
            CATCH
                被覆衣装描写 = %被覆衣装名%
            ENDCATCH
            RESULT:1   = 調教用衣装:CHARA_ID:衣装部位
            RESULTS:0  = %被覆衣装名%
            RESULTS:1  = %被覆衣装描写%
        ENDIF
        RETURN 衣装部位
        ;下半身
    CASE 被覆部位_尻,被覆部位_秘部
        IF 調教用衣装:CHARA_ID:衣装部位_上下服 != 0 && 衣装状態:CHARA_ID:衣装部位_上下服 == 0 && 下着のみ判定 == 0
            衣装部位 = 衣装部位_上下服
            CALL CLOTH_GET_COVERAGE(COVERED_PARTS, 衣装部位, 調教用衣装:CHARA_ID:衣装部位)
            IF COVERED_PARTS:被覆部位 == 1
                CLOTH_ID = 調教用衣装:CHARA_ID:衣装部位
            ELSE
                ;服で被覆されていないなら下着を対象に再判定
                CALL IS_COVERED(CHARA_ID, 被覆部位, 1)
                RETURN RESULT
            ENDIF
        ELSEIF 調教用衣装:CHARA_ID:衣装部位_下服 != 0 && 衣装状態:CHARA_ID:衣装部位_下服 == 0 && 下着のみ判定 == 0
            衣装部位 = 衣装部位_下服
            CALL CLOTH_GET_COVERAGE(COVERED_PARTS, 衣装部位, 調教用衣装:CHARA_ID:衣装部位)
            IF COVERED_PARTS:被覆部位 == 1
                CLOTH_ID = 調教用衣装:CHARA_ID:衣装部位
            ELSE
                ;服で被覆されていないなら下着を対象に再判定
                CALL IS_COVERED(CHARA_ID, 被覆部位, 1)
                RETURN RESULT
            ENDIF
        ELSEIF 調教用衣装:CHARA_ID:衣装部位_下着下 != 0 && 衣装状態:CHARA_ID:衣装部位_下着下 == 0
            衣装部位 = 衣装部位_下着下
            CALL CLOTH_GET_COVERAGE(COVERED_PARTS, 衣装部位, 調教用衣装:CHARA_ID:衣装部位)
            IF COVERED_PARTS:被覆部位 == 1
                CLOTH_ID = 調教用衣装:CHARA_ID:衣装部位
            ENDIF
        ELSEIF 調教用衣装:CHARA_ID:衣装部位_上下下着 != 0 && 衣装状態:CHARA_ID:衣装部位_上下下着 == 0
            衣装部位 = 衣装部位_上下下着
            CALL CLOTH_GET_COVERAGE(COVERED_PARTS, 衣装部位, 調教用衣装:CHARA_ID:衣装部位)
            IF COVERED_PARTS:被覆部位 == 1
                CLOTH_ID = 調教用衣装:CHARA_ID:衣装部位
            ENDIF
        ENDIF
        IF CLOTH_ID
            TRYCALLFORM CLOTH_NAME_%CLOTH_PART_NAME_ENG:衣装部位%_{CLOTH_ID}
            被覆衣装名 = %RESULTS%
            TRYCCALLFORM CLOTH_DESC_%CLOTH_PART_NAME_ENG:衣装部位%_{CLOTH_ID}
                被覆衣装描写 = %RESULTS%
            CATCH
                被覆衣装描写 = %被覆衣装名%
            ENDCATCH
            RESULT:1   = 調教用衣装:CHARA_ID:衣装部位
            RESULTS:0  = %被覆衣装名%
            RESULTS:1  = %被覆衣装描写%
        ENDIF
        RETURN 衣装部位
ENDSELECT

@CLOTH_GET_COVERAGE(COVERED_PARTS, 衣装部位, CLOTH_ID)
#DIM REF COVERED_PARTS
#DIM 衣装部位
#DIM CLOTH_ID
VARSET COVERED_PARTS, 0
TRYCALLFORM CLOTH_COVERED_%CLOTH_PART_NAME_ENG:衣装部位%_{CLOTH_ID}(COVERED_PARTS)
RETURN

;各キャラクター用の衣装ボタンIDを生成して返す
;2XXXXYY XXXX = CHARA_ID, YY = 衣装部位
@HO_GET_CLOTH_BTN_ID(CHARA_ID, 部位)
#DIM CHARA_ID
#DIM 部位
#DIM DYNAMIC BTN_ID
#DIMS DYNAMIC STR_BTN_ID
#DIMS DYNAMIC STR_CHARA_ID
#DIMS DYNAMIC STR_部位

STR_CHARA_ID '= TOSTR(CHARA_ID, "0000")
STR_部位 '= TOSTR(部位, "00")
STR_BTN_ID '= STR_CHARA_ID + STR_部位
BTN_ID = HO_キャラボタンID_衣装 + TOINT(STR_BTN_ID)

RETURN BTN_ID

;;;衣装の脱着
@HO_CHANGE_CLOTH_COND(CHARA_ID, 部位)
#DIM BTN_ID
#DIM DYNAMIC 部位
#DIM DYNAMIC CHARA_ID
#DIM DYNAMIC CLOTH_ID
#DIM DYNAMIC 下着部位
#DIM DYNAMIC 下着ID
#DIMS DYNAMIC 衣装描写
#DIMS DYNAMIC 下着描写

#DIM DYNAMIC  人目があるフラグ

;全脱衣、全着衣処理
IF 部位 == 98 ;全脱衣
    CALL HO_JUDGE_STRIP(CHARA_ID)
    CALL HO_CHECK_PUBLIC_EYE(CHARA_ID)
    SIF RESULT == 1
        人目があるフラグ = 1
    IF TCVAR:(CHARA_ID):24 > TCVAR:(CHARA_ID):25 && 人目があるフラグ != 1 || CHARA_ID == MASTER && 人目があるフラグ != 1
        IF CHARA_ID == MASTER || FLAG:クエスト_あなた不参加
            PRINTFORMW %ANAME(CHARA_ID)%は服を%SPLIT_RAND("脱ぎおろした/取り払った",1)%
        ELSE
            PRINTFORMW %ANAME(MASTER)%の頼みに応じて、%ANAME(CHARA_ID)%は%SPLIT_RAND("顔を赤らめ/羞恥に頬染め/羞恥のあまり目を伏せ",1)%つつ服を%SPLIT_RAND("脱ぎおろした/取り払った",1)%
        ENDIF
        CALL HO_UNDRESS(CHARA_ID)
        RETURN 1
    ELSEIF TCVAR:(CHARA_ID):24 > TCVAR:(CHARA_ID):25 && 人目があるフラグ == 1 && CHARA_ID != MASTER && !FLAG:クエスト_あなた不参加
        PRINTFORMW %ANAME(CHARA_ID)%に服を脱いでもらえないか頼んでみたものの、人目があるからと断られてしまった……
        RETURN 0
    ELSEIF 人目があるフラグ == 1 && (CHARA_ID == MASTER || FLAG:クエスト_あなた不参加)
        PRINTFORMW ここで服を脱ぐのは人目が気になる……
        RETURN 0
    ELSE
        PRINTFORMW %ANAME(CHARA_ID)%に服を脱いでもらえないか頼んでみたものの、%SPLIT_RAND("きっぱりと断られて/一笑に付されて/冷ややかな目で見られて/呆れた目で見られて",1)%しまった……
        RETURN 0
    ENDIF
ELSEIF 部位 == 99 ;全着衣
    CALL HO_REDRESS(CHARA_ID)
    RETURN 1
ENDIF

;個別処理
CLOTH_ID = 調教用衣装:CHARA_ID:部位
SELECTCASE 衣装状態:CHARA_ID:部位
    CASE 衣装状態_着
        SELECTCASE 部位
            CASE 衣装部位_上下服,衣装部位_上服,衣装部位_下服,衣装部位_下着下,衣装部位_下着上,衣装部位_上下下着
                CALL GET_CLOTH_DESC(部位, CLOTH_ID)
                衣装描写 '= RESULTS
                CALL HO_JUDGE_STRIP(CHARA_ID)
                CALL HO_CHECK_PUBLIC_EYE(CHARA_ID)
                SIF RESULT == 1
                    人目があるフラグ = 1
                IF TCVAR:(CHARA_ID):24 > TCVAR:(CHARA_ID):25 && 人目があるフラグ != 1 || CHARA_ID == MASTER && 人目があるフラグ != 1
                    衣装状態:CHARA_ID:部位 = 衣装状態_半脱
                    IF CHARA_ID == MASTER || FLAG:クエスト_あなた不参加
                        PRINTFORMW %ANAME(CHARA_ID)%は%衣装描写%を%SPLIT_RAND("脱ぎおろした/取り払った",1)%
                    ELSE
                        PRINTFORMW %ANAME(MASTER)%の頼みに応じて、%ANAME(CHARA_ID)%は%SPLIT_RAND("顔を赤らめ/羞恥に頬染め/羞恥のあまり目を伏せ",1)%つつ%衣装描写%を%SPLIT_RAND("脱ぎおろした/取り払った",1)%
                    ENDIF
                    IF 部位 == 衣装部位_上服 || 部位 == 衣装部位_下着上
                        CALL IS_COVERED(CHARA_ID, 被覆部位_乳首)
                        下着部位 = RESULT:0
                        下着ID = RESULT:1
                        IF 下着ID != 0
                            CALL GET_CLOTH_DESC(下着部位, 下着ID)
                            下着描写 '= RESULTS
                            CALLF TAG_PRINT(@"取り払われた%衣装描写%の下から@C:{カラー_ピンク}@%下着描写%@/C@があらわになった……", 1)
                            WAIT
                        ELSE
                            CALLF TAG_PRINT(@"取り払われた%衣装描写%の下から@C:{カラー_ピンク}@%STR_BODY("胸：長", CHARA_ID)%@/C@があらわになった……", 1)
                            WAIT
                        ENDIF
                    ENDIF
                    IF 部位 == 衣装部位_下服 || 部位 == 衣装部位_下着下
                        CALL IS_COVERED(CHARA_ID, 被覆部位_秘部)
                        下着部位 = RESULT:0
                        下着ID = RESULT:1
                        IF 下着ID != 0
                            CALL GET_CLOTH_DESC(下着部位, 下着ID)
                            下着描写 '= RESULTS
                            CALLF TAG_PRINT(@"取り払われた%衣装描写%の下から@C:{カラー_ピンク}@%下着描写%@/C@があらわになった……", 1)
                            WAIT
                        ELSE
                            IF !HAS_PENIS(CHARA_ID)
                                CALLF TAG_PRINT(@"取り払われた%衣装描写%の下から@C:{カラー_ピンク}@%STR_BODY("クリ：感度：欲情", CHARA_ID)%@/C@があらわになった……", 1)
                            ELSE
                                CALLF TAG_PRINT(@"取り払われた%衣装描写%の下から@C:{カラー_ピンク}@ペニス@/C@があらわになった……", 1)
                            ENDIF
                            WAIT
                        ENDIF
                    ENDIF
                    衣装状態:CHARA_ID:部位 = 衣装状態_半脱
                    RETURN 1
                ELSEIF TCVAR:(CHARA_ID):24 > TCVAR:(CHARA_ID):25 && 人目があるフラグ == 1 && CHARA_ID != MASTER && !FLAG:クエスト_あなた不参加
                    PRINTFORMW %ANAME(CHARA_ID)%に%衣装描写%を脱いでもらえないか頼んでみたものの、人目があるからと断られてしまった……
                    RETURN 0
                ELSEIF 人目があるフラグ == 1 && (CHARA_ID == MASTER || FLAG:クエスト_あなた不参加)
                    PRINTFORMW ここで%衣装描写%を脱ぐのは人目が気になる……
                    RETURN 0
                ELSE
                    PRINTFORMW %ANAME(CHARA_ID)%に%衣装描写%を脱いでもらえないか頼んでみたものの、%SPLIT_RAND("きっぱりと断られて/一笑に付されて/冷ややかな目で見られて/呆れた目で見られて",1)%しまった……
                    RETURN 0
                ENDIF
            CASEELSE
                衣装状態:CHARA_ID:部位 = 衣装状態_半脱
                RETURN 1
        ENDSELECT
    CASE 衣装状態_半脱
        衣装状態:CHARA_ID:部位 = 衣装状態_脱
        RETURN 1
    CASE 衣装状態_脱
        衣装状態:CHARA_ID:部位 = 衣装状態_着
        RETURN 1
ENDSELECT

@HO_JUDGE_STRIP(CHARA_ID, 判定値を表示 = 0)
#DIM CHARA_ID
#DIM 判定値を表示
;実行値の設定
TCVAR:(CHARA_ID):25 = 80

;共通部分
CALL COM_ORDER(CHARA_ID)

CALL COM_ORDER_ELEMENT(CHARA_ID, @"欲望Lv{ABL:(CHARA_ID):欲望}", ABL:(CHARA_ID):欲望 * 3)
CALL COM_ORDER_ELEMENT(CHARA_ID, @"露出Lv{ABL:(CHARA_ID):露出}", ABL:(CHARA_ID):露出 * 5)

LOCAL:0 = GET_PALAMLV(PALAM:(CHARA_ID):欲情)
CALL COM_ORDER_ELEMENT(CHARA_ID, @"欲情Lv{LOCAL:0}", MIN(LOCAL:0 * 3, 30))

IF TCVAR:(CHARA_ID):60
    CALL COM_ORDER_ELEMENT(CHARA_ID, "媚薬", 6)
ENDIF

IF TALENT:(CHARA_ID):恥じらい
    CALL COM_ORDER_ELEMENT(CHARA_ID, "恥じらい", -12)
ENDIF
IF TALENT:(CHARA_ID):恥薄い
    CALL COM_ORDER_ELEMENT(CHARA_ID, "恥薄い", 8)
ENDIF
IF TALENT:(CHARA_ID):自制心
    CALL COM_ORDER_ELEMENT(CHARA_ID, "自制心", -6)
ENDIF
IF TALENT:(CHARA_ID):目立ちたがり
    CALL COM_ORDER_ELEMENT(CHARA_ID, "目立ちたがり", 6)
ENDIF
IF TALENT:(CHARA_ID):自慰しやすい
    CALL COM_ORDER_ELEMENT(CHARA_ID, "自慰しやすい", 10)
ENDIF
IF TALENT:(CHARA_ID):快感の否定
    CALL COM_ORDER_ELEMENT(CHARA_ID, "快感の否定", -2)
ENDIF

IF !TALENT:(CHARA_ID):合意 && !TALENT:(CHARA_ID):親友
    CALL COM_ORDER_ELEMENT(CHARA_ID, "合意なし", -20)
ENDIF
PRINTL
SIF 判定値を表示 == 0
    CLEARLINE 1
RETURN 1

;衣装を初期状態に
@HO_REDRESS(CHARA_ID, 破れを直す = 0)
#DIM CHARA_ID
#DIM 破れを直す
#DIM DYNAMIC 実行フラグ

;無効なプリセットが予約された場合は通常プリセットを適用
IF !CLOTH_PRESET_IS_VALID:CHARA_ID:(CFLAG:CHARA_ID:衣装プリセット変更予約)
    CFLAG:CHARA_ID:衣装プリセット変更予約 = 衣装プリセット_通常
ENDIF

;変更予約フラグが立っており、現在適用中の衣装プリセットと異なる場合は破れを直し、プリセットを適用する
IF CFLAG:CHARA_ID:衣装プリセット変更予約 && CLOTH_PRESET_IS_VALID:CHARA_ID:(CFLAG:CHARA_ID:衣装プリセット変更予約)
    IF CFLAG:CHARA_ID:衣装プリセット != CFLAG:CHARA_ID:衣装プリセット変更予約
        破れを直す = 1
        CFLAG:CHARA_ID:衣装プリセット = CFLAG:CHARA_ID:衣装プリセット変更予約
        実行フラグ = 1
    ENDIF
ENDIF

CALL SET_CHARA_CLOTH_DATA(CHARA_ID)

FOR LOCAL, 0, CLOTH_PART_NUM
    IF 衣装状態:CHARA_ID:LOCAL != 衣装状態_着 && 衣装状態:CHARA_ID:LOCAL != 衣装状態_破
        衣装状態:CHARA_ID:LOCAL = 衣装状態_着
        実行フラグ = 1
    ELSEIF 衣装状態:CHARA_ID:LOCAL != 衣装状態_着 && 破れを直す == 1
        衣装損壊率:CHARA_ID:LOCAL = 0
        衣装状態:CHARA_ID:LOCAL = 衣装状態_着
        実行フラグ = 1
    ENDIF
NEXT
CALL C_COND_SET_CLOTH_COVERRAGE(CHARA_ID)
RETURN 実行フラグ

;問答無用で全裸
@HO_UNDRESS(CHARA_ID)
#DIM CHARA_ID
VARSET 衣装状態:CHARA_ID:0 ,衣装状態_脱
CALL C_COND_SET_CLOTH_COVERRAGE(CHARA_ID)
RETURN 1

;部位指定脱衣
;枚数指定 > 0 の場合指定枚数分実行, 0の場合指定部位が露出するまで実行
;RESULT 実際に脱いだ枚数
@HO_STRIP(CHARA_ID, 被覆部位, 衣装状態指定, 枚数指定 = 0)
#DIM CHARA_ID
#DIM 被覆部位
#DIM 衣装状態指定
#DIM 枚数指定
#DIM DYNAMIC 脱衣カウンタ

WHILE 1
    SIF 枚数指定 != 0 && 脱衣カウンタ >= 枚数指定
        BREAK
    CALL IS_COVERED(CHARA_ID, 被覆部位)
    IF RESULT:1
        衣装状態:CHARA_ID:(RESULT:0) = 衣装状態指定
        脱衣カウンタ ++
    ELSE
        BREAK
    ENDIF
WEND
CALL C_COND_SET_CLOTH_COVERRAGE(CHARA_ID)
RETURN 脱衣カウンタ

;見え判定
;RESULT = 1(乳首露出) 2(秘部露出) 3(全部露出)
@HO_CHECK_NAKED(CHARA_ID)
#DIM CHARA_ID
#DIM DYNAMIC 露出
SIF !FLAG:ハウジングモード && !FLAG:クエストモード && !FLAG:デイリーイベント
    RETURN 0
CALL IS_COVERED(CHARA_ID, 被覆部位_乳首)
SIF !RESULT:1
    露出 += 1
CALL IS_COVERED(CHARA_ID, 被覆部位_秘部)
SIF !RESULT:1
    露出 += 2
RETURN 露出


code:
;-------------------------------------------------
;ハウジングモードでの調教時に使用する関数
;-------------------------------------------------
;;; 一緒に行動できるキャラクターをまとめてMTARに登録する
;;; ACTIONは','区切りで10個まで複数指定できる
;-------------------------------------------------
@HO_SET_MTAR_PLAY_WITH(ACTION, RESET_MTAR = 1, FORCE_ADD_TO_MTAR = 1)
#DIMS ACTION
#DIM  RESET_MTAR
#DIM  FORCE_ADD_TO_MTAR
#DIMS DYNAMIC _LIST_MEMBER
#DIM DYNAMIC MEMBER_NUM
#DIM DYNAMIC CHARA_ID

VARS ACTIONS, 10
VARI ACTIONNUM
SPLIT ACTION, ",", ACTIONS
ACTIONNUM = RESULT

SIF RESET_MTAR
    CALL CLEAR_MTAR
CALL HO_GET_COMPANION_LIST
MEMBER_NUM = RESULT
_LIST_MEMBER = %RESULTS%
FOR LOCAL, 0, MEMBER_NUM
    CHARA_ID = TOINT(LIST_GET(_LIST_MEMBER, LOCAL))
    ;MPLYに既におり、MPLYが単独でない場合、MPLYからは外す
    SIF IS_MPLY(CHARA_ID) && FORCE_ADD_TO_MTAR && MPLY_NUM > 1
        CALL DEL_MPLY(CHARA_ID)
    ;同行者はMPLYでないなら追加
    IF TCVAR:CHARA_ID:相席フラグ != 1 && !IS_MPLY(CHARA_ID) && FORCE_ADD_TO_MTAR
        CALL ADD_MTAR(CHARA_ID)
    ELSE
        ;相席者は引数で指定した行動に合致していたら追加
        CALL HO_GET_CHARAACT(CHARA_ID)
        VARI I
        FOR I, 0, ACTIONNUM
            IF RESULTS == ACTIONS:I && !IS_MPLY(CHARA_ID) && FORCE_ADD_TO_MTAR
                CALL ADD_MTAR(CHARA_ID)
            ENDIF
        NEXT
    ENDIF
NEXT

RETURN

;-------------------------------------------------
;第三者から見られているかを確認
;行為を見られても許容出来る相手かも確認
;RESULT 0:見られていない 1:見られている
;-------------------------------------------------
@HO_CHECK_PUBLIC_EYE(MPLY_ID)
#DIM MPLY_ID
#DIM DYNAMIC CHARA_ID
#DIMS DYNAMIC _LIST_VISIBLE
#DIMS DYNAMIC _LIST_USAGE

;クエスト中でも領内活動中でもない場合は人目を判定することが出来ないので
;誰にも見られていないものとする
SIF !FLAG:ハウジングモード
    RETURN 0

;クエスト中はウフフ許可フラグが立っていない限り、誰かに見られているものとする
SIF FLAG:調教モード == 調教_クエスト && !FLAG:クエスト_ウフフ許可
    RETURN 1

;虜囚は許容の有無を問わない
IF CFLAG:MPLY_ID:捕虜先
    RETURN 0
ENDIF
CALL HO_GET_WHO_VISIBLE(MPLY_ID)
_LIST_VISIBLE = %RESULTS%
IF RESULT == 0
    RETURN 0
ELSE
    FOR LOCAL, 0, LIST_COUNT(_LIST_VISIBLE)
        CHARA_ID = TOINT(LIST_GET(_LIST_VISIBLE, LOCAL))
        ;合意済みであれば見られても問題なし
        IF TALENT:CHARA_ID:合意 == 1
            CONTINUE
            ;カップルに見られても問題なし
        ELSEIF CHARA_ID == CFLAG:MPLY_ID:カップル相手キャラID
            CONTINUE
            ;見られている相手が捕虜の場合、加虐的は人物は視線を意識しない
        ELSEIF (CFLAG:MPLY_ID:捕虜先 || IS_SLAVE(CHARA_ID)) && CAN_RAPE_ACCEPT(MPLY_ID)
            CONTINUE
            ;スケベモブが売春施設で見られても問題なし
        ELSEIF CFLAG:MPLY_ID:スケベモブ
            CALL HO_GET_MODULE_DATA(C_COND_MAP_GET(CHARA_ID, "設備"), "用途")
            _LIST_USAGE = %RESULTS%
            IF LIST_GMATCH(_LIST_USAGE, "OR", "売春", "性行為", "性処理")
                CONTINUE
            ENDIF
        ELSE
            RETURN 1
        ENDIF
    NEXT
ENDIF
RETURN 0
;-------------------------------------------------
;第三者から見られているかを確認
;-------------------------------------------------
@HO_GET_WHO_VISIBLE(MPLY_ID)
#DIM MPLY_ID
#DIM DYNAMIC CHARA_IDX
#DIM DYNAMIC CHARA_ID
#DIMS DYNAMIC _LIST_VISIBLE

VARSET RESULTS
WHILE _DIC_HO_CHARALIST:CHARA_IDX != "-1"
    IF _DIC_HO_CHARALIST:CHARA_IDX == "DELETED"
        CHARA_IDX ++
        CONTINUE
    ENDIF
    CALL HO_GET_CHARADATA(CHARA_IDX, "CHARA_ID")
    CHARA_ID = RESULT
    ;自分自身はチェックしない
    IF MPLY_ID == CHARA_ID
        CHARA_IDX ++
        CONTINUE
    ENDIF
    CALL HO_IS_TARGET_VISIBLE(MPLY_ID, CHARA_ID)
    SIF RESULT
        _LIST_VISIBLE = %LIST_ADD(_LIST_VISIBLE, TOSTR(CHARA_ID))%
    CHARA_IDX ++
WEND

RESULTS = %_LIST_VISIBLE%
RETURN LIST_COUNT(_LIST_VISIBLE)

;-------------------------------------------------
;ウフフ目撃判定
;引数のキャラクタが実行されたコマンドを情事を目撃したと認識するか判定する
;RETURN 0=見ていない、気がついていない 1=情事露見 2=情事ほどではないが意識してしまう行為 3=気がついたが許容
;-------------------------------------------------
@HO_CHECK_UFUFU(CHARA_ID)
#DIM CHARA_ID
#DIM  DYNAMIC キャラ現在地
#DIM  DYNAMIC ウフフチェック済み
#DIMS DYNAMIC _LIST_MEMBER
#DIMS DYNAMIC ACTION
;;許容判定
;当人がコマンドの実行または対象であれば判定しない
IF IS_MPLY(CHARA_ID) || IS_MTAR(CHARA_ID)
    RETURN 3
ENDIF

;意識不明なら判定しない
IF TCVAR:CHARA_ID:離脱フラグ == 離脱フラグ_睡眠
    RETURN 0
ENDIF

;スケベな公演は判定しない
IF TFLAG:公演_趣向 == 1
    RETURN 3
ENDIF

;適切な場所での行為は容認される
IF C_COND("エリアタグ:娼館",,CHARA_ID)
    RETURN 3
ENDIF

CALL HO_GET_CHARAACT(CHARA_ID)
ACTION = %RESULTS%
;特定の行動中
IF GROUPMATCH(ACTION, "搾乳中")
    RETURN 3
ENDIF

;;目撃判定
;キャラが現在いる部屋を取得
CALL HO_GET_CHARAPLACE(CHARA_ID)
キャラ現在地 = RESULT
;ウフフ目撃判定
IF HO_MASTER現在地 == キャラ現在地
    ;覗き見中は判定しない
    SIF TFLAG:覗き見モード
        RETURN 0
    IF ACTION != "同行中" && TCVAR:CHARA_ID:同行解除フラグ != 1
        SIF ACTION == "逢瀬中"
            RETURN 0
        ;見えない位置にいるのなら判定しなくて良い
        CALL HO_IS_TARGET_VISIBLE(CHARA_ID, MASTER)
        SIF RESULT != 1
            RETURN 0
        ;押し倒し中の場合は情事扱い
        IF FLAG:ウフフフラグ == 1 && TFLAG:奉仕モード == 0
            TCVAR:CHARA_ID:ウフフ目撃フラグ = 1
            TCVAR:CHARA_ID:ウフフ目撃場所 = キャラ現在地
            RETURN 1
        ELSE
            VARI 机使用中
            VARI 卓使用中
            VARI ウフフ隠蔽
            ;;押し倒し以外の情事判定
            ;-------------------------------------------------
            ;単発コマンドの判定 MTARにもMPLYにも絡んでいなければ目撃判定
            IF FINDELEMENT(UFUFU_COM_LIST, SELECTCOM) != -1
                ;一部の机下行為は露見しない
                机使用中 = C_COND("設備属性:机",,MASTER)
                卓使用中 = C_COND("設備属性:卓",,MASTER)
                IF 机使用中 && GROUPMATCH(SELECTCOM, 10, 11, 26, 106, 107, 90, 19, 132, 120, 100)
                    ウフフ隠蔽 = 1
                ENDIF
                IF !ウフフ隠蔽
                    IF 卓使用中 && GROUPMATCH(SELECTCOM, 14)
                        ウフフ隠蔽 = 1
                    ENDIF
                ENDIF
                IF !ウフフ隠蔽
                    IF IS_MTAR(CHARA_ID) != 1 && IS_MPLY(CHARA_ID) != 1
                        TCVAR:CHARA_ID:ウフフ目撃フラグ = 1
                        TCVAR:CHARA_ID:ウフフ目撃場所 = キャラ現在地
                        RETURN 1
                    ENDIF
                ENDIF
                ;ドキドキ判定
            ELSEIF  FINDELEMENT(DOKIDOKI_COM_LIST, SELECTCOM) != -1
                IF IS_MTAR(CHARA_ID) != 1 && IS_MPLY(CHARA_ID) != 1
                    RETURN 2
                ENDIF
            ENDIF
            ;継続中コマンドの判定 MTARにもMPLYにも絡んでいなければ目撃判定
            ;-------------------------------------------------
            机使用中 = C_COND("設備属性:机",,MASTER)
            卓使用中 = C_COND("設備属性:卓",,MASTER)
            ウフフ隠蔽 = 0
            VARSET LOCAL
            FOR LOCAL:0, 0, VARSIZE("UFUFU_COM_LIST")
                ;一部の机下行為は露見しない
                IF 机使用中 && GROUPMATCH(UFUFU_COM_LIST:(LOCAL:0), 10, 11, 26, 106, 107, 90, 19, 132, 120, 100)
                    ウフフ隠蔽 = 1
                    CONTINUE
                ENDIF
                ;一部の卓下行為は露見しない
                IF 卓使用中 && GROUPMATCH(UFUFU_COM_LIST:(LOCAL:0), 14)
                    ウフフ隠蔽 = 1
                    CONTINUE
                ENDIF
                IF IS_EQUIP(MASTER, UFUFU_COM_LIST:(LOCAL:0))
                    ウフフ隠蔽 = 0
                    LOCAL:1 ++
                ENDIF
            NEXT
            ;MASTERがウフフ行為を隠蔽出来ている状況なら以降の判定をスキップ
            IF !ウフフ隠蔽
                ; ;MASTERが情事に該当する行為のMPLYでもMTARでもなければRETURN
                ; SIF LOCAL:1 == 0
                ;     RETURN 0
                机使用中 = C_COND("設備属性:机",,CHARA_ID)
                卓使用中 = C_COND("設備属性:卓",,CHARA_ID)
                ウフフ隠蔽 = 0
                FOR LOCAL:0, 0, VARSIZE("UFUFU_COM_LIST")
                    ;一部の机下行為は露見しない
                    IF 机使用中 && GROUPMATCH(UFUFU_COM_LIST:(LOCAL:0), 10, 11, 26, 106, 107, 90, 19, 132, 120, 100)
                        ウフフ隠蔽 = 1
                        CONTINUE
                    ENDIF
                    IF 卓使用中 && GROUPMATCH(UFUFU_COM_LIST:(LOCAL:0), 14)
                        ウフフ隠蔽 = 1
                        CONTINUE
                    ENDIF
                    IF IS_EQUIP(CHARA_ID, UFUFU_COM_LIST:(LOCAL:0))
                        ウフフ隠蔽 = 0
                        LOCAL:2 ++
                    ENDIF
                NEXT
                ;MASTERが情事に該当する行為のMPLYかMTARで、CHARA_IDが行為に関わっていない第三者であれば目撃者扱い
                IF !ウフフ隠蔽 &&LOCAL:1 > 0 && LOCAL:2 == 0
                    TCVAR:CHARA_ID:ウフフ目撃フラグ = 1
                    TCVAR:CHARA_ID:ウフフ目撃場所 = キャラ現在地
                    RETURN 1
                ENDIF
                VARSET LOCAL
                ;;継続コマンドのドキドキ判定
                FOR LOCAL:0, 0, VARSIZE("DOKIDOKI_COM_LIST")
                    SIF IS_EQUIP(MASTER, DOKIDOKI_COM_LIST:(LOCAL:0))
                        LOCAL:1 ++
                NEXT
                ;MASTERがドキドキに該当する行為のMPLYでもMTARでもなければRETURN
                FOR LOCAL:0, 0, VARSIZE("DOKIDOKI_COM_LIST")
                    SIF IS_EQUIP(CHARA_ID, DOKIDOKI_COM_LIST:(LOCAL:0))
                        LOCAL:2 ++
                NEXT
                ;MASTERがドキドキに該当する行為のMPLYかMTARで、CHARA_IDが行為に関わっていない第三者であれば目撃者扱い
                IF LOCAL:1 > 0 && LOCAL:2 == 0
                    RETURN 2
                ENDIF
            ENDIF
        ENDIF
    ELSE
        ;同行中の行為は許容する
        RETURN 3
    ENDIF
ENDIF
RETURN 0

;-------------------------------------------------
;居住区モードの調教終了処理
;-------------------------------------------------
@HOUSING_EVENTEND
;体力気力を0～最大値の範囲に収める
CALL LIFE_LIMIT

TRAIN_PLACE =

FLAG:夜這い = 0
TFLAG:奉仕モード = 0
TFLAG:売春フラグ = 0

IF FLAG:調教モード == 調教_慰安
    SELECTCASE FLAG:慰安モード
        CASE 1
            CALL EVENTEND_IAN
        CASE 2
            CALL EVENTEND_ATEGAU
    ENDSELECT
    FLAG:慰安モード = 0
ENDIF

SIF FLAG:調教モード == 調教_逆調教特殊 && IS_SP_COUNTRY(CFLAG:MASTER:捕虜先)
    CALL EVENTEND_SP_COUNTRY()

CUSTOMDRAWLINE ─
CALL TITLE_CATEGORY_GENERAL("今回の調教")
PRINTL
CALL COLOR_LINE
FOR LOCAL:0, 0, CHARANUM
    SIF !IS_PARTICIPATE_TRAIN(LOCAL)
        CONTINUE
    PRINTFORML ■%ANAME(LOCAL)%:
    CALL NEW_PRINT_ORGASM(LOCAL)
    CALL COLOR_LINE
NEXT
WAIT

;慰安用のモブはここで削除する。ゲストは残す
;->交流中にキャラを消すと連想配列中のCHARA_IDが齟齬を起こすのでコメントアウト
; LOCAL:2 = CHARANUM
; FOR LOCAL:0, 0, LOCAL:2
;     LOCAL:1 = LOCAL:2 - LOCAL:0 - 1
;     IF CFLAG:(LOCAL:1):慰安モブ && !(CFLAG:(LOCAL:1):ゲストモブ)
;         CALL DELETE_CHARA(LOCAL:1)
;     ENDIF
; NEXT

;体力気力の成長
FOR LOCAL, 0, CHARANUM
    SIF !IS_PARTICIPATE_TRAIN(LOCAL) || IS_SP_CHARA(LOCAL)
        CONTINUE

    LOCAL:1 = MIN(SUM_EX(LOCAL) / 5, 20)
    LOCAL:1 += RAND:MAX(TCVAR:LOCAL:体力上昇最大値, 1)
    SELECTCASE MAXBASE:LOCAL:体力
        CASE IS > 5000
            LOCAL:1 /= 10
        CASE IS > 3000
            LOCAL:1 /= 4
    ENDSELECT

    SIF LOCAL:1 <= 0
        GOTO STR_GROW_END

    PRINTFORML %ANAME(LOCAL)%の体力 + {LOCAL:1}
    MAXBASE:LOCAL:体力 += LOCAL:1

    $STR_GROW_END

    LOCAL:1 = MIN(SUM_EX(LOCAL) / 5, 10)
    LOCAL:1 += RAND:MAX(TCVAR:LOCAL:気絶回数, 1)

    SELECTCASE MAXBASE:LOCAL:気力
        CASE IS > 5000
            LOCAL:1 /= 10
        CASE IS > 3000
            LOCAL:1 /= 4
    ENDSELECT

    SIF LOCAL:1 <= 0
        GOTO WILL_GROW_END

    MAXBASE:LOCAL:気力 += LOCAL:1
    PRINTFORML %ANAME(LOCAL)%の気力 + {LOCAL:1}

    $WILL_GROW_END
NEXT

;初対面のキャラの対面済みフラグをＯＮ
FOR LOCAL:0, 0, CHARANUM
    SIF LOCAL != MASTER && IS_PARTICIPATE_TRAIN(LOCAL) && !CFLAG:(LOCAL:0):面識
        CFLAG:(LOCAL:0):面識 = 1
NEXT

;調教参加者同士の印象値が改善される
FOR LOCAL:0, 0, CHARANUM
    SIF !IS_PARTICIPATE_TRAIN(LOCAL)
        CONTINUE
    FOR LOCAL:1, 0, CHARANUM
        SIF LOCAL:1 == LOCAL || !IS_PARTICIPATE_TRAIN(LOCAL:1)
            CONTINUE
        CALL TRAIN_CHANGE_RELATION(LOCAL:0, LOCAL:1)
    NEXT
NEXT

;現在の酔いを酔い合計値に加算
FOR LOCAL:0, 0, CHARANUM
    SIF IS_PARTICIPATE_TRAIN(LOCAL) && !IS_SP_CHARA(LOCAL)
        CFLAG:(LOCAL:0):酔い累積値 += PALAM:(LOCAL:0):酔い
NEXT

FOR LOCAL:0, 0, CHARANUM
    SIF CFLAG:LOCAL:強制友好化
        CFLAG:LOCAL:強制友好化 = 0
    SIF CFLAG:LOCAL:調教中BASE減らない
        CFLAG:LOCAL:調教中BASE減らない = 0
NEXT

;性知識アップ
FOR LOCAL:0, 0, CHARANUM
    IF IS_PARTICIPATE_TRAIN(LOCAL) && ABL:(LOCAL:0):性知識 < 4
        LOCAL:1 = MIN(SUM_EX(LOCAL:0) / MAX(ABL:(LOCAL:0):性知識 * 2, 1), 100)
        CALL PRINT_ADD_EXP(LOCAL:0, "性知識経験値", LOCAL:1, 1)
        CALL TRAIN_AUTO_ABLUP(LOCAL:0)
    ENDIF
NEXT

FOR LOCAL:0, 0, CHARANUM
    IF IS_PARTICIPATE_TRAIN(LOCAL) && !IS_SP_CHARA(LOCAL:0)
        ;素質変化に関する処理(恋慕の取得など)
        CALL TALENT_CHECK(LOCAL:0)
    ENDIF
NEXT

FOR LOCAL:0, 0, CHARANUM
    IF IS_PARTICIPATE_TRAIN(LOCAL)
        ;性的嗜好の変化
        ;あてがった場合はロックフラグを立てる
        CALL CHECK_SEXUAL_PREFERENCE(LOCAL:0)
    ENDIF
NEXT

IF CONFIG:42
    FOR LOCAL:0, 0, CHARANUM
        CALL 性癖一括変更(LOCAL,10000)
        CALL 性癖一括変更(LOCAL,12000)
        CALL 性癖一括変更(LOCAL,14000)
    NEXT
ENDIF
;追加アイテムの削除
FOR LOCAL:0, 500, 999
    ITEM:(LOCAL:0) = 0
NEXT

;助手記録を削除
FOR LOCAL:0, 0, CHARANUM
    SIF CFLAG:LOCAL:捕虜調教の助手
        CFLAG:LOCAL:捕虜調教の助手 = 0
    SIF CFLAG:LOCAL:夜這いの助手
        CFLAG:LOCAL:夜這いの助手 = 0
    SIF CFLAG:LOCAL:調教中帰らない
        CFLAG:LOCAL:調教中帰らない = 0
NEXT

FOR LOCAL, 0, CHARANUM
    SIF CFLAG:LOCAL:能力固定フラグ
        CFLAG:LOCAL:能力固定フラグ = 0
NEXT

;催眠フラグだけは折る(VIRGIN_COMMONなどにおけるTCVAR:催眠中での強制上書きが、他のところに影響しないよう)
FOR LOCAL:0, 0, CHARANUM
    TCVAR:LOCAL:催眠中 = 0
    TCVAR:LOCAL:催眠経過ターン数 = 0
NEXT

;閨逆調教フラグを折る
FLAG:閨逆調教 = 0

;次回の調教イベントに持ち越さない調教フラグを削除
CVARSET CFLAG, GETNUM(CFLAG, "主導除外") , 0
CVARSET CFLAG, GETNUM(CFLAG, "凌辱被害者フラグ") , 0
CVARSET CFLAG, GETNUM(CFLAG, "慰安参加者非対象化") , 0
CVARSET TCVAR, GETNUM(TCVAR, "奉仕者フラグ") , 0
CVARSET TCVAR, GETNUM(TCVAR, "被奉仕者フラグ") , 0

;プレイヤーとターゲットをクリア
CALL CLEAR_MPLY
CALL CLEAR_MTAR

;捕虜調教のターゲットをクリア
VARSET PRISONER_TARGET, -1

FLAG:調教モード = 調教_居住区
FLAG:ウフフフラグ = 0

RETURN

;-------------------------------------------------
;ハウジングモードで押し倒し実行時の判定
;調教タイプ 押し倒す = 0, 奉仕を要求 = 1, 過激な奉仕を要求= 2, 誘い受ける = 3
;-------------------------------------------------
@HO_JUDGE_PUSHDOWN(MTAR_ID, 調教タイプ = 0)
#DIM MTAR_ID
#DIM 調教タイプ
#DIM CONST 調教タイプ_奉仕 = 1
#DIM CONST 調教タイプ_過激な奉仕 = 2
#DIM CONST 調教タイプ_誘い受け = 3

;実行判定
;寝ているなら強制成功
IF TCVAR:MTAR_ID:睡眠深度 > 0 && TCVAR:(MTAR_ID):53
    CALL KOJO_EVENT(MTAR_ID, 8)
    RETURN 2
ENDIF

;●実行値の計算
CVARSET TCVAR, 24, -10000
CVARSET TCVAR, 25, 10000

;実行値の設定
SELECTCASE 調教タイプ
    CASE 調教タイプ_奉仕
        TCVAR:(MTAR_ID):25 = 150
    CASE 調教タイプ_過激な奉仕,
        TCVAR:(MTAR_ID):25 = 250
    CASE 調教タイプ_誘い受け
        TCVAR:(MTAR_ID):25 = 220
    CASEELSE
        TCVAR:(MTAR_ID):25 = 100
ENDSELECT

CALL COM_ORDER(MTAR_ID)

CALL COM_ORDER_ELEMENT(MTAR_ID, "欲情", GET_PALAMLV(PALAM:(MTAR_ID):欲情))
IF HAS_VAGINA(MTAR_ID)
    CALL COM_ORDER_ELEMENT(MTAR_ID, "潤滑", GET_PALAMLV(PALAM:(MTAR_ID):潤滑))
ENDIF
CALL COM_ORDER_ELEMENT(MTAR_ID, "欲望", ABL:(MTAR_ID):欲望 * 4)
CALL COM_ORDER_ELEMENT(MTAR_ID, "奉仕", ABL:(MTAR_ID):奉仕 * 2)

IF TALENT:(MTAR_ID):貞操観念
    CALL COM_ORDER_ELEMENT(MTAR_ID, "貞操観念", -10)
ENDIF

IF TALENT:(MTAR_ID):貞操無頓着
    CALL COM_ORDER_ELEMENT(MTAR_ID, "貞操無頓着", 10)
ENDIF

IF TALENT:(MTAR_ID):恥じらい
    CALL COM_ORDER_ELEMENT(MTAR_ID, "恥じらい", -6)
ENDIF

IF TALENT:(MTAR_ID):恥薄い
    CALL COM_ORDER_ELEMENT(MTAR_ID, "恥薄い", 3)
ENDIF

IF TALENT:(MTAR_ID):快感に素直
    CALL COM_ORDER_ELEMENT(MTAR_ID, "快感に素直", 5)
ENDIF

IF TALENT:(MTAR_ID):快感の否定
    CALL COM_ORDER_ELEMENT(MTAR_ID, "快感の否定", -5)
ENDIF

IF TALENT:(MTAR_ID):一線越えない
    CALL COM_ORDER_ELEMENT(MTAR_ID, "一線越えない", -15)
ENDIF

IF TALENT:(MTAR_ID):恋慕
    CALL COM_ORDER_ELEMENT(MTAR_ID, "恋慕", 12)
ENDIF

IF TALENT:(MTAR_ID):親友
    CALL COM_ORDER_ELEMENT(MTAR_ID, "親友", 6)
ENDIF

IF TALENT:(MTAR_ID):親愛
    CALL COM_ORDER_ELEMENT(MTAR_ID, "親愛", 20)
ENDIF

IF TALENT:(MTAR_ID):恋人
    CALL COM_ORDER_ELEMENT(MTAR_ID, "恋人", 6)
ENDIF

IF TALENT:(MTAR_ID):キス未経験
    CALL COM_ORDER_ELEMENT(MTAR_ID, "キス未経験", -5)
ENDIF

IF CFLAG:(MTAR_ID):スケベモブ && 調教タイプ_誘い受け
    CALL COM_ORDER_ELEMENT(MTAR_ID, "スケベモブを誘惑", 20)
ENDIF

; ;ムードボーナス
; IF TFLAG:ムード < 60
; 	CALL COM_ORDER_ELEMENT(MTAR_ID, "ムード", 0)
; ELSE
; 	CALL COM_ORDER_ELEMENT(MTAR_ID, "ムード", 5 + TFLAG:ムード / 30)
; ENDIF

;衣服の露出度
CALL HO_CHECK_NAKED(MTAR_ID)
IF RESULT
    CALL COM_ORDER_ELEMENT(MTAR_ID, "衣服露出度", RESULT * 5)
ENDIF
;好感度
IF CFLAG:(MTAR_ID):2 < 700
    IF CFLAG:(MTAR_ID):2 < 300
        LOCAL:0 = -20
    ELSEIF CFLAG:(MTAR_ID):2 < 500
        LOCAL:0 = -10
    ELSE
        LOCAL:0 = -5
    ENDIF
    CALL COM_ORDER_ELEMENT(MTAR_ID, "好感度不足", LOCAL:0)
ENDIF

;第三者　○○に恋慕に関する処理
;CALL JUDGE_PUSHDOWN_SUB

;性知識
IF ABL:(MTAR_ID):性知識 == 0
    CALL COM_ORDER_ELEMENT(MTAR_ID, "性知識なし", 80)
ENDIF

;合意
IF TALENT:(MTAR_ID):合意
    CALL COM_ORDER_ELEMENT(MTAR_ID, "合意", 10)
ENDIF

;判定結果を表示
PRINTFORMW  = {TCVAR:(MTAR_ID):24} %TOSTR_COMPARE(TCVAR:(MTAR_ID):24, TCVAR:(MTAR_ID):25)% 実行値{TCVAR:(MTAR_ID):25}

IF TCVAR:(MTAR_ID):24 >= TCVAR:(MTAR_ID):25
    ;既に[合意]済み
    IF TALENT:(MTAR_ID):合意
        CALL KOJO_EVENT(MTAR_ID, 7)

        ;酔いによる補正を除いても成功
    ELSEIF TCVAR:(MTAR_ID):24 - PALAM:(MTAR_ID):酔い / 150 >= 100
        TFLAG:酔った勢いで成功 = 0
        CALL KOJO_EVENT(MTAR_ID, 5)
        IF TCVAR:(MTAR_ID):催眠中 <= 0
            PRINTFORMW %ANAME(MTAR_ID)%の<合意>を得た
            TALENT:(MTAR_ID):合意 = 1
        ENDIF

        ;酔った勢いで成功
    ELSE
        TFLAG:酔った勢いで成功 = 1
        CALL KOJO_EVENT(MTAR_ID, 4)
    ENDIF
    RETURN 1

    ;失敗
ELSE
    CALL KOJO_EVENT(MTAR_ID, 6)
    IF 調教タイプ == 調教タイプ_誘い受け
        PRINTFORMW %ANAME(MTAR_ID)%に白い目で見られてしまった……
    ELSE
        IF !TALENT:(MTAR_ID):合意 && !TALENT:(MTAR_ID):恋慕 && !TALENT:(MTAR_ID):親友
            PRINTFORMW %CALLNAME:MASTER%の行為に%ANAME(MTAR_ID)%は激しく抵抗している……
            PRINTFORMW %ANAME(MTAR_ID)%の好感度が300下がりました
            CFLAG:(MTAR_ID):2 -= 300
        ELSEIF TALENT:(MTAR_ID):恋慕 || TALENT:(MTAR_ID):親友
            PRINTFORMW %ANAME(MTAR_ID)%は今はそんな気分ではないと%CALLNAME:MASTER%の手をやんわりと払い除けた……
        ELSE
            PRINTFORMW %ANAME(MTAR_ID)%は%CALLNAME:MASTER%の手を払い除けて拒否した……
            PRINTFORMW %ANAME(MTAR_ID)%の好感度が100下がりました
            CFLAG:(MTAR_ID):2 -= 100
        ENDIF
        IF CFLAG:MTAR_ID:捕虜先
            PRINTL
            PRINTFORMW %ANAME(MPLY:0)%は抵抗する%ANAME(MTAR_ID)%を強引に組み伏せた……
            RETURN 2
        ENDIF
    ENDIF
    RETURN 0
ENDIF

;-------------------------------------------------
; 自慰判定
;-------------------------------------------------
@HO_SELF_CHECK(CHARA_ID)
#DIM CHARA_ID

;特殊状態が立っていれば戻る
SIF CFLAG:(CHARA_ID):行動不能状態
    RETURN 0

;虚ろ・崩壊なら戻る
SIF TALENT:(CHARA_ID):虚ろ || TALENT:(CHARA_ID):崩壊
    RETURN 0

;自慰または触手オナニーの知識が必要
LOCAL:0 = 0
CALL CHECK_COM_KNOWLEDGE(CHARA_ID, 100)
LOCAL:0 += RESULT
CALL CHECK_COM_KNOWLEDGE(CHARA_ID, 211)
LOCAL:0 += RESULT
SIF !LOCAL:0
    RETURN 0

;おねだりフラグが立っていれば100%(条件判定をスルー)
IF !TCVAR:(CHARA_ID):27
    ;最低でも欲情が一定値以上必要
    SIF PALAM:(CHARA_ID):欲情 < PALAMLV:6
        RETURN 0

    ;機嫌がある程度良いとき限定
    LOCALS:0 = %TOSTR_EMOTION(CHARA_ID)%
    SIF !GROUPMATCH(LOCALS:0, "幸", "悦", "良", "平")
        RETURN 0

    ;体力・気力がゼロ、もしくは少ないなら行わない
    SIF BASE:(CHARA_ID):体力 <= 500 || BASE:(CHARA_ID):気力 <= 500
        RETURN 0
ENDIF

;●詳細な自慰判定
CALL CALC_SELF_PARAMETER(CHARA_ID)
SIF !RESULT
    RETURN 0

RETURN 1

;-------------------------------------------------
; 母乳量算出
;-------------------------------------------------
@HO_CALC_LACTATION_AMOUNT(CHARA_ID, BASE_AMOUNT)
#DIM BASE_AMOUNT
#DIM CHARA_ID
#DIM DYNAMIC AMOUNT

AMOUNT = (BASE_AMOUNT * (GET_PALAMLV(PALAM:CHARA_ID:噴乳) * 10 + 100)) / 100
CALL LEWDSKILL_MILK_INFLATION(CHARA_ID, AMOUNT)
AMOUNT += RESULT
RETURN AMOUNT


code:
;-------------------------------------------------
;ハウジングモードでの調教描写に使用する関数(文字列の描写中心)
;-------------------------------------------------

;-------------------------------------------------
;キャラの行動を描写する文字列を参加人数分生成
;-------------------------------------------------
@HO_CHARAACT_DESC()
#DIM DYNAMIC CHARA_IDX

SIF !FLAG:ハウジングモード
    RETURN 0

WHILE _DIC_HO_CHARALIST:CHARA_IDX != "-1"
    IF _DIC_HO_CHARALIST:CHARA_IDX == "DELETED"
        CHARA_IDX ++
        CONTINUE
    ENDIF
    CALL HO_CHARAACT_DESC_SETSTR(CHARA_IDX)
    CHARA_IDX ++
WEND

;CHARA_IDXかCHARA_IDからキャラクターの行動描写文字列をセット
;IDXまたはIDを指定
@HO_CHARAACT_DESC_SETSTR(CHARA_IDX = -1, CHARA_ID = -1)
#DIM  CHARA_IDX
#DIM  CHARA_ID

#DIMS DYNAMIC ACTIONTYPE
#DIM  DYNAMIC ROOM_IDX
#DIMS DYNAMIC ROOM_NAME
#DIM  DYNAMIC MODULE_ID
#DIMS DYNAMIC MODULE_NAME
#DIMS DYNAMIC MAP
#DIMS DYNAMIC ACTION_TEXT

#DIM  DYNAMIC CHARA_GROUP_LIST_IDX
#DIMS DYNAMIC _LIST_CHARA_ID
#DIM  DYNAMIC GROUP_MEMBER_NUM
#DIM  DYNAMIC GROUP_MEMBER_ID

#DIM  DYNAMIC TARGET_ID

;各種キャラデータ取得
IF CHARA_IDX == -1
    CALL HO_GET_CHARAIDX(CHARA_ID)
    CHARA_IDX = RESULT
ELSE
    CALL HO_GET_CHARADATA(CHARA_IDX, "CHARA_ID")
    CHARA_ID = RESULT
ENDIF
SIF CHARA_IDX == -1
    RETURN
CALL C_COND_SET_INFO_TO_MAP(CHARA_ID,"CHARAACT_DESC")
ACTIONTYPE  '= @"%C_COND_MAP_GETS(CHARA_ID,"行動")%"
ROOM_IDX     = C_COND_MAP_GET(CHARA_ID,"エリアIDX")
ROOM_NAME   '= @"%C_COND_MAP_GETS(CHARA_ID,"エリア名")%"
MODULE_ID    = C_COND_MAP_GET(CHARA_ID,"設備")
MODULE_NAME '= @"%C_COND_MAP_GETS(CHARA_ID,"設備名")%"
IF MODULE_ID == 1000
    MODULE_NAME '= "部屋"
ENDIF
MAP '= @"MAP_{ROOM_IDX}_ROOM_EFFECT"

;キャラクターが所属しているグループを取得
CALL HO_GET_CHARADATA(CHARA_IDX, "CHARA_GROUP")
CHARA_GROUP_LIST_IDX = RESULT
IF CHARA_GROUP_LIST_IDX != -1
    CALL HO_GET_CHARA_GROUP_LIST(CHARA_GROUP_LIST_IDX, "CHARA_LIST")
    _LIST_CHARA_ID = %RESULTS%
    GROUP_MEMBER_NUM = LIST_COUNT(_LIST_CHARA_ID)
    ;メッセージ描写用に自分以外のグループメンバーのCHARA_IDを1名分抽出
    FOR LOCAL:1, 0, GROUP_MEMBER_NUM
        TARGET_ID = TOINT(LIST_GET(_LIST_CHARA_ID, LOCAL:1))
        SIF TARGET_ID == CHARA_ID
            CONTINUE
        GROUP_MEMBER_ID = TARGET_ID
    NEXT
ENDIF

;共通文字列
ACTION_TEXT '= @"%MODULE_NAME%で"
IF GROUP_MEMBER_NUM > 1
    ACTION_TEXT += @"%ANAME(GROUP_MEMBER_ID, GROUP_MEMBER_NUM - 1)%"
ENDIF

SELECTCASE ACTIONTYPE
    CASE "待機","移動完了"
        ACTION_TEXT '= @"%SPLIT_RAND("あたりをぶらぶらして/あたりをうろうろして/所在なさげにして",1)%いる"
    CASE "移動中"
        ACTION_TEXT '= "どこかに向かって移動している"
    CASE "食事中"
        IF GROUP_MEMBER_NUM <= 1
            ACTION_TEXT '= @"%MODULE_NAME%で食事をしている"
        ELSE
            ACTION_TEXT '= @"%MODULE_NAME%で%ANAME(GROUP_MEMBER_ID, GROUP_MEMBER_NUM - 1)%と%SPLIT_RAND("お喋りしながら/話しながら/一緒に",1)%食事をしている"
        ENDIF
    CASE "炊事中"
        IF GROUP_MEMBER_NUM <= 1
            ACTION_TEXT '= @"%MODULE_NAME%で食事の支度をしている"
        ELSE
            ACTION_TEXT '= @"%MODULE_NAME%で%ANAME(GROUP_MEMBER_ID, GROUP_MEMBER_NUM - 1)%%SPLIT_RAND("を手伝いながら/と分担して/と一緒に",1)%食事の支度をしている"
        ENDIF
    CASE "配膳中"
        ACTION_TEXT '= @"%ROOM_NAME%で食事を運んでいる"
    CASE "休憩中"
        IF C_COND("設備用途:お茶",,CHARA_ID)
            LOCALS '= "お茶を飲んでいる"
        ELSE
            LOCALS '= "休憩している"
        ENDIF
        IF MODULE_NAME != ""
            IF GROUP_MEMBER_NUM <= 1
                ACTION_TEXT '= @"%MODULE_NAME%で%LOCALS%"
            ELSE
                ACTION_TEXT '= @"%MODULE_NAME%で%ANAME(GROUP_MEMBER_ID, GROUP_MEMBER_NUM - 1)%と%SPLIT_RAND("お喋りしながら/話しながら/一緒に",1)%%SPLIT_RAND(@"%LOCALS%/くつろいでいる/休んでいる",1)%"
            ENDIF
        ELSE
            ACTION_TEXT '= @"%LOCALS%"
        ENDIF
    CASE "睡眠中"
        ACTION_TEXT '= @"%MODULE_NAME%ですうすうと寝息をたてて眠っている"
    CASE "同行中"
        IF FLAG:ウフフフラグ != 1
            ACTION_TEXT '= @"%ANAME(MASTER)%と一緒にいる"
        ELSE
            ACTION_TEXT '= @"@C:{カラー_ピンク}@❤%ANAME(MASTER)%と身体を重ねている❤@/C@"
        ENDIF
    CASE "バーテンダー"
        ACTION_TEXT '= @"%MODULE_NAME%でグラスを磨いている"
    CASE "採集中"
        ACTION_TEXT '= @"%MODULE_NAME%で採集をしている"
    CASE "採掘中"
        ACTION_TEXT '= @"%MODULE_NAME%で採掘をしている"
    CASE "飲酒中"
        ACTION_TEXT '= @"%MODULE_NAME%でお酒を飲んでいる"
    CASE "入浴中"
        ACTION_TEXT '= @"%MODULE_NAME%で身体を洗っている"
    CASE "水浴び中"
        ACTION_TEXT '= @"%MODULE_NAME%で%S_R("水を浴びている/身体を洗っている")%"
    CASE "釣り中"
        ACTION_TEXT '= @"%MODULE_NAME%の側に座り込みぼんやりと釣り糸を垂れている"
    CASE "読書中"
        IF GROUP_MEMBER_NUM <= 1
            ACTION_TEXT += "本を読んでいる"
        ELSE
            ACTION_TEXT += @"%SPLIT_RAND("と本について話している/と本の内容について議論している/と一緒に本を読んでいる",1)%"
        ENDIF
    CASE "運動中"
        ACTION_TEXT '= @"%MODULE_NAME%で運動をしている"
    CASE "演奏中"
        ACTION_TEXT '= @"%MODULE_NAME%で演奏をしている"
    CASE "演舞中"
        ACTION_TEXT '= @"%MODULE_NAME%で踊りを披露している"
    CASE "クラフト中"
        ACTION_TEXT '= @"%MODULE_NAME%で何かを作っている"
    CASE "訓練中"
        ACTION_TEXT '= @"%MODULE_NAME%で訓練をしている"
    CASE "遊戯中"
        IF STRFINDU(MODULE_NAME, "テーブル") != -1
            IF GROUP_MEMBER_NUM <= 1
                ACTION_TEXT += "カードゲームをしている"
            ELSE
                ACTION_TEXT += @"%SPLIT_RAND("とカードゲームをしている/と一緒にカードで遊んでいる",1)%"
            ENDIF
        ELSE
            IF GROUP_MEMBER_NUM <= 1
                ACTION_TEXT += "遊んでいる"
            ELSE
                ACTION_TEXT += @"%SPLIT_RAND("と遊んでいる/と楽しそうに遊んでいる",1)%"
            ENDIF
        ENDIF
    CASE "店番中"
        IF GROUP_MEMBER_NUM <= 1
            ACTION_TEXT '= @"%MODULE_NAME%で"
        ELSE
            ACTION_TEXT '= @"%MODULE_NAME%で%ANAME(GROUP_MEMBER_ID, GROUP_MEMBER_NUM - 1)%%SPLIT_RAND("とお喋りしながら/を手伝って/と一緒に",1)%"
        ENDIF
        IF HO_TIME <= HO_CLOCK(1800)
            ACTION_TEXT += "店番をしている"
        ELSE
            ACTION_TEXT += "店じまいをしている"
        ENDIF
    CASE "宿番中"
        IF GROUP_MEMBER_NUM <= 1
            ACTION_TEXT '= @"%MODULE_NAME%で"
        ELSE
            ACTION_TEXT '= @"%MODULE_NAME%で%ANAME(GROUP_MEMBER_ID, GROUP_MEMBER_NUM - 1)%%SPLIT_RAND("とお喋りしながら/を手伝って/と一緒に",1)%"
        ENDIF
        ACTION_TEXT += "宿の番をしている"
    CASE "鑑賞中"
        IF MAP_HAS(MAP, "ピアノの音色")
            ACTION_TEXT '= @"%MODULE_NAME%で"
            ACTION_TEXT += "演奏に聴き入っている"
        ELSEIF MAP_HAS(MAP, "演舞")
            ACTION_TEXT '= @"%MODULE_NAME%で"
            ACTION_TEXT += "踊りに見入っている"
        ELSEIF MAP_HAS(MAP, "歌唱")
            ACTION_TEXT '= @"%MODULE_NAME%で"
            ACTION_TEXT += "歌声に聴き入っている"
        ELSE
            ACTION_TEXT '= @"%MODULE_NAME%で"
            ACTION_TEXT += "余韻に浸っている"
        ENDIF
    CASE "拘束中"
        ACTION_TEXT '= @"拘束され身動きできずにいる"
    CASE "買物中"
        ACTION_TEXT '= @"%MODULE_NAME%で商品を見繕っている"
    CASE "監禁中"
        ACTION_TEXT '= @"牢の中でうなだれている"
    CASE "売春中"
        ACTION_TEXT '= @"%ROOM_NAME%で春をひさいでいる"
    CASE "買春中"
        ACTION_TEXT '= @"%ROOM_NAME%で娼婦を見繕っている"
    CASE "自慰中", "逢瀬中"
        CALL HO_IS_TARGET_VISIBLE(MASTER, CHARA_ID)
        IF RESULT
            ACTION_TEXT '= @"@C:{カラー_ピンク}@❤%MODULE_NAME%で秘事に耽っている❤@/C@"
        ELSE
            ACTION_TEXT '= @"@C:{カラー_選択不可}@ここからではよくわからない……@/C@"
        ENDIF
    CASE "逢瀬待機中"
        ACTION_TEXT '= @"%MODULE_NAME%で誰かを待っている"
    CASEELSE
        ACTION_TEXT '= ""
ENDSELECT

HO_DESC_CHARAACT:CHARA_ID:0 = %ACTION_TEXT%

;空腹度に関する描写
@HO_GET_空腹度描写(CHARA_ID)
#DIM CHARA_ID
IF CHARA_ID == MASTER
    IF TCVAR:CHARA_ID:空腹度 < 150
        LOCALS:0 = は満腹だ
    ELSEIF TCVAR:CHARA_ID:空腹度 < 300
        LOCALS:0 = は小腹が空いている
    ELSE
        LOCALS:0 = は空腹だ
    ENDIF
ELSE
    IF TCVAR:CHARA_ID:空腹度 < 150
        LOCALS:0 = は満腹のようだ
    ELSEIF TCVAR:CHARA_ID:空腹度 < 300
        LOCALS:0 = は小腹が空いているようだ
    ELSE
        LOCALS:0 = は空腹のようだ
    ENDIF
ENDIF
RESULTS = %ANAME(CHARA_ID)%%LOCALS%

@HO_GET_精液描写(CHARA_ID, 射精部位)
#DIM CHARA_ID
#DIM 射精部位

VARSET RESULTS
SELECTCASE 射精部位
    CASE 射精部位_膣内
        IF CUM_GET_AMOUNT:CHARA_ID:射精部位_膣内 == 0
            RETURN
        ELSEIF CUM_GET_AMOUNT:CHARA_ID:射精部位_膣内 < 10
            RESULTS = 陰部が白濁液で湿っている
        ELSEIF CUM_GET_AMOUNT:CHARA_ID:射精部位_膣内 < 25
            RESULTS = 雌穴は精液で満たされている
        ELSEIF CUM_GET_AMOUNT:CHARA_ID:射精部位_膣内 < 35
            RESULTS = 雌穴から溢れた精液が大腿を伝って垂れている
        ELSE
            RESULTS = 溢れた精液が雌穴から糸を引いて滴っている
        ENDIF
    CASE 射精部位_アナル
        IF CUM_GET_AMOUNT:CHARA_ID:射精部位_アナル == 0
            RETURN
        ELSEIF CUM_GET_AMOUNT:CHARA_ID:射精部位_アナル < 10
            RESULTS = 肛孔から雄汁が垂れている
        ELSEIF CUM_GET_AMOUNT:CHARA_ID:射精部位_アナル < 25
            RESULTS = 肛孔は雄汁で満たされている
        ELSEIF CUM_GET_AMOUNT:CHARA_ID:射精部位_アナル < 35
            RESULTS = 肛孔から溢れた精液が大腿を伝って垂れている
        ELSE
            RESULTS = 肛孔から止めどなく白濁が溢れている
        ENDIF
    CASE 射精部位_口
        IF CUM_GET_AMOUNT:CHARA_ID:射精部位_口 == 0
            RETURN
        ELSEIF CUM_GET_AMOUNT:CHARA_ID:射精部位_口 < 10
            RESULTS = 口元に僅かな量の精液がこびりついている
        ELSEIF CUM_GET_AMOUNT:CHARA_ID:射精部位_口 < 25
            RESULTS = 口内は雄臭を放つ白濁で満たされている
        ELSE
            RESULTS = 口内に大量の白濁汁が注がれている
        ENDIF
    CASE 射精部位_顔
        IF CUM_GET_AMOUNT:CHARA_ID:射精部位_顔 == 0
            RETURN
        ELSEIF CUM_GET_AMOUNT:CHARA_ID:射精部位_顔 < 10
            RESULTS = 頬に僅かな量の精液がこびりついている
        ELSEIF CUM_GET_AMOUNT:CHARA_ID:射精部位_顔 < 25
            RESULTS = 顔面に淫らな白濁がべったりとこびりついている
        ELSE
            RESULTS = 白濁がすっかり顔を覆ってしまっている
        ENDIF
    CASE 射精部位_胸
        IF CUM_GET_AMOUNT:CHARA_ID:射精部位_胸 == 0
            RETURN
        ELSEIF CUM_GET_AMOUNT:CHARA_ID:射精部位_胸 < 10
            RESULTS = 胸元に僅かな量の淫汁がこびりついている
        ELSEIF CUM_GET_AMOUNT:CHARA_ID:射精部位_胸 < 25
            RESULTS = 胸元に雄臭を放つ白濁がこびりついている
        ELSE
            RESULTS = 胸元は大量の雄汁でぐちゃぐちゃに汚されている
        ENDIF
    CASE 射精部位_髪
        IF CUM_GET_AMOUNT:CHARA_ID:射精部位_髪 == 0
            RETURN
        ELSEIF CUM_GET_AMOUNT:CHARA_ID:射精部位_髪 < 10
            RESULTS = 髪に僅かな量の白濁が付着している
        ELSEIF CUM_GET_AMOUNT:CHARA_ID:射精部位_髪 < 25
            RESULTS = 髪から雄臭を放つ白濁が滴っている
        ELSE
            RESULTS = 髪は大量の雄汁でぐちゃぐちゃに汚されている
        ENDIF
    CASE 射精部位_足
        IF CUM_GET_AMOUNT:CHARA_ID:射精部位_足 == 0
            RETURN
        ELSEIF CUM_GET_AMOUNT:CHARA_ID:射精部位_足 < 10
            RESULTS = 足元が僅かな量の精液で濡れている
        ELSEIF CUM_GET_AMOUNT:CHARA_ID:射精部位_足 < 25
            RESULTS = 大腿から雄臭を放つ白濁が滴っている
        ELSE
            RESULTS = 大腿から足先まで白濁でまみれている
        ENDIF
    CASE 射精部位_尻
        IF CUM_GET_AMOUNT:CHARA_ID:射精部位_尻 == 0
            RETURN
        ELSEIF CUM_GET_AMOUNT:CHARA_ID:射精部位_尻 < 10
            RESULTS = 尻が少量の精液で濡れている
        ELSEIF CUM_GET_AMOUNT:CHARA_ID:射精部位_尻 < 25
            RESULTS = 尻の曲線に沿って雄臭を放つ白濁が滴っている
        ELSE
            RESULTS = 尻は大量の白濁液でドロドロに濡れている
        ENDIF
    CASE 射精部位_手
        IF CUM_GET_AMOUNT:CHARA_ID:射精部位_手 == 0
            RETURN
        ELSEIF CUM_GET_AMOUNT:CHARA_ID:射精部位_手 < 10
            RESULTS = 僅かな量の精液が指の間で糸を引いている
        ELSEIF CUM_GET_AMOUNT:CHARA_ID:射精部位_手 < 25
            RESULTS = 手は雄臭を放つ白濁でまみれていれる
        ELSE
            RESULTS = 手は大量の白濁でまみれていれる
        ENDIF
ENDSELECT