1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
--[[ 事先定義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

-- boss團死了
if monster.user.groupid == 8055 then
monsterWaveFuncState = WaveFuncState.disable
difficultySelectPlayer = 0
Game.KillAllMonsters() -- 下次更新後殺死所有怪物
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和等級。
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 和等級並顯示 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

-- 比較武器和等級並顯示 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

-- 怪物wave成功能
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] -- 產生的wave數

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

-- 將相機改為第三人稱,將輸入法改為基於滑鼠指標
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

-- 鎖定的使用者介面更新
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

-- 保存每個玩家的保存訊息
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

-- 載入每個玩家的保存訊息
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

-- 重置每個玩家的保存訊息
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

-- 打開商店(呼叫Studio的函數)
function ShowBuymenu(callerOn)
local triggerEnt = Game.GetTriggerEntity()
if triggerEnt and triggerEnt:IsPlayer() then
triggerEnt = triggerEnt:ToPlayer()
triggerEnt:ShowBuymenu()
end
end