-
-
Notifications
You must be signed in to change notification settings - Fork 444
/
Copy pathcleave_attack.dm
149 lines (132 loc) · 5.34 KB
/
cleave_attack.dm
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
/datum/component/cleave_attack
dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
/// Size of the attack arc in degrees
var/arc_size
/// Make this TRUE for two-handed weapons like axes
var/requires_wielded
/// How much slower is it to swing
var/swing_speed_mod
/// Which effect should this use
var/cleave_effect
/// Whether this item is disallowed from hitting more than one target
var/no_multi_hit
/// Callback when the cleave attack is finished
var/datum/callback/cleave_end_callback
/datum/component/cleave_attack/Initialize(
arc_size=90,
swing_speed_mod=1.25,
requires_wielded=FALSE,
no_multi_hit=FALSE,
datum/callback/cleave_end_callback,
cleave_effect,
...
)
if(!isitem(parent))
return COMPONENT_INCOMPATIBLE
src.arc_size = arc_size
src.swing_speed_mod = swing_speed_mod
src.requires_wielded = requires_wielded
src.no_multi_hit = no_multi_hit
src.cleave_end_callback = cleave_end_callback
set_cleave_effect(cleave_effect) // set it based on arc size if an effect wasn't specified
/datum/component/cleave_attack/InheritComponent(
datum/component/C,
i_am_original,
arc_size,
swing_speed_mod,
requires_wielded,
datum/callback/cleave_end_callback,
cleave_effect
)
if(!i_am_original)
return
if(arc_size)
src.arc_size = arc_size
if(swing_speed_mod)
src.swing_speed_mod = swing_speed_mod
if(requires_wielded)
src.requires_wielded = requires_wielded
if(no_multi_hit)
src.no_multi_hit = no_multi_hit
if(cleave_end_callback)
src.cleave_end_callback = cleave_end_callback
set_cleave_effect(cleave_effect)
/// Sets the cleave effect to the specified effect, or based on arc size if one wasn't specified.
/datum/component/cleave_attack/proc/set_cleave_effect(new_effect)
if(new_effect)
cleave_effect = new_effect
return
switch(arc_size)
if(0 to 120)
cleave_effect = /obj/effect/temp_visual/dir_setting/firing_effect/sweep_attack
if(120 to 240)
cleave_effect = /obj/effect/temp_visual/dir_setting/firing_effect/sweep_attack/semicircle
else
cleave_effect = /obj/effect/temp_visual/dir_setting/firing_effect/sweep_attack/full_circle
/datum/component/cleave_attack/RegisterWithParent()
RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
RegisterSignal(parent, COMSIG_ITEM_AFTERATTACK, PROC_REF(on_afterattack))
/datum/component/cleave_attack/UnregisterFromParent()
UnregisterSignal(parent, list(COMSIG_ATOM_EXAMINE, COMSIG_ITEM_AFTERATTACK))
/datum/component/cleave_attack/proc/on_examine(atom/examined_item, mob/user, list/examine_list)
var/arc_desc
switch(arc_size)
if(0 to 90)
arc_desc = "narrow arc"
if(90 to 180)
arc_desc = "wide arc"
if(180 to 270)
arc_desc = "very wide arc"
if(270 to INFINITY)
arc_desc = "full circle"
examine_list += "It can swing in a [arc_desc]."
/datum/component/cleave_attack/proc/on_afterattack(obj/item/item, atom/target, mob/living/user, proximity_flag, click_parameters)
if(proximity_flag || !user.combat_mode)
return // don't sweep on precise hits or non-harmful intents
var/list/modifiers = params2list(click_parameters)
if(modifiers[RIGHT_CLICK]) // might be trying to block
return
perform_sweep(item, target, user, click_parameters)
/datum/component/cleave_attack/proc/perform_sweep(obj/item/item, atom/target, mob/living/user, params)
if(user.next_move > world.time)
return // don't spam it
if(requires_wielded && !HAS_TRAIT(item, TRAIT_WIELDED))
return // if it needs to be wielded, check to make sure it is
// some information we're going to need later
var/turf/user_turf = get_turf(user)
var/turf/center_turf = get_turf_in_angle(get_angle(user, target), user_turf)
var/facing_dir = get_dir(user, center_turf)
var/swing_direction = (user.active_hand_index % 2) ? -1 : 1
// make a list of turfs to swing across
var/list/turf_list = list()
var/turfs_count = round(arc_size / 90, 1)
for(var/i in -min(turfs_count, 3) to min(turfs_count, 4)) // do NOT hit the same tile more than once
turf_list.Add(get_step(user_turf, turn(facing_dir, i * 45 * swing_direction)))
// do some effects so everyone knows you're swinging a weapon
playsound(item, 'sound/weapons/punchmiss.ogg', 50, TRUE)
new cleave_effect(user_turf, facing_dir)
// now swing across those turfs
ADD_TRAIT(item, TRAIT_CLEAVING, REF(src))
for(var/turf/T as anything in turf_list)
if(hit_atoms_on_turf(item, target, user, T, params))
break
REMOVE_TRAIT(item, TRAIT_CLEAVING, REF(src))
// do these last so they don't get overridden during the attack loop
cleave_end_callback?.Invoke(item, user)
user.do_attack_animation(center_turf, no_effect=TRUE)
user.changeNext_move(CLICK_CD_MELEE * item.weapon_stats[SWING_SPEED] * swing_speed_mod)
user.weapon_slow(item)
/// Hits all possible atoms on a turf, returns TRUE if the swing should end early
/datum/component/cleave_attack/proc/hit_atoms_on_turf(obj/item/item, atom/target, mob/living/user, turf/hit_turf, params)
for(var/atom/movable/hit_atom in hit_turf)
if(hit_atom == user || hit_atom == target)
continue // why are you hitting yourself
if(!(SEND_SIGNAL(hit_atom, COMSIG_ATOM_CLEAVE_ATTACK, item, user) & ATOM_ALLOW_CLEAVE_ATTACK))
if(hit_atom.pass_flags & LETPASSTHROW)
continue // if you can throw something over it, you can swing over it too
if(!hit_atom.density && hit_atom.uses_integrity)
continue
item.melee_attack_chain(user, hit_atom, params)
if(no_multi_hit && isliving(hit_atom))
return TRUE
return FALSE