-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathplayer-logic.dnh
215 lines (211 loc) · 8.31 KB
/
player-logic.dnh
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
#include "./lib/vector.dnh"
let bestChoice = -1, bestValue = 1000000;
const VELO_FOCUSED = 2.0, VELO_SPREAD = 4.5;
const SPREAD_THRESHOLD = 0.000020, MOVE_THRESHOLD = 0.000005;
const WEIGHT = 20, WEIGHT_VELO = 20;
const BASE_RADIUS = 7;
// extra safety space for projection dodging
const SMALL_RADIUS = 4;
// extra safety space for force dodging
const LARGE_RADIUS = 5;
task TPlayerLogicMain {
let force = GetForceVector();
let x = ObjVector_GetX(force), y = ObjVector_GetY(force);
WriteLog("Force vector: " ~ ToString(x) ~ ", " ~ ToString(y));
if (absolute(x) > MOVE_THRESHOLD) {
if (absolute(x) < SPREAD_THRESHOLD) {
SetVirtualKeyState(VK_SLOWMOVE, KEY_PUSH);
} else {
SetVirtualKeyState(VK_SLOWMOVE, KEY_PULL);
}
if (x < 0) {
SetVirtualKeyState(VK_RIGHT, KEY_PUSH);
SetVirtualKeyState(VK_LEFT, KEY_PULL);
} else {
SetVirtualKeyState(VK_LEFT, KEY_PUSH);
SetVirtualKeyState(VK_RIGHT, KEY_PULL);
}
}
if (absolute(y) > MOVE_THRESHOLD) {
if (absolute(y) < SPREAD_THRESHOLD) {
SetVirtualKeyState(VK_SLOWMOVE, KEY_PUSH);
} else {
SetVirtualKeyState(VK_SLOWMOVE, KEY_PULL);
}
if (y > 0) {
SetVirtualKeyState(VK_UP, KEY_PUSH);
SetVirtualKeyState(VK_DOWN, KEY_PULL);
} else {
SetVirtualKeyState(VK_DOWN, KEY_PUSH);
SetVirtualKeyState(VK_UP, KEY_PULL);
}
}
// cleanup
Obj_Delete(force);
}
function GetForceVector() {
// Get all the bullets worth considering
let pX = GetPlayerX(), pY = GetPlayerY();
const r1 = SMALL_RADIUS + BASE_RADIUS, r2 = LARGE_RADIUS + BASE_RADIUS;
let force = ObjVector_Create();
let consideredBullets = GetShotIdInCircleA2(pX, pY, 20, TARGET_ENEMY);
for each (bullet in consideredBullets) {
// Notations used below:
// Player position = c (Using vectors to represent Points)
// Bullet current position = a
// Bullet projected future position = b (using the GetSpeedX|Y functions)
let A = ObjVector_Create(ObjMove_GetX(bullet), ObjMove_GetY(bullet));
let B = ObjVector_Add(A, WEIGHT_VELO * ObjMove_GetSpeedX(bullet), WEIGHT_VELO * ObjMove_GetSpeedY(bullet));
let C = ObjVector_Create(pX, pY);
// The bullet's movement
// (Vector from current position to projected future position)
let AB = ObjVector_Subtract(B, A); // ab = b - a
// Vector from bullet to player
let AC = ObjVector_Subtract(C, A); // ac = c - a
// Vector from bullet to normal (projection)
let AD = ObjVector_GetProjection(AC, AB);
// The normal(projection) vector
let D = ObjVector_Add(AD, A);
let CD = ObjVector_Subtract(D, C); // cd = d - c
if (ObjVector_GetLengthSquared(AC) < r2 ^ 2){
// Update the force vector, using Coulomb's Law
let normalizedAC = ObjVector_Normalize(AC);
let weightedAC = ObjVector_Multiply(AC, WEIGHT / ObjVector_GetLengthSquared(AC));
// For configurability's sake, the WEIGHT is set as a constant
// so we can tweak on the impact factor
ObjVector_SubtractInPlace(force, weightedAC);
// remove the used temporary variables
Obj_Delete(normalizedAC);
Obj_Delete(weightedAC);
} else if (IsVectorOnSegment(D, A, B)) {
// If the projection is within the bounding Box
// formed by A (bullet original position)
// and B (bullet predicted position)
// Then we are going to move it
if (ObjVector_IsZero(CD)) {
let bulletVelo = ObjVector_Create(ObjMove_GetSpeedX(bullet), ObjMove_GetSpeedY(bullet));
bulletVelo = ObjVector_GetNormal(bulletVelo);
let normalizedVelo = ObjVector_Normalize(bulletVelo);
let weightedVelo = ObjVector_Multiply(normalizedVelo, WEIGHT * ObjVector_GetLengthSquared(AC));
ObjVector_AddInPlace(force, weightedVelo);
Obj_Delete(bulletVelo);
Obj_Delete(normalizedVelo);
Obj_Delete(weightedVelo);
} else if (ObjVector_GetLengthSquared(CD) < r1 ^ 2) {
let normalizedCD = ObjVector_Normalize(CD);
let weightedCD = ObjVector_Multiply(normalizedCD, WEIGHT / ObjVector_GetLengthSquared(AC));
ObjVector_AddInPlace(force, weightedCD);
Obj_Delete(normalizedCD);
Obj_Delete(weightedCD);
}
}
// the repulsion effects of the Wall
let height = GetStgFrameHeight(), width = GetStgFrameWidth();
let xr = absolute(power(width - pX, 2));
let xl = absolute(power(pX, 2));
let yt = absolute(power(pY, 2));
let yb = absolute(power(height - pY, 2));
let wallRepulsion = ObjVector_Create
((0.2 / xr - 0.2 / xl) * WEIGHT, (-0.6 / yt + 0.1 / yb) * WEIGHT);
// the weight of bottom repulsion is lower because we want the
// bot to stay close to the lower part of the screen
ObjVector_AddInPlace(force, wallRepulsion);
// destruct all objects before our system is clogged up
Obj_Delete(wallRepulsion);
Obj_Delete(A);
Obj_Delete(B);
Obj_Delete(C);
Obj_Delete(D);
Obj_Delete(AB);
Obj_Delete(AC);
Obj_Delete(AD);
Obj_Delete(CD);
}
return force;
}
function IsVectorOnSegment(v, a, b) {
let botright = GetMaxVector(a, b), topleft = GetMinVector(a, b);
let x = ObjVector_GetX(v), y = ObjVector_GetY(v);
let xr = ObjVector_GetX(botright), yr = ObjVector_GetY(botright);
let xl = ObjVector_GetX(topleft), yl = ObjVector_GetY(topleft);
let res = (x >= xl && x <= xr && y >= yl && y <= yr);
Obj_Delete(botright);
Obj_Delete(topleft);
return res;
}
// === Obsolete ===
function GetChoice(x) {
// Map the angle chosen to the virtual key state
// which is LRUD
if (x == 0) return 1; // right
if (x == 1) return 3; // down
if (x == 2) return 0; // left
if (x == 3) return 2; // up
}
/*
task TVOLogic {
// if (frame % 7 != 0) MovePlayer(bestChoice % 4, 1);
// Get the shots requiring concern
let shots = GetShotIdInCircleA2(GetPlayerX(), GetPlayerY(), 50, TARGET_ENEMY);
let n = length(shots);
if (!n) return;
// Calculate the radius of the blown-up version of enemy bullet
const shotSize = GetHitboxRadius(BALL_SMALL_RECT), playerHitboxSize = 2;
const radius = shotSize + playerHitboxSize;
let obstacles = [];
let centerVector = [];
for each (shot in shots) {
let vectorToShot = GetVector(GetPlayerID(), shot);
// Calculate the angle of the velocity obstacle
let distanceToShot = ObjVector_GetLength(vectorToShot);
let tangentLength = (distanceToShot ^ 2 - radius ^ 2) ^ 0.5;
let angle = atan2(radius, tangentLength);
obstacles = obstacles ~ [angle];
centerVector = centerVector ~ [vectorToShot];
}
// Calculate focused options
bestChoice = -1; bestValue = 1000000;
ascent (i in 0..4) {
let curAngle = i * 90;
// WriteLog("Current Moving angle is " ~ ToString(curAngle));
// check wall collision
let touchedEdge = GetTouchedEdge(GetPlayerX(), GetPlayerY());
let bad = false;
for each (edge in touchedEdge) {
if (edge == i) {
// ObjLogger_Warn(logger, "TPlayerLogicMain", "Hit Wall " ~ ToString(i));
bad = true;
break;
}
}
if (bad) continue;
let cntFocused = 0, cntSpread = 0;
ascent (j in 0..length(obstacles)) {
let normalizedVectorToObstacle = ObjVector_Normalize(centerVector[j]),
angle = obstacles[j];
let vectorToVelocityFocused = ObjVector_Create(cos(curAngle) * VELO_FOCUSED,
sin(curAngle) * VELO_FOCUSED);
let normalizedVectorToVelocityFocused = ObjVector_Normalize(vectorToVelocityFocused);
if (ObjVector_Dot(normalizedVectorToObstacle, normalizedVectorToVelocityFocused) < cos(angle)) {
cntFocused++;
}
let vectorToVelocitySpread = ObjVector_Create(cos(curAngle) * VELO_SPREAD,
sin(curAngle) * VELO_SPREAD);
let normalizedVectorToVelocitySpread = ObjVector_Normalize(vectorToVelocitySpread);
if (ObjVector_Dot(normalizedVectorToObstacle, normalizedVectorToVelocitySpread) < cos(angle)) {
cntSpread++;
}
}
// ObjLogger_Info(logger, "TPlayerLogicMain", "Focused Moving: " ~ ToString(cntFocused));
// ObjLogger_Info(logger, "TPlayerLogicMain", "Spread Moving: " ~ ToString(cntSpread));
if (cntFocused < bestValue) {
bestValue = cntFocused;
bestChoice = GetChoice(i) + 4;
}
if (cntSpread < bestValue) {
bestValue = cntSpread;
bestChoice = GetChoice(i);
}
}
MovePlayer(bestChoice % 4, bestChoice >= 4);
} */