-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathairstrike.py
214 lines (162 loc) · 6.08 KB
/
airstrike.py
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
from random import randint, random, uniform
from dataclasses import dataclass
from math import floor, inf
from time import sleep
from twisted.internet import reactor
from pyspades.protocol import BaseProtocol
from pyspades.constants import WEAPON_TOOL
from piqueserver.config import config
from pyspades.common import Vertex3
from pyspades.team import Team
from milsim.blast import sendGrenadePacket, explode
from milsim.weapon import UnderbarrelItem
from piqueserver.commands import command
from milsim.common import alive_only
section = config.section("airstrike")
airstrike_zoomv_time = section.option("zoomv_time", 2).get()
airstrike_delay = section.option("delay", 7 * 60).get()
aitstrike_phase = section.option("phase", 120).get()
BOMBS_COUNT = 7
BOMBER_SPEED = 10
BOMBING_DELAY = 2
AIRBOMB_DELAY = 3
AIRBOMB_RADIUS = 10
AIRBOMB_SAFE_DISTANCE = 150
AIRBOMB_GUARANTEED_KILL_RADIUS = 40
AIRSTRIKE_PASSES = 50
AIRSTRIKE_CAST_DISTANCE = 300
def airbomb_explode(protocol, player_id, x, y, z):
if player := protocol.take_player(player_id):
explode(AIRBOMB_GUARANTEED_KILL_RADIUS, AIRBOMB_SAFE_DISTANCE, player, Vertex3(x, y, z))
for i in range(AIRSTRIKE_PASSES):
X = x + randint(-AIRBOMB_RADIUS, AIRBOMB_RADIUS)
Y = y + randint(-AIRBOMB_RADIUS, AIRBOMB_RADIUS)
Z = protocol.map.get_z(X, Y)
player.grenade_destroy(X, Y, Z)
sendGrenadePacket(protocol, player.player_id, Vertex3(X, Y, Z), Vertex3(0, 0, 0), 0)
sleep(uniform(0.0, 0.05))
def drop_airbomb(protocol, player_id, x, y):
X = floor(x)
Y = floor(y)
Z = protocol.map.get_z(X, Y) - 2
airbomb_explode(protocol, player_id, X, Y, Z)
def do_bombing(protocol, player_id, x, y, vx, vy, nbombs):
for k in range(nbombs):
sleep(BOMBING_DELAY)
drop_airbomb(protocol, player_id, x, y)
x += vx * BOMBING_DELAY
y += vy * BOMBING_DELAY
def do_airstrike(name, connection):
protocol = connection.protocol
if wo := connection.world_object:
if loc := wo.cast_ray(AIRSTRIKE_CAST_DISTANCE):
protocol.broadcast_chat(
"<{}> Coordinates recieved. Over.".format(name),
global_message = False, team = connection.team
)
x, y, z = loc
o = wo.orientation
v = Vertex3(o.x, o.y, 0).normal() * BOMBER_SPEED
reactor.callInThread(do_bombing, protocol, connection.player_id, x, y, v.x, v.y, BOMBS_COUNT)
@command(admin_only = True)
@alive_only
def gift(connection):
do_airstrike("Panavia Tornado ECR", connection)
@command('airstrike', 'air')
@alive_only
def air(player, loc = None):
"""
Report time before bomber's arrival
/air
"""
if loc is not None:
return "To initiate an airstrike scope and then hold V. Use /air to check the readiness"
if o := player.get_bomber():
remaining = o.remaining()
if remaining is not None:
approx = round((remaining / 10 + 1) * 10)
o.report("Will be ready in {} seconds".format(approx))
else:
o.report("Awaiting for coordinates")
class Laser(UnderbarrelItem):
name = "Laser"
mass = 0.500
def __init__(self):
UnderbarrelItem.__init__(self)
self.timer = -inf
def on_press(self, player):
self.timer = 0
def on_hold(self, player, t, dt):
self.timer += dt
if self.timer > airstrike_zoomv_time:
self.timer = 0
if o := player.get_bomber():
o.point(player)
@dataclass
class Bomber:
name : str
team : Team
protocol : BaseProtocol
def __post_init__(self):
self.init()
def init(self, by_server = False):
self.player_id = None
self.preparation = None
self.call = None
self.ready = False
if by_server:
self.preparation = reactor.callLater(aitstrike_phase, self.start)
def point(self, conn):
if not self.active() and self.ready:
self.player_id = conn.player_id
do_airstrike(self.name, conn)
self.restart()
def active(self):
return self.call and self.call.active()
def stop(self, player_id = None):
if (player_id and player_id != self.player_id) or not player_id:
return
if self.call and self.call.active():
self.call.cancel()
self.call = None
def start(self):
if self.ready: return
self.report("Air support is ready")
self.preparation = None
self.ready = True
def restart(self):
self.stop()
self.ready = False
self.preparation = reactor.callLater(airstrike_delay, self.start)
def report(self, msg):
self.protocol.broadcast_chat(
"<{}> {}. Over.".format(self.name, msg),
global_message = False, team = self.team
)
def remaining(self):
if self.preparation:
return self.preparation.getTime() - reactor.seconds()
else:
return None
def apply_script(protocol, connection, config):
class AirstrikeProtocol(protocol):
def __init__(self, *w, **kw):
protocol.__init__(self, *w, **kw)
self.bombers = {
self.team_1.id : Bomber("B-52", self.team_1, self),
self.team_2.id : Bomber("Tu-22M", self.team_2, self)
}
def on_map_change(self, M):
for bomber in self.bombers.values():
if bomber.preparation and bomber.preparation.active():
bomber.preparation.cancel()
bomber.stop()
bomber.init(by_server = True)
protocol.on_map_change(self, M)
class AirstrikeConnection(connection):
def get_bomber(self):
return self.protocol.bombers.get(self.team.id)
def on_spawn(self, pos):
connection.on_spawn(self, pos)
self.weapon_object.item_underbarrel = Laser().mark_renewable()
return AirstrikeProtocol, AirstrikeConnection