scenariot_game.lua


--[[ game에서 필요한 상수, 변수들을 미리 정의 ]]
players = {}
difficultySelectPlayer = 0 -- 난이도를 선택중이거나 선택한 플레이어 인덱스

-- 무기, 몬스터 능력치 설정등에 사용할 기준 레벨
monsterLevel = 1 -- 몬스터 레벨
LEVEL_MIN = 1 -- 플레이어 최소 레벨
LEVEL_MAX = 99 -- 플레이어 최대 레벨
WEAPONLEVEL_MAX = 60 -- 무기 최대 레벨
mapDifficulty = SignalToGame.difficulty0 -- 현재 맵 난이도

-- 무기, 몬스터의 각종 능력치들을 레벨에따라 아래 값을 곱해 사용한다
LevelRatio = {
	1.000,
	1.050,
	1.103,
	1.158,
	1.216,
	1.276,
	1.340,
	1.407,
	1.477,
	1.551,
	1.629,
	1.710,
	1.796,
	1.886,
	1.980,
	2.079,
	2.183,
	2.292,
	2.407,
	2.527,
	2.653,
	2.786,
	2.925,
	3.072,
	3.225,
	3.386,
	3.556,
	3.733,
	3.920,
	4.116,
	4.322,
	4.538,
	4.765,
	5.003,
	5.253,
	5.516,
	5.792,
	6.081,
	6.385,
	6.705,
	7.040,
	7.392,
	7.762,
	8.150,
	8.557,
	8.985,
	9.434,
	9.906,
	10.401,
	10.921,
	11.467,
	12.041,
	12.643,
	13.275,
	13.939,
	14.636,
	15.367,
	16.136,
	16.943,
	17.790,
	18.679,
	19.613,
	20.594,
	21.623,
	22.705,
	23.840,
	25.032,
	26.283,
	27.598,
	28.978,
	30.426,
	31.948,
	33.545,
	35.222,
	36.984,
	38.833,
	40.774,
	42.813,
	44.954,
	47.201,
	49.561,
	52.040,
	54.641,
	57.374,
	60.242,
	63.254,
	66.417,
	69.738,
	73.225,
	76.886,
	80.730,
	84.767,
	89.005,
	93.455,
	98.128,
	103.035,
	108.186,
	113.596,
	119.276,
}

-- 무기 등급 결정 확률
WeaponGradeProb = {
	1.0,
	0.2,
	0.05,
	0.005
}

-- 무기 등급별 기본 데미지
WeaponGradeDamage = {
	1.0,
	1.25,
	1.5,
	2.0
}

-- 무기 랜덤 데미지 (0.15일 경우 0.85~1.15 사이에서 랜덤으로 생성)
WeaponRandomDamage = 0.15

-- 무기 레벨별 데미지
WeaponLevelDamage = 1.0

-- 게임에서 쓰일 몬스터 타입들 저장
MonsterTypes = {
	Game.MONSTERTYPE.NORMAL0,
	Game.MONSTERTYPE.NORMAL1,
	Game.MONSTERTYPE.NORMAL2,
	Game.MONSTERTYPE.NORMAL3,
	Game.MONSTERTYPE.NORMAL4,
	Game.MONSTERTYPE.NORMAL5,
	Game.MONSTERTYPE.NORMAL6,
	Game.MONSTERTYPE.RUNNER0,
	Game.MONSTERTYPE.RUNNER1,
	Game.MONSTERTYPE.RUNNER2,
	Game.MONSTERTYPE.RUNNER3,
	Game.MONSTERTYPE.RUNNER4,
	Game.MONSTERTYPE.RUNNER5,
	Game.MONSTERTYPE.RUNNER6,
	Game.MONSTERTYPE.HEAVY1,
	Game.MONSTERTYPE.HEAVY2,
	Game.MONSTERTYPE.A101AR,
	Game.MONSTERTYPE.A104RL,
}

-- 게임에서 쓰일 몬스터 등급 정의
MonsterGrade = {
	normal = 1,
	rare = 2,
	unique = 3,
	legend = 4,
	END = 4
}

-- 몬스터 무리(그룹)별로 모두 죽었는지 세는 테이블
monsterGroupCnt = {}

-- 몬스터 웨이브 관리 테이블(몬스터 스포너와 유사한 동작)
monsterWaveCnt = {}
monsterWavePosition = {}

-- wave 몬스터들에 대한 처리를 할지말지
WaveFuncState = {
	enable = 1, -- 모든 wave 활성화 상태
	disable = 3, -- wave 불가능
}
monsterWaveFuncState = WaveFuncState.enable

-- 몬스터 등급 결정 확률
MonsterGradeProb = {
	1.0,
	0.3,
	0.1,
	0.01
}

-- 몬스터 등급별 무기 드랍 확률(최대 1.0)
WeaponDropProb = {
	0.035,
	0.08,
	0.08,
	1
}

-- 몬스터 레벨별 세팅 값들
MonsterLevelVar = {
	normal =	{hpMin = 30, hpMax = 60, armorMin = 5, armorMax = 30, damageMin = 10, damageMax = 20, coinMin = 1, coinMax = 5},
	runner =	{hpMin = 20, hpMax = 50, armorMin = 0, armorMax = 20, damageMin = 10, damageMax = 30, coinMin = 3, coinMax = 8},
	heavy =		{hpMin = 150, hpMax = 250, armorMin = 20, armorMax = 55, damageMin = 20, damageMax = 40, coinMin = 10, coinMax = 15},
	a101ar =	{hpMin = 150, hpMax = 250, armorMin = 30, armorMax = 50, damageMin = 4, damageMax = 4, coinMin = 20, coinMax = 25},
	a104rl =	{hpMin = 150, hpMax = 250, armorMin = 30, armorMax = 50, damageMin = 17, damageMax = 17, coinMin = 20, coinMax = 25},
	etc =		{hpMin = 20, hpMax = 50, armorMin = 0, armorMax = 20, damageMin = 10, damageMax = 35, coinMin = 3, coinMax = 8},
}

-- 몬스터 체력에따른 경험치 값(exp == hp * MonsterExpMult)
MonsterExpMult = 0.6

-- 최대레벨과 레벨별 필요경험치 설정
PlayerRequireExp = {
	87,
	360,
	864,
	1584,
	2625,
	3996,
	5586,
	7680,
	10206,
	13200,
	17061,
	21168,
	25857,
	31752,
	38475,
	45312,
	53754,
	64152,
	74727,
	87600,
	100548,
	116160,
	133308,
	152064,
	174375,
	196716,
	223074,
	251664,
	285099,
	318600,
	357492,
	402432,
	447579,
	499392,
	554925,
	618192,
	685869,
	758100,
	839592,
	926400,
	1023729,
	1127196,
	1236981,
	1359072,
	1494450,
	1637784,
	1795917,
	1969920,
	2153697,
	2355000,
	2574990,
	2806752,
	3067428,
	3341736,
	3639075,
	3960768,
	4308174,
	4682688,
	5096184,
	5529600,
	5994531,
	6504048,
	7060851,
	7643136,
	8276775,
	8964648,
	9696240,
	10487232,
	11340702,
	12245100,
	13232625,
	14292288,
	15427455,
	16641564,
	17955000,
	19355376,
	20864151,
	22486464,
	24208839,
	26073600,
	28067958,
	30217656,
	32509191,
	34948368,
	37584450,
	40404348,
	43415784,
	46626624,
	50092404,
	53775900,
	57735132,
	61956480,
	66476214,
	71306520,
	76486875,
	82003968,
	87898878,
	94215240,
	100000000
}


--[[ 몬스터 관련 사용자 정의 함수들 ]]

-- 레벨에 따라서 몬스터의 능력치를 설정
function SetMonsterAttribute(monster, grade)

	if monster == nil then
		return
	end

	monster.user.level = monsterLevel
	monster.user.grade = grade

	monster.applyKnockback = true -- 몬스터가 넉백을 받도록
	monster.canJump = false -- 몬스터가 점프 할수 없도록
	monster.viewDistance = 12 -- 적을 발견하는 시야범위

	local levelVar
	if Game.MONSTERTYPE.NORMAL0 <= monster.type and monster.type <= Game.MONSTERTYPE.NORMAL6 then
		levelVar = MonsterLevelVar.normal
	elseif Game.MONSTERTYPE.RUNNER0 <= monster.type and monster.type <= Game.MONSTERTYPE.RUNNER6 then
		levelVar = MonsterLevelVar.runner
	elseif Game.MONSTERTYPE.HEAVY1 <= monster.type and monster.type <= Game.MONSTERTYPE.HEAVY2 then
		levelVar = MonsterLevelVar.heavy
	elseif monster.type == Game.MONSTERTYPE.A101AR then
		levelVar = MonsterLevelVar.a101ar
	elseif monster.type == Game.MONSTERTYPE.A104RL then
		levelVar = MonsterLevelVar.a104rl
	else
		levelVar = MonsterLevelVar.etc
	end

	-- 데미지는 다른 수치들보다 좀더 완만하게
	local damageMult = ((LevelRatio[monsterLevel] - 1.0) * 0.5) + 1.0
	monster.damage = math.floor(Game.RandomInt(levelVar.damageMin, levelVar.damageMax) * damageMult)

	monster.health = math.floor(Game.RandomInt(levelVar.hpMin, levelVar.hpMax) * LevelRatio[monsterLevel])
	monster.coin = math.floor(Game.RandomInt(levelVar.coinMin, levelVar.coinMax) * LevelRatio[monsterLevel])
	monster.user.exp = math.floor(monster.health * MonsterExpMult) -- 이 몬스터를 잡았을때 플레이어가 얻게 될 경험치

	-- 결정된 등급에따라 색상과 능력치 설정
	if grade == MonsterGrade.rare then
		monster:SetRenderFX(Game.RENDERFX.GLOWSHELL)
		monster:SetRenderColor({r = 0, g = 30, b = 255})
		monster.health = math.floor(monster.health * 3.0)
		monster.damage = monster.damage * 1.5
		monster.speed = 1.5
		monster.user.exp = math.floor(monster.user.exp * 1.5)
	elseif grade == MonsterGrade.unique then
		monster:SetRenderFX(Game.RENDERFX.GLOWSHELL)
		monster:SetRenderColor({r = 255, g = 30, b = 30})
		monster.health = math.floor(monster.health * 5.0)
		monster.damage = monster.damage * 2.0
		monster.speed = 1.5
		monster.user.exp = math.floor(monster.user.exp * 3.0)
	elseif grade == MonsterGrade.legend then
		monster:SetRenderFX(Game.RENDERFX.GLOWSHELL)
		monster:SetRenderColor({r = 255, g = 255, b = 100})
		monster.health = math.floor(monster.health * 12.0)
		monster.damage = monster.damage * 2.0
		monster.speed = 2.5
		monster.user.exp = math.floor(monster.user.exp * 10.0)
	end
end

-- 지정한 장소에 몬스터무리 생성
function CreateMonsters(type, num, pos, groupid, grade)

	if grade == nil then
		grade = MonsterGrade.normal
	end

	result = {}

	-- num만큼 몬스터 생성
	for i = 1, num do
		monster = Game.Monster.Create(type, pos)
		if monster then
			-- 몬스터 등급에따라 능력치 설정
			SetMonsterAttribute(monster, grade)

			-- 몬스터가 속한 무리번호를 저장하고 개수를 하나 증가시킨다
			monster.user.groupid = groupid
			if monsterGroupCnt[groupid] then
				monsterGroupCnt[groupid] = monsterGroupCnt[groupid] + 1
			else
				monsterGroupCnt[groupid] = 1;
			end

			-- 결과값 저장
			table.insert(result, monster)
		end
	end

	return result
end

-- 몬스터 무리가 모두 죽었을때 불리는 함수
function OnMonsterKilled(monster)
    if monster.user.waveFunc then
		if monsterWaveFuncState == WaveFuncState.enable then
			monster.user.waveFunc(true, monster.user.waveFuncArg)
		else
			monster.user.waveFunc = nil
		end
    end

    -- 보스 그룹이 죽었다
    if monster.user.groupid == 8055 then
        monsterWaveFuncState = WaveFuncState.disable
        difficultySelectPlayer = 0
        Game.KillAllMonsters() -- 다음 OnUpdate 이후에 모든 몬스터를 죽인다
    end

    Game.SetTrigger('OnMonsterKilled' ..  monster.user.groupid, true)
end


--[[ 무기 관련 사용자 정의 함수들 ]]


-- 모든 무기의 최소 기본 능력치 설정
function SetWeaponAttributeDefault(weapon)
	if weapon == nil then
		return
	end

	-- 보조무기는 무한탄창
	if weapon:GetWeaponType() == Game.WEAPONTYPE.PISTOL then
		weapon.infiniteclip = true
	else
		weapon:AddClip1(3) -- 기본 탄창 3개 지급
	end

	-- 기본 레벨
	if weapon.user.level == nil then
		weapon.user.level = Common.GetWeaponOption(weapon.weaponid).user.level
	end

	-- 기본 등급
	weapon.user.grade = WeaponGrade.normal
	weapon.color = Game.WEAPONCOLOR.WHITE
end

-- 레벨에 따라서 무기의 능력치를 설정
function SetWeaponAttribute(weapon, level)

	if weapon == nil then
		return
	end

	-- 기본 능력치 설정
	SetWeaponAttributeDefault(weapon)

	-- 레벨 설정
	weapon.user.level = level

	-- 현재 무기의 WeaponOption과 grade를 얻어냄
	local option = Common.GetWeaponOption(weapon.weaponid)
	local grade = option.user.grade

	-- 등급별 등장확률 결정
	local weightMax = 0.0
	for i = grade, WeaponGrade.END do
		weightMax = weightMax + WeaponGradeProb[i]
	end

	local weight = Game.RandomFloat(0.0, weightMax)

	local weightSum = 0.0
	for i = grade, WeaponGrade.END do
		weightSum = weightSum + WeaponGradeProb[i]
		if weight <= weightSum  then
			grade = i
			break
		end
	end

	-- 레벨별, 등급별, 랜덤 데미지 계산
	weapon.damage = WeaponLevelDamage * LevelRatio[level]
	weapon.damage = weapon.damage * (WeaponGradeDamage[grade] + Game.RandomFloat(-WeaponRandomDamage, WeaponRandomDamage))

	-- 랜덤 능력치 최대 개수
	local maxAttrNum = 0

	-- 결정된 등급에따라 색상과 능력치 개수 설정
	if grade == WeaponGrade.normal then
		weapon.color = Game.WEAPONCOLOR.WHITE
		maxAttrNum = 0
	elseif grade == WeaponGrade.rare then
		weapon.color = Game.WEAPONCOLOR.BLUE
		maxAttrNum = 1
	elseif grade == WeaponGrade.unique then
		weapon.color = Game.WEAPONCOLOR.RED
		maxAttrNum = 2
	elseif grade == WeaponGrade.legend then
		weapon.color = Game.WEAPONCOLOR.ORANGE
		maxAttrNum = 3
	end

	-- 중복된 능력치가 나오지 않도록
	local attrDuplicateCheck = {}
	if weapon:GetWeaponType() == Game.WEAPONTYPE.PISTOL then
		attrDuplicateCheck[5] = true
	end

	-- 능력치 결정
	local attrNum = Game.RandomInt(0, maxAttrNum)
	for i = 1, attrNum do
		local attrType = Game.RandomInt(1, 5) -- speed부터 infiniteclip까지 5종류
		if attrDuplicateCheck[attrType] then
			-- 능력치가 중복될 경우 다시
			i = i - 1
		else
			attrDuplicateCheck[attrType] = true

			if attrType == 1 then
				weapon.speed = Game.RandomFloat(1.2, 1.3)
			elseif attrType == 2 then
				weapon.knockback = Game.RandomFloat(1.2, 2.0)
				weapon.flinch = Game.RandomFloat(1.2, 2.0)
			elseif attrType == 3 then
				weapon.criticalrate = Game.RandomFloat(0.03, 0.2)
				weapon.criticaldamage = Game.RandomFloat(1.5, 2.5)
			elseif attrType == 4 then
				weapon.bloodsucking = Game.RandomFloat(0.01, 0.03)
			elseif attrType == 5 then
				weapon.infiniteclip = true
			end
		end
	end
end

-- 무기 레벨 랜덤 결정(몬스터 레벨 사용)
function GetWeaponRandomLevel(level)

	local minLevel = level - 5
	local maxLevel = level + 3

	if minLevel < LEVEL_MIN then
		minLevel = LEVEL_MIN
	end
	if maxLevel > WEAPONLEVEL_MAX then
		maxLevel = WEAPONLEVEL_MAX
	end

	return Game.RandomInt(minLevel, maxLevel)
end

-- 랜덤 무기 생성(레벨, 위치 사용)
function CreateWeapon(level, pos)

	-- 무기 타입 랜덤 결정(무기 레벨 사용)
	local weightMax = 0.0
	local list = {}
	for i = 1, #WeaponList do
		local weaponOption = Common.GetWeaponOption(WeaponList[i])
		if weaponOption.user.level <= level then
			table.insert(list, weaponOption)
			weightMax = weightMax + (LevelRatio[weaponOption.user.level] * WeaponGradeProb[weaponOption.user.grade])
		end
	end

	local type = 0
	local weightSum = 0.0
	local weight = Game.RandomFloat(0.0, weightMax)
	for i = 1, #list do
		weightSum = weightSum + (LevelRatio[list[i].user.level] * WeaponGradeProb[list[i].user.grade])
		if weight <= weightSum  then
			type = list[i].weaponid
			break
		end
	end

	if type == 0 then
		return nil
	end

	local weapon = Game.Weapon.CreateAndDrop(type, pos)
	if weapon then
		SetWeaponAttribute(weapon, level)
	end

	return weapon
end


--[[ 플레이어 레벨 관련 사용자 정의 함수들 ]]


-- 플레이어가 레벨업할때
function OnLevelUp(player)

	-- 체력 증가
	player.maxhealth = math.floor(100 * LevelRatio[player.user.level])
	player.health = player.maxhealth

	-- 일반 난이도에서는 플레이어 레벨에따라 몬스터 레벨이 변화한다
	if mapDifficulty == SignalToGame.difficulty0 then
		if player.user.level > monsterLevel then
			monsterLevel = player.user.level
			if monsterLevel > 30 then
				monsterLevel = 30
			end
		end
	end

	-- WeaponOption과 level을 비교해서 UI 잠김 표시(상점창)를 보여준다
	for i = 1, #BuymenuWeaponList do
		local option = Common.GetWeaponOption(BuymenuWeaponList[i])
		if option then
			player:SetBuymenuLockedUI(BuymenuWeaponList[i], option.user.level > player.user.level, option.user.level)
		end
	end

	-- Weapon과 level을 비교해서 UI 잠김 표시(무기인벤창)를 보여준다
	local invenWeapons = player:GetWeaponInvenList()
	for i = 1, #invenWeapons do
		player:SetWeaponInvenLockedUI(invenWeapons[i], invenWeapons[i].user.level > player.user.level, invenWeapons[i].user.level)
	end
end

-- 플레이어의 현재 레벨과 경험치를 가지고 경험치 비율을 계산한다
function CalcExpRate(level, exp)
	return exp / PlayerRequireExp[level]
end

-- 플레이어에게 경험치 지급
function AddExp(player, exp)

	local pu = player.user

	-- 만렙이면 스킵
	if pu.level >= LEVEL_MAX then
		return
	end

	pu.exp = pu.exp + exp

	 -- 레벨업
	if pu.exp > PlayerRequireExp[pu.level] then
		pu.level = pu.level + 1

		if pu.level >= LEVEL_MAX then
			pu.exp = 0
		else
			pu.exp = pu.exp - PlayerRequireExp[pu.level - 1]
		end

		OnLevelUp(player)
	end

	-- 레벨/경험치 UI를 보여주고 갱신한다
	pu.expRate = CalcExpRate(pu.level, pu.exp)
	player:SetLevelUI(pu.level, pu.expRate)
end


--[[ 스튜디오 호출용 함수들(무기, 몬스터 관련) ]]


-- 문자열 분리 함수
function splitstr_tonumber(inputstr)
	local t = {}
	for str in string.gmatch(inputstr, "([^,]*)") do
		table.insert(t, tonumber(str))
	end
	return t
end

-- 몬스터 그룹별 AttackTo 장소 지정
MonsterAttackPos = {

	-- CreateDefaultMonsters
	[1] = {x = -12, y = 100, z = 1},
	[2] = {x = -9, y = 98, z = 1},
	[3] = {x = 14, y = 77, z = 1},
	[7] = {x = 80, y = 26, z = 1},
	[9] = {x = 81, y = 22, z = 1},
	[11] = {x = 81, y = 24, z = 1},
	[13] = {x = 36, y = -14, z = 1},
	[14] = {x = 35, y = -11, z = 1},
	[17] = {x = 34, y = -32, z = -3},
	[18] = {x = 35, y = -40, z = -3},
	[8055] = {x = 25, y = -31, z = -3},

	-- CreateWaveMonsters
	[1000] = {x = -4, y = 72, z = 1},

	-- CreateSpecialMonsters
	[10000] = {x = -12, y = 108, z = 1},
}

-- 몬스터 등급 결정
function GetRandomGrade(min, max)
	if min == max then
		return min
	end

	local grade = min

	local weightMax = 0.0
	for i = min, max do
		weightMax = weightMax + MonsterGradeProb[i]
	end

	local weight = Game.RandomFloat(0.0, weightMax)

	local weightSum = 0.0
	for i = min, max do
		weightSum = weightSum + MonsterGradeProb[i]
		if weight <= weightSum  then
			return i
		end
	end

	return min
end

function CreateDefaultMonsters(callerOn, arg)

	if callerOn == nil or callerOn == false then
		return
	end

	local args = splitstr_tonumber(arg) -- arg 문자열을 나눠서 숫자배열로 담는다
	local type = MonsterTypes[args[1]] -- 몬스터 타입
	local num = args[2] -- 소환할 몬스터 숫자
	local groupid = args[3] -- 몬스터그룹 id
	local grade = GetRandomGrade(args[4], args[5]) -- 몬스터 등급

	-- Game.GetScriptCaller() : 이 함수를 호출한 스크립트 함수 호출 VoxelEntity를 얻는다
	local monsters = CreateMonsters(type, num, Game.GetScriptCaller().position, groupid, grade)

	-- AttackTo 위치 지정이 필요한 몬스터그룹들
	for i = 1, #monsters do
		if MonsterAttackPos[groupid] then
			monsters[i]:AttackTo(MonsterAttackPos[groupid]) -- 지정된 좌표로 이동하면서 공격
		end
	end
end

-- 몬스터 웨이브생성 함수들
function CreateWaveMonsters(callerOn, arg)
	if callerOn == nil or callerOn == false then
		return
	end

	local args = splitstr_tonumber(arg)
	local type = MonsterTypes[args[1]]
	local num = args[2]
	local groupid = args[3]
	local grade = GetRandomGrade(args[4], args[5])
	local waveCnt = args[6] -- 웨이브 생성 횟수

	if monsterWaveCnt[groupid] then
		monsterWaveCnt[groupid] = monsterWaveCnt[groupid] + 1
		if monsterWaveCnt[groupid] > waveCnt then
			if monsterWaveCnt[groupid] == waveCnt + 1 then
				Game.SetTrigger('OnWaveEnded' ..  groupid, true)
			end

			return
		end
	else
		monsterWaveCnt[groupid] = 1
	end

	if Game.GetScriptCaller() then
		monsterWavePosition[groupid] = Game.GetScriptCaller().position
	end

	local monsters = CreateMonsters(type, num, monsterWavePosition[groupid], groupid, grade)
	for i = 1, #monsters do
		monsters[i].user.waveFunc = CreateWaveMonsters
		monsters[i].user.waveFuncArg = arg

		if MonsterAttackPos[groupid] then
			monsters[i]:AttackTo(MonsterAttackPos[groupid]) -- 지정된 좌표로 이동하면서 공격
		end
	end
end

-- 특정 아이템 드랍 몬스터생성 함수
function CreateSpecialMonsters(callerOn, arg)
	if callerOn then

		local args = splitstr_tonumber(arg)
		local type = MonsterTypes[args[1]]
		local num = args[2]
		local groupid = args[3]
		local grade = GetRandomGrade(args[4], args[5])

		local monsters = CreateMonsters(type, num, Game.GetScriptCaller().position, groupid, grade)
		for i = 1, #monsters do
			monsters[i].user.specialWeaponDrop = groupid -- 특정 아이템 드랍 지정

			if MonsterAttackPos[groupid] then
				monsters[i]:AttackTo(MonsterAttackPos[groupid]) -- 지정된 좌표로 이동하면서 공격
			end
		end
	end
end

-- 특정 아이템 드랍 지정 함수
function CreateSpecialWeapon(specialWeaponDrop, position)

	if specialWeaponDrop == 10000 then
		local weapon = Game.Weapon.CreateAndDrop(Common.WEAPON.P90, position)
		SetWeaponAttributeDefault(weapon)
	elseif specialWeaponDrop == 10001 then
		local weapon = Game.Weapon.CreateAndDrop(Common.WEAPON.M134Minigun, position)
		SetWeaponAttributeDefault(weapon)
	end
end

-- 맵에 배치할 기본 무기 생성
function CreateDefaultWeapon()
    local weapon = Game.Weapon.CreateAndDrop(Common.WEAPON.USP45, {x = -6, y = 150, z = 1})
	SetWeaponAttributeDefault(weapon)
end

-- 랜덤 무기 생성
function CreateRandomWeapon(callerOn, arg)
	if callerOn then
		CreateWeapon(tonumber(arg), Game.GetScriptCaller().position)
	end
end


--[[ 이벤트 함수들 ]]


-- 플레이어가 클래스를 선택한후 처음 스폰할때 불리는 함수
function Game.Rule:OnPlayerJoiningSpawn(player)

	-- 플레이어 배열에 저장
	players[player.index] = player

	-- 카메라를 3인칭으로 바꾸고 입력방식을 마우스포인터 기반으로 바꾼다
	player:SetThirdPersonFixedView(-45, 53, 100, 250) -- yaw, pitch, minDist, maxDist

	-- ThirdPersonFixedView 기반에서 마우스포인터의 레이캐스트 위치 계산 방식을 수정한다
	player:SetThirdPersonFixedPlane(Game.THIRDPERSON_FIXED_PLANE.GROUND)

	-- 기본 레벨, 경험치
	player.user.level = 1
	player.user.exp = 0
	player.user.expRate = 0

	-- 레벨별 UI 초기화
	OnLevelUp(player) -- 무기잠김 ui 초기화 용도
	player:SetLevelUI(player.user.level, player.user.expRate) -- 레벨/경험치 UI 세팅

	-- 팀 설정
	player.team = Game.TEAM.CT
end

-- entity(몬스터, 플레이어 등)가 피해를 받았을때 불리는 함수
function Game.Rule:OnTakeDamage(victim, attacker, damage, weapontype, hitbox)
	if attacker == nil or victim == nil then return end

	if victim:IsMonster() then
		victim = victim:ToMonster()
		victim:ShowOverheadDamage(damage, 0) -- 머리위에 데미지를 표시한다. 두번째인수(플레이어 인덱스)가 0일 경우 모든 사람에게 보여준다
	end
end

-- entity가 죽었을때 불리는 함수
function Game.Rule:OnKilled(victim, killer)
	if victim == nil or killer == nil then
		return
	end

	-- 플레이어가 죽을경우 부활시킨다
	if victim:IsPlayer() then

		victim = victim:ToPlayer()

		-- 일반 난이도가 아닐경우 경험치를 10% 깎는다
		if mapDifficulty ~= SignalToGame.difficulty0 then
			AddExp(victim, -math.floor(PlayerRequireExp[victim.user.level] / 10.0))
		end

		if victim.user.spawnable == true then
			victim:Respawn()
		end

		-- 장전 ui를 숨긴다
		victim:Signal(SignalToUI.reloadFinished)
	end

	-- 몬스터가 죽을경우
	if victim:IsMonster() then

		victim = victim:ToMonster()

		-- killer가 플레이어일경우 경험치를 준다
		if killer:IsPlayer() then
			killer = killer:ToPlayer()
			AddExp(killer, victim.user.exp)
		end

		-- 몬스터에 지정된 무기를 만들거나 몬스터등급에따른 무기드랍확률을 검사해 무기를 떨어뜨린다
		if victim.user.specialWeaponDrop then
			CreateSpecialWeapon(victim.user.specialWeaponDrop, victim.position)
		else
			weight = Game.RandomFloat(0.0, 1.0)
			if weight <= WeaponDropProb[victim.user.grade] then
				CreateWeapon(GetWeaponRandomLevel(victim.user.level), victim.position)
			end
		end

		-- 몬스터 무리의 개수를 체크하여 0마리가 됐을경우 OnMonsterKilled 함수에서 스튜디오로 전달한다
		monsterGroupCnt[victim.user.groupid] = monsterGroupCnt[victim.user.groupid] - 1
		if monsterGroupCnt[victim.user.groupid] <= 0 then
			monsterGroupCnt[victim.user.groupid] = nil
			OnMonsterKilled(victim)
		end
	end
end

-- 무기를 구매할수 있는지 검사하는 함수
-- 스크립트 무기 리스트에 포함되지 않는 무기는 불리지 않는다
function Game.Rule:CanBuyWeapon(player, weaponid)
	local weaponOption = Common.GetWeaponOption(weaponid)
	return weaponOption.user.level <= player.user.level
end

-- 무기를 손에 들수 있는지 검사하는 함수
-- 스크립트 무기 리스트에 포함되지 않는 무기는 불리지 않는다
-- Weapon 클래스를 사용한적이 없을 경우 weapon인수는 nil로 전달된다
function Game.Rule:CanHaveWeaponInHand(player, weaponid, weapon)
	local weaponOptionCheck = Common.GetWeaponOption(weaponid).user.level <= player.user.level
	local weaponCheck = weapon == nil or weapon.user.level == nil or weapon.user.level <= player.user.level

	return weaponOptionCheck and weaponCheck
end

-- 무기를 획득 했을때 불리는 함수
-- 스크립트 무기 리스트에 포함되지 않는 무기는 불리지 않는다
-- Weapon 클래스를 사용한적이 없을 경우 weapon인수는 nil로 전달된다
function Game.Rule:OnGetWeapon(player, weaponid, weapon)
	if weapon == nil then
		return
	end

	-- 기본 능력치 설정
	if weapon.user.level == nil then
		SetWeaponAttributeDefault(weapon)
	end

	-- 잠김 ui 갱신
	player:SetWeaponInvenLockedUI(weapon, weapon.user.level > player.user.level, weapon.user.level)
end

-- 장전 시간 전달용 변수
reloadTimeSync = Game.SyncValue.Create("reloadTime")

-- 장전할때 불리는 함수
-- Weapon 클래스를 사용한적이 없거나 스크립트 무기가 아닐경우 weapon인수는 nil로 전달된다
function Game.Rule:OnReload(player, weapon, time)
	-- 장전 ui를 보여준다
	player:Signal(SignalToUI.reloadStarted)
	-- 장전 시간 전달
	reloadTimeSync.value = time
end

-- 장전이 끝나고 불리는 함수
-- Weapon 클래스를 사용한적이 없거나 스크립트 무기가 아닐경우 weapon인수는 nil로 전달된다
function Game.Rule:OnReloadFinished(player, weapon)
	player:Signal(SignalToUI.reloadFinished) -- 장전 ui를 숨긴다
end

-- 플레이어가 무기를 교체했을때 불리는 함수
function Game.Rule:OnSwitchWeapon(player)
	player:Signal(SignalToUI.reloadFinished) -- 장전 ui를 숨긴다
end

-- 발사이후 불리는 함수
-- Weapon 클래스를 사용한적이 없거나 스크립트 무기가 아닐경우 weapon인수는 nil로 전달된다
function Game.Rule:PostFireWeapon(player, weapon, time)

	-- 발사 간격이 1초 이상인 무기들에 대해서만 장전ui를 보여준다
	if time > 1.0 then
		-- 장전 ui를 보여준다
		player:Signal(SignalToUI.reloadStarted)
		-- 장전 시간 전달
		reloadTimeSync.value = time
	end
end

-- 플레이어가 무기를 꺼내들었을때 불리는 함수
function Game.Rule:OnDeployWeapon(player, weapon)
	player:Signal(SignalToUI.reloadFinished) -- 장전 ui를 숨긴다
end

-- 플레이어별 save 정보 저장
function Game.Rule:OnGameSave(player)
	if player == nil then
		return
	end

	-- 레벨, 경험치 저장
	player:SetGameSave('level', player.user.level)
	player:SetGameSave('exp', player.user.exp)

	-- 현재 들고있는 무기 레벨 저장
	local primaryWeapon = player:GetPrimaryWeapon()
	local secondaryWeapon = player:GetSecondaryWeapon()

	if primaryWeapon then
		player:SetGameSave('wpn_lv_pri', primaryWeapon.user.level)
	else
		player:SetGameSave('wpn_lv_pri', 0)
	end

	if secondaryWeapon then
		player:SetGameSave('wpn_lv_sec', secondaryWeapon.user.level)
	else
		player:SetGameSave('wpn_lv_sec', 0)
	end

	-- 인벤토리 무기 레벨 저장
	local invenWeapons = player:GetWeaponInvenList()
	for i = 1, #invenWeapons do
		player:SetGameSave('wpn_lv_inven' .. i, invenWeapons[i].user.level)
	end
end

function DoubleToInt(number)
	return math.floor(math.abs(number + EPSILON))
end

-- 플레이어별 save 정보 로드
function Game.Rule:OnLoadGameSave(player)
	if player == nil then
		return
	end

	-- 레벨, 경험치 로드
	player.user.level = DoubleToInt(player:GetGameSave('level'))
	player.user.exp = DoubleToInt(player:GetGameSave('exp'))

	if player.user.level == nil then
		player.user.level = 1
	end
	if player.user.exp == nil then
		player.user.exp = 0
	end

	player.user.expRate = CalcExpRate(player.user.level, player.user.exp)

	-- 현재 들고있는 무기 로드
	local primaryWeapon = player:GetPrimaryWeapon()
	local secondaryWeapon = player:GetSecondaryWeapon()

	if primaryWeapon then
		primaryWeapon.user.level = DoubleToInt(player:GetGameSave('wpn_lv_pri'))
	end

	if secondaryWeapon then
		secondaryWeapon.user.level = DoubleToInt(player:GetGameSave('wpn_lv_sec'))
	end

	-- 인벤토리 무기 레벨 로드
	local invenWeapons = player:GetWeaponInvenList()
	for i = 1, #invenWeapons do
		invenWeapons[i].user.level = DoubleToInt(player:GetGameSave('wpn_lv_inven' .. i))
	end

	-- 레벨별 UI 초기화
	OnLevelUp(player) -- 무기잠김 ui 초기화 용도
	player:SetLevelUI(player.user.level, player.user.expRate) -- 레벨/경험치 UI 세팅
end

-- 플레이어별 save 정보 초기화
function Game.Rule:OnClearGameSave(player)
	player.user.level = 1
	player.user.exp = 0
	player.user.expRate = 0
	player:RemoveWeapon()
	player:ClearWeaponInven()

	-- 레벨별 UI 초기화
	OnLevelUp(player) -- 무기잠김 ui 초기화 용도
	player:SetLevelUI(player.user.level, player.user.expRate) -- 레벨/경험치 UI 세팅
end


--[[ UI와의 정보전달 ]]


function Game.Rule:OnPlayerSignal(player, signal)

	if signal == SignalToGame.openWeaponInven then
		player:ToggleWeaponInven()
	end
end

-- 상점 열기(스튜디오 호출용 함수)
function ShowBuymenu(callerOn)
	local triggerEnt = Game.GetTriggerEntity()
	if triggerEnt and triggerEnt:IsPlayer() then
		triggerEnt = triggerEnt:ToPlayer()
		triggerEnt:ShowBuymenu()
	end
end
generated by LDoc 1.4.6 Last updated 2019-11-08 21:27:50