상세 컨텐츠

본문 제목

[ 광쇄의 리벌처 ] 롤 20 유/무료 계정 세팅 방법

롤 20, TRPG 배포용 자료/롤 20, TRPG 이모저모

by 배포하는 Shen 2023. 8. 3. 18:23

본문

  요새 뜸하다가 다시 입문탁이 좀 잡히기도 하고, 이번에 양천일염님 api를 수정해서 생각난 김에 간단히 적어봅니다. 광쇄리 자체가 크게 무언가 세팅을 할 필요는 없기 때문에... 뭔가 많이 들어가지는 않습니다. 제 기준으로 작성했기 때문에 꼭 이런 식으로 세팅하지 않으셔도 됩니다. 

  문제가 되거나, 오류 사항이 있거나, 제대로 작동하지 않는다면 배포계의 DM을 찾아와주세요! 

 

 

 

1. 맵시트 세팅

 

먼저 맵시트를 찾습니다. 

저는 https://posty.pe/ilwc0b 이 분의 맵시트를 사용(이전에 롤20에서의 사용도 허락을 받았습니다)했고, 웬만한 경우에는 뎁스 레벨 제외한 맵시트를 사용하시면 됩니다. 

배치는 해당하는 페이지의 세팅 방법대로 하면 됩니다. 

저는 그리드는 딱히 사용하지 않았습니다. 사용하지 않아도 마스터링에 큰 지장이 없습니다. 

 

실제 소라바미 데이터가 아닙니다.

한 차례 세팅을 마친 맵입니다. 

 

웨폰과 감정회로, 독트린은 툴팁에 세부 내용을 적습니다.

 

웨폰이나 어빌리티 등에 이름을 넣고 싶다면 위쪽 일반 정보에서 점 세 개, 플레이어 권한 > 보기를 눌러주시고, 이름을 적은 뒤 이름 표시까지 눌러주시면 됩니다. 

 

 

전장의 지배자 등의 횟수 제한이 있는 어빌리티가 포함된 어빌리티의 경우 토큰을 클릭한 뒤, 특성치에 사용한 횟수/전체 횟수 순으로 적어주시면 됩니다. 반드시 점 세 개 눌러서 플레이어 권한에서 '보기'와 텍스트 오버레이를 '모두에게 표시'로 변경해주세요. 

따로 저널 연동은 하지 않아도 됩니다. 

 

 

리벌처의 토큰 바도 위와 같은 식으로 권한을 업데이트 해준 뒤, 위쪽은 실드 장수, 아래쪽은 실드 내구력으로 맞춰주면 됩니다. 리벌처 토큰을 플레이어도 움직이게 하고 싶으면 제어 권한을 '모든 플레이어'로 업데이트 해주세요. 

소라바미의 토큰 바도 위쪽은 실드 장수, 아래쪽은 실드 내구력으로 맞춥니다. 플레이어 권한은 업데이트 하지 않습니다. 텍스트 오버레이 없이도, 스탯이 어느정도 남았는지 보여주면 안 됩니다. 

 

 

여기에서 이제, 독트린과 에너미 토큰, 에너미 포트레이트를 선택한 뒤, 마우스 우클릭 > 레이어 > GM레이어를 선택해서 이동시켜줍니다. 이렇게 하면 플레이어에게는 보이지 않습니다. 

 

 

공개할 시에는 레이어 선택에 가서 'GM 정보' 레이어로 이동해서, 공개하려는 토큰을 선택한 뒤, 마우스 우클릭 > 레이어 > 토큰 레이어를 선택해서 이동시켜주시면 플레이어에게 공개됩니다. 

 

 

 

 

만약에 방을 자주 복사하는 편이고, 할 때마다 독트린 개수가 달라져서 바꾸기 번거롭다 하시는 분은 참고해 주세요. 

일단 통상과 함께 전부 토큰을 설치해주세요. 필요하다면 E까지 해주시고, 제노사이드 토큰은 2개를 설치해두시는 것이 좋습니다. 

또한, 트리거도 사용하게 되었다면 MAIN 아래의 3번째 줄에 트리거 독트린 토큰까지도 기본적으로 설치해주세요. 

 

그런 뒤에 당장에 사용하지 않는 토큰을 클릭해 주세요. 

그런 뒤에 Token Setting에서 색조 입히기를 사용하시면 이렇게 불이 꺼진 듯한 효과를 줄 수 있습니다. 이것은 GM레이어에 옮기게 되어도 차이가 확실히 나니까 유용하게 사용하셨으면 좋겠습니다. 

 

 

 

 

 

 

2. 감정회로 같은 사용 제한이 있는 것

 

위의 색조 입히기와 동일합니다. 감정회로 같이 한 번 쓴 것을 구분할 필요가 있는 것, 횟수 제한이 있는 어빌리티의 횟수를 다 소진했을 때 표시로 좋습니다. 

위와 동일하게 검은색을 입혀주면 이렇게 됩니다. 배경에 따라서 전혀 다른 색이 될 수도 있습니다. 

 

 

반대로 활성화가 된 경우(에임 스탠스 같이)를 구분할 때는 다른 색조를 입히는 것도 좋습니다. 

 

 

 

 

 

 

3. 매크로 세팅

 

저는 사실 표굴림 외에는 매크로를 잘 세팅하지 않는 편입니다. 어택 판정이나 에너지 충전용 명령어가 그렇게 긴 건 아니기 때문에 그렇게까지 필요하다고 생각하지는 않아요. 자신의 시간을 들여서 플레이어를 편하게 하고 싶다! 그러면 같이 세팅해주세요.

 

3-1. 상황표 세팅

 

컬렉션 하단에 목록에서 뽑기 > +추가를 눌러주세요. 

상황 1~6을 장면표처럼 적어두신 뒤, 비중은 큰 이변이 없으면 똑같이 1로 설정합니다. 보통 광쇄의 리벌처는 상황표가 1D6 기준이니까요. 

 

 

상황표 세팅이 끝나면 '플레이어 목록 뽑기 허용'이 제대로 체크되어있는지 반드시 확인합니다. 

 

 

그리고 매크로에서는 [[1t[표이름]]]으로 세팅해주세요. 

이렇게 하고 나서 채팅창에 확인하면 노란 바탕으로 제대로 나옵니다. 노란 바탕은 무료 계정은 지우기 힘듭니다. 

 

 

 

3-2. 에너지 충전 및 어택 판정 세팅

 

 

왼쪽은 에너지 충전용 코드이고 오른쪽은 플뤼겔을 기준으로 한 어택 판정용 코드입니다. 

d6s처럼 뒤에 s를 붙이면 오름차순으로 정렬을 해줍니다. 

 

에너지 충전: /r (한 턴에 얻는 에너지 개수)d6s

어택 판정: /r (어택판정 n다이스의 n)d6s < 목표치

매번 플레이어가 바꿔서 입력 가능한 코드: /r ?{어택 판정 다이스 수}d6s<?{목표치}

 

이렇게 기본명령어입니다.

롤20에서 <=는 오류가 나고, <를 하면 자동으로 미만이 아닌 이하로 계산을 해서 Successes를 세어줍니다. 이 때 크리티컬은 제외되기 때문에 Successes에 크리티컬인 1의 개수를 더해서 히트 수를 세어주세요. 

 

 

반드시 플레이어에게 표시(선택사항)에서 모든 플레이어로 해주세요. 

 

 

 

이렇게 하면 무료 세팅은 모든 준비가 끝났습니다. 여기에 채팅창 매크로나 인트로 매크로는 본인의 선택입니다. 

아래는 API로 스탯 변경시 채팅창에 표시되길 바랄 때 사용하는 선택적인 요소입니다. 

 

아래는 API 출처입니다. 일부 설명문도 차용했습니다. 

 

원본 코드 작성자의 이름: 양천일염
원본 코드의 출처 링크: https://github.com/kibkibe/roll20-api-scripts/blob/master/attribute_tracker/attribute_tracker.js
원본 코드의 이름: attribute_tracker/attribute_tracker.js
원본 코드의 CC 라이선스: CC BY-NC (저작자표시-비영리)

 

 

자세한 건 attribute_tracker 페이지를 참고해주세요. 

 

1. 세션방의 대문에 해당하는 페이지에서 [설정]->[API 스크립트]를 선택해 스크립트 수정 페이지로 들어갑니다. (PRO 계정에서만 이 메뉴가 보입니다.)
2. ability에 횟수제한이 있는 어빌리티의 이름을 넣습니다. 횟수 제한 있는 어빌리티가 여럿이라면 그만큼 {attr: "ability", name: "어빌리티"}와 {attr: "ability_max", name: "최대_어빌리티"}를 수정해주세요. 
3. prior_list: "리벌처 「이름」"에 저널로 사용할 리벌처의 이름으로 수정해주세요. 
4. New Script에 이 코드들을 복사해 붙여놓습니다. 

더보기

 

/* 원본 출처: https://github.com/kibkibe/roll20-api-scripts/tree/master/attribute_tracker */
/* (attribute_tracker.js) 210720 코드 시작 */

// define: option
const at_setting = {
	// option: 변경을 감지할 속성을 목록 형태로 지정합니다.
	// 룰별 check list코드 공유페이지 https://docs.google.com/spreadsheets/d/1_uTqPs6FQJfjzDotRWqtJn8U6cVw_lVycDRal8vxZb8/edit#gid=609977791
	check_list:
	/* 체크리스트 시작 */
	[	{attr: "shield", name: "실드"},
		{attr: "stamina", name: "내구력"},
		{attr: "ability", name: "어빌리티"},
        {attr: "shield_max", name: "최대_실드"},
		{attr: "stamina_max", name: "최대_내구력"},
		{attr: "ability_max", name: "최대_어빌리티"}]
	/* 체크리스트 끝 */
	,
	// option: 필수적으로 변화를 체크할 캐릭터의 이름을 기입합니다.
	// 이 값은 ignore_list보다 우선됩니다. (복수입력시 콤마(,)로 구분)
	prior_list: "리벌처 「이름」",
	// option: 로그 표시에서 제외할 캐릭터의 이름을 기입합니다. (복수입력시 콤마(,)로 구분)
	// "GM"을 넣으면 GM에게만 조작권한이 있는 모든 캐릭터를 일괄적으로 제외합니다.
	ignore_list: "GM",
	// option: !at 명령어를 이용한 숨김/표시 모드를 사용하지 않은 기본상태에서 스테이터스 변경 내역을 GM에게 귓말로만 보낼지(true) 모두에게 표시할지(false) 설정합니다.
	use_secret_mode: false
}
// /define: option
    
on('ready', function() {
	// on.ready
	if (state.hide_tracking != at_setting.use_secret_mode) {
		state.hide_tracking = at_setting.use_secret_mode;
		show_current_status();
	}
    state.new_character = [];
    on("add:character",function(obj) {
        state.new_character.push({id:obj._id, time: Date.now()});
    });
    on("add:attribute", function(obj) {
        const now = Date.now();
        const interval = 3000;
        let check = true;
        for (let index = 0; index < state.new_character.length; index++) {
            const element = state.new_character[index];
            if (obj._id == element.id) {
                if (now - element.time > interval) {
                    state.new_character.splice(index,1);
                } else {
                    check = false;
                }
                break;
            }
        }
        if (check) {
            check_attribute(obj, null);
        }
    });
	// /on.ready
});
    
on("change:attribute", function(obj, prev) {
	// on.change:attribute
    check_attribute(obj, prev);
	// /on.change:attribute
});

on("chat:message", function(msg)
{
if (msg.type == "api" ){
	// on.chat:message:api
    if (msg.content.indexOf("!at") === 0 && (msg.playerid == 'API' || playerIsGM(msg.playerid))) {
		if (msg.content == "!at") {
			sendChat("attribute_tracker.js","/w gm [ 명령어 ]<br>- **!at show**: 로그 표시하기 / **!at hide**: 로그 숨기기",null,{noarchive:true});
		} else if (msg.content.toLowerCase().includes('hide')) {
            state.hide_tracking = true;
        } else if (msg.content.toLowerCase().includes('show')) {
            state.hide_tracking = false;
        }
		show_current_status();
	}
	// /on.chat:message:api
}
});

// define: global function
function show_current_status() {

	sendChat("attribute_tracker.js","/w gm <br>- 코드상의 옵션: **" + (at_setting.use_secret_mode ? "숨김":"표시")
	+ (at_setting.use_secret_mode == state.hide_tracking ? "" : "** / 명령어로 지정된 모드: **" + (state.hide_tracking ? "숨김" : "표시"))
	+ "**<br>- 현재 스테이터스 변동내역이 " + (state.hide_tracking ? "GM에게만 귓속말로 전달되고 있습니다.":"모든 사용자에게 공개되고 있습니다."),null,{noarchive:true});
}

function check_attribute(obj,prev) {
    try {
        var check_pl = false;
        let cha = getObj('character',obj.get('_characterid'));
		const prior_list = at_setting.prior_list.split(/\s*,\s*/g);
		const ignore_list = at_setting.ignore_list.split(/\s*,\s*/g);
        if (prior_list.indexOf(cha.get('name')) > -1 || at_setting.ignore_list == 0) {
            check_pl = true;
        } else if (ignore_list.indexOf(cha.get('name')) > -1) {
            check_pl = false;
        } else if (ignore_list.indexOf('GM') > -1) {
            let controller = cha.get('controlledby').split(",");
            for (var i=0;i<controller.length;i++) {
                if (controller[i].length > 0 && !playerIsGM(controller[i])) {
                    check_pl = true;
                    break;
                }
            }
        }
        
        if (check_pl) {
            for (var i=0;i<at_setting.check_list.length;i++) {
                var check = false;
                var item = at_setting.check_list[i];
                let item_id = null;
                let attr_name = item.attr.replace('attr_');
                let check_max = attr_name.includes('_max');
                if (prev == null || check_max && obj.get('current') == prev.current || !check_max && obj.get('current') != prev.current) {
                    if (attr_name.replace('_max','') == obj.get('name')) {
                        check = true;
                    } else if (attr_name.indexOf('*id*') > -1) {
                        let split_attr = attr_name.split('*id*');
                        if (split_attr.length == 2 && obj.get('name').startsWith(split_attr[0]) && obj.get('name').endsWith(split_attr[1])) {
                            check = true;
                            item_id = obj.get('name').replace(split_attr[0],'').replace(split_attr[1],'');
                        }
                    }
                }
                if (check) {
                    var name = item.name;
                    if (name.indexOf('*id*') > -1) {
						name = item.name.replace('*id*',item_id);
                        var search_name = findObjs({type: "attribute", name: name, characterid: obj.get('_characterid')});
                        if (search_name.length > 0) {
                            name = search_name[0].get('current');
                        } else {
                            sendChat("error","/w gm 이름이 " + name + "인 값이 캐릭터시트에 없습니다.",null,{noarchive:true}); return;
                        }
                    }
                    sendChat("character|"+obj.get('_characterid'),
                    (state.hide_tracking ? "/w GM ":"") +
                        "**" + name + "** / <span style='color:#aaaaaa'>" + (prev == null?"":(check_max? prev.max:prev.current)) + "</span><span style='color:#777777'> → </span><b>" +
                        (check_max ? obj.get('max'):obj.get('current'))+"</b>",null,{noarchive:false});
                    break;
                }
            }
        }
    } catch (err) {
        sendChat("error","/w gm " + err,null,{noarchive:true});
    }
}
// /define: global function

 

 

5. 리벌처 이름으로 된 저널을 만들어줍니다. 그런 뒤, Attributes & Abilities에 가서 Attribute에 shield(최대 실드 장수)와 stamina(최대 내구력), 횟수제한이 있는 어빌리티를 차례로 만들어줍니다.

저널 이름은 prior_list에 입력한 이름과, 어빌리티는 수정한 이름과 동일하게 해주어야 합니다. 

 

 

6. 위쪽에서 했던 리벌처 토큰을 저널과 연동해서 특성치를 불러옵니다. 어빌리티는 어빌리티 토큰에 저널을 연동한 뒤, 특성치를 불러와주세요. 

 

왼쪽은 리벌처 토큰, 오른쪽은 어빌리티 토큰

 

7. 이제 리벌처 특성치를 저널에서 바로 하든, 토큰을 통해하든 이렇게 채팅창에 변동된 수치가 표시됩니다. 

 

 

 

 


※ 本作は「どらこにあん」及び「株式会社アークライト」が権利を有する『光砕のリヴァルチャー』の二次創作です。
이 작품은 드라코니언 및 주식회사 아크라이트에 권리가 있는 광쇄의 리벌처 2차창작입니다.

Copyright 2023 @Shen_TRPG019271 at twitter. All rights reserved.

관련글 더보기

댓글 영역