TR_m3-725_RotZ_Object_60
TR_Mainland.esm
Variables
17
Local Variables
tasks
OnPCDrop
ang_limit
rot_limit
dis_limit
timer_limit
theta
t1
timer
mx
my
mvx
mvy
ma
pvx
pvy
ca
Source
1begin TR_m3-725_RotZ_Object_60
2;Author: Seneca37
3;This script will rotate an object (usually a skull) about its Z axis,
4;+/- 60 degrees, based on the location of the player.
5;This gives the effect that the skull is watching/following the player.
6;This currently works with the following misc skull IDs
7;misc_skull00
8;misc_skull10
9;T_Com_SkullArgonian_01/02
10;T_Com_SkullKhajiit_01/02
11;T_Com_SkullOrc_01/02
12;T_Com_SkullVampire_01/02
13;NOTE: if the nice static human skulls (TR\f\TR_furn_skel_skull...) are ever
14;made into misc objects, this script would need to be modified to handle the
15;fact that when facing the player this skull pivots about its Y axis (not Z)
16
17;rot_limit, and timer_limit have been randomized to give each skull
18;its own unique motion
19
20float ang_limit
21float rot_limit
22float dis_limit
23float timer_limit
24float theta
25float t1
26float timer
27float mx
28float my
29float mvx
30float mvy
31float ma
32float pvx
33float pvy
34float ca
35short tasks
36short OnPCDrop
37
38if ( MenuMode )
39 Return
40endif
41
42if ( OnPCDrop == 1 )
43 set tasks to 0 ;reset position and angles
44 set OnPCDrop to 0
45endif
46
47if ( timer_limit == 0.0 )
48 set timer_limit to 0.1
49endif
50set timer to ( timer + GetSecondsPassed )
51if ( timer < timer_limit )
52 Return
53endif
54
55
56if ( tasks == 0 )
57 ;These calculations only need to be done once
58 set ang_limit to 60.0 ;allow skull to only rotate +/- 60 degrees from starting position
59; set rot_limit to 3.0 ;largest angular step
60;Letting each item/skull rotate a different amount
61;This value will be between 0.5 and 3.0
62 set rot_limit to random 30
63 set rot_limit to rot_limit / 10
64 if ( rot_limit < 0.5 )
65 set rot_limit to 0.5
66 elseif ( rot_limit > 3.0 )
67 set rot_limit to 3.0
68 endif
69;Letting each item/skull set own timer_limit value
70 set timer_limit to random 100
71 set timer_limit to timer_limit / 333.0
72 if ( timer_limit < 0.01 )
73 set timer_limit to 0.01
74 elseif ( timer_limit > 0.3 )
75 set timer_limit to 0.3
76 endif
77 set dis_limit to 400.0 ;distance from skull to start rotating
78
79 set mx to ( GetPos, X )
80 set my to ( GetPos, Y )
81 set ma to ( GetAngle, Z ) ;ma will be between 0 and 360
82
83 ;create unit vector facing forward of the skull
84 ;NOTE: the Maclaurin series expansion of the arc cosine and
85 ; arc sine functions is very very poor at angles < 37 deg
86 ; and > 143 degrees. So I use a unit vector pointing
87 ; 90 degrees away from a forward facing vector. This way
88 ; when the player is directly in front of the skull, the angle
89 ; between the vector from the skull to the player, and
90 ; this unit vector will be 90 degrees.
91 ; This additional 90 degrees is subtracted out later.
92 set mvx to 1.0
93 set mvy to 0.0
94 if ( ma == 360.0 ) ;occasionally I've seen this in the CS
95 set mvx to 1.0 ;have not noticed it reported in-game
96 set mvy to 0.0 ;better safe than sorry
97 elseif ( ma == 0.0 )
98 set mvx to 1.0
99 set mvy to 0.0
100 elseif ( ma == 90.0 )
101 set mvx to 0.0
102 set mvy to -1.0
103 elseif ( ma == 180.0 )
104 set mvx to -1.0
105 set mvy to 0.0
106 elseif ( ma == 270.0 )
107 set mvx to 0.0
108 set mvy to 1.0
109 else
110 ;Use Rotation Matrix on above unit vectors to properly align unit vector
111 ; R(theta) = [ cos -sin]
112 ; [ sin cos]
113
114 ;Series expansion of cos and sin
115 ;Angle x is in radians
116 ;sin(x) = x - x^3/6 + x^5/120 +O(x^7)
117 ;cos(x) = 1-x^2/2 + x^4/24 - x^6/720 + O(x^7)
118 ;these are best at angles -70 >= x <= 70
119 ; sin is easier to calculate
120
121 ;Also note that in-game axes are opposite the normal
122 ;convention, so the angles will be neg
123
124 ;To be sure that this is a unit vector
125 ;Only rotate 1 component then use the relationship
126 ;sin^2(theta) + cos^2(theta) = 1 to find other compenent
127 ;The above never returns a neg value, so the sign of the 2nd component
128 ;has to changed in the proper quadrants
129 if ( ma <= 45.0 ) ;Use 0 unit vector
130 ;set mvx to 1.0
131 ;set mvy to 0.0
132 ;convert ma to radians and change sign
133 set t1 to ( ma * -0.0174533 )
134 set mvy to t1 - ( t1 * t1 * t1 / 6.0 )
135 set mvy to mvy + ( t1 * t1 * t1 * t1 * t1 / 120.0 )
136 ; the following should not happen - but better to be safe
137 if ( mvy > 1.0 )
138 set mvy to 1.0
139 elseif ( mvy < -1.0 )
140 set mvy to -1.0
141 endif
142 set mvx to ( 1.0 - mvy * mvy )
143 set mvx to GetSquareRoot mvx
144 elseif ( ma <= 135.0 ) ;use 90 unit vector
145 ;set mvx to 0.0
146 ;set mvy to -1.0
147 set t1 to ( ( ma - 90.0 ) * -0.0174533 )
148 set mvx to t1 - ( t1 * t1 * t1 / 6.0 )
149 set mvx to mvx + ( t1 * t1 * t1 * t1 * t1 / 120.0 )
150 ; the following should not happen - but better to be safe
151 if ( mvx > 1.0 )
152 set mvx to 1.0
153 elseif ( mvx < -1.0 )
154 set mvx to -1.0
155 endif
156 set mvy to ( 1.0 - mvx * mvx )
157 set mvy to GetSquareRoot mvy
158 set mvy to ( -1.0 * mvy ) ;quadrant correction
159 elseif ( ma <= 225.0 ) ; use 180 unit vector
160 ;set mvx to -1.0
161 ;set mvy to 0.0
162 ;convert ma to radians and change sign
163 set t1 to ( ( ma - 180.0 ) * -0.0174533 )
164 set mvy to t1 - ( t1 * t1 * t1 / 6.0 )
165 set mvy to mvy + ( t1 * t1 * t1 * t1 * t1 / 120.0 )
166 set mvy to ( mvy * -1.0 )
167 ; the following should not happen - but better to be safe
168 if ( mvy > 1.0 )
169 set mvy to 1.0
170 elseif ( mvy < -1.0 )
171 set mvy to -1.0
172 endif
173 set mvx to ( 1.0 - mvy * mvy )
174 set mvx to GetSquareRoot mvx
175 set mvx to ( -1.0 * mvx ) ;quadrant correction
176 elseif ( ma <= 315.0 ) ; use 270 unit vector
177 ;set mvx to 0.0
178 ;set mvy to 1.0
179 set t1 to ( ma - 270.0 ) * -0.0174533
180 set mvx to t1 - ( t1 * t1 * t1 / 6.0 )
181 set mvx to mvx + ( t1 * t1 * t1 * t1 * t1 / 120.0 )
182 set mvx to mvx * -1.0
183 ; the following should not happen - but better to be safe
184 if ( mvx > 1.0 )
185 set mvx to 1.0
186 elseif ( mvx <-1.0 )
187 set mvx to -1.0
188 endif
189 set mvy to ( 1 - mvx * mvx )
190 set mvy to GetSquareRoot mvy
191 else ;Use 0 unit vector
192 ;set mvx to 1.0
193 ;set mvy to 0.0
194 ;convert ma to radians and change sign
195 set t1 to ( ma - 360 ) * -0.0174533
196 set mvy to t1 - ( t1 * t1 * t1 / 6.0 )
197 set mvy to mvy + (t1 * t1 * t1 * t1 * t1 / 120.0 )
198 ; the following should not happen - but better to be safe
199 if ( mvy > 1.0 )
200 set mvy to 1.0
201 elseif ( mvy <-1.0 )
202 set mvy to -1.0
203 endif
204 set mvx to ( 1 - mvy * mvy )
205 set mvx to GetSquareRoot mvx
206 endif
207 endif
208 set tasks to 1
209 set timer to 0
210 Return
211elseif ( tasks == 1 )
212 if ( GetDistance Player < dis_limit )
213 ;2d vector from skull to player
214 set pvx to ( ( "Player"->getPos X ) - mx )
215 set pvy to ( ( "Player"->getPos Y ) - my )
216 ;normalize vector
217 set t1 to ( pvx * pvx + pvy * pvy )
218 set t1 to GetSquareRoot t1
219 set pvx to pvx / t1
220 set pvy to pvy / t1
221
222 ;calc angle between 2 vectors (or lines)
223 ;arc_cos(a.b/|a|*|b|)
224 ;vectors a and b are unit vecors, only need to calculate the dot product
225 set t1 to ( mvx * pvx + mvy * pvy )
226 ;the Maclaurin series of the arc cos function is
227 ;arc cos(x) = pi/2 -(x + x^3/6 + 3*x^5/40 +...)
228 set theta to 1.570796 - t1 - ( ( t1 * t1 * t1 ) / 6.0 )
229 set theta to theta - ( 3 * ( t1 * t1 * t1 * t1 * t1 ) / 40.0 )
230 set theta to theta * 57.296 ;convert to degrees
231 set theta to theta - 90.0 ;remove 90 degrees
232
233 ;get current angle of skull
234 set ca to GetAngle, Z
235 set ca to ca - ma ;how much has skull already rotated
236 set t1 to theta - ca ;how much skull needs to rotate
237 if ( t1 > 0.0 )
238 if ( ca < ang_limit )
239 if ( t1 > rot_limit )
240 set ca to ca + rot_limit
241 else
242 set ca to ca + t1
243 endif
244 endif
245 set ca to ca + ma ;add back original angle
246 SetAngle Z ca
247 elseif ( t1 < 0.0 )
248 if ( ca > ( -1.0 * ang_limit ) )
249 if ( t1 < ( -1.0 * rot_limit ) )
250 set ca to ca - rot_limit
251 else
252 set ca to ca + t1
253 endif
254 endif
255 set ca to ca + ma
256 SetAngle Z ca
257 endif
258 else
259 ;player is too far away - if skull is rotated - rotate it back to original position
260 ;get current angle of skull
261 set ca to GetAngle, Z
262 set t1 to ( ca - ma )
263 if ( t1 > 0.0 )
264 if ( t1 > rot_limit )
265 set ca to ca - rot_limit
266 else
267 set ca to ca - t1
268 endif
269 SetAngle Z ca
270 elseif ( t1 < 0.0 )
271 if ( t1 < ( -1.0 * rot_limit ) )
272 set ca to ca + rot_limit
273 else
274 set ca to ca - t1 ;t1 is already negative
275 endif
276 SetAngle Z ca
277 endif
278 endif
279 set timer to 0
280endif
281
282end TR_m3-725_RotZ_Object_60